diff --git a/.cursor/agents/coder.md b/.cursor/agents/coder.md new file mode 100644 index 000000000..8e2381233 --- /dev/null +++ b/.cursor/agents/coder.md @@ -0,0 +1,7 @@ +--- +name: coder +model: claude-4.6-sonnet-medium +description: Use this agent to implement coding tasks by provided specs only when it's explicitly specified +--- + +Ты профессиональный разработчик ПО высокого уровня. Тебе поступает на вход файл со спецификацией задачи. Реализуй эту задачу. \ No newline at end of file diff --git a/.cursor/agents/planner.md b/.cursor/agents/planner.md new file mode 100644 index 000000000..ba817997c --- /dev/null +++ b/.cursor/agents/planner.md @@ -0,0 +1,25 @@ +--- +name: planner +model: gemini-3-flash +description: Use this agent only when explicitly specified to sequentially run coding subagents when you have numbered list of tasks +readonly: true +--- + +На вход тебе подается каталог, в котором лежат файлы .md со списком задач и файл main-task со спецификацией задачи в целом. +Файлы подзадач пронумерованы префиксом вида 01, 02 и т.д. + +Твоя задача - запустить субагент сoder, передав ему очередную задачу по порядку, дождаться изменений от субагента и запустить новый субагент, уже со следующей задачей из перечня. Ты сам не должен писать код, ты находишься в Ask Mode, твоя задача запускать субагент coder, передавая ему очередную задачу (файл с описанием) + +И так до тех пор, пока перечень задач не будет выполнен целиком. + +Если какая-то задача не может быть выполнена и субагент выдает ошибку или вопрос - прерывай цикл выполнения, задай мне вопрос или сообщи текст ошибки и жди дальнейших инструкций. + +Итого, твои действия: + +1. изучить конечную цель (файл main-task) +2. взять файл с описанием задачи 01-* и сформулировать задачу агенту coder по одному и только одному файлу плана. +3. запустить агент coder, передав ему весь нужный контекст +4. получить ответ от агента coder и если он выполнил задачу успешно, перейти к следующему файлу с задачей (02-*) +5. если агент не смог выполнить задачу - прервать цикл +6. продолжать брать следующие по порядку задачи и снова запускать агент coder, передавая ему только одну задачу из файлов-планов по порядку +7. когда все файлы задач будут реализованы - завершиться. \ No newline at end of file diff --git a/.cursor/rules/langversion.mdc b/.cursor/rules/langversion.mdc new file mode 100644 index 000000000..d91672a00 --- /dev/null +++ b/.cursor/rules/langversion.mdc @@ -0,0 +1,5 @@ +--- +alwaysApply: true +--- + +Проект использует .net 8.0 и LangVersion 8, поэтому при генерации кода не используй языковые фичи C#, которые отсутствуют в C# 8. diff --git a/.cursor/rules/runbsltests.mdc b/.cursor/rules/runbsltests.mdc new file mode 100644 index 000000000..37287eab7 --- /dev/null +++ b/.cursor/rules/runbsltests.mdc @@ -0,0 +1,34 @@ +--- +description: When you need to run main tests +alwaysApply: false +--- + +Когда тебе потребуется запустить приемочные тесты всего проекта, убедиться что ничего не сломано, тебе потребуется запустить bsl тесты. Для этого используются готовые скрипты: + +1. **ОБЯЗАТЕЛЬНО** собери консольное приложение oscript из исходников с последними изменениями: + + ``` + dotnet build src/oscript/oscript.csproj + ``` + + После сборки движок будет находиться здесь: + - Windows: `src\oscript\bin\Debug\net8.0\oscript.exe` + - Linux/macOS: `src/oscript/bin/Debug/net8.0/oscript` + + **КРИТИЧЕСКИ ВАЖНО**: Тесты должны запускаться только на свежесобранном oscript, а не на версии из системы! Это гарантирует, что тесты валидируют актуальный код после внесённых изменений. + +2. Запусти нужный скрипт, **ЯВНО** передав путь к свежесобранному движку: + - Windows: `tests\run-bsl-tests.cmd src\oscript\bin\Debug\net8.0\oscript.exe` + - Linux/macOS: `tests/run-bsl-tests.sh src/oscript/bin/Debug/net8.0/oscript` + + **ВАЖНО**: Не полагайся на автоматический поиск oscript в PATH — всегда указывай путь к свежесобранному бинарнику первым параметром! + + Оба скрипта автоматически: + - Устанавливают путь к библиотекам OVM через переменную `OVM_LIB_PATH` (по умолчанию `%USERPROFILE%\AppData\Local\ovm\current\lib` для Windows и `$HOME/.local/share/ovm/current/lib` для Linux/macOS) + - Настраивают `OSCRIPT_CONFIG=lib.system=`, если эта переменная не была задана заранее + - Переходят в каталог `tests` и выполняют `testrunner.os -runAll .` + +3. Для запуска одного теста также используй свежесобранный oscript.exe: `<путь\к\собранному\oscript.exe> testrunner.os -run <имя файла теста.os>`. +4. Всегда используй для запуска тестов новую консоль, не переиспользуй консоль от предыдущих запусков. +5. Если ошибка запуска тестов связана с Component.dll или NativeApi (эти dll упоминаются в ошибках тестов), то такие тесты следует игнорировать и смотреть только на другие ошибки тестов. +6. Ошибки теста http.os и "Тест_ДолженПроверитьЧтоПеремещениеФайлаРаботаетПоHTTP" следует игнорировать. \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 065265706..491571d38 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ -*.sh text eol=lf \ No newline at end of file +*.sh text eol=lf +install/opm text eol=lf +install/oscript text eol=lf +#*.zip filter=lfs diff=lfs merge=lfs -text diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index b9644c37b..9808531a8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,20 +4,6 @@ about: Предложить идею для проекта --- - - # Описание задачи **Опишите вашу Цель, которую вы сможете достичь с помощью новой функциональности** diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..96a141791 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,13 @@ +Команда запуска полной сборки и прогона тестов на Linux: + +```sh +dotnet msbuild Build.csproj /t:"CleanAll;MakeFDD;GatherLibrary;ComposeDistributionFolders;Test" /p:Configuration=LinuxDebug /p:NoCppCompiler=True +``` + +Запуск приемочных тестов: + +```sh +dotnet oscript.dll tests/testrunner.os -runAll tests +``` + +ВСЕГДА проверяй изменения перед коммитом. Не коммить бинарные файлы (exe, ospx, и другие) если тебя об этом явно не попросили. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..8331a54b4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + diff --git a/.github/logo-small-2.png b/.github/logo-small-2.png new file mode 100644 index 000000000..073ed01fe Binary files /dev/null and b/.github/logo-small-2.png differ diff --git a/.github/logo-small.png b/.github/logo-small.png new file mode 100644 index 000000000..6dc83fca0 Binary files /dev/null and b/.github/logo-small.png differ diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..a53c8d0ab --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,37 @@ +name: "OneScript Development Environment Setup" + +# Allow testing of the setup steps from your repository's "Actions" tab. +on: workflow_dispatch + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + # Set the permissions to the lowest permissions possible needed for your steps. + # Copilot will be given its own token for its operations. + permissions: + # Clone the repository to install dependencies + contents: read + + # Setup steps for OneScript development environment + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - run: git config --global core.quotepath false + + - uses: actions/setup-dotnet@v5 + with: + dotnet-version: '8.0.x' + + - name: Установка OneScript + uses: otymko/setup-onescript@v1.5 + with: + version: stable + + - name: Установка зависимостей + run: | + opm install opm + opm update --all + diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml new file mode 100644 index 000000000..2902fe38c --- /dev/null +++ b/.github/workflows/rebase.yml @@ -0,0 +1,19 @@ +name: Automatic Rebase +on: + issue_comment: + types: [created] +jobs: + rebase: + name: Rebase + if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') + runs-on: ubuntu-latest + steps: + - name: Checkout the latest code + uses: actions/checkout@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 # otherwise, you will fail to push refs to dest repo + - name: Automatic Rebase + uses: cirrus-actions/rebase@1.8 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml new file mode 100644 index 000000000..f41bd6bde --- /dev/null +++ b/.github/workflows/sonar.yml @@ -0,0 +1,69 @@ +name: QA + +on: + push: + pull_request: + +jobs: + sonar: + name: SonarQube + runs-on: windows-latest + if: (github.repository == 'EvilBeaver/OneScript' ) && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.event.repository.full_name) + steps: + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + java-version: 17 + distribution: temurin + - uses: actions/checkout@v6 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarQube packages + uses: actions/cache@v5 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache SonarQube scanner + id: cache-sonar-scanner + uses: actions/cache@v5 + with: + path: .\.sonar\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + - name: Install SonarQube scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\.sonar\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner + - name: Install coverage tool + shell: powershell + run: | + dotnet tool install --global dotnet-coverage + - name: Compute branch name + uses: nelonoel/branch-name@v1.0.1 + - name: Prepare analysis (branch) + if: github.event_name == 'push' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + shell: powershell + run: | + .\.sonar\scanner\dotnet-sonarscanner begin /k:"OneScript" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.branch.name=${{ env.BRANCH_NAME }} /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonar.openbsl.ru" + - name: Prepare analysis (pull-request) + if: github.event_name == 'pull_request' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + shell: powershell + run: | + .\.sonar\scanner\dotnet-sonarscanner begin /k:"OneScript" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.pullrequest.key=${{ github.event.pull_request.number }} /d:sonar.pullrequest.branch=${{ github.event.pull_request.head.ref }} /d:sonar.pullrequest.base=${{ github.event.pull_request.base.ref }} /d:sonar.scm.revision=${{ github.event.pull_request.head.sha }} /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonar.openbsl.ru" + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + shell: powershell + run: | + dotnet publish src/oscript/oscript.csproj + dotnet-coverage collect "dotnet msbuild Build_Core.csproj -t:UnitTests" -f xml -o "coverage.xml" + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" diff --git a/.gitignore b/.gitignore index df68528e7..23abe6c72 100644 --- a/.gitignore +++ b/.gitignore @@ -1,68 +1,68 @@ -.vs/ -.idea/ -.DS_Store -.vscode/ -tmp/ - -*.vssscc -*.vspscc - -*.suo -*.user -*.sln.iml - -/src/**/[Bb]in/ -/src/**/[Oo]bj/ -[Dd]ebug -[Rr]elease -/*.log - -/src/**/*.exe -/src/**/*.dll -!/src/packages/**/*.dll -*_i.c -*_p.c -*_i.h -*.ilk -*.tlb - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user -JetBrains.ReSharper.CommandLineTools - -# CI and full build|deploy -built/ -*.os.xml -/src/packages/DotNetZip.1.9.3/lib/net20 - -# Snegopat -/src/ScriptEngine.Snegopat/dlldata.c - -# Snegopat autogenerated interfaces -src/ScriptEngine.Snegopat/Snegopat_h.h - -# Песональный вспомагательный скрипт -build.user.bat -src/packages/ -src/DebugServer/node_modules/ -# Visual Studio OpenCover and Test result -src/OpenCover -TestResult.xml -tests/component/Component.dll - -src/oscript/Properties/launchSettings\.json - -*.DotSettings +.vs/ +.idea/ +.DS_Store +tmp/ + +*.vssscc +*.vspscc + +*.suo +*.user +*.sln.iml + +/src/**/[Bb]in/ +/src/**/[Oo]bj/ +[Dd]ebug +[Rr]elease +/*.log + +/src/**/*.exe +/src/**/*.dll +!/src/packages/**/*.dll +*_i.c +*_p.c +*_i.h +*.ilk +*.tlb + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user +JetBrains.ReSharper.CommandLineTools + +# CI and full build|deploy +built/ +*.os.xml +/src/packages/DotNetZip.1.9.3/lib/net20 + +# Snegopat +/src/ScriptEngine.Snegopat/dlldata.c + +# Snegopat autogenerated interfaces +src/ScriptEngine.Snegopat/Snegopat_h.h + +# Песональный вспомагательный скрипт +build.user.bat +src/packages/ +src/DebugServer/node_modules/ +# Visual Studio OpenCover and Test result +src/OpenCover +TestResult.xml +tests/component/Component.dll +tests/tests.xml + +src/oscript/Properties/launchSettings\.json + +*.DotSettings diff --git a/.travis.yml b/.travis.yml index 6f963bc02..ff391af44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: required dotnet: 2.0.0 solution: src/1Script_Mono.sln install: - - wget oscript.io/Downloads/latest/deb && sudo dpkg -i deb + - wget oscript.io/Downloads/latest/x64/deb && sudo dpkg -i deb - sudo opm install oscript-config - sudo opm install logos - sudo opm install asserts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..1c5256771 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dotnet.defaultSolution": "src/1Script.sln" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1993bfcb8..0ce0df558 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,24 +1,20 @@ { - "version": "0.1.0", + "version": "2.0.0", "command": "oscript", - "isShellCommand": true, - "showOutput": "silent", "args": [ "-encoding=utf-8" ], "tasks": [ { - "taskName": "Testing project", + "label": "Testing project", + "type": "shell", + "command": "oscript", "args": [ + "-encoding=utf-8", "${workspaceRoot}/tests/testrunner.os", "-runall", "${workspaceRoot}/tests" ], - "echoCommand": true, - "showOutput": "always", - "suppressTaskName": true, - // "isBuildCommand": false, - "isTestCommand": false, "problemMatcher": { "fileLocation": "absolute", "pattern": { @@ -30,17 +26,15 @@ } }, { - "taskName": "Test current test-file", + "label": "Test current test-file", + "type": "shell", + "command": "oscript", "args": [ + "-encoding=utf-8", "${workspaceRoot}/tests/testrunner.os", "-run", "${file}" ], - "echoCommand": true, - "showOutput": "always", - "suppressTaskName": true, - // "isBuildCommand": false, - "isTestCommand": false, "problemMatcher": { "fileLocation": "absolute", "pattern": { @@ -52,48 +46,47 @@ } }, { - "taskName": "OneScript: compile", + "label": "OneScript: compile", + "type": "shell", + "command": "oscript", "args": [ + "-encoding=utf-8", "-compile", "${file}" ], - "echoCommand": true, - "showOutput": "always", - "suppressTaskName": true, - "isBuildCommand": false + "problemMatcher": [] }, { - "taskName": "OneScript: check", + "label": "OneScript: check", + "type": "shell", + "command": "oscript", "args": [ + "-encoding=utf-8", "-check", "${file}" ], - "echoCommand": true, - "showOutput": "always", - "suppressTaskName": true, - "isBuildCommand": false + "problemMatcher": [] }, { - "taskName": "OneScript: make", + "label": "OneScript: make", + "type": "shell", + "command": "oscript", "args": [ + "-encoding=utf-8", "-make", "${file}", "${fileBasename}.exe" ], - "echoCommand": true, - "showOutput": "always", - "suppressTaskName": true, - "isBuildCommand": false + "problemMatcher": [] }, { - "taskName": "OneScript: run", + "label": "OneScript: run", + "type": "shell", + "command": "oscript", "args": [ + "-encoding=utf-8", "${file}" ], - "echoCommand": true, - "showOutput": "always", - "suppressTaskName": true, - "isBuildCommand": true, "problemMatcher": { "fileLocation": "absolute", "pattern": { @@ -102,7 +95,8 @@ "location": 2, "message": 3 } - } + }, + "group": "build" } ] } \ No newline at end of file diff --git a/Build.csproj b/Build.csproj index e1cc01b2e..76f6f506b 100644 --- a/Build.csproj +++ b/Build.csproj @@ -1,27 +1,45 @@ - - - 1.1.0 + 2.0.0 + + $(VersionPrefix) + $(VersionPrefix)-$(VersionSuffix) + $(BUILD_NUMBER) 0 $(MSBuildProjectDirectory)/built - $(MSBuildProjectDirectory)/src/1Script.sln + $(ArtifactsRoot)/lib + $(ArtifactsRoot)\vscode\ Release + $(MSBuildProjectDirectory)/src/1Script.sln + + net8.0 + x86 - bin32 + Win32 - Any CPU - bin + "Any CPU" + x64 - + + + + oscript.csproj + $(ActiveFramework) + + + TestApp.csproj + $(ActiveFramework)-windows + + + @@ -29,79 +47,100 @@ - + - - - - - - + - - - - - - + + + + + + - + + + + + + + + - $(ArtifactsRoot)/tmp - $(TempFolder)/lib - $(TempFolder)/bin - $(TempFolder)/examples - $(TempFolder)/doc - $(ArtifactsRoot)\vscode\ - $(ArtifactsRoot)/mddoc + $(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration) - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + - - - - $(TempFolder)/bin - - - $(TempFolder)/bin32 - - + + + + + + + + + + + $(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration) + - - - $(TempFolder)/bin - - - $(TempFolder)/bin32 - + + - - - - - + + + + + + + + + + + + + + + - $(MSBuildProjectDirectory)/src/VSCode.DebugAdapter/bin/$(Configuration)/net452 + src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj + $(DebugAdapterDir)/tmp + + + @@ -111,140 +150,258 @@ - - - + + + + + + + + + + + + + + + - + - - + + + + + + + + + + FirstItem = Items.First().ItemSpec; + + + + + - - - + + + - - + + + + - - - - - - + + - - + + + + + opm\src\cmd\opm.os + dotnet $(Runner) "$(LibDir)\$(OpmEntryPoint)" + + - + + + + + + - + + + - - + - + + + + + + + + - - + + + + + + + + + + + + + + - - - - + + + + + + + - - - - - - + + + - - + + + $(MSBuildProjectDirectory)/src/Tests + - - - - + + + + - + + + + + + + + + - - - - - "$(InnoSetupPath)\iscc.exe" - $(ArtifactsRoot) - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - - + + - + + --version-suffix $(VersionSuffix) + + + - - + + + + + + + - - - - - - - - - - - - - - - - - - - - + + - - + + + + $(ArtifactsRoot)/win-x64/bin + $(ArtifactsRoot)/docs + $(ArtifactsRoot)/tools/documenter + - - - - - + + + + + + + - - + + + + + + + diff --git a/Jenkinsfile b/Jenkinsfile index 9277da364..706ed496d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,32 +4,19 @@ pipeline { agent none environment { - ReleaseNumber = '1.5.0' + VersionPrefix = '2.0.2' + VersionSuffix = 'rc.1'+"+${BUILD_NUMBER}" outputEnc = '65001' } stages { stage('Build'){ parallel { - stage('Prepare Linux Environment') { - agent{ label 'master'} - steps{ - dir('install'){ - sh 'chmod +x make-dockers.sh && ./make-dockers.sh' - } - withCredentials([usernamePassword(credentialsId: 'docker-hub', passwordVariable: 'dockerpassword', usernameVariable: 'dockeruser')]) { - sh """ - docker login -p $dockerpassword -u $dockeruser - docker push oscript/onescript-builder:deb - docker push oscript/onescript-builder:rpm - """.stripIndent() - } - } - } - stage('Windows Build') { agent { label 'windows' } + options { skipDefaultCheckout() } + // пути к инструментам доступны только когда // нода уже определена environment { @@ -57,10 +44,37 @@ pipeline { bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" src/1Script.sln /t:restore && mkdir doctool" bat "chcp $outputEnc > nul\r\n dotnet publish src/OneScriptDocumenter/OneScriptDocumenter.csproj -c Release -o doctool" - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:CleanAll;PrepareDistributionContent /p:OneScriptDocumenter=\"%WORKSPACE%/doctool/OneScriptDocumenter.exe\"" + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:CleanAll;PrepareDistributionFiles;CreateNuget" - stash includes: 'tests, built/**', name: 'buildResults' + stash includes: 'built/**', name: 'buildResults' + stash includes: 'tests/native-api/bin*/*.dll', name: 'nativeApiTestsDll' + } + } + } + + stage('Linux Build') { + agent { + docker { + image 'oscript/onescript-builder:gcc' + label 'linux' + } + } + + steps { + sh 'mkdir -p built/tmp/na-proxy && mkdir -p built/tmp/na-tests' + dir('src/ScriptEngine.NativeApi') { + sh './build.sh' + sh 'cp *.so ../../built/tmp/na-proxy' + } + dir('tests/native-api') { + sh './build.sh' + sh 'cp *.so ../../built/tmp/na-tests' + } + dir('output') { + sh 'cp -Rv ../built/tmp/* .' } + stash includes: 'output/na-proxy/*.so', name: 'nativeApiSo' + stash includes: 'output/na-tests/*.so', name: 'nativeApiTestsSo' } } } @@ -68,7 +82,7 @@ pipeline { stage('VSCode debugger Build') { agent { docker { - image 'node' + image 'node:lts-alpine3.20' label 'linux' } } @@ -89,36 +103,51 @@ pipeline { parallel{ stage('Windows testing') { agent { label 'windows' } - + options { skipDefaultCheckout() } + environment { + OSCRIPT_CONFIG = 'systemlanguage=ru' + } steps { ws(env.WORKSPACE.replaceAll("%", "_").replaceAll(/(-[^-]+$)/, "")) { + step([$class: 'WsCleanup']) + checkout scm + dir('install/build'){ deleteDir() } unstash 'buildResults' - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:xUnitTest" + unstash 'nativeApiTestsDll' + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:Test" - junit 'tests/tests.xml' + junit 'tests/*.xml' } } } stage('Linux testing') { - agent{ - docker{ - image 'evilbeaver/mono-ru:5.4' - label 'master' + agent{ + docker { + image 'mcr.microsoft.com/dotnet/sdk:6.0' + label 'linux' } } + environment { + OSCRIPT_CONFIG = 'systemlanguage=ru' + } steps { - dir('install/build'){ + dir('built'){ deleteDir() } unstash 'buildResults' + unstash 'nativeApiSo' + unstash 'nativeApiTestsSo' + + sh 'cp output/na-proxy/*.so ./built/linux-x64/bin/' + sh 'mkdir -p tests/native-api/build64 && cp output/na-tests/*.so ./tests/native-api/build64/' sh '''\ if [ ! -d lintests ]; then @@ -126,7 +155,7 @@ pipeline { fi rm lintests/*.xml -f cd tests - mono ../built/tmp/bin/oscript.exe testrunner.os -runall . xddReportPath ../lintests || true + dotnet ../built/linux-x64/bin/oscript.dll testrunner.os -runall . xddReportPath ../lintests || true exit 0 '''.stripIndent() @@ -138,157 +167,231 @@ pipeline { } stage('Packaging') { - parallel { - stage('Windows distribution'){ - agent { label 'windows' } - - environment { - InnoSetupPath = "${tool 'InnoSetup'}" - } - - steps { - ws(env.WORKSPACE.replaceAll("%", "_").replaceAll(/(-[^-]+$)/, "")) - { - dir('built'){ - deleteDir() - } - - unstash 'buildResults' - script - { - if (env.BRANCH_NAME == "preview") { - echo 'Building preview' - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:CreateDistributions /p:Suffix=-pre%BUILD_NUMBER%" - } - else{ - bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:CreateDistributions" - } - } - archiveArtifacts artifacts: 'built/**', fingerprint: true - stash includes: 'built/**', name: 'winDist' - } - } - } - - stage('DEB distribution') { - agent { - docker { - image 'oscript/onescript-builder:deb' - label 'master' - } - } - - steps { - unstash 'buildResults' - sh '/bld/build.sh' - archiveArtifacts artifacts: 'out/deb/*', fingerprint: true - stash includes: 'out/deb/*', name: 'debian' - } - } + agent { label 'windows' } - stage('RPM distribution') { - agent { - docker { - image 'oscript/onescript-builder:rpm' - label 'master' - } - } + options { skipDefaultCheckout() } - steps { - unstash 'buildResults' - sh '/bld/build.sh' - archiveArtifacts artifacts: 'out/rpm/*', fingerprint: true - stash includes: 'out/rpm/*', name: 'redhat' + steps { + ws(env.WORKSPACE.replaceAll("%", "_").replaceAll(/(-[^-]+$)/, "")) + { + step([$class: 'WsCleanup']) + checkout scm + + dir('built'){ + deleteDir() } + + unstash 'buildResults' + unstash 'nativeApiSo' + + bat ''' + chcp 65001 > nul + dir output\\na-proxy + xcopy output\\na-proxy\\*64.so built\\linux-x64\\bin\\ /F + '''.stripIndent() + + bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PackDistributions" + + archiveArtifacts artifacts: 'built/**', fingerprint: true + stash includes: 'built/**', name: 'dist' } } } stage ('Publishing night-build') { - when { anyOf { - branch 'develop'; - branch 'release/*' - } - } - + when { + anyOf { + branch 'develop'; + } + } agent { label 'master' } + options { skipDefaultCheckout() } steps { + cleanWs() - unstash 'winDist' - unstash 'debian' - unstash 'redhat' + unstash 'dist' unstash 'vsix' - dir('targetContent') { - sh ''' - WIN=../built - DEB=../out/deb - RPM=../out/rpm - mkdir x64 - mv $WIN/OneScript*-x64*.exe x64/ - mv $WIN/OneScript*-x64*.zip x64/ - mv $WIN/vscode/*.vsix x64/ - mv $WIN/OneScript*-x86*.exe ./ - mv $WIN/OneScript*-x86*.zip ./ - mv $RPM/*.rpm x64/ - mv $DEB/*.deb x64/ - TARGET="/var/www/oscript.io/download/versions/night-build/" - sudo rsync -rv --delete --exclude mddoc*.zip --exclude *.src.rpm . $TARGET - '''.stripIndent() - } + publishRelease('night-build', false) } } + + stage ('Publishing preview') { + when { + anyOf { + branch 'release/preview'; + } + } + agent { label 'master' } + options { skipDefaultCheckout() } + + steps { + cleanWs() + checkout scm // чтобы получить файл release-notes + unstash 'dist' + unstash 'vsix' - stage ('Publishing master') { - when { branch 'master' } + // Положит описание для сайта + publishReleaseNotes('preview') + // Положит файлы дистрибутива в целевую папку + publishRelease('preview', true) + } + } + + stage ('Publishing latest') { + when { + anyOf { + branch 'release/latest'; + } + } agent { label 'master' } - + options { skipDefaultCheckout() } + steps { - - unstash 'winDist' - unstash 'debian' - unstash 'redhat' + cleanWs() + checkout scm // чтобы получить файл release-notes + unstash 'dist' unstash 'vsix' - - dir('targetContent') { - - sh ''' - WIN=../built - DEB=../out/deb - RPM=../out/rpm - mkdir x64 - mv $WIN/OneScript*-x64*.exe x64/ - mv $WIN/OneScript*-x64*.zip x64/ - mv $WIN/vscode/*.vsix x64/ - mv $WIN/OneScript*-x86*.exe ./ - mv $WIN/OneScript*-x86*.zip ./ - mv $RPM/*.rpm x64/ - mv $DEB/*.deb x64/ - TARGET="/var/www/oscript.io/download/versions/latest/" - sudo rsync -rv --delete --exclude mddoc*.zip --exclude *.src.rpm . $TARGET - '''.stripIndent() - - sh """ - TARGET="/var/www/oscript.io/download/versions/${ReleaseNumber.replace('.', '_')}/" - sudo rsync -rv --delete --exclude mddoc*.zip --exclude *.src.rpm . \$TARGET - """.stripIndent() - } + + // Положит описание для сайта + publishReleaseNotes('latest') + + // Положит файлы дистрибутива в целевую папку + publishRelease('latest', true) } } + + stage ('Publishing artifacts to clouds') { + when { + anyOf { + branch 'release/latest'; + branch 'release/preview'; + } + } - stage ('Publishing artifacts to clouds'){ - when { branch 'master' } agent { label 'windows' } steps{ - unstash 'winDist' + + unstash 'buildResults' + withCredentials([string(credentialsId: 'NuGetToken', variable: 'NUGET_TOKEN')]) { bat "chcp $outputEnc > nul\r\n\"${tool 'MSBuild'}\" Build.csproj /t:PublishNuget /p:NugetToken=$NUGET_TOKEN" } } } + + stage ('Publishing docker-images') { + parallel { + stage('Build v1') { + agent { label 'linux' } + when { + anyOf { + branch 'release/lts' + expression { + return env.TAG_NAME && env.TAG_NAME.startsWith('v1.') + } + } + } + steps { + script { + def codename = env.TAG_NAME ? env.TAG_NAME : 'lts' + publishDockerImage('v1', codename) + } + } + } + + stage('Build v2') { + agent { label 'linux' } + when { + anyOf { + branch 'develop' + branch 'release/latest' + } + } + steps { + script { + def codename = '' + if (env.VersionSuffix != null && !env.VersionSuffix.isEmpty()) { + codename = 'dev' + } + else + { + codename = fullVersionNumber() + } + + publishDockerImage('v2', codename) + } + } + } + } + } + } +} + +def fullVersionNumber() { + def version = env.VersionPrefix + if (env.VersionSuffix != null && !env.VersionSuffix.isEmpty()) + { + version = version + "-${env.VersionSuffix}" } -} \ No newline at end of file + return version +} + +def underscoredVersion() { + return fullVersionNumber().replaceAll("\\.", "_") +} + +def publishRelease(codename, isNumbered) { + dir('targetContent') { + sh """ + ZIPS=../built + NUGET=../built/nuget + VSIX=../built/vscode + mv \$ZIPS/*.zip ./ + mv \$VSIX/*.vsix ./ + + TARGET="/var/www/oscript.io/download/versions/${codename}/" + mkdir -p \$TARGET + sudo rsync -rv --delete --exclude mddoc*.zip --exclude *.src.rpm . \$TARGET + """.stripIndent() + + if (isNumbered) { + + def version = underscoredVersion() + + sh """ + TARGET="/var/www/oscript.io/download/versions/${version}/" + sudo mkdir -p \$TARGET + sudo rsync -rv --delete --exclude mddoc*.zip --exclude *.src.rpm . \$TARGET + """.stripIndent() + } + } +} + +def publishReleaseNotes(codename) { + dir('markdownContent') { + def version=underscoredVersion() + def targetDir='/var/www/oscript.io/markdown/versions' + + sh """ + cp ../install/release-notes.md "./${codename}.md" + cp ../install/release-notes.md "./${version}.md" + + sudo rsync -rv . ${targetDir} + """.stripIndent() + } +} + +def publishDockerImage(flavour, codename) { + def imageName = "evilbeaver/onescript:${codename}" + + docker.build( + imageName, + "--load -f install/builders/base-image/Dockerfile_${flavour} ." + ).push() +} + diff --git a/README-EN.md b/README-EN.md index be9cfc8fa..531111fc8 100644 --- a/README-EN.md +++ b/README-EN.md @@ -1,21 +1,106 @@ -# README # +# OneScript # -### The project is an independent cross-platform open-source implementation of a virtual machine that executes scripts written in BSL (1C:Enterprise scripting language). +[![Join telegram chat](https://img.shields.io/badge/chat-telegram-blue?style=flat&logo=telegram)](https://t.me/oscript_library) [![DEV Build Status](https://build.oscript.io/buildStatus/icon?job=1Script%2Fdevelop&style=flat-square&subject=dev)](https://build.oscript.io/job/1Script/job/develop/) [![STABLE Build Status](https://build.oscript.io/buildStatus/icon?job=1Script%2Fmaster&style=flat-square&subject=stable)](https://build.oscript.io/job/1Script/job/master/) -Script execution doesn't use any 1C:Enterprise system libraries and doesn't require installation of 1C:Enterprise system on the target machine. +## The project is an independent cross-platform implementation of a virtual machine that executes scripts written in the 1C:Enterprise language ## -In other words, this is a possibility to write programs in the 1C (BSL) language without using the 1C:Enterprise platform. +![Logo](.github/logo-small-2.png) ![Logo](.github/logo-small.png) -## Name and pronunciation +The 1C:Enterprise system libraries are not used and installation of the 1C:Enterprise system on the target machine is not required. -The project is called OneScript, can be abbreviated to the title 1Script on writing. +In other words, this is a tool for writing and executing programs in the 1C language without using the 1C:Enterprise platform. -## Project site +## Name and pronunciation ## -Basic information about the project, releases and technical documentation are located on the official website. +The project is called OneScript, can be abbreviated to the title 1Script when writing. Pronounced as `[uanskript]`. -http://oscript.io +OneScript allows you to create and execute text scripts written in a language familiar to any specialist working with the 1C:Enterprise system. Using a familiar language for script automation significantly increases a specialist's productivity by simplifying the automation of manual operations. -## Library of useful scripts +## Project site ## -The OneScript package already includes a set of commonly used packages. +Main information about the project, releases and technical documentation are located on the official website + +[https://oscript.io](https://oscript.io) + +## Library of useful scripts ## + +The OneScript distribution already includes a set of the most commonly used packages. These, as well as other packages, are located in the [oscript-library](https://github.com/oscript-library) repository and are available to everyone. There is a package manager [opm](https://github.com/oscript-library/opm). + +## Installation ## + +### Windows ### + +- (interactively) download from the [official website](https://oscript.io) or installer from the [Releases](https://github.com/EvilBeaver/OneScript/releases) section and run it. Then, Next, Done. + +### Linux ### + +- 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 + ``` + +### 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). +- Extract the archive to a convenient directory. +- Perform additional configuration to remove quarantine and sign the binary: + ```bash + chmod +x ./oscript + xattr -d com.apple.quarantine *.dylib oscript + codesign -s - ./oscript + ``` + + +# Manual local build + +## 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. + +* 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. + +## 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: + +* 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 + +**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 + +All distribution files will be placed in the `built` directory at the root of the 1Script repository + +### Building distribution contents in a separate directory + +```bat +msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles +``` + +### Building with manual version specification + +```bat +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 +``` + +### Documentation generation + +```bat +msbuild Build.csproj /t:BuildDocumentation +``` diff --git a/README.md b/README.md index b1004a379..959253f91 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ [![Join telegram chat](https://img.shields.io/badge/chat-telegram-blue?style=flat&logo=telegram)](https://t.me/oscript_library) [![DEV Build Status](https://build.oscript.io/buildStatus/icon?job=1Script%2Fdevelop&style=flat-square&subject=dev)](https://build.oscript.io/job/1Script/job/develop/) [![STABLE Build Status](https://build.oscript.io/buildStatus/icon?job=1Script%2Fmaster&style=flat-square&subject=stable)](https://build.oscript.io/job/1Script/job/master/) - ## Проект является независимой кросс-платформенной реализацией виртуальной машины, исполняющей скрипты на языке 1С:Предприятие ## +![Logo](.github/logo-small-2.png) ![Logo](.github/logo-small.png) + При этом библиотеки системы 1С:Предприятие не используются и не требуется установка системы 1С:Предприятие на целевой машине. Иными словами, это инструмент для написания и выполнения программ на языке 1С без использования платформы 1С:Предприятие. @@ -33,15 +34,73 @@ OneScript позволяет создавать и выполнять текст ### Linux ### -- (интерактивно) скачать нужный пакет [официального сайта](https://oscript.io) или установщик из раздела [Releases](https://github.com/EvilBeaver/OneScript/releases) и установить его. +- Скачать ZIP-архив для Linux со [страницы релизов](https://github.com/EvilBeaver/OneScript/releases) или с [официального сайта](https://oscript.io). +- Распаковать архив в удобный каталог. +- Установить права на выполнение: + ```bash + chmod +x oscript + ``` ### MacOS ### -Интерактивного установщика нет, но движок можно установить из командной строки: +- Скачать ZIP-архив для macOS (x64 или arm64) со [страницы релизов](https://github.com/EvilBeaver/OneScript/releases) или с [официального сайта](https://oscript.io). +- Распаковать архив в удобный каталог. +- Выполнить донастройку для снятия карантина и подписи: + ```bash + chmod +x ./oscript + xattr -d com.apple.quarantine *.dylib oscript + codesign -s - ./oscript + ``` + + +# Ручная локальная сборка + +## Подготовка + +Ниже приведены ссылки на дистрибутивы, однако, учтите, что ссылки могут меняться со временем и их актуальность не гарантируется. Нужен dotnet SDK и компилятор C++, скачать можно из любого места, которое нагуглится. + +* Установить [MS BuildTools](https://visualstudio.microsoft.com/ru/thank-you-downloading-visual-studio/?sku=buildtools&rel=16), при установке включить таргетинг на .net6, .net4.8, установить компилятор C++. + +## Сборка + +Запустить Developer Command Prompt (появится в меню Пуск после установки MSBuildTools или Visual Studio). Перейти в каталог репозитория OneScript. Далее приведены команды в консоли Developer Command Prompt +Сборка выполняется с помощью msbuild. Таргеты: + +* CleanAll - очистка результатов предыдущих сборок +* BuildAll - подготовить файлы для поставки +* MakeCPP;MakeFDD;MakeSCD;BuildDebugger - отдельные таргеты сборки для подготовки разных типов поставки +* PrepareDistributionFiles - сборка полных пакетов поставки (включая библиотеки) +* PackDistributions - подготовка ZIP архивов поставки +* CreateNuget - создать пакеты для публикации в NuGet + +**Параметры сборки** + +* VersionPrefix - префикс номера релиза, его основная часть, например, 2.0.0 +* VersionSuffix - суффикс номера, который обычно выступает в качестве произвольного суффикса версионирования по semver, например, beta-786 (необязателен) +* NoCppCompiler - если True - не установлен компилятор C++, в сборку не будут добавлены компоненты C++ (поддержка NativeApi) + +Все поставляемые файлы будут размещены в каталоге `built` в корне репозитория 1Script + +### Сборка содержимого дистрибутивов в отдельном каталоге + +```bat +msbuild Build.csproj /t:CleanAll;PrepareDistributionFiles +``` + +### Сборка с ручным указанием версии + +```bat +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 +``` + +### Генерация документации -- установить [homebrew](https://brew.sh/index_ru) -- установить mono командой `brew install mono` -- скачать [ovm](https://github.com/oscript-library/ovm/releases) -- выполнить команду `mono ovm.exe install stable` -- выполнить команду `mono ovm.exe use stable` -- перезапустить терминал +```bat +msbuild Build.csproj /t:BuildDocumentation +``` \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index fdc611d3f..871975189 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.3.0.{build} +version: 1.8.1.{build} image: Visual Studio 2019 init: - cmd: choco install msbuild.communitytasks @@ -7,7 +7,7 @@ init: - ps: Restart-Computer environment: - ReleaseNumber: 1.3.0 + ReleaseNumber: 2.0.3 InnoSetupPath: C:/Program Files (x86)/Inno Setup 6 StandardLibraryPacks: C:/Program Files (x86)/OneScript/lib @@ -21,7 +21,7 @@ build_script: msbuild src/1Script.sln /t:restore dotnet publish src\OneScriptDocumenter\OneScriptDocumenter.csproj -o doctool - msbuild Build.csproj /t:CleanAll;PrepareDistributionContent;CreateDistributions /p:ReleaseNumber=%ReleaseNumber% /p:InnoSetupPath="%InnoSetupPath%" /p:OneScriptDocumenter="doctool\OneScriptDocumenter.exe" + msbuild Build.csproj /t:CleanAll;PrepareDistributionContent /p:OneScriptDocumenter="doctool\OneScriptDocumenter.exe" test_script: - cmd: | diff --git a/docs/contexts.md b/docs/contexts.md new file mode 100644 index 000000000..69b55c98f --- /dev/null +++ b/docs/contexts.md @@ -0,0 +1,174 @@ +# BSL-контексты и глобальные методы: руководство разработчика + +Этот документ — практическая инструкция по добавлению в OneScript новых BSL‑контекстов (классов), методов и свойств, а также глобальных методов. Здесь собраны готовые сниппеты, чек‑лист и ссылки на ключевые места в исходниках. + +См. также «Архитектурный обзор»: docs/arhitecture_overview.md (карта компонентов и «куда лезть»). + +Содержание + +- Что такое BSL‑контекст +- Добавление нового BSL‑класса (контекста) +- Добавление свойства +- Добавление метода +- Создание глобального контекста и глобальных методов +- Регистрация библиотек и package‑loader.os +- i18n для API (двуязычные имена) +- Депрекейшен и предупреждения +- Тестирование (C# и BSL) +- Документация (OneScriptDocumenter) +- Безопасность +- Чек‑лист готовности + +1. Что такое BSL‑контекст + +- Контекст — это .NET‑класс, методы/свойства которого доступны из BSL. Экземпляр контекста может создаваться оператором Новый (класс‑контекст) или предоставляться глобально (глобальный контекст). +- Отражение и метаданные описываются атрибутами: + - [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 + +2. Добавление нового BSL‑класса (контекста) + +Минимальный шаблон + +```csharp +using OneScript.Core.Contexts; +using OneScript.Core.Types; +using OneScript.Core.Values; +using ScriptEngine; +using ScriptEngine.Machine.Contexts; // AutoContext +using OneScript.Core.Execution; // IBslProcess + +[ContextClass("ПримерКласс", "SampleClass")] +public class SampleClass : AutoContext +{ + // Конструктор для BSL: Новый ПримерКласс() + [ScriptConstructor(Name = "Без параметров")] + public static SampleClass Ctor(TypeActivationContext ctx) + => new SampleClass(); + + // Свойство только для чтения + [ContextProperty("Версия", "Version", CanWrite = false)] + public IValue Version => ValueFactory.Create("1.0"); + + // Процедура с доступом к bsl-процессу (возможность запускать свой код bsl из кода c#) + [ContextMethod("Сообщить", "Message")] + public void Message(IBslProcess process, IValue text) + { + // вызов bsl-метода в том же стеке вызовов, что и у переданного процесса + process.Run(/*...*/); + } + + // Функция с возвратом значения + [ContextMethod("Сложить", "Add")] + public IValue Add(IValue a, IValue b) + { + var sum = a.AsNumber() + b.AsNumber(); + return ValueFactory.Create(sum); + } +} +``` + +Комментарии к шаблону + +- Наследуемся от AutoContext — это стандартная база для классов‑контекстов. +- [ScriptConstructor] — статический фабричный метод, возможно, принимающий TypeActivationContext. Можно объявить несколько перегрузок. +- IBslProcess можно внедрять первым параметром метода, чтобы получить доступ к сервисам/окружению выполнения. +- Возвраты: + - Процедура — метод без возвращаемого значения (void). + - Функция — возвращает IValue или конвертируемый тип C# (см. ContextValuesMarshaller). + +Регистрация в движке + +- При старте ContextDiscoverer просканирует сборку и автоматически зарегистрирует в движке все классы, помеченные атрибутами ContextClass, GlobalContext, EnumerationType + +3. Добавление свойства + +Шаблон + +```csharp +[ContextProperty("Порог", "Threshold", CanRead = true, CanWrite = true)] +public IValue Threshold +{ + get => ValueFactory.Create(_threshold); + set => _threshold = value.AsNumber(); +} +private decimal _threshold = 0m; +``` + +Заметки + +- CanRead/CanWrite управляют доступностью геттера/сеттера из BSL, если не указаны, берутся наличия стандартных get/set у свойства. +- Маршаллинг значений свойства автоматический. + + +4. Добавление метода + +Шаблон процедуры и функции + +```csharp +// Процедура, изменяющая параметр по ссылке +[ContextMethod("УдвоитьЧисло", "DoubleNumber")] +public int DoubleNumber(int number) +{ + var doubled = number * 2; + return doubled; +} +``` + +Значения параметров и результат метода будут автоматически сконвертированы из типов C# в тиаы bsl. + +Заметки + +- Для передачи аргумента по ссылке: используйте тип IVariable — в него можно присвоить новое значение через .Value. +- По значению: используйте типы C# напрямую, если они поддерживаются маршаллером, или IValue. + +5. Создание глобального контекста и глобальных методов + +Глобальный контекст + +```csharp +using OneScript.Core.Contexts; +using OneScript.Core.Values; +using ScriptEngine.Machine.Contexts; + +[GlobalContext(Category = "Мои функции")] +public class MyGlobals : GlobalContextBase +{ + // Фабрика экземпляра для внедрения в глобальную область + public static IAttachableContext CreateInstance() => new MyGlobals(); + + [ContextMethod("МояФункция", "MyFunc")] + public IValue MyFunc(IValue x) + { + return ValueFactory.Create(x.ToString().Length); + } +} +``` + +Заметки + +- По умолчанию глобальные контексты регистрируются автоматически (ManualRegistration = false). Достаточно, чтобы сборка была добавлена в окружение. +- Вручную можно внедрить через HostedScriptEngine.InjectObject или IRuntimeEnvironment.InjectObject. + +Добавление метода в существующий глобальный контекст + +- Например, StandardGlobalContext: добавьте [ContextMethod] в соответствующий класс и реализуйте логику. +- Внимание: изменение публичного API стандартной библиотеки требует обсуждения с мэйнтейнерами. + +6. Регистрация библиотек и package‑loader.os + +- HostedScript ищет библиотеку и вызывает package‑loader.os (дефолтный или кастомный). +- Основные операции загрузчика (см. src/ScriptEngine.HostedScript/LibraryLoader.cs): + - ДобавитьКласс/AddClass("path", "ИмяКласса") — регистрирует новый BSL‑тип; + - ДобавитьМодуль/AddModule("path", "ИмяМодуля") — подключает модуль как глобальный; + - ДобавитьМакет/AddTemplate — регистрирует шаблон. \ No newline at end of file diff --git a/docs/developer_docs.md b/docs/developer_docs.md new file mode 100644 index 000000000..b3c38febe --- /dev/null +++ b/docs/developer_docs.md @@ -0,0 +1,201 @@ +# Developer Docs — архитектура и навигация по проекту OneScript (для новых контрибьюторов) + +Этот документ помогает быстро разобраться, что лежит в репозитории, зачем это нужно, как устроено внутри и как использовать. + +## 1 Картина целиком: из чего состоит OneScript + +OneScript — реализация языка, совместимого с синтаксисом 1С/BSL. В основе - стековая виртуальная машина. Проект включает реализацию компилятора, исполняющей среды, системы типов и стандартной библиотеки BSL. Поверх этого — инструменты (CLI, раннер), отладка (DAP), веб-обёртки и API для нативных расширений. + +OneScript — открытая реализация языка 1С/BSL поверх .NET, со стековой ВМ и стандартной библиотекой. Сценарии исполняются 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/ScriptEngine (стековая ВМ) + - src/OneScript.Native (нативный бэкенд). +- Ядро/язык: + - OneScript.Core (типы/контексты) — основные инфраструктурные компоненты для обоих рантаймов + - OneScript.Language (лексер/парсер/AST) +- Стандартная библиотека языка BSL: + - OneScript.StandardLibrary — реализация стандартных прикладных классов (массивы, работа с файлами, сетью и пр.) +- Интеграции: ScriptEngine.NativeApi (Мост к внешним компонентам на C++, совместимым с NativeApi 1С). +- Инструмент генерации автодокументации OneScriptDocumenter + +## 2 Быстрый старт для контрибьютора +- Где собирать: решение src/1Script.sln. +- Входной CLI: src/oscript (консольное приложение для запуска .os‑скриптов). +- Запуск тестов: проекты src/Tests/* (C#) и папка tests/* (скрипты .os). +- Если добавляете новый контекст/тип — обычно правки в OneScript.Core/ScriptEngine/StandardLibrary, плюс модульные тесты. + +Псевдонимы API приняты двуязычные: РусИмя/EngName (см. атрибуты ContextClass/Method/Property). + +Соглашение по ссылкам: указываются относительные пути репозитория. + +## 3 Обзор проектов (назначение, ключевые узлы) + +Ниже по каждому проекту — зачем он нужен, где искать основную логику и какие классы отвечают за ключевые задачи. + +### 3.1 OneScript.Language — лексер/препроцессор/парсер/AST + +Назначение: преобразует исходный 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. +- Жизненный цикл: + 1) Лексер производит Lexem с типом/токеном. + 2) Препроцессор обрабатывает директивы (#Если/#Область/#Использовать). + 3) Парсер строит AST (BslSyntaxNode), восстанавливается после ошибок (IErrorRecoveryStrategy). + 4) AST передаётся компилятору (CompilerFrontend) рантайма. +- Точки расширения: + - собственные директивы препроцессора (IDirectiveHandler -> зарегистрировать в DI); + - обход AST через BslSyntaxWalker. + +На выходе вы получаете ModuleNode/AST, пригодный для компиляции рантаймом/бэкендом или обработки статическим анализатором. + +### 3.2 OneScript.Core — система типов, значения, отражение контекстов +Назначение: общий объектный каркас значений BSL, контекстов (объекты/методы/свойства), аннотаций и исключений. + +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 и др. +- Жизненный цикл контекстов: + 1) ContextDiscoverer (ScriptEngine.Machine.Contexts) сканирует сборки, находит [ContextClass]/[GlobalContext]/[EnumerationType]/[SystemEnum]. + 2) Регистрирует типы/глобальные контексты в IRuntimeEnvironment/IGlobalsManager. + 3) Отражение формирует Bsl*Info для рантайма/документации. + +### 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 и обратно. +- Точки расширения: + - дополнительные IExecutorProvider (альтернативные рантаймы); + - предопределённые интерфейсы/итераторы (Interfaces/Iterables handlers); + - сбор кода-статистики (ICodeStatCollector) + +### 3.4 OneScript.Native — нативный бэкенд (Expression Trees) компилятор/исполнитель, встроенные функции + +Альтернативная среда исполнения (не основная). Предоставляет компиляцию BSL в ExpressionTrees фреймворка .NET. +Данная среда исполнения имеет ряд ограничений и в целом является экспериментом. В какой именно среде будет исполнен скрипт решает директива в начале скрипта: + +* `#native` - скрипт будет скомпилировать в Native Runtime +* `#stack` или отсутствие директивы - скрипт будет использован основной средой исполнения. + +Назначение: преобразует AST+символы в исполняемую форму и предоставляет нативный бэкенд выполнения, включая встроенные функции. +- Компиляция: Compiler/ + - ModuleCompiler.cs, MethodCompiler.cs — генерация модулей/методов. + - ExpressionTreeGeneratorBase.cs, BinaryOperationCompiler.cs — выражения/операции. + - BuiltInFunctionsCache.cs, ContextMethodsCache.cs — кеширование метаданных/встроенных функций. +- Исполнение: Runtime/ + - NativeExecutorProvider.cs — провайдер исполнителя. + - BuiltInFunctions.cs — реализации встроенных функций. + - DynamicOperations.cs — динамические операции. + +- Ограничения: может не поддерживать полный паритет со стековой машиной; выбор режима делает CompilerBackendSelector по соответствующим директивам в начале файла. + +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. +- Жизненный цикл: + 1) Читает настройки из системного `oscript.cfg`, файла `oscript.cfg` рядом с entrypoint, и переменной окружения OSCRIPT_CONFIG (в порядке возрастания приоритета, то есть переменная окружения перезаписывает все предыдущие настройки). + 2) Инициализация HostedScriptEngine → глобальные объекты → процесс → компиляция/исполнение модуля. + 3) Загрузка библиотек: default или кастомный package-loader.os, последующая регистрация символов и компиляция задержанных модулей. + +### 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) +Назначение: адаптеры поверх 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. +- Примеры/демо: + - Component — простая .NET‑библиотека/компонент и примеры использования. + - TestApp — WPF‑приложение‑песочница (подсветка синтаксиса, запуск модулей). + +### 3.10 ScriptEngine.NativeApi — C++ API для нативных расширений +- Основные файлы: 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 diff --git a/install/builders/base-image/Dockerfile.mono b/install/builders/base-image/Dockerfile.mono new file mode 100644 index 000000000..a0cc5e450 --- /dev/null +++ b/install/builders/base-image/Dockerfile.mono @@ -0,0 +1,17 @@ +FROM ubuntu:18.04 + +# MAINTAINER EvilBeaver + +ENV LANG ru_RU.UTF-8 + +RUN apt update \ + && apt-get -y install locales tzdata \ + && locale-gen ru_RU && locale-gen ru_RU.UTF-8 \ + && update-locale LANG=ru_RU.UTF-8 \ + && apt install -y gnupg ca-certificates \ + && apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \ + && sh -c 'echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" > /etc/apt/sources.list.d/mono-official-stable.list' \ + && apt-get update \ + && apt-get -y install mono-runtime binutils curl mono-devel ca-certificates-mono mono-vbnc referenceassemblies-pcl mono-fastcgi-server4 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* \ No newline at end of file diff --git a/install/builders/base-image/Dockerfile_ovm b/install/builders/base-image/Dockerfile_ovm new file mode 100644 index 000000000..e5f2c2797 --- /dev/null +++ b/install/builders/base-image/Dockerfile_ovm @@ -0,0 +1,10 @@ +FROM evilbeaver/mono-ru:6.12 +LABEL MAINTAINER="EvilBeaver " + +# Файл базируется на моно с русской локалью +# Устанавливает утилиту ovm +# Образ предназначен для создания других образов с установленным движком +# Использование: mono /var/ovm/ovm.exe use --install $VERSION + +RUN mkdir -p /var/ovm && \ + curl -L https://github.com/oscript-library/ovm/releases/latest/download/ovm.exe > /var/ovm/ovm.exe \ No newline at end of file diff --git a/install/builders/base-image/Dockerfile_v1 b/install/builders/base-image/Dockerfile_v1 new file mode 100644 index 000000000..a1cc0c9db --- /dev/null +++ b/install/builders/base-image/Dockerfile_v1 @@ -0,0 +1,14 @@ +FROM evilbeaver/mono-ru:6.12 + +# Файл базируется на моно с русской локалью +# Устанавливает через ovm версию движка + +LABEL MAINTAINER="EvilBeaver " + +ARG VERSION=stable + +RUN curl -L https://github.com/oscript-library/ovm/releases/latest/download/ovm.exe > ovm.exe \ + && mono ovm.exe use --install $VERSION + +ENV OSCRIPTBIN=/root/.local/share/ovm/current/bin +ENV PATH="$OSCRIPTBIN:$PATH" diff --git a/install/builders/base-image/Dockerfile_v2 b/install/builders/base-image/Dockerfile_v2 new file mode 100644 index 000000000..7412f7d47 --- /dev/null +++ b/install/builders/base-image/Dockerfile_v2 @@ -0,0 +1,39 @@ +FROM evilbeaver/ovm:latest + +# Использует в качестве системы с ovm и mono образ onescript, создаваемый из файла Dockerfile_ovm (лежит рядом в репо) +# Потом копирует бинари из него в образ от Microsoft + +LABEL MAINTAINER="EvilBeaver " + +ARG VERSION="dev" + +# Установка FDD двойки через ovm, имеющийся в базовом образе +RUN mono /var/ovm/ovm.exe install --fdd ${VERSION} + +# Основной образ +FROM mcr.microsoft.com/dotnet/aspnet:8.0-jammy + +RUN apt-get update \ + && apt-get install -y locales \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* + +# Locale +RUN sed -i -e \ + 's/# ru_RU.UTF-8 UTF-8/ru_RU.UTF-8 UTF-8/' /etc/locale.gen \ + && locale-gen + +ENV LANG=ru_RU.UTF-8 +ENV LANGUAGE=ru_RU:ru +ENV LC_LANG=ru_RU.UTF-8 +ENV LC_ALL=ru_RU.UTF-8 + +# +Timezone +ENV TZ=Europe/Moscow +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +ARG VERSION="dev" + +RUN mkdir -p /var/oscript +COPY --from=0 /root/.local/share/ovm/${VERSION}/ /var/oscript/ +ENV PATH="$PATH:/var/oscript/bin" \ No newline at end of file diff --git a/install/builders/deb/Dockerfile b/install/builders/deb/Dockerfile deleted file mode 100644 index f82abdd21..000000000 --- a/install/builders/deb/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM ubuntu:16.04 -MAINTAINER sergey.batanov@dmpas.ru - - -RUN apt-get update && apt-get install -y \ - dpkg\ - debconf\ - debhelper\ - lintian\ - md5deep\ - fakeroot \ - locales - -# чтобы запустить тесты -RUN locale-gen --lang ru_RU.UTF-8 -ENV LANG ru_RU.UTF-8 -ENV ARTIFACTS_ROOT built/tmp - -COPY ./ /opt/deb/ -RUN mkdir /bld && cp /opt/deb/build.sh /bld/build.sh && chmod +x /bld/build.sh - - diff --git a/install/builders/deb/build.sh b/install/builders/deb/build.sh deleted file mode 100755 index 029ed1b75..000000000 --- a/install/builders/deb/build.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/sh - -DATAROOT=$(pwd)/${ARTIFACTS_ROOT} -SRCPATH=${DATAROOT} -BINPATH=${SRCPATH}/bin/ -DEBBUILDROOT=/tmp/deb/ -BUILDERROOT=/opt/deb/ - -if [ -d "$DEBBUILDROOT" ]; then - rm -rf $DEBBUILDROOT - mkdir -p $DEBBUILDROOT -fi - -VERSION=$(cat ${DATAROOT}/VERSION | grep -oE '([[:digit:]]+\.){2}[[:digit:]]+') -PAKNAME=onescript-engine -DSTPATH=${DEBBUILDROOT}${PAKNAME} - -mkdir -p $DSTPATH -mkdir -p $DSTPATH/DEBIAN -mkdir -p $DSTPATH/usr/bin -mkdir -p $DSTPATH/usr/share/oscript/lib -mkdir -p $DSTPATH/usr/share/oscript/bin -mkdir -p $DSTPATH/etc -mkdir -p $DSTPATH/etc/bash_completion.d - -cp ${BUILDERROOT}settings/dirs $DSTPATH/DEBIAN/ -cat ${BUILDERROOT}settings/control | sed -r "s/VERSION/$VERSION/g" > $DSTPATH/DEBIAN/control -cp ${BINPATH}*.exe $DSTPATH/usr/share/oscript/bin -cp ${BINPATH}*.dll $DSTPATH/usr/share/oscript/bin -cp ${BUILDERROOT}oscript $DSTPATH/usr/bin -cp ${BUILDERROOT}oscript-cgi $DSTPATH/usr/bin -cp ${BUILDERROOT}oscript-opm $DSTPATH/usr/bin -cp ${BUILDERROOT}oscript-opm-completion $DSTPATH/etc/bash_completion.d -cp -r ${SRCPATH}/lib/* $DSTPATH/usr/share/oscript/lib -cp ${BINPATH}/oscript.cfg $DSTPATH/etc - -ln -s /usr/bin/oscript-opm $DSTPATH/usr/bin/opm - -# TODO: Убрать это! -cp ${BINPATH}/oscript.cfg $DSTPATH/usr/share/oscript/bin - -fakeroot dpkg-deb --build $DSTPATH - -rm -rf $DSTPATH -chmod 777 $DSTPATH.deb -dpkg-name -o $DSTPATH.deb - -#copy results -OUTPUT=out/deb -mkdir -p $OUTPUT - -cp $DEBBUILDROOT/*.deb $OUTPUT - diff --git a/install/builders/deb/oscript b/install/builders/deb/oscript deleted file mode 100755 index 65af38659..000000000 --- a/install/builders/deb/oscript +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -mono /usr/share/oscript/bin/oscript.exe "$@" - diff --git a/install/builders/deb/oscript-cgi b/install/builders/deb/oscript-cgi deleted file mode 100755 index f62a3fe07..000000000 --- a/install/builders/deb/oscript-cgi +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if [ -z "$SCRIPT_FILENAME" ]; then - export SCRIPT_FILENAME=$1 -fi - -mono /usr/share/oscript/bin/oscript.exe -cgi "$@" - diff --git a/install/builders/deb/oscript-opm b/install/builders/deb/oscript-opm deleted file mode 100755 index f847629a6..000000000 --- a/install/builders/deb/oscript-opm +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -OSPATH=/usr/share/oscript -mono $OSPATH/bin/oscript.exe $OSPATH/lib/opm/src/cmd/opm.os "$@" - diff --git a/install/builders/deb/oscript-opm-completion b/install/builders/deb/oscript-opm-completion deleted file mode 100644 index 8589d684b..000000000 --- a/install/builders/deb/oscript-opm-completion +++ /dev/null @@ -1,16 +0,0 @@ -_opm_complete() -{ - local cur opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - if [[ $COMP_CWORD == 1 ]] ; then - opts="build prepare install update app help config list run test version" - COMPREPLY=( $(compgen -W "$opts" -- ${cur}) ) - return 0 - fi - COMPREPLY=( $(compgen -df ${cur}) ) - return 0 -} -complete -F _opm_complete opm - -# vim: filetype=sh diff --git a/install/builders/deb/settings/control b/install/builders/deb/settings/control deleted file mode 100644 index 44931aca2..000000000 --- a/install/builders/deb/settings/control +++ /dev/null @@ -1,16 +0,0 @@ -Package: onescript-engine -Version: VERSION -Provides: onescript -Maintainer: Sergey Batanov -Architecture: all -Section: interpreters -Description: 1Script execution engine. - Cross-platform scripting engine - for DevOps who use 1C:Enterprise Platform (http://1c-dn.com/1c_enterprise) -Depends: mono-runtime, - libmono-system-core4.0-cil | libmono-system-core4.5-cil, - libmono-system4.0-cil | libmono-system4.5-cil, - libmono-corlib4.0-cil | libmono-corlib4.5-cil, - libmono-i18n4.0-all | libmono-i18n4.5-all -Recommends: mono-complete -Origin: https://github.com/EvilBeaver/OneScript/ diff --git a/install/builders/deb/settings/dirs b/install/builders/deb/settings/dirs deleted file mode 100644 index d48b6e912..000000000 --- a/install/builders/deb/settings/dirs +++ /dev/null @@ -1,6 +0,0 @@ -/usr/bin -/etc -/usr/share/oscript/bin -/usr/share/oscript/lib -/etc/bash_completion.d - diff --git a/install/builders/deb/settings/docs b/install/builders/deb/settings/docs deleted file mode 100644 index 3ad7355f4..000000000 --- a/install/builders/deb/settings/docs +++ /dev/null @@ -1,3 +0,0 @@ -README.md -LICENSE - diff --git a/install/builders/msi/README.md b/install/builders/msi/README.md deleted file mode 100644 index 394545228..000000000 --- a/install/builders/msi/README.md +++ /dev/null @@ -1 +0,0 @@ -# WiX wili be here \ No newline at end of file diff --git a/install/builders/nativeapi/Dockerfile b/install/builders/nativeapi/Dockerfile new file mode 100644 index 000000000..c3df70df9 --- /dev/null +++ b/install/builders/nativeapi/Dockerfile @@ -0,0 +1,5 @@ +FROM ubuntu:18.04 + +RUN apt-get update -qq \ + && dpkg --add-architecture i386 \ + && apt-get install -y -qq build-essential cmake uuid-dev libc6-dev gcc-multilib g++-multilib \ No newline at end of file diff --git a/install/builders/rpm/Dockerfile b/install/builders/rpm/Dockerfile deleted file mode 100644 index ca48eed7d..000000000 --- a/install/builders/rpm/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM fedora:26 -MAINTAINER Chmouel Boudjnah - - -# Install various packages to get compile environment -RUN dnf update -y && dnf -y group install 'Development Tools' - -# Install git command to access GitHub repository -RUN dnf -y install sudo git rpmdevtools rpm-build yum-utils dnf-plugins-core - -RUN sed -i.bak -n -e '/^Defaults.*requiretty/ { s/^/# /;};/^%wheel.*ALL$/ { s/^/# / ;} ;/^#.*wheel.*NOPASSWD/ { s/^#[ ]*//;};p' /etc/sudoers - -# This is an optimisation for caching, since using the auto generated one will -# make docker always run the builddep steps since new file -ENV RPMSOURCE /opt/rpm -COPY ./ ${RPMSOURCE} - -RUN dnf builddep -y --spec ${RPMSOURCE}/oscript.spec - -#uid 1000 is used by jenkins agent -RUN useradd -s /bin/bash -u 1000 -G adm,wheel,systemd-journal -m rpm - -ENV VERSION "" -ENV RELEASE "" -ENV ARTIFACTS_ROOT built/tmp - -RUN mkdir /bld && cp /opt/rpm/build.sh /bld/build.sh && chmod +x /bld/build.sh diff --git a/install/builders/rpm/build.sh b/install/builders/rpm/build.sh deleted file mode 100644 index 22b306863..000000000 --- a/install/builders/rpm/build.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -# script run inside the container - -DATAROOT=$(pwd)/${ARTIFACTS_ROOT} - -VERSION=$(cat ${DATAROOT}/VERSION | grep -oE '([[:digit:]]+\.){2}[[:digit:]]+') -BLDTMP=/tmp/rpm-src -TMPDIR=${BLDTMP}/OneScript-$VERSION -DISTPATH=${DATAROOT} -mkdir -p $TMPDIR - -echo "Copying sources to tmpdir" -cp -r -v $DISTPATH/bin $TMPDIR -cp -r -v $DISTPATH/lib $TMPDIR -cp -r -v $DISTPATH/doc $TMPDIR -cp -r -v $DISTPATH/examples $TMPDIR -cp -r -v ${RPMSOURCE}/oscript $TMPDIR/oscript -cp -r -v ${RPMSOURCE}/oscript-opm $TMPDIR/oscript-opm -cp -r -v ${RPMSOURCE}/oscript-opm-completion $TMPDIR/oscript-opm-completion - -pushd ${BLDTMP} -echo "Compressing OneScript-$VERSION to tar" -tar -czvf OneScript-$VERSION.tar.gz OneScript-$VERSION/ -popd - -BUILDDIR=/tmp/rpm-out -mkdir -p ${BUILDDIR} - -rpmdev-setuptree -define="" -if [ -z $VERSION ]; then - echo "" -else - define="${define} --define '_version ${VERSION}'" -fi - -if [ -z $RELEASE ]; then - echo "" -else - define='$define --define "Release $RELEASE"' -fi - -echo $define -cp -arv $RPMSOURCE/* ~/rpmbuild/SOURCES/ -cp -arv $BLDTMP/*.tar.gz ~/rpmbuild/SOURCES/ -cp -arv $RPMSOURCE/*.spec ~/rpmbuild/SPECS/ -rpmbuild -ba \ - --define "_version ${VERSION:-1.0.13}" \ - ~/rpmbuild/SPECS/oscript.spec || exit 1 - -[[ -d $BUILDDIR ]] || exit 0 - -mkdir -p $BUILDDIR/RPMS -mkdir -p $BUILDDIR/SRPMS - -cp -ar ~/rpmbuild/RPMS/ $BUILDDIR/ -cp -ar ~/rpmbuild/SRPMS/ $BUILDDIR/ - -#copy results -OUTPUT=$(pwd)/out/rpm -mkdir -p $OUTPUT - -mv $BUILDDIR/RPMS/noarch/*.rpm $OUTPUT -mv $BUILDDIR/SRPMS/*.rpm $OUTPUT diff --git a/install/builders/rpm/oscript b/install/builders/rpm/oscript deleted file mode 100644 index 65af38659..000000000 --- a/install/builders/rpm/oscript +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -mono /usr/share/oscript/bin/oscript.exe "$@" - diff --git a/install/builders/rpm/oscript-opm b/install/builders/rpm/oscript-opm deleted file mode 100644 index f847629a6..000000000 --- a/install/builders/rpm/oscript-opm +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -OSPATH=/usr/share/oscript -mono $OSPATH/bin/oscript.exe $OSPATH/lib/opm/src/cmd/opm.os "$@" - diff --git a/install/builders/rpm/oscript-opm-completion b/install/builders/rpm/oscript-opm-completion deleted file mode 100644 index 8589d684b..000000000 --- a/install/builders/rpm/oscript-opm-completion +++ /dev/null @@ -1,16 +0,0 @@ -_opm_complete() -{ - local cur opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - if [[ $COMP_CWORD == 1 ]] ; then - opts="build prepare install update app help config list run test version" - COMPREPLY=( $(compgen -W "$opts" -- ${cur}) ) - return 0 - fi - COMPREPLY=( $(compgen -df ${cur}) ) - return 0 -} -complete -F _opm_complete opm - -# vim: filetype=sh diff --git a/install/builders/rpm/oscript.spec b/install/builders/rpm/oscript.spec deleted file mode 100644 index 6be476470..000000000 --- a/install/builders/rpm/oscript.spec +++ /dev/null @@ -1,59 +0,0 @@ -%global debug_package %{nil} - -Name: onescript-engine -Version: %{_version} -Release: 1%{?dist} -Summary: 1Script execution engine. - -License: MPL 2.0 -URL: https://github.com/EvilBeaver/OneScript -Source0: OneScript-%{version}.tar.gz -BuildArch: noarch -BuildRequires: wget -Requires: mono-core -Requires: mono-locale-extras - -%define _empty_manifest_terminate_build 0 -%define _subdir OneScript-%{version} - -%description -1Script execution engine. - Cross-platform scripting engine - for DevOps who use 1C:Enterprise Platform (http://1c-dn.com/1c_enterprise) - -%prep -pwd -%setup -c %{source0} - -%build -echo "build" -pwd - -%install -%{__rm} -rf %{buildroot} -pushd %{_subdir} -mkdir -p %{buildroot}%{_datadir}/oscript/ -mkdir -p %{buildroot}%{_bindir} -mkdir -p %{buildroot}%{_sysconfdir}/bash_completion.d -install -p oscript -m 755 %{buildroot}%{_bindir}/oscript -install -p oscript-opm -m 755 %{buildroot}%{_bindir}/oscript-opm -install -p oscript-opm -m 755 %{buildroot}%{_bindir}/opm -install -p oscript-opm-completion -m 755 %{buildroot}%{_sysconfdir}/bash_completion.d/oscript-opm-completion -rm oscript -rm oscript-opm -rm oscript-opm-completion -%{__cp} -fpr ./ %{buildroot}%{_datadir}/oscript/ -popd - -%files -%defattr(-,root,root) -%{_bindir}/* -%{_datadir}/oscript/* -%{_sysconfdir}/bash_completion.d/oscript-* -%doc - - - -%changelog -* Sat Apr 23 2016 shenja@sosna.zp.ua -- diff --git a/install/choco/onescript.nuspec b/install/choco/onescript.nuspec deleted file mode 100644 index 09c54bd4d..000000000 --- a/install/choco/onescript.nuspec +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - onescript - onescript (Install) - __REPLACE__ - __REPLACE_AUTHORS_OF_SOFTWARE_COMMA_SEPARATED__ - __REPLACE_YOUR_NAME__ - __REPLACE__ - __REPLACE__MarkDown_Okay - - - - - onescript admin SPACE_SEPARATED - - - false - - - __REPLACE_OR_REMOVE__MarkDown_Okay - - - - - - diff --git a/install/choco/tools/ReadMe.md b/install/choco/tools/ReadMe.md deleted file mode 100644 index e9d10ef2b..000000000 --- a/install/choco/tools/ReadMe.md +++ /dev/null @@ -1,91 +0,0 @@ -## Summary -How do I create packages? See https://github.com/chocolatey/choco/wiki/CreatePackages - -If you are submitting packages to the community feed (https://chocolatey.org) -always try to ensure you have read, understood and adhere to the create -packages wiki link above. - -## Automatic Packages? -Consider making this package an automatic package, for the best -maintainability over time. Read up at https://github.com/chocolatey/choco/wiki/AutomaticPackages - -## Shim Generation -Any executables you include in the package or download (but don't call -install against using the built-in functions) will be automatically shimmed. - -This means those executables will automatically be included on the path. -Shim generation runs whether the package is self-contained or uses automation -scripts. - -By default, these are considered console applications. - -If the application is a GUI, you should create an empty file next to the exe -named 'name.exe.gui' e.g. 'bob.exe' would need a file named 'bob.exe.gui'. -See https://github.com/chocolatey/choco/wiki/CreatePackages#how-do-i-set-up-batch-redirects-for-applications-that-have-a-gui - -If you want to ignore the executable, create an empty file next to the exe -named 'name.exe.ignore' e.g. 'bob.exe' would need a file named -'bob.exe.ignore'. -See https://github.com/chocolatey/choco/wiki/CreatePackages#how-do-i-exclude-executables-from-getting-batch-redirects - -## Self-Contained? -If you have a self-contained package, you can remove the automation scripts -entirely and just include the executables, they will automatically get shimmed, -which puts them on the path. Ensure you have the legal right to distribute -the application though. See https://github.com/chocolatey/choco/wiki/Legal. - -You should read up on the Shim Generation section to familiarize yourself -on what to do with GUI applications and/or ignoring shims. - -## Automation Scripts -You have a powerful use of Chocolatey, as you are using PowerShell. So you -can do just about anything you need. Choco has some very handy built-in -functions that you can use, these are sometimes called the helpers. - -### Built-In Functions -https://github.com/chocolatey/choco/wiki/HelpersReference - -A note about a couple: -* Get-BinRoot - this is a horribly named function that doesn't do what new folks think it does. It gets you the 'tools' root, which by default is set to 'c:\tools', not the chocolateyInstall bin folder. -* Install-BinFile - used for non-exe files - executables are automatically shimmed... -* Uninstall-BinFile - used for non-exe files - executables are automatically shimmed - -### Getting package specific information -Use the package parameters pattern - see https://github.com/chocolatey/choco/wiki/How-To-Parse-PackageParameters-Argument - -### Need to mount an ISO? -https://github.com/chocolatey/choco/wiki/How-To-Mount-An-Iso-In-Chocolatey-Package - - -### Environment Variables -Chocolatey makes a number of environment variables available (You can access any of these with $env:TheVariableNameBelow): - - * TEMP = Overridden to the CacheLocation, but may be the same as the original TEMP folder - * ChocolateyInstall = Top level folder where Chocolatey is installed - * chocolateyPackageName = The name of the package, equivalent to the id in the nuspec (0.9.9+) - * chocolateyPackageVersion = The version of the package, equivalent to the version in the nuspec (0.9.9+) - * chocolateyPackageFolder = The top level location of the package folder - -#### Advanced Environment Variables -The following are more advanced settings: - - * chocolateyPackageParameters = (0.9.8.22+) - * CHOCOLATEY_VERSION = The version of Choco you normally see. Use if you are 'lighting' things up based on choco version. (0.9.9+) - - Otherwise take a dependency on the specific version you need. - * chocolateyForceX86 = If available and set to 'true', then user has requested 32bit version. (0.9.9+) - - Automatically handled in built in Choco functions. - * OS_PLATFORM = Like Windows, OSX, Linux. (0.9.9+) - * OS_VERSION = The version of OS, like 6.1 something something for Windows. (0.9.9+) - * OS_NAME = The reported name of the OS. (0.9.9+) - * IS_PROCESSELEVATED = Is the process elevated? (0.9.9+) - -#### Experimental Environment Variables -The following are experimental or use not recommended: - - * OS_IS64BIT = This may not return correctly - it may depend on the process the app is running under (0.9.9+) - * CHOCOLATEY_VERSION_PRODUCT = the version of Choco that may match CHOCOLATEY_VERSION but may be different (0.9.9+) - - it's based on git describe - * IS_ADMIN = Is the user an administrator? But doesn't tell you if the process is elevated. (0.9.9+) - * chocolateyInstallOverride = Not for use in package automation scripts. (0.9.9+) - * chocolateyInstallArguments = the installer arguments meant for the native installer. You should use chocolateyPackageParameters intead. (0.9.9+) - diff --git a/install/choco/tools/chocolateyinstall.ps1 b/install/choco/tools/chocolateyinstall.ps1 deleted file mode 100644 index 0748cbf8a..000000000 --- a/install/choco/tools/chocolateyinstall.ps1 +++ /dev/null @@ -1,90 +0,0 @@ -#NOTE: Please remove any commented lines to tidy up prior to releasing the package, including this one -# REMOVE ANYTHING BELOW THAT IS NOT NEEDED - -$ErrorActionPreference = 'Stop'; # stop on all errors - - -$packageName = 'onescript' # arbitrary name for the package, used in messages -$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" -$url = 'http://oscript.io/downloads/latest/msi' # download url -$url64 = '' # 64bit URL here or remove - if installer is both, use $url - -$packageArgs = @{ - packageName = $packageName - unzipLocation = $toolsDir - fileType = 'EXE_MSI_OR_MSU' #only one of these: exe, msi, msu - url = $url - url64bit = $url64 - - #MSI - silentArgs = "/qn /norestart /l*v '$env:TEMP\chocolatey\$packageName\install.log'" # ALLUSERS=1 DISABLEDESKTOPSHORTCUT=1 ADDDESKTOPICON=0 ADDSTARTMENU=0 - validExitCodes= @(0, 3010, 1641) - #OTHERS - #silentArgs ='/S' # "/s /S /q /Q /quiet /silent /SILENT /VERYSILENT -s" # try any of these to get the silent installer - #validExitCodes= @(0) #please insert other valid exit codes here - - # optional - registryUninstallerKey = 'onescript' #ensure this is the value in the registry - checksum = '' - checksumType = 'md5' #default is md5, can also be sha1 - checksum64 = '' - checksumType64= 'md5' #default is checksumType -} - -Install-ChocolateyPackage @packageArgs -#Install-ChocolateyZipPackage @packageArgs - -## Main helper functions - these have error handling tucked into them already -## see https://github.com/chocolatey/choco/wiki/HelpersReference - -## Install an application, will assert administrative rights -## add additional optional arguments as necessary -##Install-ChocolateyPackage $packageName $fileType $silentArgs $url [$url64 -validExitCodes $validExitCodes -checksum $checksum -checksumType $checksumType -checksum64 $checksum64 -checksumType64 $checksumType64] - -## Download and unpack a zip file -##Install-ChocolateyZipPackage $packageName $url $toolsDir [$url64 -checksum $checksum -checksumType $checksumType -checksum64 $checksum64 -checksumType64 $checksumType64] - -## Install Visual Studio Package -#Install-ChocolateyVsixPackage $packageName $url [$vsVersion] [-checksum $checksum -checksumType $checksumType] -#Install-ChocolateyVsixPackage @packageArgs - -# see the full list at https://github.com/chocolatey/choco/wiki/HelpersReference -# downloader that the main helpers use to download items -# if removing $url64, please remove from here -#Get-ChocolateyWebFile $packageName 'DOWNLOAD_TO_FILE_FULL_PATH' $url $url64 -# installer, will assert administrative rights - used by Install-ChocolateyPackage -#Install-ChocolateyInstallPackage $packageName $fileType $silentArgs '_FULLFILEPATH_' -validExitCodes $validExitCodes -# unzips a file to the specified location - auto overwrites existing content -#Get-ChocolateyUnzip "FULL_LOCATION_TO_ZIP.zip" $toolsDir -# Runs processes asserting UAC, will assert administrative rights - used by Install-ChocolateyInstallPackage -#Start-ChocolateyProcessAsAdmin 'STATEMENTS_TO_RUN' 'Optional_Application_If_Not_PowerShell' -validExitCodes $validExitCodes -# add specific folders to the path - any executables found in the chocolatey package folder will already be on the path. This is used in addition to that or for cases when a native installer doesn't add things to the path. -#Install-ChocolateyPath 'LOCATION_TO_ADD_TO_PATH' 'User_OR_Machine' # Machine will assert administrative rights -# add specific files as shortcuts to the desktop -#$target = Join-Path $toolsDir "$($packageName).exe" -# Install-ChocolateyShortcut -shortcutFilePath "" -targetPath "" [-workDirectory "C:\" -arguments "C:\test.txt" -iconLocation "C:\test.ico" -description "This is the description"] -# outputs the bitness of the OS (either "32" or "64") -#$osBitness = Get-ProcessorBits -#Install-ChocolateyEnvironmentVariable -variableName "SOMEVAR" -variableValue "value" [-variableType = 'Machine' #Defaults to 'User'] - -#Install-ChocolateyFileAssociation -#Install-BinFile ## only use this for non-exe files - chocolatey will automatically pick up the exe files and shim them automatically -## https://github.com/chocolatey/choco/wiki/CreatePackages#how-do-i-exclude-executables-from-getting-batch-redirects - -##PORTABLE EXAMPLE -#$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" -# despite the name "Install-ChocolateyZipPackage" this also works with 7z archives -#Install-ChocolateyZipPackage $packageName $url $toolsDir $url64 -## END PORTABLE EXAMPLE - -## [DEPRECATING] PORTABLE EXAMPLE -#$binRoot = Get-BinRoot -#$installDir = Join-Path $binRoot "$packageName" -#Write-Host "Adding `'$installDir`' to the path and the current shell path" -#Install-ChocolateyPath "$installDir" -#$env:Path = "$($env:Path);$installDir" - -# if removing $url64, please remove from here -# despite the name "Install-ChocolateyZipPackage" this also works with 7z archives -#Install-ChocolateyZipPackage "$packageName" "$url" "$installDir" "$url64" -## END PORTABLE EXAMPLE diff --git a/install/choco/tools/chocolateyuninstall.ps1 b/install/choco/tools/chocolateyuninstall.ps1 deleted file mode 100644 index 0fa02081d..000000000 --- a/install/choco/tools/chocolateyuninstall.ps1 +++ /dev/null @@ -1,61 +0,0 @@ -#NOTE: Please remove any commented lines to tidy up prior to releasing the package, including this one -# REMOVE ANYTHING BELOW THAT IS NOT NEEDED -# Auto Uninstaller should be able to detect and handle registry uninstalls (if it is turned on, it is in preview for 0.9.9). - -$ErrorActionPreference = 'Stop'; # stop on all errors - -$packageName = 'onescript' -# registry uninstaller key name is the key that is found at HKLM:\Software\Windows\CurrentVersion\Uninstall\ THE NAME -$registryUninstallerKeyName = 'onescript' #ensure this is the value in the registry -$msiProductCodeGuid = '{insert it here}' -$shouldUninstall = $true - -$local_key = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$registryUninstallerKeyName" -# local key 6432 probably never exists -$local_key6432 = "HKCU:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$registryUninstallerKeyName" -$machine_key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$registryUninstallerKeyName" -$machine_key6432 = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$registryUninstallerKeyName" - -$file = @($local_key, $local_key6432, $machine_key, $machine_key6432) ` - | ?{ Test-Path $_ } ` - | Get-ItemProperty ` - | Select-Object -ExpandProperty UninstallString - -if ($file -eq $null -or $file -eq '') { - Write-Host "$packageName has already been uninstalled by other means." - $shouldUninstall = $false -} - -# The below is somewhat naive and built for EXE installers -#$installerType = 'EXE' -#$silentArgs = '/S' -#$validExitCodes = @(0) - -#if (!(Test-Path $file)) { -# Write-Host "$packageName has already been uninstalled by other means." -# $shouldUninstall = $false -#} - -# The below is somewhat naive and built for MSI installers -$installerType = 'MSI' -# The Product Code GUID is all that should be passed for MSI, and very FIRST, -# because it comes directly after /x, which is already set in the -# Uninstall-ChocolateyPackage msiargs (facepalm). -$silentArgs = "$msiProductCodeGuid /qn /norestart" -# https://msdn.microsoft.com/en-us/library/aa376931(v=vs.85).aspx -$validExitCodes = @(0, 3010, 1605, 1614, 1641) -# Don't pass anything for file, it is ignored for msi (facepalm number 2) -# Alternatively if you need to pass a path to an msi, determine that and use -# it instead of $msiProductCodeGuid in silentArgs, still very first -$file = '' - -if ($shouldUninstall) { - Uninstall-ChocolateyPackage -PackageName $packageName -FileType $installerType -SilentArgs $silentArgs -validExitCodes $validExitCodes -File $file -} - -## OTHER HELPERS -## https://github.com/chocolatey/choco/wiki/HelpersReference -#Uninstall-ChocolateyZipPackage -#Uninstall-BinFile # Only needed if you added one in the installer script, choco will remove the ones it added automatically. -#remove any shortcuts you added - diff --git a/install/dotNetFx40_Full_setup.exe b/install/dotNetFx40_Full_setup.exe deleted file mode 100644 index 384fb8343..000000000 Binary files a/install/dotNetFx40_Full_setup.exe and /dev/null differ diff --git a/install/examples/distr_build.os b/install/examples/distr_build.os index 7281783c6..09e4177ee 100644 --- a/install/examples/distr_build.os +++ b/install/examples/distr_build.os @@ -132,7 +132,6 @@ ПараметрыЗапуска = Новый Массив; ПараметрыЗапуска.Добавить("CREATEINFOBASE"); ПараметрыЗапуска.Добавить("File="""+КаталогВременнойБазы+""";"); - ПараметрыЗапуска.Добавить("/UseTemplate""" + мКаталогСборки + "\source.cf" + """"); ПараметрыЗапуска.Добавить("/Out""" + ФайлИнформации() + """"); Сообщить("Создание временной базы"); diff --git a/install/install.iss b/install/install.iss deleted file mode 100644 index adce2f3b7..000000000 --- a/install/install.iss +++ /dev/null @@ -1,204 +0,0 @@ -#include -#define AppName "OneScript engine" -#define FSFriendlyName "OneScript" -#define MainExe "TestApp.exe" -#define ArtifactRoot "..\built\tmp" - -#define VerMajor -#define VerMinor -#define VerRelease -#define Build - -#ifndef Suffix - #define Suffix "x86" -#endif - -#if Suffix == "x64" - #define Binaries="bin" -#else - #define Binaries="bin32" -#endif - -; duplicates ArtifactsRoot because ISPP can't resolve directives -#expr ParseVersion(ArtifactRoot + "\" + Binaries + "\ScriptEngine.dll",VerMajor,VerMinor,VerRelease,Build) - -[Setup] -AppName={#AppName} -AppVersion={#VerMajor}.{#VerMinor}.{#VerRelease} -AppPublisher=1Script Team (Open Source) -DefaultDirName="{pf}\{#FSFriendlyName}" -DefaultGroupName="{#FSFriendlyName}" -OutputBaseFilename="OneScript-{#VerMajor}.{#VerMinor}.{#VerRelease}-{#Suffix}" -DisableProgramGroupPage=yes -UninstallDisplayIcon="{app}\bin\{#MainExe}" -Compression=lzma2 -SolidCompression=yes -VersionInfoVersion={#VerMajor}.{#VerMinor}.{#VerRelease}.{#Build} -#if Suffix == "x64" - ArchitecturesInstallIn64BitMode="x64" -#endif - -[InstallDelete] -Type: files; Name: {app}\*.dll -Type: files; Name: {app}\*.exe -Type: files; Name: {app}\*.bat -Type: files; Name: {app}\*.cmd - -[Types] -Name: "normal"; Description: "Стандартная установка" -Name: "custom"; Description: "Выборочная установка"; Flags: iscustom - -[Components] -Name: "main"; Description: "Основные файлы"; Types: normal custom; Flags: fixed -Name: "isapi"; Description: "Обработчик HTTP-сервисов"; Types: normal custom; -Name: "stdlib"; Description: "Стандартная библиотека скриптов"; Types: normal custom; -Name: "testapp"; Description: "Тестовая консоль (TestApp)"; -Name: "docs"; Description: "Документация по свойствам и методам (синтакс-помощник)"; - -[Files] -Source: "{#ArtifactRoot}\{#Binaries}\oscript.exe"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\ScriptEngine.HostedScript.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\ScriptEngine.NativeApi.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\ScriptEngine.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\OneScript.DebugProtocol.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\OneScript.DebugServices.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\OneScript.Language.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\DotNetZip.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\Newtonsoft.Json.dll"; DestDir: "{app}\bin"; Components: main -Source: "{#ArtifactRoot}\{#Binaries}\oscript.cfg"; DestDir: "{app}\bin"; Components: main; Flags: onlyifdoesntexist - -Source: "{#ArtifactRoot}\examples\*"; DestDir: "{app}\examples"; Components: main - -;isapi -Source: "{#ArtifactRoot}\{#Binaries}\ASPNETHandler.dll"; DestDir: "{app}\bin"; Components: isapi; - -; testapp -Source: "{#ArtifactRoot}\{#Binaries}\TestApp.exe"; DestDir: "{app}\bin"; Components: testapp -Source: "{#ArtifactRoot}\{#Binaries}\ICSharpCode.AvalonEdit.dll"; DestDir: "{app}\bin"; Components: testapp - -; библиотека -Source: "{#ArtifactRoot}\lib\*"; DestDir: "{app}\lib"; Components: stdlib; Flags: recursesubdirs -Source: "{#ArtifactRoot}\{#Binaries}\*.bat"; DestDir: "{app}\bin"; Components: stdlib - -; документация -Source: "{#ArtifactRoot}\doc\*"; DestDir: "{app}\doc"; Components: docs; Flags: recursesubdirs - -Source: "dotNetFx40_Full_setup.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: not IsRequiredDotNetDetected - -[Icons] -Name: "{group}\{#FSFriendlyName}"; Filename: "{app}\bin\{#MainExe}" - -[Registry] -Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\bin;"; Check: NeedsAddPath(ExpandConstant('{app}\bin')) - -[Run] -Filename: {tmp}\dotNetFx40_Full_setup.exe; Parameters: "/q:a /c:""install /l /q"""; Check: not IsRequiredDotNetDetected; StatusMsg: Microsoft .NET Framework 4.0 is being installed. Please wait.. -Filename: "{app}\bin\{#MainExe}"; Description: "Launch application"; Components: testapp; Flags: postinstall nowait skipifsilent unchecked - -[Code] - -#IFDEF UNICODE - #DEFINE AW "W" -#ELSE - #DEFINE AW "A" -#ENDIF -type - INSTALLSTATE = Longint; -const - INSTALLSTATE_INVALIDARG = -2; // An invalid parameter was passed to the function. - INSTALLSTATE_UNKNOWN = -1; // The product is neither advertised or installed. - INSTALLSTATE_ADVERTISED = 1; // The product is advertised but not installed. - INSTALLSTATE_ABSENT = 2; // The product is installed for a different user. - INSTALLSTATE_DEFAULT = 5; // The product is installed for the current user. - - // Microsoft Visual C++ 2012 x86 Minimum Runtime - 11.0.61030.0 (Update 4) - VC_2012_REDIST_MIN_UPD4_X86 = '{BD95A8CD-1D9F-35AD-981A-3E7925026EBB}'; - VC_2012_REDIST_MIN_UPD4_X64 = '{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}'; - // Microsoft Visual C++ 2012 x86 Additional Runtime - 11.0.61030.0 (Update 4) - VC_2012_REDIST_ADD_UPD4_X86 = '{B175520C-86A2-35A7-8619-86DC379688B9}'; - VC_2012_REDIST_ADD_UPD4_X64 = '{37B8F9C7-03FB-3253-8781-2517C99D7C00}'; - -function NeedsAddPath(Param: string): boolean; -var - OrigPath: string; -begin - if not RegQueryStringValue(HKEY_LOCAL_MACHINE,'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', OrigPath) - then begin - Result := True; - exit; - end; - // look for the path with leading and trailing semicolon - // Pos() returns 0 if not found - Result := Pos(';' + UpperCase(Param) + ';', ';' + UpperCase(OrigPath) + ';') = 0; - if Result = True then - Result := Pos(';' + UpperCase(Param) + '\;', ';' + UpperCase(OrigPath) + ';') = 0; -end; - -function IsDotNetDetected(version: string; service: cardinal): boolean; -// Indicates whether the specified version and service pack of the .NET Framework is installed. -// -// version -- Specify one of these strings for the required .NET Framework version: -// 'v1.1.4322' .NET Framework 1.1 -// 'v2.0.50727' .NET Framework 2.0 -// 'v3.0' .NET Framework 3.0 -// 'v3.5' .NET Framework 3.5 -// 'v4\Client' .NET Framework 4.0 Client Profile -// 'v4\Full' .NET Framework 4.0 Full Installation -// 'v4.5' .NET Framework 4.5 -// -// service -- Specify any non-negative integer for the required service pack level: -// 0 No service packs required -// 1, 2, etc. Service pack 1, 2, etc. required -var - key: string; - install, release, serviceCount: cardinal; - check45, success: boolean; -//var reqNetVer : string; -begin - // .NET 4.5 installs as update to .NET 4.0 Full - if version = 'v4.5' then begin - version := 'v4\Full'; - check45 := true; - end else - check45 := false; - - // installation key group for all .NET versions - key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + version; - - // .NET 3.0 uses value InstallSuccess in subkey Setup - if Pos('v3.0', version) = 1 then begin - success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install); - end else begin - success := RegQueryDWordValue(HKLM, key, 'Install', install); - end; - - // .NET 4.0/4.5 uses value Servicing instead of SP - if Pos('v4', version) = 1 then begin - success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount); - end else begin - success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount); - end; - - // .NET 4.5 uses additional value Release - if check45 then begin - success := success and RegQueryDWordValue(HKLM, key, 'Release', release); - success := success and (release >= 378389); - end; - - result := success and (install = 1) and (serviceCount >= service); -end; - -function IsRequiredDotNetDetected(): Boolean; -begin - result := IsDotNetDetected('v4\Full', 0); -end; - -function InitializeSetup(): Boolean; -begin - if not IsDotNetDetected('v4\Full', 0) then begin - MsgBox('{#AppName} requires Microsoft .NET Framework 4.0 Client Profile.'#13#13 - 'The installer will attempt to install it', mbInformation, MB_OK); - end; - - result := true; -end; diff --git a/install/make-dockers.sh b/install/make-dockers.sh index a3ced6a6e..54521cb72 100755 --- a/install/make-dockers.sh +++ b/install/make-dockers.sh @@ -2,8 +2,5 @@ THISDIR=$(pwd) -cd $THISDIR/builders/deb -docker build -t oscript/onescript-builder:deb . - -cd $THISDIR/builders/rpm -docker build -t oscript/onescript-builder:rpm . \ No newline at end of file +cd $THISDIR +docker build -t oscript/onescript-builder:gcc -f $THISDIR/builders/nativeapi/Dockerfile .. diff --git a/install/opm b/install/opm new file mode 100644 index 000000000..e6e62bc7b --- /dev/null +++ b/install/opm @@ -0,0 +1,8 @@ +#!/bin/bash + +THISDIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +LIB="$THISDIR/../lib" +OPM=$LIB/opm/src/cmd/opm.os + +oscript $OPM "$@" diff --git a/install/opm-0.16.2.ospx b/install/opm-0.16.2.ospx deleted file mode 100644 index 0e3e6dd33..000000000 Binary files a/install/opm-0.16.2.ospx and /dev/null differ diff --git a/install/opm.bat b/install/opm.bat index 3fd12a362..1105eba2c 100644 --- a/install/opm.bat +++ b/install/opm.bat @@ -5,4 +5,4 @@ setlocal set lib="%~dp0..\lib" set opm=%lib%\opm\src\cmd\opm.os -oscript.exe %opm% %* +oscript %opm% %* diff --git a/install/opm.ospx b/install/opm.ospx new file mode 100644 index 000000000..b30cc6ee3 Binary files /dev/null and b/install/opm.ospx differ diff --git a/install/oscript b/install/oscript new file mode 100644 index 000000000..d2cbc30be --- /dev/null +++ b/install/oscript @@ -0,0 +1,5 @@ +#!/bin/bash + +THISDIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +dotnet "$THISDIR/oscript.dll" "$@" diff --git a/install/oscript-config.exe b/install/oscript-config.exe deleted file mode 100644 index 76de3cf40..000000000 Binary files a/install/oscript-config.exe and /dev/null differ diff --git a/install/oscript.bat b/install/oscript.bat new file mode 100644 index 000000000..abb4ca583 --- /dev/null +++ b/install/oscript.bat @@ -0,0 +1,3 @@ +@echo off + +dotnet %~dp0oscript.dll %* diff --git a/install/package-loader.os b/install/package-loader.os index 59d4845d0..6cad5d28e 100644 --- a/install/package-loader.os +++ b/install/package-loader.os @@ -1,4 +1,7 @@ -// Пояснения по переменным даны в конце модуля +// Стандартный загрузчик библиотек, включаемый в дистрибутив OneScript +// Данный загрузчик используется, если библиотека не предоставляет свой, альтернативный загрузчик. +// Можно сказать, это загрузчик по-умолчанию. + Перем ПоказатьСообщенияЗагрузки; Перем ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей; @@ -106,6 +109,9 @@ КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "src", "Модули")); КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "src", "Modules")); + КаталогиВК = Новый Массив; + КаталогиВК.Добавить(ОбъединитьПути(Путь, "Components")); + КаталогиВК.Добавить(ОбъединитьПути(Путь, "Компоненты")); Для Каждого мКаталог Из КаталогиКлассов Цикл @@ -118,6 +124,12 @@ ОбработатьКаталогМодулей(мКаталог, СтандартнаяОбработка, Отказ); КонецЦикла; + + Для Каждого мКаталог Из КаталогиВК Цикл + + ОбработатьКаталогВК(мКаталог, СтандартнаяОбработка, Отказ); + + КонецЦикла; КонецПроцедуры @@ -161,6 +173,79 @@ КонецПроцедуры +// По соглашению ВК должны лежать в подпапках, названных, как значения перечисления ТипПлатформы. +// Имя файла, являющегося внешней компонентой должно иметь префикс 1script_ +// Components +// net4 (фреймворк .net48 +// 1script_barcode.dll +// dotnet (.net современных версий, он же netcore) +// 1script_barcode.dll +// NativeApi +// Windows_x86 +// 1script_barcode.dll +// Windows_x86_64 +// 1script_barcode.dll +// Linux_x86_64 +// 1script_barcode.so +// остальные не поддерживаются (ЖВПР) +// +Процедура ОбработатьКаталогВК(Знач Путь, СтандартнаяОбработка, Отказ) + + СИ = Новый СистемнаяИнформация(); + МажорнаяВерсия = Лев(СИ.Версия,1); + + Если МажорнаяВерсия = "1" Тогда + ОбработатьБиблиотекиCLR(ОбъединитьПути(Путь, "net4"), СтандартнаяОбработка); + ИначеЕсли МажорнаяВерсия = "2" Тогда + ОбработатьБиблиотекиCLR(ОбъединитьПути(Путь, "dotnet"), СтандартнаяОбработка); + Иначе + Вывести("Неизвестная мажорная версия системы: " + МажорнаяВерсия); + КонецЕсли; + + ОбработатьКомпонентыNativeApi(Путь); + +КонецПроцедуры + +Процедура ОбработатьБиблиотекиCLR(Знач Путь, СтандартнаяОбработка) + + КандидатыВКомпоненты = НайтиФайлы(Путь, "1script_*.dll"); + Для Каждого Кандидат Из КандидатыВКомпоненты Цикл + + Если Не Кандидат.ЭтоФайл() Тогда + Продолжить; + КонецЕсли; + + Вывести("Загружаю файл " + Кандидат.Имя); + ЗагрузитьБиблиотеку(Кандидат.ПолноеИмя); + СтандартнаяОбработка = Ложь; + + КонецЦикла; + +КонецПроцедуры + +Процедура ОбработатьКомпонентыNativeApi(Путь) + СИ = Новый СистемнаяИнформация; + ИмяПодкаталога = ОбъединитьПути(Путь, Строка(СИ.ТипПлатформы)); + Вывести("Ищу внешние компоненты в каталоге " + Путь); + + #Если Windows Тогда + Расширение = ".dll"; + #Иначе + Расширение = ".so"; + #КонецЕсли + + КандидатыВКомпоненты = НайтиФайлы(ИмяПодкаталога, "1script_*"+Расширение); + Для Каждого Кандидат Из КандидатыВКомпоненты Цикл + Если Не Кандидат.ЭтоФайл() Тогда + Продолжить; + КонецЕсли; + + Вывести("Загружаю файл " + Кандидат.Имя); + ПодключитьВнешнююКомпоненту(Кандидат.ПолноеИмя, Кандидат.Имя, ТипВнешнейКомпоненты.Native); + + КонецЦикла; +КонецПроцедуры + Процедура ДобавитьКлассЕслиРанееНеДобавляли(ПутьФайла, ИмяКласса) Вывести("Добавляю класс, если ранее не добавляли " + ИмяКласса); Если ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей Тогда @@ -171,13 +256,14 @@ КлассУжеЕсть = Ложь; Попытка - Объект = Новый(ИмяКласса); - КлассУжеЕсть = Истина; - Исключение - СообщениеОшибки = ОписаниеОшибки(); - ИскомаяОшибка = СтрШаблон("Конструктор не найден (%1)", ИмяКласса); - КлассУжеЕсть = СтрНайти(СообщениеОшибки, ИскомаяОшибка) = 0; - КонецПопытки; + ИзвестныйТип = Тип(ИмяКласса); + КлассУжеЕсть = Истина; + Исключение + СообщениеОшибки = ОписаниеОшибки(); + ШаблонОшибки = НСтр("ru = 'Тип не зарегистрирован (%1)';en = 'Type is not registered (%1)'"); + ИскомаяОшибка = СтрШаблон(ШаблонОшибки, ИмяКласса); + КлассУжеЕсть = СтрНайти(СообщениеОшибки, ИскомаяОшибка) = 0; + КонецПопытки; Если Не КлассУжеЕсть Тогда Вывести("Добавляю класс, т.к. он не найден - " + ИмяКласса); diff --git a/install/release-notes.md b/install/release-notes.md new file mode 100644 index 000000000..8eeb7d412 --- /dev/null +++ b/install/release-notes.md @@ -0,0 +1,15 @@ +# Версия 2.0.1 + +## Исправление ошибок + +* #1646 Ошибка метода ЗаполнитьЗначенияСвойств на ФиксированнойСтруктуре +* #1647 Исправлена вставка ключа Null в соответствие +* #1649 Исправлена обработка символов '\0' в каталогах WebDAV +* #1654 В методе можно было объявлять параметры, не разделяя их запятой +* #1652 Исправлена обработка аннотаций для списка переменных, объявленных через запятую +* #1657 Исправлен приоритет активации источников в конфиге + +## Рефакторинг и оптимизация + +* В парсере применен приоритет операторов вместо рекурсивного спуска (улучшена скорость компиляции) +* Загрузчик библиотек теперь разделяет ситуацию ненайденных библиотек и тех, которые не удается загрузить (улучшена диагностика ненайденных библиотек) diff --git a/src/1Script.sln b/src/1Script.sln index 556c43322..ee4c3a253 100644 --- a/src/1Script.sln +++ b/src/1Script.sln @@ -1,318 +1,364 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29009.5 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "TestApp\TestApp.csproj", "{4585BA5D-9EC4-4C89-8250-2033D2AC2999}" - ProjectSection(ProjectDependencies) = postProject - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} = {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine", "ScriptEngine\ScriptEngine.csproj", "{F062D1D9-D307-492A-A56B-FF3C55F8F6C0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine.HostedScript", "ScriptEngine.HostedScript\ScriptEngine.HostedScript.csproj", "{F09A46BD-5737-45E7-BA60-A47C9F7821A9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "oscript", "oscript\oscript.csproj", "{2590E2BB-CC1F-4775-80ED-451F45C9A4F1}" - ProjectSection(ProjectDependencies) = postProject - {795AA2F5-3074-4BC5-A30F-1B6354044D9B} = {795AA2F5-3074-4BC5-A30F-1B6354044D9B} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneRunner", "StandaloneRunner\StandaloneRunner.csproj", "{795AA2F5-3074-4BC5-A30F-1B6354044D9B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D14BF321-348C-46B8-B96A-43A22BA7AC10}" - ProjectSection(SolutionItems) = preProject - ..\Build.csproj = ..\Build.csproj - oscommon.targets = oscommon.targets - EndProjectSection -EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Installer", "Installer\Installer.wixproj", "{BBE794A6-B159-422F-B655-B7F03F25F223}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnitTests", "NUnitTests\NUnitTests.csproj", "{93ACC849-E7E1-4695-B59D-54B3737E48A6}" - ProjectSection(ProjectDependencies) = postProject - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1} = {2590E2BB-CC1F-4775-80ED-451F45C9A4F1} - {F09A46BD-5737-45E7-BA60-A47C9F7821A9} = {F09A46BD-5737-45E7-BA60-A47C9F7821A9} - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} = {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} - {795AA2F5-3074-4BC5-A30F-1B6354044D9B} = {795AA2F5-3074-4BC5-A30F-1B6354044D9B} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Component", "Component\Component.csproj", "{B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VSCode.DebugAdapter", "VSCode.DebugAdapter\VSCode.DebugAdapter.csproj", "{C979F151-AA29-47E4-B020-3039BA0986D9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.DebugProtocol", "OneScript.DebugProtocol\OneScript.DebugProtocol.csproj", "{727A498F-BF50-42F8-8523-40C0B5B1B233}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "oscriptTests", "oscriptTest\oscriptTests.csproj", "{69A7869C-203C-4F09-AC3F-04E9B52AD7AB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{91059C5B-526C-4B81-B106-99DEFF542D1F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTTPServices", "ASPNETHandler\HTTPServices.csproj", "{B7CD7F52-E387-490E-8F77-E1FB060401B5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWebApp", "TestWebApp\TestWebApp.csproj", "{6DE97986-8304-45AE-BD47-63A5563F9C3A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Language", "OneScript.Language\OneScript.Language.csproj", "{0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Language.Tests", "OneScript.Language.Tests\OneScript.Language.Tests.csproj", "{872BFF20-7AD9-4741-B163-CD648AD3418F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScriptDocumenter", "OneScriptDocumenter\OneScriptDocumenter.csproj", "{76F2521D-44D7-48C9-A678-074E22B46092}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Core.Tests", "OneScript.Core.Tests\OneScript.Core.Tests.csproj", "{4FF7C82D-BFEF-415E-81FF-5C0337E99845}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.DebugServices", "OneScript.DebugServices\OneScript.DebugServices.csproj", "{600ACA55-4077-4D29-8C51-DBA93EB34609}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScriptEngine.NativeApi", "ScriptEngine.NativeApi\ScriptEngine.NativeApi.vcxproj", "{2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AddInNative", "..\tests\native-api\AddInNative.vcxproj", "{55890DF2-D13E-4C89-A01D-79CAD6726246}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - 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|x64.ActiveCfg = Debug|Any CPU - {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {4585BA5D-9EC4-4C89-8250-2033D2AC2999}.Release|x64.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 - {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|x64.ActiveCfg = Debug|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.Release|x64.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 - {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|x64.ActiveCfg = Debug|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.Release|x64.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 - {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|x64.ActiveCfg = Debug|Any CPU - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Release|x64.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 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x64.ActiveCfg = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x64.Build.0 = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x86.ActiveCfg = Debug|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x86.Build.0 = Debug|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|Any CPU.Build.0 = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x64.ActiveCfg = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x64.Build.0 = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x86.ActiveCfg = Release|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x86.Build.0 = Release|x86 - {BBE794A6-B159-422F-B655-B7F03F25F223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BBE794A6-B159-422F-B655-B7F03F25F223}.Debug|x64.ActiveCfg = Debug|Any CPU - {BBE794A6-B159-422F-B655-B7F03F25F223}.Debug|x86.ActiveCfg = Debug|x86 - {BBE794A6-B159-422F-B655-B7F03F25F223}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BBE794A6-B159-422F-B655-B7F03F25F223}.Release|x64.ActiveCfg = Release|Any CPU - {BBE794A6-B159-422F-B655-B7F03F25F223}.Release|x86.ActiveCfg = Release|x86 - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|x64.ActiveCfg = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|x64.Build.0 = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|x86.ActiveCfg = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|x64.ActiveCfg = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|x64.Build.0 = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|x86.ActiveCfg = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Debug|Any CPU.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}.Debug|x86.ActiveCfg = Debug|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|Any CPU.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 - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|x86.ActiveCfg = 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|x64.ActiveCfg = Debug|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.Release|x64.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 - {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|x64.ActiveCfg = Debug|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.Release|x64.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 - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|x64.ActiveCfg = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|x64.Build.0 = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|x86.ActiveCfg = Debug|x86 - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|x64.ActiveCfg = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|x64.Build.0 = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|x86.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x64.ActiveCfg = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x64.Build.0 = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x86.ActiveCfg = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x86.Build.0 = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|Any CPU.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x64.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x64.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x86.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x86.Build.0 = Release|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Debug|x64.ActiveCfg = Debug|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Debug|x64.Build.0 = Debug|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Debug|x86.ActiveCfg = Debug|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Release|x64.ActiveCfg = Release|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Release|x64.Build.0 = Release|Any CPU - {6DE97986-8304-45AE-BD47-63A5563F9C3A}.Release|x86.ActiveCfg = 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|x64.ActiveCfg = Debug|Any CPU - {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}.Release|x64.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 - {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|x64.ActiveCfg = Debug|Any CPU - {872BFF20-7AD9-4741-B163-CD648AD3418F}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {872BFF20-7AD9-4741-B163-CD648AD3418F}.Release|x64.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 - {76F2521D-44D7-48C9-A678-074E22B46092}.Debug|Any CPU.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}.Debug|x86.ActiveCfg = Debug|Any CPU - {76F2521D-44D7-48C9-A678-074E22B46092}.Release|Any CPU.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 - {76F2521D-44D7-48C9-A678-074E22B46092}.Release|x86.ActiveCfg = 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|x64.ActiveCfg = Debug|Any CPU - {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Debug|x64.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}.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|x64.ActiveCfg = Release|Any CPU - {4FF7C82D-BFEF-415E-81FF-5C0337E99845}.Release|x64.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 - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Debug|Any CPU.Build.0 = Debug|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Debug|x64.ActiveCfg = Debug|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Debug|x64.Build.0 = Debug|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Debug|x86.ActiveCfg = Debug|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Debug|x86.Build.0 = Debug|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Release|Any CPU.ActiveCfg = Release|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Release|Any CPU.Build.0 = Release|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Release|x64.ActiveCfg = Release|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Release|x64.Build.0 = Release|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Release|x86.ActiveCfg = Release|Any CPU - {600ACA55-4077-4D29-8C51-DBA93EB34609}.Release|x86.Build.0 = Release|Any CPU - {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|Any CPU.ActiveCfg = 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}.Debug|x86.ActiveCfg = Debug|Win32 - {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Debug|x86.Build.0 = Debug|Win32 - {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|x64.ActiveCfg = Release|x64 - {2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}.Release|x64.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 - {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|Any CPU.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}.Debug|x86.ActiveCfg = Debug|Win32 - {55890DF2-D13E-4C89-A01D-79CAD6726246}.Debug|x86.Build.0 = Debug|Win32 - {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|x64.ActiveCfg = Release|x64 - {55890DF2-D13E-4C89-A01D-79CAD6726246}.Release|x64.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 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {93ACC849-E7E1-4695-B59D-54B3737E48A6} = {91059C5B-526C-4B81-B106-99DEFF542D1F} - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB} = {91059C5B-526C-4B81-B106-99DEFF542D1F} - {4FF7C82D-BFEF-415E-81FF-5C0337E99845} = {91059C5B-526C-4B81-B106-99DEFF542D1F} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A4A871EF-C5A7-478F-907E-31C69A869973} - EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - Policies = $0 - $0.DotNetNamingPolicy = $1 - $1.DirectoryNamespaceAssociation = PrefixedHierarchical - $0.TextStylePolicy = $2 - $2.inheritsSet = null - $2.scope = application/xaml+xml - $0.XmlFormattingPolicy = $3 - $3.inheritsSet = null - $3.scope = application/xaml+xml - EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34701.34 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "TestApp\TestApp.csproj", "{4585BA5D-9EC4-4C89-8250-2033D2AC2999}" + ProjectSection(ProjectDependencies) = postProject + {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} = {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine", "ScriptEngine\ScriptEngine.csproj", "{F062D1D9-D307-492A-A56B-FF3C55F8F6C0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine.HostedScript", "ScriptEngine.HostedScript\ScriptEngine.HostedScript.csproj", "{F09A46BD-5737-45E7-BA60-A47C9F7821A9}" + ProjectSection(ProjectDependencies) = postProject + {8873BA09-919E-4439-8EEB-87CB8E74656C} = {8873BA09-919E-4439-8EEB-87CB8E74656C} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "oscript", "oscript\oscript.csproj", "{2590E2BB-CC1F-4775-80ED-451F45C9A4F1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D14BF321-348C-46B8-B96A-43A22BA7AC10}" + ProjectSection(SolutionItems) = preProject + ..\Build.csproj = ..\Build.csproj + ..\Jenkinsfile = ..\Jenkinsfile + oscommon.targets = oscommon.targets + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Component", "Component\Component.csproj", "{B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VSCode.DebugAdapter", "VSCode.DebugAdapter\VSCode.DebugAdapter.csproj", "{C979F151-AA29-47E4-B020-3039BA0986D9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.DebugProtocol", "OneScript.DebugProtocol\OneScript.DebugProtocol.csproj", "{727A498F-BF50-42F8-8523-40C0B5B1B233}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{91059C5B-526C-4B81-B106-99DEFF542D1F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Language", "OneScript.Language\OneScript.Language.csproj", "{0B30DF8B-BD15-4C63-9CC4-1E123566B5B3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Language.Tests", "Tests\OneScript.Language.Tests\OneScript.Language.Tests.csproj", "{872BFF20-7AD9-4741-B163-CD648AD3418F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScriptDocumenter", "OneScriptDocumenter\OneScriptDocumenter.csproj", "{76F2521D-44D7-48C9-A678-074E22B46092}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Core.Tests", "Tests\OneScript.Core.Tests\OneScript.Core.Tests.csproj", "{4FF7C82D-BFEF-415E-81FF-5C0337E99845}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.StandardLibrary", "OneScript.StandardLibrary\OneScript.StandardLibrary.csproj", "{86CFEC6C-2835-4EEB-9842-14B6A455A80C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ScriptEngine.NativeApi", "ScriptEngine.NativeApi\ScriptEngine.NativeApi.vcxproj", "{2D3C486D-B370-451C-8B0C-EFF2D6C9FB61}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AddInNative", "..\tests\native-api\AddInNative.vcxproj", "{55890DF2-D13E-4C89-A01D-79CAD6726246}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.DebugProtocol.Test", "Tests\OneScript.DebugProtocol.Test\OneScript.DebugProtocol.Test.csproj", "{6D02017A-189F-45D7-B286-D67536AB4907}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.DebugServices", "OneScript.DebugServices\OneScript.DebugServices.csproj", "{B17AE4BE-3640-49CB-B771-C9D653067C5E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Core", "OneScript.Core\OneScript.Core.csproj", "{6A246728-007D-4C1E-830A-DC8420ACA864}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Dynamic.Tests", "Tests\OneScript.Dynamic.Tests\OneScript.Dynamic.Tests.csproj", "{0F5E6099-39BA-41CF-B55F-357F7DF4DE00}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Native", "OneScript.Native\OneScript.Native.csproj", "{90E896C1-FEAE-4C71-8E47-3E3F2D9C926C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.StandardLibrary.Tests", "Tests\OneScript.StandardLibrary.Tests\OneScript.StandardLibrary.Tests.csproj", "{2F264379-B3B4-44B3-9CBA-A4B0AF3D8785}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Web.Server", "OneScript.Web.Server\OneScript.Web.Server.csproj", "{8873BA09-919E-4439-8EEB-87CB8E74656C}" + ProjectSection(ProjectDependencies) = postProject + {86CFEC6C-2835-4EEB-9842-14B6A455A80C} = {86CFEC6C-2835-4EEB-9842-14B6A455A80C} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumenterTests", "Tests\DocumenterTests\DocumenterTests.csproj", "{BD385142-E9B4-43C1-8F88-067F24E5AF6D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + LinuxDebug|Any CPU = LinuxDebug|Any CPU + LinuxDebug|x86 = LinuxDebug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + 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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|x86.ActiveCfg = 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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + {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}.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}.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 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {872BFF20-7AD9-4741-B163-CD648AD3418F} = {91059C5B-526C-4B81-B106-99DEFF542D1F} + {4FF7C82D-BFEF-415E-81FF-5C0337E99845} = {91059C5B-526C-4B81-B106-99DEFF542D1F} + {55890DF2-D13E-4C89-A01D-79CAD6726246} = {91059C5B-526C-4B81-B106-99DEFF542D1F} + {6D02017A-189F-45D7-B286-D67536AB4907} = {91059C5B-526C-4B81-B106-99DEFF542D1F} + {0F5E6099-39BA-41CF-B55F-357F7DF4DE00} = {91059C5B-526C-4B81-B106-99DEFF542D1F} + {2F264379-B3B4-44B3-9CBA-A4B0AF3D8785} = {91059C5B-526C-4B81-B106-99DEFF542D1F} + {BD385142-E9B4-43C1-8F88-067F24E5AF6D} = {91059C5B-526C-4B81-B106-99DEFF542D1F} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A4A871EF-C5A7-478F-907E-31C69A869973} + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + Policies = $0 + $0.DotNetNamingPolicy = $1 + $1.DirectoryNamespaceAssociation = PrefixedHierarchical + $0.TextStylePolicy = $2 + $2.inheritsSet = null + $2.scope = application/xaml+xml + $0.XmlFormattingPolicy = $3 + $3.inheritsSet = null + $3.scope = application/xaml+xml + EndGlobalSection +EndGlobal diff --git a/src/1Script_Mono.sln b/src/1Script_Mono.sln deleted file mode 100644 index f138cfbd8..000000000 --- a/src/1Script_Mono.sln +++ /dev/null @@ -1,165 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2020 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine", "ScriptEngine\ScriptEngine.csproj", "{F062D1D9-D307-492A-A56B-FF3C55F8F6C0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptEngine.HostedScript", "ScriptEngine.HostedScript\ScriptEngine.HostedScript.csproj", "{F09A46BD-5737-45E7-BA60-A47C9F7821A9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "oscript", "oscript\oscript.csproj", "{2590E2BB-CC1F-4775-80ED-451F45C9A4F1}" - ProjectSection(ProjectDependencies) = postProject - {795AA2F5-3074-4BC5-A30F-1B6354044D9B} = {795AA2F5-3074-4BC5-A30F-1B6354044D9B} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneRunner", "StandaloneRunner\StandaloneRunner.csproj", "{795AA2F5-3074-4BC5-A30F-1B6354044D9B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D14BF321-348C-46B8-B96A-43A22BA7AC10}" - ProjectSection(SolutionItems) = preProject - oscommon.targets = oscommon.targets - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnitTests", "NUnitTests\NUnitTests.csproj", "{93ACC849-E7E1-4695-B59D-54B3737E48A6}" - ProjectSection(ProjectDependencies) = postProject - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1} = {2590E2BB-CC1F-4775-80ED-451F45C9A4F1} - {F09A46BD-5737-45E7-BA60-A47C9F7821A9} = {F09A46BD-5737-45E7-BA60-A47C9F7821A9} - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} = {F062D1D9-D307-492A-A56B-FF3C55F8F6C0} - {795AA2F5-3074-4BC5-A30F-1B6354044D9B} = {795AA2F5-3074-4BC5-A30F-1B6354044D9B} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Component", "Component\Component.csproj", "{B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DebugServer", "DebugServer\DebugServer.csproj", "{C979F151-AA29-47E4-B020-3039BA0986D9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.DebugProtocol", "OneScript.DebugProtocol\OneScript.DebugProtocol.csproj", "{727A498F-BF50-42F8-8523-40C0B5B1B233}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "oscriptTests", "oscriptTest\oscriptTests.csproj", "{69A7869C-203C-4F09-AC3F-04E9B52AD7AB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{91059C5B-526C-4B81-B106-99DEFF542D1F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HTTPServices", "ASPNETHandler\HTTPServices.csproj", "{B7CD7F52-E387-490E-8F77-E1FB060401B5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x86 = Release|x86 - ReleaseNetStandard|Any CPU = ReleaseNetStandard|Any CPU - ReleaseNetStandard|x86 = ReleaseNetStandard|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {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}.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}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {F062D1D9-D307-492A-A56B-FF3C55F8F6C0}.ReleaseNetStandard|x86.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}.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}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {F09A46BD-5737-45E7-BA60-A47C9F7821A9}.ReleaseNetStandard|x86.Build.0 = Release|Any CPU - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.Debug|Any CPU.ActiveCfg = Debug|x86 - {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}.Release|Any CPU.ActiveCfg = Release|x86 - {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}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|x86 - {2590E2BB-CC1F-4775-80ED-451F45C9A4F1}.ReleaseNetStandard|x86.ActiveCfg = Release|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x86.ActiveCfg = Debug|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Debug|x86.Build.0 = Debug|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|Any CPU.Build.0 = Release|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x86.ActiveCfg = Release|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.Release|x86.Build.0 = Release|x86 - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {795AA2F5-3074-4BC5-A30F-1B6354044D9B}.ReleaseNetStandard|x86.ActiveCfg = Release|x86 - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Debug|x86.ActiveCfg = Debug|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|Any CPU.Build.0 = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.Release|x86.ActiveCfg = Release|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6}.ReleaseNetStandard|x86.ActiveCfg = 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}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|Any CPU.Build.0 = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.Release|x86.ActiveCfg = Release|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {B6C3C000-699B-4A2F-92D1-EEAEA9CFE2AB}.ReleaseNetStandard|x86.ActiveCfg = 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}.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}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {C979F151-AA29-47E4-B020-3039BA0986D9}.ReleaseNetStandard|x86.ActiveCfg = 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}.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}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {727A498F-BF50-42F8-8523-40C0B5B1B233}.ReleaseNetStandard|x86.Build.0 = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Debug|x86.ActiveCfg = Debug|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|Any CPU.Build.0 = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.Release|x86.ActiveCfg = Release|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.ReleaseNetStandard|Any CPU.ActiveCfg = ReleaseNetStandard|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.ReleaseNetStandard|Any CPU.Build.0 = ReleaseNetStandard|Any CPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x86.ActiveCfg = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Debug|x86.Build.0 = Debug|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|Any CPU.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x86.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.Release|x86.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.ReleaseNetStandard|Any CPU.ActiveCfg = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.ReleaseNetStandard|Any CPU.Build.0 = Release|Any CPU - {B7CD7F52-E387-490E-8F77-E1FB060401B5}.ReleaseNetStandard|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB} = {91059C5B-526C-4B81-B106-99DEFF542D1F} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A4A871EF-C5A7-478F-907E-31C69A869973} - EndGlobalSection -EndGlobal diff --git a/src/ASPNETHandler/ASPNETHandler.cs b/src/ASPNETHandler/ASPNETHandler.cs deleted file mode 100644 index 2817ae345..000000000 --- a/src/ASPNETHandler/ASPNETHandler.cs +++ /dev/null @@ -1,318 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using System.Runtime.Caching; -using System.IO; - -using ScriptEngine; -using ScriptEngine.Machine; -using ScriptEngine.Environment; -using ScriptEngine.HostedScript; - -using System.Runtime.CompilerServices; - -namespace OneScript.ASPNETHandler -{ - public class ASPNETHandler : IHttpHandler, System.Web.SessionState.IRequiresSessionState - { - HostedScriptEngine _hostedScript; - // Разрешает или запрещает кэширование исходников *.os В Linux должно быть false иначе после изменений исходника старая версия будет в кэше - // web.config -> -> - static bool _cachingEnabled; - // Список дополнительных сборок, которые надо приаттачить к движку. Могут быть разные расширения - // web.config -> -> Сделано так для простоты. Меньше настроек - дольше жизнь :) - static List _assembliesForAttaching; - - public bool IsReusable - { - // Разрешаем повторное использование и храним среду выполнения и контекст - get { return true; } - } - static ASPNETHandler() - { - _assembliesForAttaching = new List(); - - System.Collections.Specialized.NameValueCollection appSettings = System.Web.Configuration.WebConfigurationManager.AppSettings; - - TextWriter logWriter = OpenLog(appSettings); - - _cachingEnabled = (appSettings["cachingEnabled"] == "true"); - WriteToLog(logWriter, "Start assemblies loading."); - - foreach (string assemblyName in appSettings.AllKeys) - { - if (appSettings[assemblyName] == "attachAssembly") - { - try - { - _assembliesForAttaching.Add(System.Reflection.Assembly.Load(assemblyName)); - } - // TODO: Исправить - должно падать. Если конфиг сайта неработоспособен - сайт не должен быть работоспособен. - catch(Exception ex) - { - WriteToLog(logWriter, "Error loading assembly: " + assemblyName + " " + ex.ToString()); - if (appSettings["handlerLoadingPolicy"] == "strict") - throw; // Must fail! - } - } - } - - WriteToLog(logWriter, "Stop assemblies loading."); - CloseLog(logWriter); - } - - public ASPNETHandler() - { - System.Collections.Specialized.NameValueCollection appSettings = System.Web.Configuration.WebConfigurationManager.AppSettings; - - // Инициализируем логгирование, если надо - TextWriter logWriter = OpenLog(appSettings); - - WriteToLog(logWriter, "Start loading."); - - try - { - _hostedScript = new HostedScriptEngine(); - // метод настраивает внутренние переменные у SystemGlobalContext - if (appSettings["enableEcho"] == "true") - _hostedScript.SetGlobalEnvironment(new ASPNetApplicationHost(), new AspEntryScriptSrc(appSettings["startupScript"] ?? HttpContext.Current.Server.MapPath("~/web.config"))); - else - _hostedScript.SetGlobalEnvironment(new AspNetNullApplicationHost(), new AspNetNullEntryScriptSrc()); - - _hostedScript.Initialize(); - - - // Размещаем oscript.cfg вместе с web.config. Так наверное привычнее - _hostedScript.CustomConfig = appSettings["configFilePath"] ?? HttpContext.Current.Server.MapPath("~/oscript.cfg"); - _hostedScript.AttachAssembly(System.Reflection.Assembly.GetExecutingAssembly()); - // Аттачим доп сборки. По идее должны лежать в Bin - foreach (System.Reflection.Assembly assembly in _assembliesForAttaching) - { - try - { - _hostedScript.AttachAssembly(assembly); - } - catch (Exception ex) - { - // Возникла проблема при аттаче сборки - WriteToLog(logWriter, "Assembly attaching error: " + ex.Message); - if (appSettings["handlerLoadingPolicy"] == "strict") - throw; - } - } - - //Загружаем библиотечные скрипты aka общие модули - string libPath = appSettings["commonModulesPath"]; - - if (libPath != null) - { - libPath = HttpContext.Current.Server.MapPath(libPath); - - string[] files = System.IO.Directory.GetFiles(libPath, "*.os"); - - - foreach (string filePathName in files) - { - _hostedScript.InjectGlobalProperty(System.IO.Path.GetFileNameWithoutExtension(filePathName), ValueFactory.Create(), true); - } - - foreach (string filePathName in files) - { - try - { - ICodeSource src = _hostedScript.Loader.FromFile(filePathName); - - var compilerService = _hostedScript.GetCompilerService(); - var module = compilerService.Compile(src); - var loaded = _hostedScript.EngineInstance.LoadModuleImage(module); - var instance = (IValue)_hostedScript.EngineInstance.NewObject(loaded); - _hostedScript.EngineInstance.Environment.SetGlobalProperty(System.IO.Path.GetFileNameWithoutExtension(filePathName), instance); - } - catch (Exception ex) - { - // Возникла проблема при загрузке файла os, логгируем, если логгирование включено - WriteToLog(logWriter, "Error loading " + System.IO.Path.GetFileNameWithoutExtension(filePathName) + " : " + ex.Message); - if (appSettings["handlerLoadingPolicy"] == "strict") - throw; - } - } - } - - _hostedScript.EngineInstance.Environment.LoadMemory(MachineInstance.Current); - - } - catch (Exception ex) - { - // Возникла проблема при инициализации хэндлера - WriteToLog(logWriter, ex.Message); - - if (appSettings["handlerLoadingPolicy"] == "strict") - throw; // Must fail! - } - finally - { - WriteToLog(logWriter, "End loading."); - CloseLog(logWriter); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static TextWriter OpenLog(System.Collections.Specialized.NameValueCollection appSettings = null) - { - if (appSettings == null) - appSettings = System.Web.Configuration.WebConfigurationManager.AppSettings; - - string logPath = appSettings["logToPath"]; - - try - { - if (logPath != null) - { - logPath = HttpContext.Current.Server.MapPath(logPath); - string logFileName = Guid.NewGuid().ToString().Replace("-", "") + ".txt"; - return File.CreateText(Path.Combine(logPath, logFileName)); - } - else - return null; - } - catch - { - return null; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void WriteToLog(TextWriter logWriter, string message) - { - if (logWriter == null) - return; - try - { - logWriter.WriteLine(message); - } - catch { /* что-то не так, ничего не делаем */ } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static void CloseLog(TextWriter logWriter) - { - if (logWriter != null) - { - try - { - logWriter.Flush(); - logWriter.Close(); - } - catch - { /*что-то не так, ничего не делаем.*/ } - } - } - - public void ProcessRequest(HttpContext context) - { - CallScriptHandler(context); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ProduceResponse(HttpContext context, IRuntimeContextInstance runner) - { - int methodIndex = runner.FindMethod("ОбработкаВызоваHTTPСервиса"); - IValue result; - IValue[] args = new IValue[1]; - args[0] = new ScriptEngine.HostedScript.Library.HTTPService.HTTPServiceRequestImpl(context); - runner.CallAsFunction(methodIndex, args, out result); - - // Обрабатываем результаты - var response = (ScriptEngine.HostedScript.Library.HTTPService.HTTPServiceResponseImpl) result; - context.Response.StatusCode = response.StatusCode; - - if (response.Headers != null) - { - foreach (var ch in response.Headers) - { - context.Response.AddHeader(ch.Key.AsString(), ch.Value.AsString()); - } - } - - if (response.Reason != "") - { - context.Response.Status = response.Reason; - } - - if (response.BodyStream != null) - { - response.BodyStream.Seek(0, SeekOrigin.Begin); - response.BodyStream.CopyTo(context.Response.OutputStream); - } - - context.Response.Charset = response.ContentCharset; - context.Response.End(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private IRuntimeContextInstance CreateServiceInstance(LoadedModule module) - { - var runner = _hostedScript.EngineInstance.NewObject(module); - return runner; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private LoadedModule LoadByteCode(string filePath) - { - var code = _hostedScript.EngineInstance.Loader.FromFile(filePath); - var compiler = _hostedScript.GetCompilerService(); - var byteCode = compiler.Compile(code); - var module = _hostedScript.EngineInstance.LoadModuleImage(byteCode); - return module; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CallScriptHandler(HttpContext context) - { - _hostedScript.EngineInstance.Environment.LoadMemory(MachineInstance.Current); - #region Загружаем скрипт (файл .os) - // Кэшируем исходный файл, если файл изменился (изменили скрипт .os) загружаем заново - // В Linux под Mono не работает подписка на изменение файла. - LoadedModule module = null; - ObjectCache cache = MemoryCache.Default; - - if (_cachingEnabled) - { - module = cache[context.Request.PhysicalPath] as LoadedModule; - - if (module == null) - { - CacheItemPolicy policy = new CacheItemPolicy(); - - List filePaths = new List(); - filePaths.Add(context.Request.PhysicalPath); - policy.ChangeMonitors.Add(new HostFileChangeMonitor(filePaths)); - - // Загружаем файл и помещаем его в кэш - module = LoadByteCode(context.Request.PhysicalPath); - cache.Set(context.Request.PhysicalPath, module, policy); - } - } - else - { - module = LoadByteCode(context.Request.PhysicalPath); - } - - #endregion - - var runner = CreateServiceInstance(module); - - ProduceResponse(context, runner); - } - } -} diff --git a/src/ASPNETHandler/ASPNetApplicationHost.cs b/src/ASPNETHandler/ASPNetApplicationHost.cs deleted file mode 100644 index a5859d651..000000000 --- a/src/ASPNETHandler/ASPNetApplicationHost.cs +++ /dev/null @@ -1,75 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; - -namespace OneScript.ASPNETHandler -{ - class ASPNetApplicationHost : IHostApplication - { - public void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary) - { - if (status == MessageStatusEnum.Ordinary) - { - Console.WriteLine(str); - } - else - { - var oldColor = Console.ForegroundColor; - ConsoleColor newColor; - - switch (status) - { - case MessageStatusEnum.Information: - newColor = ConsoleColor.Green; - break; - case MessageStatusEnum.Attention: - newColor = ConsoleColor.Yellow; - break; - case MessageStatusEnum.Important: - case MessageStatusEnum.VeryImportant: - newColor = ConsoleColor.Red; - break; - default: - newColor = oldColor; - break; - } - - try - { - Console.ForegroundColor = newColor; - Console.WriteLine(str); - } - finally - { - Console.ForegroundColor = oldColor; - } - } - } - - public void ShowExceptionInfo(Exception exc) - { - throw new NotImplementedException(); - } - - public bool InputString(out string result, int maxLen) - { - throw new NotImplementedException(); - } - - public string[] GetCommandLineArguments() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/ASPNETHandler/ASPNetNullApplicationHost.cs b/src/ASPNETHandler/ASPNetNullApplicationHost.cs deleted file mode 100644 index 489f4afc0..000000000 --- a/src/ASPNETHandler/ASPNetNullApplicationHost.cs +++ /dev/null @@ -1,50 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; - -namespace OneScript.ASPNETHandler -{ - // Класс-заглушка для вывода Сообщить. - // Поведение как на сервере 1С - public class AspNetNullApplicationHost : IHostApplication - { - public AspNetNullApplicationHost() - { - - } - - public void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary) - { - // Обработчик Сообщить, в HTTPСервисе ничего не делает, также поступаем и мы - } - - public void ShowExceptionInfo(Exception exc) - { - // Непонятно что это, наверное аналог системного диалога, на сервере нет никаких диалогов - } - - public bool InputString(out string result, int maxLen) - { - // Мы не можем вводить никаких строк на сервере в 1С это недоступно - result = null; - return false; - } - - public string[] GetCommandLineArguments() - { - // У нас нет никаких аргументов командной строки - return new string[0]; // возвращаем массив из 0 аргументов т.к у нас их нет - } - } -} diff --git a/src/ASPNETHandler/AspEntryScriptSrc.cs b/src/ASPNETHandler/AspEntryScriptSrc.cs deleted file mode 100644 index 5acac9023..000000000 --- a/src/ASPNETHandler/AspEntryScriptSrc.cs +++ /dev/null @@ -1,36 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.Environment; - -namespace OneScript.ASPNETHandler -{ - class AspEntryScriptSrc : ICodeSource - { - string _sourceDescription; - - public string Code - { - get { return ""; } - } - public AspEntryScriptSrc(string sourceDescription) - { - _sourceDescription = sourceDescription; - } - public string SourceDescription - { - get { return _sourceDescription; } - } - } -} diff --git a/src/ASPNETHandler/AspNetNullEntryScriptSrc.cs b/src/ASPNETHandler/AspNetNullEntryScriptSrc.cs deleted file mode 100644 index b2bfbb5f3..000000000 --- a/src/ASPNETHandler/AspNetNullEntryScriptSrc.cs +++ /dev/null @@ -1,32 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.Environment; - - -namespace OneScript.ASPNETHandler -{ - // Класс заглушка стартового скрипта. У нас нет стартового скрипта, поскольку это веб-приложение - class AspNetNullEntryScriptSrc : ICodeSource - { - public string Code - { - get { return ""; } - } - - public string SourceDescription - { - get { return ""; } - } - } -} diff --git a/src/ASPNETHandler/DemoApp/Default.os b/src/ASPNETHandler/DemoApp/Default.os deleted file mode 100644 index 4b851ab35..000000000 --- a/src/ASPNETHandler/DemoApp/Default.os +++ /dev/null @@ -1,57 +0,0 @@ - -// Это предопределенная функция - обработчик запроса HTTP-сервиса -// -Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт - - Если Запрос.HTTPМетод = "GET" Тогда - - Возврат ПоказатьГлавнуюСтраницу(Запрос); - - Иначе - - Возврат ПоказатьИсходныйКод(Запрос); - - КонецЕсли; - -КонецФункции - -Функция ПоказатьГлавнуюСтраницу(Запрос) - - Контекст = Запрос.Контекст; - - ИмяФайлаШаблона = СтрЗаменить(Контекст.ФизическийПуть, "Default.os", "Default.ospt"); - СтрокаШаблон = ПолучитьМакетСтраницы(ИмяФайлаШаблона); - СтрокаТело = СтрЗаменить(СтрокаШаблон, "{ClientAddress}", Контекст.АдресКлиента); - - Ответ = Новый HTTPСервисОтвет(200); - Ответ.УстановитьТелоИзСтроки(СтрокаТело); - - Возврат Ответ; - -КонецФункции - -Функция ПоказатьИсходныйКод(Запрос) - - Контекст = Запрос.Контекст; - - СтрокаИсходныйКод = ПолучитьМакетСтраницы(Контекст.ФизическийПуть); - - ИмяФайлаШаблона = СтрЗаменить(Контекст.ФизическийПуть, "Default.os", "Source.ospt"); - СтрокаШаблон = ПолучитьМакетСтраницы(ИмяФайлаШаблона); - - СтрокаТело = СтрЗаменить(СтрокаШаблон, "{SourceCode}", СтрокаИсходныйКод); - - Ответ = Новый HTTPСервисОтвет(200); - Ответ.УстановитьТелоИзСтроки(СтрокаТело); - - Возврат Ответ; - -КонецФункции - -Функция ПолучитьМакетСтраницы(ПутьКФайлу) - - Документ = Новый ТекстовыйДокумент; - Документ.Прочитать(ПутьКФайлу); - Возврат Документ.ПолучитьТекст(); - -КонецФункции \ No newline at end of file diff --git a/src/ASPNETHandler/DemoApp/Default.ospt b/src/ASPNETHandler/DemoApp/Default.ospt deleted file mode 100644 index b70b1d892..000000000 --- a/src/ASPNETHandler/DemoApp/Default.ospt +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - OneScript HTTP Service Test Page - - - -
-

Привет ({ClientAddress})!

-

Эта страница создана HTTP сервисом OneScript.

-
-
-
-
- - -
-
-
- - - \ No newline at end of file diff --git a/src/ASPNETHandler/DemoApp/highlight.pack.js b/src/ASPNETHandler/DemoApp/highlight.pack.js deleted file mode 100644 index 2b63060fe..000000000 --- a/src/ASPNETHandler/DemoApp/highlight.pack.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ -!function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("1c",function(s){var x="[A-Za-zА-Яа-яёЁ_][A-Za-zА-Яа-яёЁ_0-9]+",o="далее ",m="возврат вызватьисключение выполнить для если и из или иначе иначеесли исключение каждого конецесли конецпопытки конеццикла не новый перейти перем по пока попытка прервать продолжить тогда цикл экспорт ",t=o+m,l="загрузитьизфайла ",e="вебклиент вместо внешнеесоединение клиент конецобласти мобильноеприложениеклиент мобильноеприложениесервер наклиенте наклиентенасервере наклиентенасерверебезконтекста насервере насерверебезконтекста область перед после сервер толстыйклиентобычноеприложение толстыйклиентуправляемоеприложение тонкийклиент ",n=l+e,a="разделительстраниц разделительстрок символтабуляции ",d="ansitooem oemtoansi ввестивидсубконто ввестиперечисление ввестипериод ввестиплансчетов выбранныйплансчетов датагод датамесяц датачисло заголовоксистемы значениевстроку значениеизстроки каталогиб каталогпользователя кодсимв конгода конецпериодаби конецрассчитанногопериодаби конецстандартногоинтервала конквартала конмесяца коннедели лог лог10 максимальноеколичествосубконто названиеинтерфейса названиенабораправ назначитьвид назначитьсчет найтиссылки началопериодаби началостандартногоинтервала начгода начквартала начмесяца начнедели номерднягода номерднянедели номернеделигода обработкаожидания основнойжурналрасчетов основнойплансчетов основнойязык очиститьокносообщений периодстр получитьвремята получитьдатута получитьдокументта получитьзначенияотбора получитьпозициюта получитьпустоезначение получитьта префиксавтонумерации пропись пустоезначение разм разобратьпозициюдокумента рассчитатьрегистрына рассчитатьрегистрыпо симв создатьобъект статусвозврата стрколичествострок сформироватьпозициюдокумента счетпокоду текущеевремя типзначения типзначениястр установитьтана установитьтапо фиксшаблон шаблон ",i="acos asin atan base64значение base64строка cos exp log log10 pow sin sqrt tan xmlзначение xmlстрока xmlтип xmlтипзнч активноеокно безопасныйрежим безопасныйрежимразделенияданных булево ввестидату ввестизначение ввестистроку ввестичисло возможностьчтенияxml вопрос восстановитьзначение врег выгрузитьжурналрегистрации выполнитьобработкуоповещения выполнитьпроверкуправдоступа вычислить год данныеформывзначение дата день деньгода деньнедели добавитьмесяц заблокироватьданныедляредактирования заблокироватьработупользователя завершитьработусистемы загрузитьвнешнююкомпоненту закрытьсправку записатьjson записатьxml записатьдатуjson записьжурналарегистрации заполнитьзначениясвойств запроситьразрешениепользователя запуститьприложение запуститьсистему зафиксироватьтранзакцию значениевданныеформы значениевстрокувнутр значениевфайл значениезаполнено значениеизстрокивнутр значениеизфайла изxmlтипа импортмоделиxdto имякомпьютера имяпользователя инициализироватьпредопределенныеданные информацияобошибке каталогбиблиотекимобильногоустройства каталогвременныхфайлов каталогдокументов каталогпрограммы кодироватьстроку кодлокализацииинформационнойбазы кодсимвола командасистемы конецгода конецдня конецквартала конецмесяца конецминуты конецнедели конецчаса конфигурациябазыданныхизмененадинамически конфигурацияизменена копироватьданныеформы копироватьфайл краткоепредставлениеошибки лев макс местноевремя месяц мин минута монопольныйрежим найти найтинедопустимыесимволыxml найтиокнопонавигационнойссылке найтипомеченныенаудаление найтипоссылкам найтифайлы началогода началодня началоквартала началомесяца началоминуты началонедели началочаса начатьзапросразрешенияпользователя начатьзапускприложения начатькопированиефайла начатьперемещениефайла начатьподключениевнешнейкомпоненты начатьподключениерасширенияработыскриптографией начатьподключениерасширенияработысфайлами начатьпоискфайлов начатьполучениекаталогавременныхфайлов начатьполучениекаталогадокументов начатьполучениерабочегокаталогаданныхпользователя начатьполучениефайлов начатьпомещениефайла начатьпомещениефайлов начатьсозданиедвоичныхданныхизфайла начатьсозданиекаталога начатьтранзакцию начатьудалениефайлов начатьустановкувнешнейкомпоненты начатьустановкурасширенияработыскриптографией начатьустановкурасширенияработысфайлами неделягода необходимостьзавершениясоединения номерсеансаинформационнойбазы номерсоединенияинформационнойбазы нрег нстр обновитьинтерфейс обновитьнумерациюобъектов обновитьповторноиспользуемыезначения обработкапрерыванияпользователя объединитьфайлы окр описаниеошибки оповестить оповеститьобизменении отключитьобработчикзапросанастроекклиенталицензирования отключитьобработчикожидания отключитьобработчикоповещения открытьзначение открытьиндекссправки открытьсодержаниесправки открытьсправку открытьформу открытьформумодально отменитьтранзакцию очиститьжурналрегистрации очиститьнастройкипользователя очиститьсообщения параметрыдоступа перейтипонавигационнойссылке переместитьфайл подключитьвнешнююкомпоненту подключитьобработчикзапросанастроекклиенталицензирования подключитьобработчикожидания подключитьобработчикоповещения подключитьрасширениеработыскриптографией подключитьрасширениеработысфайлами подробноепредставлениеошибки показатьвводдаты показатьвводзначения показатьвводстроки показатьвводчисла показатьвопрос показатьзначение показатьинформациюобошибке показатьнакарте показатьоповещениепользователя показатьпредупреждение полноеимяпользователя получитьcomобъект получитьxmlтип получитьадреспоместоположению получитьблокировкусеансов получитьвремязавершенияспящегосеанса получитьвремязасыпанияпассивногосеанса получитьвремяожиданияблокировкиданных получитьданныевыбора получитьдополнительныйпараметрклиенталицензирования получитьдопустимыекодылокализации получитьдопустимыечасовыепояса получитьзаголовокклиентскогоприложения получитьзаголовоксистемы получитьзначенияотборажурналарегистрации получитьидентификаторконфигурации получитьизвременногохранилища получитьимявременногофайла получитьимяклиенталицензирования получитьинформациюэкрановклиента получитьиспользованиежурналарегистрации получитьиспользованиесобытияжурналарегистрации получитькраткийзаголовокприложения получитьмакетоформления получитьмаскувсефайлы получитьмаскувсефайлыклиента получитьмаскувсефайлысервера получитьместоположениепоадресу получитьминимальнуюдлинупаролейпользователей получитьнавигационнуюссылку получитьнавигационнуюссылкуинформационнойбазы получитьобновлениеконфигурациибазыданных получитьобновлениепредопределенныхданныхинформационнойбазы получитьобщиймакет получитьобщуюформу получитьокна получитьоперативнуюотметкувремени получитьотключениебезопасногорежима получитьпараметрыфункциональныхопцийинтерфейса получитьполноеимяпредопределенногозначения получитьпредставлениянавигационныхссылок получитьпроверкусложностипаролейпользователей получитьразделительпути получитьразделительпутиклиента получитьразделительпутисервера получитьсеансыинформационнойбазы получитьскоростьклиентскогосоединения получитьсоединенияинформационнойбазы получитьсообщенияпользователю получитьсоответствиеобъектаиформы получитьсоставстандартногоинтерфейсаodata получитьструктурухранениябазыданных получитьтекущийсеансинформационнойбазы получитьфайл получитьфайлы получитьформу получитьфункциональнуюопцию получитьфункциональнуюопциюинтерфейса получитьчасовойпоясинформационнойбазы пользователиос поместитьвовременноехранилище поместитьфайл поместитьфайлы прав праводоступа предопределенноезначение представлениекодалокализации представлениепериода представлениеправа представлениеприложения представлениесобытияжурналарегистрации представлениечасовогопояса предупреждение прекратитьработусистемы привилегированныйрежим продолжитьвызов прочитатьjson прочитатьxml прочитатьдатуjson пустаястрока рабочийкаталогданныхпользователя разблокироватьданныедляредактирования разделитьфайл разорватьсоединениесвнешнимисточникомданных раскодироватьстроку рольдоступна секунда сигнал символ скопироватьжурналрегистрации смещениелетнеговремени смещениестандартноговремени соединитьбуферыдвоичныхданных создатькаталог создатьфабрикуxdto сокрл сокрлп сокрп сообщить состояние сохранитьзначение сохранитьнастройкипользователя сред стрдлина стрзаканчиваетсяна стрзаменить стрнайти стрначинаетсяс строка строкасоединенияинформационнойбазы стрполучитьстроку стрразделить стрсоединить стрсравнить стрчисловхождений стрчислострок стршаблон текущаядата текущаядатасеанса текущаяуниверсальнаядата текущаяуниверсальнаядатавмиллисекундах текущийвариантинтерфейсаклиентскогоприложения текущийвариантосновногошрифтаклиентскогоприложения текущийкодлокализации текущийрежимзапуска текущийязык текущийязыксистемы тип типзнч транзакцияактивна трег удалитьданныеинформационнойбазы удалитьизвременногохранилища удалитьобъекты удалитьфайлы универсальноевремя установитьбезопасныйрежим установитьбезопасныйрежимразделенияданных установитьблокировкусеансов установитьвнешнююкомпоненту установитьвремязавершенияспящегосеанса установитьвремязасыпанияпассивногосеанса установитьвремяожиданияблокировкиданных установитьзаголовокклиентскогоприложения установитьзаголовоксистемы установитьиспользованиежурналарегистрации установитьиспользованиесобытияжурналарегистрации установитькраткийзаголовокприложения установитьминимальнуюдлинупаролейпользователей установитьмонопольныйрежим установитьнастройкиклиенталицензирования установитьобновлениепредопределенныхданныхинформационнойбазы установитьотключениебезопасногорежима установитьпараметрыфункциональныхопцийинтерфейса установитьпривилегированныйрежим установитьпроверкусложностипаролейпользователей установитьрасширениеработыскриптографией установитьрасширениеработысфайлами установитьсоединениесвнешнимисточникомданных установитьсоответствиеобъектаиформы установитьсоставстандартногоинтерфейсаodata установитьчасовойпоясинформационнойбазы установитьчасовойпояссеанса формат цел час часовойпояс часовойпояссеанса число числопрописью этоадресвременногохранилища ",c="wsссылки библиотекакартинок библиотекамакетовоформлениякомпоновкиданных библиотекастилей бизнеспроцессы внешниеисточникиданных внешниеобработки внешниеотчеты встроенныепокупки главныйинтерфейс главныйстиль документы доставляемыеуведомления журналыдокументов задачи информацияобинтернетсоединении использованиерабочейдаты историяработыпользователя константы критерииотбора метаданные обработки отображениерекламы отправкадоставляемыхуведомлений отчеты панельзадачос параметрзапуска параметрысеанса перечисления планывидоврасчета планывидовхарактеристик планыобмена планысчетов полнотекстовыйпоиск пользователиинформационнойбазы последовательности проверкавстроенныхпокупок рабочаядата расширенияконфигурации регистрыбухгалтерии регистрынакопления регистрырасчета регистрысведений регламентныезадания сериализаторxdto справочники средствагеопозиционирования средствакриптографии средствамультимедиа средстваотображениярекламы средствапочты средствателефонии фабрикаxdto файловыепотоки фоновыезадания хранилищанастроек хранилищевариантовотчетов хранилищенастроекданныхформ хранилищеобщихнастроек хранилищепользовательскихнастроекдинамическихсписков хранилищепользовательскихнастроекотчетов хранилищесистемныхнастроек ",r=a+d+i+c,p="webцвета windowsцвета windowsшрифты библиотекакартинок рамкистиля символы цветастиля шрифтыстиля ",b="автоматическоесохранениеданныхформывнастройках автонумерациявформе автораздвижениесерий анимациядиаграммы вариантвыравниванияэлементовизаголовков вариантуправлениявысотойтаблицы вертикальнаяпрокруткаформы вертикальноеположение вертикальноеположениеэлемента видгруппыформы виддекорацииформы виддополненияэлементаформы видизмененияданных видкнопкиформы видпереключателя видподписейкдиаграмме видполяформы видфлажка влияниеразмеранапузырекдиаграммы горизонтальноеположение горизонтальноеположениеэлемента группировкаколонок группировкаподчиненныхэлементовформы группыиэлементы действиеперетаскивания дополнительныйрежимотображения допустимыедействияперетаскивания интервалмеждуэлементамиформы использованиевывода использованиеполосыпрокрутки используемоезначениеточкибиржевойдиаграммы историявыборапривводе источникзначенийоситочекдиаграммы источникзначенияразмерапузырькадиаграммы категориягруппыкоманд максимумсерий начальноеотображениедерева начальноеотображениесписка обновлениетекстаредактирования ориентациядендрограммы ориентациядиаграммы ориентацияметокдиаграммы ориентацияметоксводнойдиаграммы ориентацияэлементаформы отображениевдиаграмме отображениевлегендедиаграммы отображениегруппыкнопок отображениезаголовкашкалыдиаграммы отображениезначенийсводнойдиаграммы отображениезначенияизмерительнойдиаграммы отображениеинтерваладиаграммыганта отображениекнопки отображениекнопкивыбора отображениеобсужденийформы отображениеобычнойгруппы отображениеотрицательныхзначенийпузырьковойдиаграммы отображениепанелипоиска отображениеподсказки отображениепредупрежденияприредактировании отображениеразметкиполосырегулирования отображениестраницформы отображениетаблицы отображениетекстазначениядиаграммыганта отображениеуправленияобычнойгруппы отображениефигурыкнопки палитрацветовдиаграммы поведениеобычнойгруппы поддержкамасштабадендрограммы поддержкамасштабадиаграммыганта поддержкамасштабасводнойдиаграммы поисквтаблицепривводе положениезаголовкаэлементаформы положениекартинкикнопкиформы положениекартинкиэлементаграфическойсхемы положениекоманднойпанелиформы положениекоманднойпанелиэлементаформы положениеопорнойточкиотрисовки положениеподписейкдиаграмме положениеподписейшкалызначенийизмерительнойдиаграммы положениесостоянияпросмотра положениестрокипоиска положениетекстасоединительнойлинии положениеуправленияпоиском положениешкалывремени порядокотображенияточекгоризонтальнойгистограммы порядоксерийвлегендедиаграммы размеркартинки расположениезаголовкашкалыдиаграммы растягиваниеповертикалидиаграммыганта режимавтоотображениясостояния режимвводастроктаблицы режимвыборанезаполненного режимвыделениядаты режимвыделениястрокитаблицы режимвыделениятаблицы режимизмененияразмера режимизменениясвязанногозначения режимиспользованиядиалогапечати режимиспользованияпараметракоманды режиммасштабированияпросмотра режимосновногоокнаклиентскогоприложения режимоткрытияокнаформы режимотображениявыделения режимотображениягеографическойсхемы режимотображениязначенийсерии режимотрисовкисеткиграфическойсхемы режимполупрозрачностидиаграммы режимпробеловдиаграммы режимразмещениянастранице режимредактированияколонки режимсглаживаниядиаграммы режимсглаживанияиндикатора режимсписказадач сквозноевыравнивание сохранениеданныхформывнастройках способзаполнениятекстазаголовкашкалыдиаграммы способопределенияограничивающегозначениядиаграммы стандартнаягруппакоманд стандартноеоформление статусоповещенияпользователя стильстрелки типаппроксимациилиниитрендадиаграммы типдиаграммы типединицышкалывремени типимпортасерийслоягеографическойсхемы типлиниигеографическойсхемы типлиниидиаграммы типмаркерагеографическойсхемы типмаркерадиаграммы типобластиоформления типорганизацииисточникаданныхгеографическойсхемы типотображениясериислоягеографическойсхемы типотображенияточечногообъектагеографическойсхемы типотображенияшкалыэлементалегендыгеографическойсхемы типпоискаобъектовгеографическойсхемы типпроекциигеографическойсхемы типразмещенияизмерений типразмещенияреквизитовизмерений типрамкиэлементауправления типсводнойдиаграммы типсвязидиаграммыганта типсоединениязначенийпосериямдиаграммы типсоединенияточекдиаграммы типсоединительнойлинии типстороныэлементаграфическойсхемы типформыотчета типшкалырадарнойдиаграммы факторлиниитрендадиаграммы фигуракнопки фигурыграфическойсхемы фиксациявтаблице форматдняшкалывремени форматкартинки ширинаподчиненныхэлементовформы ",w="виддвижениябухгалтерии виддвижениянакопления видпериодарегистрарасчета видсчета видточкимаршрутабизнеспроцесса использованиеагрегатарегистранакопления использованиегруппиэлементов использованиережимапроведения использованиесреза периодичностьагрегатарегистранакопления режимавтовремя режимзаписидокумента режимпроведениядокумента ",h="авторегистрацияизменений допустимыйномерсообщения отправкаэлементаданных получениеэлементаданных ",j="использованиерасшифровкитабличногодокумента ориентациястраницы положениеитоговколоноксводнойтаблицы положениеитоговстроксводнойтаблицы положениетекстаотносительнокартинки расположениезаголовкагруппировкитабличногодокумента способчтениязначенийтабличногодокумента типдвустороннейпечати типзаполненияобластитабличногодокумента типкурсоровтабличногодокумента типлиниирисункатабличногодокумента типлинииячейкитабличногодокумента типнаправленияпереходатабличногодокумента типотображениявыделениятабличногодокумента типотображениялинийсводнойтаблицы типразмещениятекстатабличногодокумента типрисункатабличногодокумента типсмещениятабличногодокумента типузоратабличногодокумента типфайлатабличногодокумента точностьпечати чередованиерасположениястраниц ",z="отображениевремениэлементовпланировщика ",f="типфайлаформатированногодокумента ",k="обходрезультатазапроса типзаписизапроса ",u="видзаполнениярасшифровкипостроителяотчета типдобавленияпредставлений типизмеренияпостроителяотчета типразмещенияитогов ",y="доступкфайлу режимдиалогавыборафайла режимоткрытияфайла ",N="типизмеренияпостроителязапроса ",g="видданныханализа методкластеризации типединицыинтервалавременианализаданных типзаполнениятаблицырезультатаанализаданных типиспользованиячисловыхзначенийанализаданных типисточникаданныхпоискаассоциаций типколонкианализаданныхдереворешений типколонкианализаданныхкластеризация типколонкианализаданныхобщаястатистика типколонкианализаданныхпоискассоциаций типколонкианализаданныхпоискпоследовательностей типколонкимоделипрогноза типмерырасстоянияанализаданных типотсеченияправилассоциации типполяанализаданных типстандартизациианализаданных типупорядочиванияправилассоциациианализаданных типупорядочиванияшаблоновпоследовательностейанализаданных типупрощениядереварешений ",E="wsнаправлениепараметра вариантxpathxs вариантзаписидатыjson вариантпростоготипаxs видгруппымоделиxs видфасетаxdto действиепостроителяdom завершенностьпростоготипаxs завершенностьсоставноготипаxs завершенностьсхемыxs запрещенныеподстановкиxs исключениягруппподстановкиxs категорияиспользованияатрибутаxs категорияограниченияидентичностиxs категорияограниченияпространствименxs методнаследованияxs модельсодержимогоxs назначениетипаxml недопустимыеподстановкиxs обработкапробельныхсимволовxs обработкасодержимогоxs ограничениезначенияxs параметрыотбораузловdom переносстрокjson позициявдокументеdom пробельныесимволыxml типатрибутаxml типзначенияjson типканоническогоxml типкомпонентыxs типпроверкиxml типрезультатаdomxpath типузлаdom типузлаxml формаxml формапредставленияxs форматдатыjson экранированиесимволовjson ",M="видсравнениякомпоновкиданных действиеобработкирасшифровкикомпоновкиданных направлениесортировкикомпоновкиданных расположениевложенныхэлементоврезультатакомпоновкиданных расположениеитоговкомпоновкиданных расположениегруппировкикомпоновкиданных расположениеполейгруппировкикомпоновкиданных расположениеполякомпоновкиданных расположениереквизитовкомпоновкиданных расположениересурсовкомпоновкиданных типбухгалтерскогоостаткакомпоновкиданных типвыводатекстакомпоновкиданных типгруппировкикомпоновкиданных типгруппыэлементовотборакомпоновкиданных типдополненияпериодакомпоновкиданных типзаголовкаполейкомпоновкиданных типмакетагруппировкикомпоновкиданных типмакетаобластикомпоновкиданных типостаткакомпоновкиданных типпериодакомпоновкиданных типразмещениятекстакомпоновкиданных типсвязинаборовданныхкомпоновкиданных типэлементарезультатакомпоновкиданных расположениелегендыдиаграммыкомпоновкиданных типпримененияотборакомпоновкиданных режимотображенияэлементанастройкикомпоновкиданных режимотображениянастроеккомпоновкиданных состояниеэлементанастройкикомпоновкиданных способвосстановлениянастроеккомпоновкиданных режимкомпоновкирезультата использованиепараметракомпоновкиданных автопозицияресурсовкомпоновкиданных вариантиспользованиягруппировкикомпоновкиданных расположениересурсоввдиаграммекомпоновкиданных фиксациякомпоновкиданных использованиеусловногооформлениякомпоновкиданных ",_="важностьинтернетпочтовогосообщения обработкатекстаинтернетпочтовогосообщения способкодированияинтернетпочтовоговложения способкодированиянеasciiсимволовинтернетпочтовогосообщения типтекстапочтовогосообщения протоколинтернетпочты статусразборапочтовогосообщения ",v="режимтранзакциизаписижурналарегистрации статустранзакциизаписижурналарегистрации уровеньжурналарегистрации ",A="расположениехранилищасертификатовкриптографии режимвключениясертификатовкриптографии режимпроверкисертификатакриптографии типхранилищасертификатовкриптографии ",C="кодировкаименфайловвzipфайле методсжатияzip методшифрованияzip режимвосстановленияпутейфайловzip режимобработкиподкаталоговzip режимсохраненияпутейzip уровеньсжатияzip ",L="звуковоеоповещение направлениепереходакстроке позициявпотоке порядокбайтов режимблокировкиданных режимуправленияблокировкойданных сервисвстроенныхпокупок состояниефоновогозадания типподписчикадоставляемыхуведомлений уровеньиспользованиязащищенногосоединенияftp ",Z="направлениепорядкасхемызапроса типдополненияпериодамисхемызапроса типконтрольнойточкисхемызапроса типобъединениясхемызапроса типпараметрадоступнойтаблицысхемызапроса типсоединениясхемызапроса ",$="httpметод автоиспользованиеобщегореквизита автопрефиксномеразадачи вариантвстроенногоязыка видиерархии видрегистранакопления видтаблицывнешнегоисточникаданных записьдвиженийприпроведении заполнениепоследовательностей индексирование использованиебазыпланавидоврасчета использованиебыстроговыбора использованиеобщегореквизита использованиеподчинения использованиеполнотекстовогопоиска использованиеразделяемыхданныхобщегореквизита использованиереквизита назначениеиспользованияприложения назначениерасширенияконфигурации направлениепередачи обновлениепредопределенныхданных оперативноепроведение основноепредставлениевидарасчета основноепредставлениевидахарактеристики основноепредставлениезадачи основноепредставлениепланаобмена основноепредставлениесправочника основноепредставлениесчета перемещениеграницыприпроведении периодичностьномерабизнеспроцесса периодичностьномерадокумента периодичностьрегистрарасчета периодичностьрегистрасведений повторноеиспользованиевозвращаемыхзначений полнотекстовыйпоискпривводепостроке принадлежностьобъекта проведение разделениеаутентификацииобщегореквизита разделениеданныхобщегореквизита разделениерасширенийконфигурацииобщегореквизита режимавтонумерацииобъектов режимзаписирегистра режимиспользованиямодальности режимиспользованиясинхронныхвызововрасширенийплатформыивнешнихкомпонент режимповторногоиспользованиясеансов режимполученияданныхвыборапривводепостроке режимсовместимости режимсовместимостиинтерфейса режимуправленияблокировкойданныхпоумолчанию сериикодовпланавидовхарактеристик сериикодовпланасчетов сериикодовсправочника созданиепривводе способвыбора способпоискастрокипривводепостроке способредактирования типданныхтаблицывнешнегоисточникаданных типкодапланавидоврасчета типкодасправочника типмакета типномерабизнеспроцесса типномерадокумента типномеразадачи типформы удалениедвижений ",q="важностьпроблемыприменениярасширенияконфигурации вариантинтерфейсаклиентскогоприложения вариантмасштабаформклиентскогоприложения вариантосновногошрифтаклиентскогоприложения вариантстандартногопериода вариантстандартнойдатыначала видграницы видкартинки видотображенияполнотекстовогопоиска видрамки видсравнения видцвета видчисловогозначения видшрифта допустимаядлина допустимыйзнак использованиеbyteordermark использованиеметаданныхполнотекстовогопоиска источникрасширенийконфигурации клавиша кодвозвратадиалога кодировкаxbase кодировкатекста направлениепоиска направлениесортировки обновлениепредопределенныхданных обновлениеприизмененииданных отображениепанелиразделов проверказаполнения режимдиалогавопрос режимзапускаклиентскогоприложения режимокругления режимоткрытияформприложения режимполнотекстовогопоиска скоростьклиентскогосоединения состояниевнешнегоисточникаданных состояниеобновленияконфигурациибазыданных способвыборасертификатаwindows способкодированиястроки статуссообщения типвнешнейкомпоненты типплатформы типповеденияклавишиenter типэлементаинформацииовыполненииобновленияконфигурациибазыданных уровеньизоляциитранзакций хешфункция частидаты",B=p+b+w+h+j+z+f+k+u+y+N+g+E+M+_+v+A+C+L+Z+$+q,I="comобъект ftpсоединение httpзапрос httpсервисответ httpсоединение wsопределения wsпрокси xbase анализданных аннотацияxs блокировкаданных буфердвоичныхданных включениеxs выражениекомпоновкиданных генераторслучайныхчисел географическаясхема географическиекоординаты графическаясхема группамоделиxs данныерасшифровкикомпоновкиданных двоичныеданные дендрограмма диаграмма диаграммаганта диалогвыборафайла диалогвыборацвета диалогвыборашрифта диалограсписаниярегламентногозадания диалогредактированиястандартногопериода диапазон документdom документhtml документацияxs доставляемоеуведомление записьdom записьfastinfoset записьhtml записьjson записьxml записьzipфайла записьданных записьтекста записьузловdom запрос защищенноесоединениеopenssl значенияполейрасшифровкикомпоновкиданных извлечениетекста импортxs интернетпочта интернетпочтовоесообщение интернетпочтовыйпрофиль интернетпрокси интернетсоединение информациядляприложенияxs использованиеатрибутаxs использованиесобытияжурналарегистрации источникдоступныхнастроеккомпоновкиданных итераторузловdom картинка квалификаторыдаты квалификаторыдвоичныхданных квалификаторыстроки квалификаторычисла компоновщикмакетакомпоновкиданных компоновщикнастроеккомпоновкиданных конструктормакетаоформлениякомпоновкиданных конструкторнастроеккомпоновкиданных конструкторформатнойстроки линия макеткомпоновкиданных макетобластикомпоновкиданных макетоформлениякомпоновкиданных маскаxs менеджеркриптографии наборсхемxml настройкикомпоновкиданных настройкисериализацииjson обработкакартинок обработкарасшифровкикомпоновкиданных обходдереваdom объявлениеатрибутаxs объявлениенотацииxs объявлениеэлементаxs описаниеиспользованиясобытиядоступжурналарегистрации описаниеиспользованиясобытияотказвдоступежурналарегистрации описаниеобработкирасшифровкикомпоновкиданных описаниепередаваемогофайла описаниетипов определениегруппыатрибутовxs определениегруппымоделиxs определениеограниченияидентичностиxs определениепростоготипаxs определениесоставноготипаxs определениетипадокументаdom определенияxpathxs отборкомпоновкиданных пакетотображаемыхдокументов параметрвыбора параметркомпоновкиданных параметрызаписиjson параметрызаписиxml параметрычтенияxml переопределениеxs планировщик полеанализаданных полекомпоновкиданных построительdom построительзапроса построительотчета построительотчетаанализаданных построительсхемxml поток потоквпамяти почта почтовоесообщение преобразованиеxsl преобразованиекканоническомуxml процессорвыводарезультатакомпоновкиданныхвколлекциюзначений процессорвыводарезультатакомпоновкиданныхвтабличныйдокумент процессоркомпоновкиданных разыменовательпространствименdom рамка расписаниерегламентногозадания расширенноеимяxml результатчтенияданных своднаядиаграмма связьпараметравыбора связьпотипу связьпотипукомпоновкиданных сериализаторxdto сертификатклиентаwindows сертификатклиентафайл сертификаткриптографии сертификатыудостоверяющихцентровwindows сертификатыудостоверяющихцентровфайл сжатиеданных системнаяинформация сообщениепользователю сочетаниеклавиш сравнениезначений стандартнаядатаначала стандартныйпериод схемаxml схемакомпоновкиданных табличныйдокумент текстовыйдокумент тестируемоеприложение типданныхxml уникальныйидентификатор фабрикаxdto файл файловыйпоток фасетдлиныxs фасетколичестваразрядовдробнойчастиxs фасетмаксимальноговключающегозначенияxs фасетмаксимальногоисключающегозначенияxs фасетмаксимальнойдлиныxs фасетминимальноговключающегозначенияxs фасетминимальногоисключающегозначенияxs фасетминимальнойдлиныxs фасетобразцаxs фасетобщегоколичестваразрядовxs фасетперечисленияxs фасетпробельныхсимволовxs фильтрузловdom форматированнаястрока форматированныйдокумент фрагментxs хешированиеданных хранилищезначения цвет чтениеfastinfoset чтениеhtml чтениеjson чтениеxml чтениеzipфайла чтениеданных чтениетекста чтениеузловdom шрифт элементрезультатакомпоновкиданных ",P="comsafearray деревозначений массив соответствие списокзначений структура таблицазначений фиксированнаяструктура фиксированноесоответствие фиксированныймассив ",T=I+P,W="null истина ложь неопределено",D=s.inherit(s.NM),F={cN:"string",b:'"|\\|',e:'"|$',c:[{b:'""'}]},G={b:"'",e:"'",eB:!0,eE:!0,c:[{cN:"number",b:"\\d{4}([\\.\\\\/:-]?\\d{2}){0,5}"}]},H=s.inherit(s.CLCM),J={cN:"meta",l:x,b:"#|&",e:"$",k:{"meta-keyword":t+n},c:[H]},K={cN:"symbol",b:"~",e:";|:",eE:!0},O={cN:"function",l:x,v:[{b:"процедура|функция",e:"\\)",k:"процедура функция"},{b:"конецпроцедуры|конецфункции",k:"конецпроцедуры конецфункции"}],c:[{b:"\\(",e:"\\)",endsParent:!0,c:[{cN:"params",l:x,b:x,e:",",eE:!0,eW:!0,k:{keyword:"знач",literal:W},c:[D,F,G]},H]},s.inherit(s.TM,{b:x})]};return{cI:!0,l:x,k:{keyword:t,built_in:r,"class":B,type:T,literal:W},c:[J,O,H,K,D,F,G]}}); \ No newline at end of file diff --git a/src/ASPNETHandler/DemoApp/home.png b/src/ASPNETHandler/DemoApp/home.png deleted file mode 100644 index a1686704c..000000000 Binary files a/src/ASPNETHandler/DemoApp/home.png and /dev/null differ diff --git a/src/ASPNETHandler/DemoApp/source.ospt b/src/ASPNETHandler/DemoApp/source.ospt deleted file mode 100644 index 4d9ce78fb..000000000 --- a/src/ASPNETHandler/DemoApp/source.ospt +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - OneScript HTTP Service Test Page - - - - - - - - -
- - Go to W3Schools! - -
-

-            {SourceCode}
-        
- - - \ No newline at end of file diff --git a/src/ASPNETHandler/DemoApp/styles/agate.css b/src/ASPNETHandler/DemoApp/styles/agate.css deleted file mode 100644 index 8d64547c5..000000000 --- a/src/ASPNETHandler/DemoApp/styles/agate.css +++ /dev/null @@ -1,108 +0,0 @@ -/*! - * Agate by Taufik Nurrohman - * ---------------------------------------------------- - * - * #ade5fc - * #a2fca2 - * #c6b4f0 - * #d36363 - * #fcc28c - * #fc9b9b - * #ffa - * #fff - * #333 - * #62c8f3 - * #888 - * - */ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #333; - color: white; -} - -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-code, -.hljs-emphasis { - font-style: italic; -} - -.hljs-tag { - color: #62c8f3; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-selector-id, -.hljs-selector-class { - color: #ade5fc; -} - -.hljs-string, -.hljs-bullet { - color: #a2fca2; -} - -.hljs-type, -.hljs-title, -.hljs-section, -.hljs-attribute, -.hljs-quote, -.hljs-built_in, -.hljs-builtin-name { - color: #ffa; -} - -.hljs-number, -.hljs-symbol, -.hljs-bullet { - color: #d36363; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal { - color: #fcc28c; -} - -.hljs-comment, -.hljs-deletion, -.hljs-code { - color: #888; -} - -.hljs-regexp, -.hljs-link { - color: #c6b4f0; -} - -.hljs-meta { - color: #fc9b9b; -} - -.hljs-deletion { - background-color: #fc9b9b; - color: #333; -} - -.hljs-addition { - background-color: #a2fca2; - color: #333; -} - -.hljs a { - color: inherit; -} - -.hljs a:focus, -.hljs a:hover { - color: inherit; - text-decoration: underline; -} diff --git a/src/ASPNETHandler/DemoApp/styles/androidstudio.css b/src/ASPNETHandler/DemoApp/styles/androidstudio.css deleted file mode 100644 index bc8e473b5..000000000 --- a/src/ASPNETHandler/DemoApp/styles/androidstudio.css +++ /dev/null @@ -1,66 +0,0 @@ -/* -Date: 24 Fev 2015 -Author: Pedro Oliveira -*/ - -.hljs { - color: #a9b7c6; - background: #282b2e; - display: block; - overflow-x: auto; - padding: 0.5em; -} - -.hljs-number, -.hljs-literal, -.hljs-symbol, -.hljs-bullet { - color: #6897BB; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-deletion { - color: #cc7832; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-link { - color: #629755; -} - -.hljs-comment, -.hljs-quote { - color: #808080; -} - -.hljs-meta { - color: #bbb529; -} - -.hljs-string, -.hljs-attribute, -.hljs-addition { - color: #6A8759; -} - -.hljs-section, -.hljs-title, -.hljs-type { - color: #ffc66d; -} - -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #e8bf6a; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/arduino-light.css b/src/ASPNETHandler/DemoApp/styles/arduino-light.css deleted file mode 100644 index 4b8b7fd3c..000000000 --- a/src/ASPNETHandler/DemoApp/styles/arduino-light.css +++ /dev/null @@ -1,88 +0,0 @@ -/* - -Arduino® Light Theme - Stefania Mellai - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #FFFFFF; -} - -.hljs, -.hljs-subst { - color: #434f54; -} - -.hljs-keyword, -.hljs-attribute, -.hljs-selector-tag, -.hljs-doctag, -.hljs-name { - color: #00979D; -} - -.hljs-built_in, -.hljs-literal, -.hljs-bullet, -.hljs-code, -.hljs-addition { - color: #D35400; -} - -.hljs-regexp, -.hljs-symbol, -.hljs-variable, -.hljs-template-variable, -.hljs-link, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #00979D; -} - -.hljs-type, -.hljs-string, -.hljs-selector-id, -.hljs-selector-class, -.hljs-quote, -.hljs-template-tag, -.hljs-deletion { - color: #005C5F; -} - -.hljs-title, -.hljs-section { - color: #880000; - font-weight: bold; -} - -.hljs-comment { - color: rgba(149,165,166,.8); -} - -.hljs-meta-keyword { - color: #728E00; -} - -.hljs-meta { - color: #728E00; - color: #434f54; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-function { - color: #728E00; -} - -.hljs-number { - color: #8A7B52; -} diff --git a/src/ASPNETHandler/DemoApp/styles/arta.css b/src/ASPNETHandler/DemoApp/styles/arta.css deleted file mode 100644 index 75ef3a9e5..000000000 --- a/src/ASPNETHandler/DemoApp/styles/arta.css +++ /dev/null @@ -1,73 +0,0 @@ -/* -Date: 17.V.2011 -Author: pumbur -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #222; -} - -.hljs, -.hljs-subst { - color: #aaa; -} - -.hljs-section { - color: #fff; -} - -.hljs-comment, -.hljs-quote, -.hljs-meta { - color: #444; -} - -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-regexp { - color: #ffcc33; -} - -.hljs-number, -.hljs-addition { - color: #00cc66; -} - -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-template-variable, -.hljs-attribute, -.hljs-link { - color: #32aaee; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #6644aa; -} - -.hljs-title, -.hljs-variable, -.hljs-deletion, -.hljs-template-tag { - color: #bb1166; -} - -.hljs-section, -.hljs-doctag, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/ascetic.css b/src/ASPNETHandler/DemoApp/styles/ascetic.css deleted file mode 100644 index 48397e889..000000000 --- a/src/ASPNETHandler/DemoApp/styles/ascetic.css +++ /dev/null @@ -1,45 +0,0 @@ -/* - -Original style from softwaremaniacs.org (c) Ivan Sagalaev - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: white; - color: black; -} - -.hljs-string, -.hljs-variable, -.hljs-template-variable, -.hljs-symbol, -.hljs-bullet, -.hljs-section, -.hljs-addition, -.hljs-attribute, -.hljs-link { - color: #888; -} - -.hljs-comment, -.hljs-quote, -.hljs-meta, -.hljs-deletion { - color: #ccc; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-section, -.hljs-name, -.hljs-type, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-cave-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-cave-dark.css deleted file mode 100644 index 65428f3b1..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-cave-dark.css +++ /dev/null @@ -1,83 +0,0 @@ -/* Base16 Atelier Cave Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Cave Comment */ -.hljs-comment, -.hljs-quote { - color: #7e7887; -} - -/* Atelier-Cave Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-regexp, -.hljs-link, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #be4678; -} - -/* Atelier-Cave Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #aa573c; -} - -/* Atelier-Cave Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #2a9292; -} - -/* Atelier-Cave Blue */ -.hljs-title, -.hljs-section { - color: #576ddb; -} - -/* Atelier-Cave Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #955ae7; -} - -.hljs-deletion, -.hljs-addition { - color: #19171c; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #be4678; -} - -.hljs-addition { - background-color: #2a9292; -} - -.hljs { - display: block; - overflow-x: auto; - background: #19171c; - color: #8b8792; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-cave-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-cave-light.css deleted file mode 100644 index b419f9fd8..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-cave-light.css +++ /dev/null @@ -1,85 +0,0 @@ -/* Base16 Atelier Cave Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Cave Comment */ -.hljs-comment, -.hljs-quote { - color: #655f6d; -} - -/* Atelier-Cave Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #be4678; -} - -/* Atelier-Cave Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #aa573c; -} - -/* Atelier-Cave Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #2a9292; -} - -/* Atelier-Cave Blue */ -.hljs-title, -.hljs-section { - color: #576ddb; -} - -/* Atelier-Cave Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #955ae7; -} - -.hljs-deletion, -.hljs-addition { - color: #19171c; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #be4678; -} - -.hljs-addition { - background-color: #2a9292; -} - -.hljs { - display: block; - overflow-x: auto; - background: #efecf4; - color: #585260; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-dune-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-dune-dark.css deleted file mode 100644 index 1684f5225..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-dune-dark.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Dune Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Dune Comment */ -.hljs-comment, -.hljs-quote { - color: #999580; -} - -/* Atelier-Dune Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #d73737; -} - -/* Atelier-Dune Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #b65611; -} - -/* Atelier-Dune Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #60ac39; -} - -/* Atelier-Dune Blue */ -.hljs-title, -.hljs-section { - color: #6684e1; -} - -/* Atelier-Dune Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #b854d4; -} - -.hljs { - display: block; - overflow-x: auto; - background: #20201d; - color: #a6a28c; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-dune-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-dune-light.css deleted file mode 100644 index 547719de8..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-dune-light.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Dune Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Dune Comment */ -.hljs-comment, -.hljs-quote { - color: #7d7a68; -} - -/* Atelier-Dune Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #d73737; -} - -/* Atelier-Dune Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #b65611; -} - -/* Atelier-Dune Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #60ac39; -} - -/* Atelier-Dune Blue */ -.hljs-title, -.hljs-section { - color: #6684e1; -} - -/* Atelier-Dune Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #b854d4; -} - -.hljs { - display: block; - overflow-x: auto; - background: #fefbec; - color: #6e6b5e; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-estuary-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-estuary-dark.css deleted file mode 100644 index a5e507187..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-estuary-dark.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Base16 Atelier Estuary Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/estuary) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Estuary Comment */ -.hljs-comment, -.hljs-quote { - color: #878573; -} - -/* Atelier-Estuary Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #ba6236; -} - -/* Atelier-Estuary Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #ae7313; -} - -/* Atelier-Estuary Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #7d9726; -} - -/* Atelier-Estuary Blue */ -.hljs-title, -.hljs-section { - color: #36a166; -} - -/* Atelier-Estuary Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #5f9182; -} - -.hljs-deletion, -.hljs-addition { - color: #22221b; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #ba6236; -} - -.hljs-addition { - background-color: #7d9726; -} - -.hljs { - display: block; - overflow-x: auto; - background: #22221b; - color: #929181; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-estuary-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-estuary-light.css deleted file mode 100644 index 1daee5d98..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-estuary-light.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Base16 Atelier Estuary Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/estuary) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Estuary Comment */ -.hljs-comment, -.hljs-quote { - color: #6c6b5a; -} - -/* Atelier-Estuary Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #ba6236; -} - -/* Atelier-Estuary Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #ae7313; -} - -/* Atelier-Estuary Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #7d9726; -} - -/* Atelier-Estuary Blue */ -.hljs-title, -.hljs-section { - color: #36a166; -} - -/* Atelier-Estuary Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #5f9182; -} - -.hljs-deletion, -.hljs-addition { - color: #22221b; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #ba6236; -} - -.hljs-addition { - background-color: #7d9726; -} - -.hljs { - display: block; - overflow-x: auto; - background: #f4f3ec; - color: #5f5e4e; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-forest-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-forest-dark.css deleted file mode 100644 index 0ef4fae31..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-forest-dark.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Forest Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Forest Comment */ -.hljs-comment, -.hljs-quote { - color: #9c9491; -} - -/* Atelier-Forest Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #f22c40; -} - -/* Atelier-Forest Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #df5320; -} - -/* Atelier-Forest Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #7b9726; -} - -/* Atelier-Forest Blue */ -.hljs-title, -.hljs-section { - color: #407ee7; -} - -/* Atelier-Forest Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #6666ea; -} - -.hljs { - display: block; - overflow-x: auto; - background: #1b1918; - color: #a8a19f; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-forest-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-forest-light.css deleted file mode 100644 index bbedde18a..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-forest-light.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Forest Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Forest Comment */ -.hljs-comment, -.hljs-quote { - color: #766e6b; -} - -/* Atelier-Forest Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #f22c40; -} - -/* Atelier-Forest Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #df5320; -} - -/* Atelier-Forest Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #7b9726; -} - -/* Atelier-Forest Blue */ -.hljs-title, -.hljs-section { - color: #407ee7; -} - -/* Atelier-Forest Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #6666ea; -} - -.hljs { - display: block; - overflow-x: auto; - background: #f1efee; - color: #68615e; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-heath-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-heath-dark.css deleted file mode 100644 index fe01ff721..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-heath-dark.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Heath Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Heath Comment */ -.hljs-comment, -.hljs-quote { - color: #9e8f9e; -} - -/* Atelier-Heath Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #ca402b; -} - -/* Atelier-Heath Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #a65926; -} - -/* Atelier-Heath Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #918b3b; -} - -/* Atelier-Heath Blue */ -.hljs-title, -.hljs-section { - color: #516aec; -} - -/* Atelier-Heath Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #7b59c0; -} - -.hljs { - display: block; - overflow-x: auto; - background: #1b181b; - color: #ab9bab; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-heath-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-heath-light.css deleted file mode 100644 index ee43786d1..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-heath-light.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Heath Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Heath Comment */ -.hljs-comment, -.hljs-quote { - color: #776977; -} - -/* Atelier-Heath Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #ca402b; -} - -/* Atelier-Heath Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #a65926; -} - -/* Atelier-Heath Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #918b3b; -} - -/* Atelier-Heath Blue */ -.hljs-title, -.hljs-section { - color: #516aec; -} - -/* Atelier-Heath Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #7b59c0; -} - -.hljs { - display: block; - overflow-x: auto; - background: #f7f3f7; - color: #695d69; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-lakeside-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-lakeside-dark.css deleted file mode 100644 index a937d3bf5..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-lakeside-dark.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Lakeside Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Lakeside Comment */ -.hljs-comment, -.hljs-quote { - color: #7195a8; -} - -/* Atelier-Lakeside Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #d22d72; -} - -/* Atelier-Lakeside Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #935c25; -} - -/* Atelier-Lakeside Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #568c3b; -} - -/* Atelier-Lakeside Blue */ -.hljs-title, -.hljs-section { - color: #257fad; -} - -/* Atelier-Lakeside Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #6b6bb8; -} - -.hljs { - display: block; - overflow-x: auto; - background: #161b1d; - color: #7ea2b4; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-lakeside-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-lakeside-light.css deleted file mode 100644 index 6c7e8f9ef..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-lakeside-light.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Lakeside Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Lakeside Comment */ -.hljs-comment, -.hljs-quote { - color: #5a7b8c; -} - -/* Atelier-Lakeside Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #d22d72; -} - -/* Atelier-Lakeside Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #935c25; -} - -/* Atelier-Lakeside Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #568c3b; -} - -/* Atelier-Lakeside Blue */ -.hljs-title, -.hljs-section { - color: #257fad; -} - -/* Atelier-Lakeside Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #6b6bb8; -} - -.hljs { - display: block; - overflow-x: auto; - background: #ebf8ff; - color: #516d7b; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-plateau-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-plateau-dark.css deleted file mode 100644 index 3bb052693..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-plateau-dark.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Base16 Atelier Plateau Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/plateau) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Plateau Comment */ -.hljs-comment, -.hljs-quote { - color: #7e7777; -} - -/* Atelier-Plateau Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #ca4949; -} - -/* Atelier-Plateau Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #b45a3c; -} - -/* Atelier-Plateau Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #4b8b8b; -} - -/* Atelier-Plateau Blue */ -.hljs-title, -.hljs-section { - color: #7272ca; -} - -/* Atelier-Plateau Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #8464c4; -} - -.hljs-deletion, -.hljs-addition { - color: #1b1818; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #ca4949; -} - -.hljs-addition { - background-color: #4b8b8b; -} - -.hljs { - display: block; - overflow-x: auto; - background: #1b1818; - color: #8a8585; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-plateau-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-plateau-light.css deleted file mode 100644 index 5f0222bec..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-plateau-light.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Base16 Atelier Plateau Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/plateau) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Plateau Comment */ -.hljs-comment, -.hljs-quote { - color: #655d5d; -} - -/* Atelier-Plateau Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #ca4949; -} - -/* Atelier-Plateau Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #b45a3c; -} - -/* Atelier-Plateau Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #4b8b8b; -} - -/* Atelier-Plateau Blue */ -.hljs-title, -.hljs-section { - color: #7272ca; -} - -/* Atelier-Plateau Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #8464c4; -} - -.hljs-deletion, -.hljs-addition { - color: #1b1818; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #ca4949; -} - -.hljs-addition { - background-color: #4b8b8b; -} - -.hljs { - display: block; - overflow-x: auto; - background: #f4ecec; - color: #585050; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-savanna-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-savanna-dark.css deleted file mode 100644 index 38f831431..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-savanna-dark.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Base16 Atelier Savanna Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Savanna Comment */ -.hljs-comment, -.hljs-quote { - color: #78877d; -} - -/* Atelier-Savanna Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #b16139; -} - -/* Atelier-Savanna Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #9f713c; -} - -/* Atelier-Savanna Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #489963; -} - -/* Atelier-Savanna Blue */ -.hljs-title, -.hljs-section { - color: #478c90; -} - -/* Atelier-Savanna Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #55859b; -} - -.hljs-deletion, -.hljs-addition { - color: #171c19; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #b16139; -} - -.hljs-addition { - background-color: #489963; -} - -.hljs { - display: block; - overflow-x: auto; - background: #171c19; - color: #87928a; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-savanna-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-savanna-light.css deleted file mode 100644 index 1ccd7c685..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-savanna-light.css +++ /dev/null @@ -1,84 +0,0 @@ -/* Base16 Atelier Savanna Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Savanna Comment */ -.hljs-comment, -.hljs-quote { - color: #5f6d64; -} - -/* Atelier-Savanna Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #b16139; -} - -/* Atelier-Savanna Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #9f713c; -} - -/* Atelier-Savanna Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #489963; -} - -/* Atelier-Savanna Blue */ -.hljs-title, -.hljs-section { - color: #478c90; -} - -/* Atelier-Savanna Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #55859b; -} - -.hljs-deletion, -.hljs-addition { - color: #171c19; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #b16139; -} - -.hljs-addition { - background-color: #489963; -} - -.hljs { - display: block; - overflow-x: auto; - background: #ecf4ee; - color: #526057; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-seaside-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-seaside-dark.css deleted file mode 100644 index df29949c6..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-seaside-dark.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Seaside Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Seaside Comment */ -.hljs-comment, -.hljs-quote { - color: #809980; -} - -/* Atelier-Seaside Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #e6193c; -} - -/* Atelier-Seaside Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #87711d; -} - -/* Atelier-Seaside Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #29a329; -} - -/* Atelier-Seaside Blue */ -.hljs-title, -.hljs-section { - color: #3d62f5; -} - -/* Atelier-Seaside Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #ad2bee; -} - -.hljs { - display: block; - overflow-x: auto; - background: #131513; - color: #8ca68c; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-seaside-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-seaside-light.css deleted file mode 100644 index 9d960f29f..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-seaside-light.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Seaside Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Seaside Comment */ -.hljs-comment, -.hljs-quote { - color: #687d68; -} - -/* Atelier-Seaside Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #e6193c; -} - -/* Atelier-Seaside Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #87711d; -} - -/* Atelier-Seaside Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #29a329; -} - -/* Atelier-Seaside Blue */ -.hljs-title, -.hljs-section { - color: #3d62f5; -} - -/* Atelier-Seaside Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #ad2bee; -} - -.hljs { - display: block; - overflow-x: auto; - background: #f4fbf4; - color: #5e6e5e; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-sulphurpool-dark.css b/src/ASPNETHandler/DemoApp/styles/atelier-sulphurpool-dark.css deleted file mode 100644 index c2ab7938d..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-sulphurpool-dark.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Sulphurpool Dark - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Sulphurpool Comment */ -.hljs-comment, -.hljs-quote { - color: #898ea4; -} - -/* Atelier-Sulphurpool Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #c94922; -} - -/* Atelier-Sulphurpool Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #c76b29; -} - -/* Atelier-Sulphurpool Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #ac9739; -} - -/* Atelier-Sulphurpool Blue */ -.hljs-title, -.hljs-section { - color: #3d8fd1; -} - -/* Atelier-Sulphurpool Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #6679cc; -} - -.hljs { - display: block; - overflow-x: auto; - background: #202746; - color: #979db4; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atelier-sulphurpool-light.css b/src/ASPNETHandler/DemoApp/styles/atelier-sulphurpool-light.css deleted file mode 100644 index 96c47d086..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atelier-sulphurpool-light.css +++ /dev/null @@ -1,69 +0,0 @@ -/* Base16 Atelier Sulphurpool Light - Theme */ -/* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool) */ -/* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ - -/* Atelier-Sulphurpool Comment */ -.hljs-comment, -.hljs-quote { - color: #6b7394; -} - -/* Atelier-Sulphurpool Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-regexp, -.hljs-link, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #c94922; -} - -/* Atelier-Sulphurpool Orange */ -.hljs-number, -.hljs-meta, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #c76b29; -} - -/* Atelier-Sulphurpool Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet { - color: #ac9739; -} - -/* Atelier-Sulphurpool Blue */ -.hljs-title, -.hljs-section { - color: #3d8fd1; -} - -/* Atelier-Sulphurpool Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #6679cc; -} - -.hljs { - display: block; - overflow-x: auto; - background: #f5f7ff; - color: #5e6687; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atom-one-dark.css b/src/ASPNETHandler/DemoApp/styles/atom-one-dark.css deleted file mode 100644 index 1616aafe3..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atom-one-dark.css +++ /dev/null @@ -1,96 +0,0 @@ -/* - -Atom One Dark by Daniel Gamage -Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax - -base: #282c34 -mono-1: #abb2bf -mono-2: #818896 -mono-3: #5c6370 -hue-1: #56b6c2 -hue-2: #61aeee -hue-3: #c678dd -hue-4: #98c379 -hue-5: #e06c75 -hue-5-2: #be5046 -hue-6: #d19a66 -hue-6-2: #e6c07b - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #abb2bf; - background: #282c34; -} - -.hljs-comment, -.hljs-quote { - color: #5c6370; - font-style: italic; -} - -.hljs-doctag, -.hljs-keyword, -.hljs-formula { - color: #c678dd; -} - -.hljs-section, -.hljs-name, -.hljs-selector-tag, -.hljs-deletion, -.hljs-subst { - color: #e06c75; -} - -.hljs-literal { - color: #56b6c2; -} - -.hljs-string, -.hljs-regexp, -.hljs-addition, -.hljs-attribute, -.hljs-meta-string { - color: #98c379; -} - -.hljs-built_in, -.hljs-class .hljs-title { - color: #e6c07b; -} - -.hljs-attr, -.hljs-variable, -.hljs-template-variable, -.hljs-type, -.hljs-selector-class, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-number { - color: #d19a66; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-link, -.hljs-meta, -.hljs-selector-id, -.hljs-title { - color: #61aeee; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-link { - text-decoration: underline; -} diff --git a/src/ASPNETHandler/DemoApp/styles/atom-one-light.css b/src/ASPNETHandler/DemoApp/styles/atom-one-light.css deleted file mode 100644 index d5bd1d2a9..000000000 --- a/src/ASPNETHandler/DemoApp/styles/atom-one-light.css +++ /dev/null @@ -1,96 +0,0 @@ -/* - -Atom One Light by Daniel Gamage -Original One Light Syntax theme from https://github.com/atom/one-light-syntax - -base: #fafafa -mono-1: #383a42 -mono-2: #686b77 -mono-3: #a0a1a7 -hue-1: #0184bb -hue-2: #4078f2 -hue-3: #a626a4 -hue-4: #50a14f -hue-5: #e45649 -hue-5-2: #c91243 -hue-6: #986801 -hue-6-2: #c18401 - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #383a42; - background: #fafafa; -} - -.hljs-comment, -.hljs-quote { - color: #a0a1a7; - font-style: italic; -} - -.hljs-doctag, -.hljs-keyword, -.hljs-formula { - color: #a626a4; -} - -.hljs-section, -.hljs-name, -.hljs-selector-tag, -.hljs-deletion, -.hljs-subst { - color: #e45649; -} - -.hljs-literal { - color: #0184bb; -} - -.hljs-string, -.hljs-regexp, -.hljs-addition, -.hljs-attribute, -.hljs-meta-string { - color: #50a14f; -} - -.hljs-built_in, -.hljs-class .hljs-title { - color: #c18401; -} - -.hljs-attr, -.hljs-variable, -.hljs-template-variable, -.hljs-type, -.hljs-selector-class, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-number { - color: #986801; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-link, -.hljs-meta, -.hljs-selector-id, -.hljs-title { - color: #4078f2; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-link { - text-decoration: underline; -} diff --git a/src/ASPNETHandler/DemoApp/styles/brown-paper.css b/src/ASPNETHandler/DemoApp/styles/brown-paper.css deleted file mode 100644 index f0197b924..000000000 --- a/src/ASPNETHandler/DemoApp/styles/brown-paper.css +++ /dev/null @@ -1,64 +0,0 @@ -/* - -Brown Paper style from goldblog.com.ua (c) Zaripov Yura - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background:#b7a68e url(./brown-papersq.png); -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal { - color:#005599; - font-weight:bold; -} - -.hljs, -.hljs-subst { - color: #363c69; -} - -.hljs-string, -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-attribute, -.hljs-symbol, -.hljs-bullet, -.hljs-built_in, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable, -.hljs-link, -.hljs-name { - color: #2c009f; -} - -.hljs-comment, -.hljs-quote, -.hljs-meta, -.hljs-deletion { - color: #802022; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-doctag, -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/brown-papersq.png b/src/ASPNETHandler/DemoApp/styles/brown-papersq.png deleted file mode 100644 index 3813903db..000000000 Binary files a/src/ASPNETHandler/DemoApp/styles/brown-papersq.png and /dev/null differ diff --git a/src/ASPNETHandler/DemoApp/styles/codepen-embed.css b/src/ASPNETHandler/DemoApp/styles/codepen-embed.css deleted file mode 100644 index 195c4a078..000000000 --- a/src/ASPNETHandler/DemoApp/styles/codepen-embed.css +++ /dev/null @@ -1,60 +0,0 @@ -/* - codepen.io Embed Theme - Author: Justin Perry - Original theme - https://github.com/chriskempson/tomorrow-theme -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #222; - color: #fff; -} - -.hljs-comment, -.hljs-quote { - color: #777; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-regexp, -.hljs-meta, -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-params, -.hljs-symbol, -.hljs-bullet, -.hljs-link, -.hljs-deletion { - color: #ab875d; -} - -.hljs-section, -.hljs-title, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-type, -.hljs-attribute { - color: #9b869b; -} - -.hljs-string, -.hljs-keyword, -.hljs-selector-tag, -.hljs-addition { - color: #8f9c6c; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/color-brewer.css b/src/ASPNETHandler/DemoApp/styles/color-brewer.css deleted file mode 100644 index 7934d986a..000000000 --- a/src/ASPNETHandler/DemoApp/styles/color-brewer.css +++ /dev/null @@ -1,71 +0,0 @@ -/* - -Colorbrewer theme -Original: https://github.com/mbostock/colorbrewer-theme (c) Mike Bostock -Ported by Fabrício Tavares de Oliveira - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #fff; -} - -.hljs, -.hljs-subst { - color: #000; -} - -.hljs-string, -.hljs-meta, -.hljs-symbol, -.hljs-template-tag, -.hljs-template-variable, -.hljs-addition { - color: #756bb1; -} - -.hljs-comment, -.hljs-quote { - color: #636363; -} - -.hljs-number, -.hljs-regexp, -.hljs-literal, -.hljs-bullet, -.hljs-link { - color: #31a354; -} - -.hljs-deletion, -.hljs-variable { - color: #88f; -} - - - -.hljs-keyword, -.hljs-selector-tag, -.hljs-title, -.hljs-section, -.hljs-built_in, -.hljs-doctag, -.hljs-type, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-strong { - color: #3182bd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-attribute { - color: #e6550d; -} diff --git a/src/ASPNETHandler/DemoApp/styles/darcula.css b/src/ASPNETHandler/DemoApp/styles/darcula.css deleted file mode 100644 index be182d0b5..000000000 --- a/src/ASPNETHandler/DemoApp/styles/darcula.css +++ /dev/null @@ -1,77 +0,0 @@ -/* - -Darcula color scheme from the JetBrains family of IDEs - -*/ - - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #2b2b2b; -} - -.hljs { - color: #bababa; -} - -.hljs-strong, -.hljs-emphasis { - color: #a8a8a2; -} - -.hljs-bullet, -.hljs-quote, -.hljs-link, -.hljs-number, -.hljs-regexp, -.hljs-literal { - color: #6896ba; -} - -.hljs-code, -.hljs-selector-class { - color: #a6e22e; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-section, -.hljs-attribute, -.hljs-name, -.hljs-variable { - color: #cb7832; -} - -.hljs-params { - color: #b9b9b9; -} - -.hljs-string { - color: #6a8759; -} - -.hljs-subst, -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-symbol, -.hljs-selector-id, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-template-tag, -.hljs-template-variable, -.hljs-addition { - color: #e0c46c; -} - -.hljs-comment, -.hljs-deletion, -.hljs-meta { - color: #7f7f7f; -} diff --git a/src/ASPNETHandler/DemoApp/styles/dark.css b/src/ASPNETHandler/DemoApp/styles/dark.css deleted file mode 100644 index b4724f5f5..000000000 --- a/src/ASPNETHandler/DemoApp/styles/dark.css +++ /dev/null @@ -1,63 +0,0 @@ -/* - -Dark style from softwaremaniacs.org (c) Ivan Sagalaev - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #444; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-section, -.hljs-link { - color: white; -} - -.hljs, -.hljs-subst { - color: #ddd; -} - -.hljs-string, -.hljs-title, -.hljs-name, -.hljs-type, -.hljs-attribute, -.hljs-symbol, -.hljs-bullet, -.hljs-built_in, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable { - color: #d88; -} - -.hljs-comment, -.hljs-quote, -.hljs-deletion, -.hljs-meta { - color: #777; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-title, -.hljs-section, -.hljs-doctag, -.hljs-type, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/darkula.css b/src/ASPNETHandler/DemoApp/styles/darkula.css deleted file mode 100644 index f4646c3c5..000000000 --- a/src/ASPNETHandler/DemoApp/styles/darkula.css +++ /dev/null @@ -1,6 +0,0 @@ -/* - Deprecated due to a typo in the name and left here for compatibility purpose only. - Please use darcula.css instead. -*/ - -@import url('darcula.css'); diff --git a/src/ASPNETHandler/DemoApp/styles/default.css b/src/ASPNETHandler/DemoApp/styles/default.css deleted file mode 100644 index f1bfade31..000000000 --- a/src/ASPNETHandler/DemoApp/styles/default.css +++ /dev/null @@ -1,99 +0,0 @@ -/* - -Original highlight.js style (c) Ivan Sagalaev - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #F0F0F0; -} - - -/* Base color: saturation 0; */ - -.hljs, -.hljs-subst { - color: #444; -} - -.hljs-comment { - color: #888888; -} - -.hljs-keyword, -.hljs-attribute, -.hljs-selector-tag, -.hljs-meta-keyword, -.hljs-doctag, -.hljs-name { - font-weight: bold; -} - - -/* User color: hue: 0 */ - -.hljs-type, -.hljs-string, -.hljs-number, -.hljs-selector-id, -.hljs-selector-class, -.hljs-quote, -.hljs-template-tag, -.hljs-deletion { - color: #880000; -} - -.hljs-title, -.hljs-section { - color: #880000; - font-weight: bold; -} - -.hljs-regexp, -.hljs-symbol, -.hljs-variable, -.hljs-template-variable, -.hljs-link, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #BC6060; -} - - -/* Language color: hue: 90; */ - -.hljs-literal { - color: #78A960; -} - -.hljs-built_in, -.hljs-bullet, -.hljs-code, -.hljs-addition { - color: #397300; -} - - -/* Meta color: hue: 200 */ - -.hljs-meta { - color: #1f7199; -} - -.hljs-meta-string { - color: #4d99bf; -} - - -/* Misc effects */ - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/docco.css b/src/ASPNETHandler/DemoApp/styles/docco.css deleted file mode 100644 index db366be37..000000000 --- a/src/ASPNETHandler/DemoApp/styles/docco.css +++ /dev/null @@ -1,97 +0,0 @@ -/* -Docco style used in http://jashkenas.github.com/docco/ converted by Simon Madine (@thingsinjars) -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #000; - background: #f8f8ff; -} - -.hljs-comment, -.hljs-quote { - color: #408080; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-subst { - color: #954121; -} - -.hljs-number { - color: #40a070; -} - -.hljs-string, -.hljs-doctag { - color: #219161; -} - -.hljs-selector-id, -.hljs-selector-class, -.hljs-section, -.hljs-type { - color: #19469d; -} - -.hljs-params { - color: #00f; -} - -.hljs-title { - color: #458; - font-weight: bold; -} - -.hljs-tag, -.hljs-name, -.hljs-attribute { - color: #000080; - font-weight: normal; -} - -.hljs-variable, -.hljs-template-variable { - color: #008080; -} - -.hljs-regexp, -.hljs-link { - color: #b68; -} - -.hljs-symbol, -.hljs-bullet { - color: #990073; -} - -.hljs-built_in, -.hljs-builtin-name { - color: #0086b3; -} - -.hljs-meta { - color: #999; - font-weight: bold; -} - -.hljs-deletion { - background: #fdd; -} - -.hljs-addition { - background: #dfd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/dracula.css b/src/ASPNETHandler/DemoApp/styles/dracula.css deleted file mode 100644 index d591db680..000000000 --- a/src/ASPNETHandler/DemoApp/styles/dracula.css +++ /dev/null @@ -1,76 +0,0 @@ -/* - -Dracula Theme v1.2.0 - -https://github.com/zenorocha/dracula-theme - -Copyright 2015, All rights reserved - -Code licensed under the MIT license -http://zenorocha.mit-license.org - -@author Éverton Ribeiro -@author Zeno Rocha - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #282a36; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-section, -.hljs-link { - color: #8be9fd; -} - -.hljs-function .hljs-keyword { - color: #ff79c6; -} - -.hljs, -.hljs-subst { - color: #f8f8f2; -} - -.hljs-string, -.hljs-title, -.hljs-name, -.hljs-type, -.hljs-attribute, -.hljs-symbol, -.hljs-bullet, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable { - color: #f1fa8c; -} - -.hljs-comment, -.hljs-quote, -.hljs-deletion, -.hljs-meta { - color: #6272a4; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-title, -.hljs-section, -.hljs-doctag, -.hljs-type, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/far.css b/src/ASPNETHandler/DemoApp/styles/far.css deleted file mode 100644 index 2b3f87b56..000000000 --- a/src/ASPNETHandler/DemoApp/styles/far.css +++ /dev/null @@ -1,71 +0,0 @@ -/* - -FAR Style (c) MajestiC - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #000080; -} - -.hljs, -.hljs-subst { - color: #0ff; -} - -.hljs-string, -.hljs-attribute, -.hljs-symbol, -.hljs-bullet, -.hljs-built_in, -.hljs-builtin-name, -.hljs-template-tag, -.hljs-template-variable, -.hljs-addition { - color: #ff0; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-section, -.hljs-type, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-variable { - color: #fff; -} - -.hljs-comment, -.hljs-quote, -.hljs-doctag, -.hljs-deletion { - color: #888; -} - -.hljs-number, -.hljs-regexp, -.hljs-literal, -.hljs-link { - color: #0f0; -} - -.hljs-meta { - color: #008080; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-title, -.hljs-section, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/foundation.css b/src/ASPNETHandler/DemoApp/styles/foundation.css deleted file mode 100644 index f1fe64b37..000000000 --- a/src/ASPNETHandler/DemoApp/styles/foundation.css +++ /dev/null @@ -1,88 +0,0 @@ -/* -Description: Foundation 4 docs style for highlight.js -Author: Dan Allen -Website: http://foundation.zurb.com/docs/ -Version: 1.0 -Date: 2013-04-02 -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #eee; color: black; -} - -.hljs-link, -.hljs-emphasis, -.hljs-attribute, -.hljs-addition { - color: #070; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong, -.hljs-string, -.hljs-deletion { - color: #d14; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-quote, -.hljs-comment { - color: #998; - font-style: italic; -} - -.hljs-section, -.hljs-title { - color: #900; -} - -.hljs-class .hljs-title, -.hljs-type { - color: #458; -} - -.hljs-variable, -.hljs-template-variable { - color: #336699; -} - -.hljs-bullet { - color: #997700; -} - -.hljs-meta { - color: #3344bb; -} - -.hljs-code, -.hljs-number, -.hljs-literal, -.hljs-keyword, -.hljs-selector-tag { - color: #099; -} - -.hljs-regexp { - background-color: #fff0ff; - color: #880088; -} - -.hljs-symbol { - color: #990073; -} - -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #007700; -} diff --git a/src/ASPNETHandler/DemoApp/styles/github-gist.css b/src/ASPNETHandler/DemoApp/styles/github-gist.css deleted file mode 100644 index 155f0b916..000000000 --- a/src/ASPNETHandler/DemoApp/styles/github-gist.css +++ /dev/null @@ -1,71 +0,0 @@ -/** - * GitHub Gist Theme - * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro - */ - -.hljs { - display: block; - background: white; - padding: 0.5em; - color: #333333; - overflow-x: auto; -} - -.hljs-comment, -.hljs-meta { - color: #969896; -} - -.hljs-string, -.hljs-variable, -.hljs-template-variable, -.hljs-strong, -.hljs-emphasis, -.hljs-quote { - color: #df5000; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-type { - color: #a71d5d; -} - -.hljs-literal, -.hljs-symbol, -.hljs-bullet, -.hljs-attribute { - color: #0086b3; -} - -.hljs-section, -.hljs-name { - color: #63a35c; -} - -.hljs-tag { - color: #333333; -} - -.hljs-title, -.hljs-attr, -.hljs-selector-id, -.hljs-selector-class, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #795da3; -} - -.hljs-addition { - color: #55a532; - background-color: #eaffea; -} - -.hljs-deletion { - color: #bd2c00; - background-color: #ffecec; -} - -.hljs-link { - text-decoration: underline; -} diff --git a/src/ASPNETHandler/DemoApp/styles/github.css b/src/ASPNETHandler/DemoApp/styles/github.css deleted file mode 100644 index 791932b87..000000000 --- a/src/ASPNETHandler/DemoApp/styles/github.css +++ /dev/null @@ -1,99 +0,0 @@ -/* - -github.com style (c) Vasily Polovnyov - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #333; - background: #f8f8f8; -} - -.hljs-comment, -.hljs-quote { - color: #998; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-subst { - color: #333; - font-weight: bold; -} - -.hljs-number, -.hljs-literal, -.hljs-variable, -.hljs-template-variable, -.hljs-tag .hljs-attr { - color: #008080; -} - -.hljs-string, -.hljs-doctag { - color: #d14; -} - -.hljs-title, -.hljs-section, -.hljs-selector-id { - color: #900; - font-weight: bold; -} - -.hljs-subst { - font-weight: normal; -} - -.hljs-type, -.hljs-class .hljs-title { - color: #458; - font-weight: bold; -} - -.hljs-tag, -.hljs-name, -.hljs-attribute { - color: #000080; - font-weight: normal; -} - -.hljs-regexp, -.hljs-link { - color: #009926; -} - -.hljs-symbol, -.hljs-bullet { - color: #990073; -} - -.hljs-built_in, -.hljs-builtin-name { - color: #0086b3; -} - -.hljs-meta { - color: #999; - font-weight: bold; -} - -.hljs-deletion { - background: #fdd; -} - -.hljs-addition { - background: #dfd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/googlecode.css b/src/ASPNETHandler/DemoApp/styles/googlecode.css deleted file mode 100644 index 884ad6353..000000000 --- a/src/ASPNETHandler/DemoApp/styles/googlecode.css +++ /dev/null @@ -1,89 +0,0 @@ -/* - -Google Code style (c) Aahan Krish - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: white; - color: black; -} - -.hljs-comment, -.hljs-quote { - color: #800; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-section, -.hljs-title, -.hljs-name { - color: #008; -} - -.hljs-variable, -.hljs-template-variable { - color: #660; -} - -.hljs-string, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-regexp { - color: #080; -} - -.hljs-literal, -.hljs-symbol, -.hljs-bullet, -.hljs-meta, -.hljs-number, -.hljs-link { - color: #066; -} - -.hljs-title, -.hljs-doctag, -.hljs-type, -.hljs-attr, -.hljs-built_in, -.hljs-builtin-name, -.hljs-params { - color: #606; -} - -.hljs-attribute, -.hljs-subst { - color: #000; -} - -.hljs-formula { - background-color: #eee; - font-style: italic; -} - -.hljs-selector-id, -.hljs-selector-class { - color: #9B703F -} - -.hljs-addition { - background-color: #baeeba; -} - -.hljs-deletion { - background-color: #ffc8bd; -} - -.hljs-doctag, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/grayscale.css b/src/ASPNETHandler/DemoApp/styles/grayscale.css deleted file mode 100644 index 5376f3406..000000000 --- a/src/ASPNETHandler/DemoApp/styles/grayscale.css +++ /dev/null @@ -1,101 +0,0 @@ -/* - -grayscale style (c) MY Sun - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #333; - background: #fff; -} - -.hljs-comment, -.hljs-quote { - color: #777; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-subst { - color: #333; - font-weight: bold; -} - -.hljs-number, -.hljs-literal { - color: #777; -} - -.hljs-string, -.hljs-doctag, -.hljs-formula { - color: #333; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAJ0lEQVQIW2O8e/fufwYGBgZBQUEQxcCIIfDu3Tuwivfv30NUoAsAALHpFMMLqZlPAAAAAElFTkSuQmCC) repeat; -} - -.hljs-title, -.hljs-section, -.hljs-selector-id { - color: #000; - font-weight: bold; -} - -.hljs-subst { - font-weight: normal; -} - -.hljs-class .hljs-title, -.hljs-type, -.hljs-name { - color: #333; - font-weight: bold; -} - -.hljs-tag { - color: #333; -} - -.hljs-regexp { - color: #333; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPUlEQVQYV2NkQAN37979r6yszIgujiIAU4RNMVwhuiQ6H6wQl3XI4oy4FMHcCJPHcDS6J2A2EqUQpJhohQDexSef15DBCwAAAABJRU5ErkJggg==) repeat; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-link { - color: #000; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAKElEQVQIW2NkQAO7d+/+z4gsBhJwdXVlhAvCBECKwIIwAbhKZBUwBQA6hBpm5efZsgAAAABJRU5ErkJggg==) repeat; -} - -.hljs-built_in, -.hljs-builtin-name { - color: #000; - text-decoration: underline; -} - -.hljs-meta { - color: #999; - font-weight: bold; -} - -.hljs-deletion { - color: #fff; - background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAADCAYAAABS3WWCAAAAE0lEQVQIW2MMDQ39zzhz5kwIAQAyxweWgUHd1AAAAABJRU5ErkJggg==) repeat; -} - -.hljs-addition { - color: #000; - background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAALUlEQVQYV2N89+7dfwYk8P79ewZBQUFkIQZGOiu6e/cuiptQHAPl0NtNxAQBAM97Oejj3Dg7AAAAAElFTkSuQmCC) repeat; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/gruvbox-dark.css b/src/ASPNETHandler/DemoApp/styles/gruvbox-dark.css deleted file mode 100644 index f563811a8..000000000 --- a/src/ASPNETHandler/DemoApp/styles/gruvbox-dark.css +++ /dev/null @@ -1,108 +0,0 @@ -/* - -Gruvbox style (dark) (c) Pavel Pertsev (original style at https://github.com/morhetz/gruvbox) - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #282828; -} - -.hljs, -.hljs-subst { - color: #ebdbb2; -} - -/* Gruvbox Red */ -.hljs-deletion, -.hljs-formula, -.hljs-keyword, -.hljs-link, -.hljs-selector-tag { - color: #fb4934; -} - -/* Gruvbox Blue */ -.hljs-built_in, -.hljs-emphasis, -.hljs-name, -.hljs-quote, -.hljs-strong, -.hljs-title, -.hljs-variable { - color: #83a598; -} - -/* Gruvbox Yellow */ -.hljs-attr, -.hljs-params, -.hljs-template-tag, -.hljs-type { - color: #fabd2f; -} - -/* Gruvbox Purple */ -.hljs-builtin-name, -.hljs-doctag, -.hljs-literal, -.hljs-number { - color: #8f3f71; -} - -/* Gruvbox Orange */ -.hljs-code, -.hljs-meta, -.hljs-regexp, -.hljs-selector-id, -.hljs-template-variable { - color: #fe8019; -} - -/* Gruvbox Green */ -.hljs-addition, -.hljs-meta-string, -.hljs-section, -.hljs-selector-attr, -.hljs-selector-class, -.hljs-string, -.hljs-symbol { - color: #b8bb26; -} - -/* Gruvbox Aqua */ -.hljs-attribute, -.hljs-bullet, -.hljs-class, -.hljs-function, -.hljs-function .hljs-keyword, -.hljs-meta-keyword, -.hljs-selector-pseudo, -.hljs-tag { - color: #8ec07c; -} - -/* Gruvbox Gray */ -.hljs-comment { - color: #928374; -} - -/* Gruvbox Purple */ -.hljs-link_label, -.hljs-literal, -.hljs-number { - color: #d3869b; -} - -.hljs-comment, -.hljs-emphasis { - font-style: italic; -} - -.hljs-section, -.hljs-strong, -.hljs-tag { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/gruvbox-light.css b/src/ASPNETHandler/DemoApp/styles/gruvbox-light.css deleted file mode 100644 index ff45468eb..000000000 --- a/src/ASPNETHandler/DemoApp/styles/gruvbox-light.css +++ /dev/null @@ -1,108 +0,0 @@ -/* - -Gruvbox style (light) (c) Pavel Pertsev (original style at https://github.com/morhetz/gruvbox) - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #fbf1c7; -} - -.hljs, -.hljs-subst { - color: #3c3836; -} - -/* Gruvbox Red */ -.hljs-deletion, -.hljs-formula, -.hljs-keyword, -.hljs-link, -.hljs-selector-tag { - color: #9d0006; -} - -/* Gruvbox Blue */ -.hljs-built_in, -.hljs-emphasis, -.hljs-name, -.hljs-quote, -.hljs-strong, -.hljs-title, -.hljs-variable { - color: #076678; -} - -/* Gruvbox Yellow */ -.hljs-attr, -.hljs-params, -.hljs-template-tag, -.hljs-type { - color: #b57614; -} - -/* Gruvbox Purple */ -.hljs-builtin-name, -.hljs-doctag, -.hljs-literal, -.hljs-number { - color: #8f3f71; -} - -/* Gruvbox Orange */ -.hljs-code, -.hljs-meta, -.hljs-regexp, -.hljs-selector-id, -.hljs-template-variable { - color: #af3a03; -} - -/* Gruvbox Green */ -.hljs-addition, -.hljs-meta-string, -.hljs-section, -.hljs-selector-attr, -.hljs-selector-class, -.hljs-string, -.hljs-symbol { - color: #79740e; -} - -/* Gruvbox Aqua */ -.hljs-attribute, -.hljs-bullet, -.hljs-class, -.hljs-function, -.hljs-function .hljs-keyword, -.hljs-meta-keyword, -.hljs-selector-pseudo, -.hljs-tag { - color: #427b58; -} - -/* Gruvbox Gray */ -.hljs-comment { - color: #928374; -} - -/* Gruvbox Purple */ -.hljs-link_label, -.hljs-literal, -.hljs-number { - color: #8f3f71; -} - -.hljs-comment, -.hljs-emphasis { - font-style: italic; -} - -.hljs-section, -.hljs-strong, -.hljs-tag { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/hopscotch.css b/src/ASPNETHandler/DemoApp/styles/hopscotch.css deleted file mode 100644 index 32e60d230..000000000 --- a/src/ASPNETHandler/DemoApp/styles/hopscotch.css +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Hopscotch - * by Jan T. Sott - * https://github.com/idleberg/Hopscotch - * - * This work is licensed under the Creative Commons CC0 1.0 Universal License - */ - -/* Comment */ -.hljs-comment, -.hljs-quote { - color: #989498; -} - -/* Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-attribute, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-link, -.hljs-deletion { - color: #dd464c; -} - -/* Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params { - color: #fd8b19; -} - -/* Yellow */ -.hljs-class .hljs-title { - color: #fdcc59; -} - -/* Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #8fc13e; -} - -/* Aqua */ -.hljs-meta { - color: #149b93; -} - -/* Blue */ -.hljs-function, -.hljs-section, -.hljs-title { - color: #1290bf; -} - -/* Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #c85e7c; -} - -.hljs { - display: block; - background: #322931; - color: #b9b5b8; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/hybrid.css b/src/ASPNETHandler/DemoApp/styles/hybrid.css deleted file mode 100644 index 29735a189..000000000 --- a/src/ASPNETHandler/DemoApp/styles/hybrid.css +++ /dev/null @@ -1,102 +0,0 @@ -/* - -vim-hybrid theme by w0ng (https://github.com/w0ng/vim-hybrid) - -*/ - -/*background color*/ -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #1d1f21; -} - -/*selection color*/ -.hljs::selection, -.hljs span::selection { - background: #373b41; -} - -.hljs::-moz-selection, -.hljs span::-moz-selection { - background: #373b41; -} - -/*foreground color*/ -.hljs { - color: #c5c8c6; -} - -/*color: fg_yellow*/ -.hljs-title, -.hljs-name { - color: #f0c674; -} - -/*color: fg_comment*/ -.hljs-comment, -.hljs-meta, -.hljs-meta .hljs-keyword { - color: #707880; -} - -/*color: fg_red*/ -.hljs-number, -.hljs-symbol, -.hljs-literal, -.hljs-deletion, -.hljs-link { - color: #cc6666 -} - -/*color: fg_green*/ -.hljs-string, -.hljs-doctag, -.hljs-addition, -.hljs-regexp, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #b5bd68; -} - -/*color: fg_purple*/ -.hljs-attribute, -.hljs-code, -.hljs-selector-id { - color: #b294bb; -} - -/*color: fg_blue*/ -.hljs-keyword, -.hljs-selector-tag, -.hljs-bullet, -.hljs-tag { - color: #81a2be; -} - -/*color: fg_aqua*/ -.hljs-subst, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable { - color: #8abeb7; -} - -/*color: fg_orange*/ -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-quote, -.hljs-section, -.hljs-selector-class { - color: #de935f; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/idea.css b/src/ASPNETHandler/DemoApp/styles/idea.css deleted file mode 100644 index 3bf1892bd..000000000 --- a/src/ASPNETHandler/DemoApp/styles/idea.css +++ /dev/null @@ -1,97 +0,0 @@ -/* - -Intellij Idea-like styling (c) Vasily Polovnyov - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #000; - background: #fff; -} - -.hljs-subst, -.hljs-title { - font-weight: normal; - color: #000; -} - -.hljs-comment, -.hljs-quote { - color: #808080; - font-style: italic; -} - -.hljs-meta { - color: #808000; -} - -.hljs-tag { - background: #efefef; -} - -.hljs-section, -.hljs-name, -.hljs-literal, -.hljs-keyword, -.hljs-selector-tag, -.hljs-type, -.hljs-selector-id, -.hljs-selector-class { - font-weight: bold; - color: #000080; -} - -.hljs-attribute, -.hljs-number, -.hljs-regexp, -.hljs-link { - font-weight: bold; - color: #0000ff; -} - -.hljs-number, -.hljs-regexp, -.hljs-link { - font-weight: normal; -} - -.hljs-string { - color: #008000; - font-weight: bold; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-formula { - color: #000; - background: #d0eded; - font-style: italic; -} - -.hljs-doctag { - text-decoration: underline; -} - -.hljs-variable, -.hljs-template-variable { - color: #660e7a; -} - -.hljs-addition { - background: #baeeba; -} - -.hljs-deletion { - background: #ffc8bd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/ir-black.css b/src/ASPNETHandler/DemoApp/styles/ir-black.css deleted file mode 100644 index bd4c755ed..000000000 --- a/src/ASPNETHandler/DemoApp/styles/ir-black.css +++ /dev/null @@ -1,73 +0,0 @@ -/* - IR_Black style (c) Vasily Mikhailitchenko -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #000; - color: #f8f8f8; -} - -.hljs-comment, -.hljs-quote, -.hljs-meta { - color: #7c7c7c; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-tag, -.hljs-name { - color: #96cbfe; -} - -.hljs-attribute, -.hljs-selector-id { - color: #ffffb6; -} - -.hljs-string, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-addition { - color: #a8ff60; -} - -.hljs-subst { - color: #daefa3; -} - -.hljs-regexp, -.hljs-link { - color: #e9c062; -} - -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-doctag { - color: #ffffb6; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-variable, -.hljs-template-variable, -.hljs-literal { - color: #c6c5fe; -} - -.hljs-number, -.hljs-deletion { - color:#ff73fd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/kimbie.dark.css b/src/ASPNETHandler/DemoApp/styles/kimbie.dark.css deleted file mode 100644 index d139cb5d0..000000000 --- a/src/ASPNETHandler/DemoApp/styles/kimbie.dark.css +++ /dev/null @@ -1,74 +0,0 @@ -/* - Name: Kimbie (dark) - Author: Jan T. Sott - License: Creative Commons Attribution-ShareAlike 4.0 Unported License - URL: https://github.com/idleberg/Kimbie-highlight.js -*/ - -/* Kimbie Comment */ -.hljs-comment, -.hljs-quote { - color: #d6baad; -} - -/* Kimbie Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-meta { - color: #dc3958; -} - -/* Kimbie Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-deletion, -.hljs-link { - color: #f79a32; -} - -/* Kimbie Yellow */ -.hljs-title, -.hljs-section, -.hljs-attribute { - color: #f06431; -} - -/* Kimbie Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #889b4a; -} - -/* Kimbie Purple */ -.hljs-keyword, -.hljs-selector-tag, -.hljs-function { - color: #98676a; -} - -.hljs { - display: block; - overflow-x: auto; - background: #221a0f; - color: #d3af86; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/kimbie.light.css b/src/ASPNETHandler/DemoApp/styles/kimbie.light.css deleted file mode 100644 index 04ff6ed3a..000000000 --- a/src/ASPNETHandler/DemoApp/styles/kimbie.light.css +++ /dev/null @@ -1,74 +0,0 @@ -/* - Name: Kimbie (light) - Author: Jan T. Sott - License: Creative Commons Attribution-ShareAlike 4.0 Unported License - URL: https://github.com/idleberg/Kimbie-highlight.js -*/ - -/* Kimbie Comment */ -.hljs-comment, -.hljs-quote { - color: #a57a4c; -} - -/* Kimbie Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-meta { - color: #dc3958; -} - -/* Kimbie Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-deletion, -.hljs-link { - color: #f79a32; -} - -/* Kimbie Yellow */ -.hljs-title, -.hljs-section, -.hljs-attribute { - color: #f06431; -} - -/* Kimbie Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #889b4a; -} - -/* Kimbie Purple */ -.hljs-keyword, -.hljs-selector-tag, -.hljs-function { - color: #98676a; -} - -.hljs { - display: block; - overflow-x: auto; - background: #fbebd4; - color: #84613d; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/magula.css b/src/ASPNETHandler/DemoApp/styles/magula.css deleted file mode 100644 index 44dee5e8e..000000000 --- a/src/ASPNETHandler/DemoApp/styles/magula.css +++ /dev/null @@ -1,70 +0,0 @@ -/* -Description: Magula style for highligh.js -Author: Ruslan Keba -Website: http://rukeba.com/ -Version: 1.0 -Date: 2009-01-03 -Music: Aphex Twin / Xtal -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background-color: #f4f4f4; -} - -.hljs, -.hljs-subst { - color: black; -} - -.hljs-string, -.hljs-title, -.hljs-symbol, -.hljs-bullet, -.hljs-attribute, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable { - color: #050; -} - -.hljs-comment, -.hljs-quote { - color: #777; -} - -.hljs-number, -.hljs-regexp, -.hljs-literal, -.hljs-type, -.hljs-link { - color: #800; -} - -.hljs-deletion, -.hljs-meta { - color: #00e; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-doctag, -.hljs-title, -.hljs-section, -.hljs-built_in, -.hljs-tag, -.hljs-name { - font-weight: bold; - color: navy; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/mono-blue.css b/src/ASPNETHandler/DemoApp/styles/mono-blue.css deleted file mode 100644 index 884c97c76..000000000 --- a/src/ASPNETHandler/DemoApp/styles/mono-blue.css +++ /dev/null @@ -1,59 +0,0 @@ -/* - Five-color theme from a single blue hue. -*/ -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #eaeef3; -} - -.hljs { - color: #00193a; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-title, -.hljs-section, -.hljs-doctag, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-comment { - color: #738191; -} - -.hljs-string, -.hljs-title, -.hljs-section, -.hljs-built_in, -.hljs-literal, -.hljs-type, -.hljs-addition, -.hljs-tag, -.hljs-quote, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #0048ab; -} - -.hljs-meta, -.hljs-subst, -.hljs-symbol, -.hljs-regexp, -.hljs-attribute, -.hljs-deletion, -.hljs-variable, -.hljs-template-variable, -.hljs-link, -.hljs-bullet { - color: #4c81c9; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/monokai-sublime.css b/src/ASPNETHandler/DemoApp/styles/monokai-sublime.css deleted file mode 100644 index 2864170da..000000000 --- a/src/ASPNETHandler/DemoApp/styles/monokai-sublime.css +++ /dev/null @@ -1,83 +0,0 @@ -/* - -Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/ - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #23241f; -} - -.hljs, -.hljs-tag, -.hljs-subst { - color: #f8f8f2; -} - -.hljs-strong, -.hljs-emphasis { - color: #a8a8a2; -} - -.hljs-bullet, -.hljs-quote, -.hljs-number, -.hljs-regexp, -.hljs-literal, -.hljs-link { - color: #ae81ff; -} - -.hljs-code, -.hljs-title, -.hljs-section, -.hljs-selector-class { - color: #a6e22e; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-name, -.hljs-attr { - color: #f92672; -} - -.hljs-symbol, -.hljs-attribute { - color: #66d9ef; -} - -.hljs-params, -.hljs-class .hljs-title { - color: #f8f8f2; -} - -.hljs-string, -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-selector-id, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-addition, -.hljs-variable, -.hljs-template-variable { - color: #e6db74; -} - -.hljs-comment, -.hljs-deletion, -.hljs-meta { - color: #75715e; -} diff --git a/src/ASPNETHandler/DemoApp/styles/monokai.css b/src/ASPNETHandler/DemoApp/styles/monokai.css deleted file mode 100644 index 775d53f91..000000000 --- a/src/ASPNETHandler/DemoApp/styles/monokai.css +++ /dev/null @@ -1,70 +0,0 @@ -/* -Monokai style - ported by Luigi Maselli - http://grigio.org -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #272822; color: #ddd; -} - -.hljs-tag, -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-strong, -.hljs-name { - color: #f92672; -} - -.hljs-code { - color: #66d9ef; -} - -.hljs-class .hljs-title { - color: white; -} - -.hljs-attribute, -.hljs-symbol, -.hljs-regexp, -.hljs-link { - color: #bf79db; -} - -.hljs-string, -.hljs-bullet, -.hljs-subst, -.hljs-title, -.hljs-section, -.hljs-emphasis, -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable { - color: #a6e22e; -} - -.hljs-comment, -.hljs-quote, -.hljs-deletion, -.hljs-meta { - color: #75715e; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-doctag, -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-selector-id { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/obsidian.css b/src/ASPNETHandler/DemoApp/styles/obsidian.css deleted file mode 100644 index 356630fa2..000000000 --- a/src/ASPNETHandler/DemoApp/styles/obsidian.css +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Obsidian style - * ported by Alexander Marenin (http://github.com/ioncreature) - */ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #282b2e; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-selector-id { - color: #93c763; -} - -.hljs-number { - color: #ffcd22; -} - -.hljs { - color: #e0e2e4; -} - -.hljs-attribute { - color: #668bb0; -} - -.hljs-code, -.hljs-class .hljs-title, -.hljs-section { - color: white; -} - -.hljs-regexp, -.hljs-link { - color: #d39745; -} - -.hljs-meta { - color: #557182; -} - -.hljs-tag, -.hljs-name, -.hljs-bullet, -.hljs-subst, -.hljs-emphasis, -.hljs-type, -.hljs-built_in, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable { - color: #8cbbad; -} - -.hljs-string, -.hljs-symbol { - color: #ec7600; -} - -.hljs-comment, -.hljs-quote, -.hljs-deletion { - color: #818e96; -} - -.hljs-selector-class { - color: #A082BD -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-doctag, -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-name, -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/ocean.css b/src/ASPNETHandler/DemoApp/styles/ocean.css deleted file mode 100644 index 5901581b4..000000000 --- a/src/ASPNETHandler/DemoApp/styles/ocean.css +++ /dev/null @@ -1,74 +0,0 @@ -/* Ocean Dark Theme */ -/* https://github.com/gavsiu */ -/* Original theme - https://github.com/chriskempson/base16 */ - -/* Ocean Comment */ -.hljs-comment, -.hljs-quote { - color: #65737e; -} - -/* Ocean Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-deletion { - color: #bf616a; -} - -/* Ocean Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-meta, -.hljs-link { - color: #d08770; -} - -/* Ocean Yellow */ -.hljs-attribute { - color: #ebcb8b; -} - -/* Ocean Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #a3be8c; -} - -/* Ocean Blue */ -.hljs-title, -.hljs-section { - color: #8fa1b3; -} - -/* Ocean Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #b48ead; -} - -.hljs { - display: block; - overflow-x: auto; - background: #2b303b; - color: #c0c5ce; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/paraiso-dark.css b/src/ASPNETHandler/DemoApp/styles/paraiso-dark.css deleted file mode 100644 index e7292401c..000000000 --- a/src/ASPNETHandler/DemoApp/styles/paraiso-dark.css +++ /dev/null @@ -1,72 +0,0 @@ -/* - Paraíso (dark) - Created by Jan T. Sott (http://github.com/idleberg) - Inspired by the art of Rubens LP (http://www.rubenslp.com.br) -*/ - -/* Paraíso Comment */ -.hljs-comment, -.hljs-quote { - color: #8d8687; -} - -/* Paraíso Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-link, -.hljs-meta { - color: #ef6155; -} - -/* Paraíso Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-deletion { - color: #f99b15; -} - -/* Paraíso Yellow */ -.hljs-title, -.hljs-section, -.hljs-attribute { - color: #fec418; -} - -/* Paraíso Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #48b685; -} - -/* Paraíso Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #815ba4; -} - -.hljs { - display: block; - overflow-x: auto; - background: #2f1e2e; - color: #a39e9b; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/paraiso-light.css b/src/ASPNETHandler/DemoApp/styles/paraiso-light.css deleted file mode 100644 index 944857cd8..000000000 --- a/src/ASPNETHandler/DemoApp/styles/paraiso-light.css +++ /dev/null @@ -1,72 +0,0 @@ -/* - Paraíso (light) - Created by Jan T. Sott (http://github.com/idleberg) - Inspired by the art of Rubens LP (http://www.rubenslp.com.br) -*/ - -/* Paraíso Comment */ -.hljs-comment, -.hljs-quote { - color: #776e71; -} - -/* Paraíso Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-link, -.hljs-meta { - color: #ef6155; -} - -/* Paraíso Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-deletion { - color: #f99b15; -} - -/* Paraíso Yellow */ -.hljs-title, -.hljs-section, -.hljs-attribute { - color: #fec418; -} - -/* Paraíso Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #48b685; -} - -/* Paraíso Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #815ba4; -} - -.hljs { - display: block; - overflow-x: auto; - background: #e7e9db; - color: #4f424c; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/pojoaque.css b/src/ASPNETHandler/DemoApp/styles/pojoaque.css deleted file mode 100644 index 2e07847b2..000000000 --- a/src/ASPNETHandler/DemoApp/styles/pojoaque.css +++ /dev/null @@ -1,83 +0,0 @@ -/* - -Pojoaque Style by Jason Tate -http://web-cms-designs.com/ftopict-10-pojoaque-style-for-highlight-js-code-highlighter.html -Based on Solarized Style from http://ethanschoonover.com/solarized - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #dccf8f; - background: url(./pojoaque.jpg) repeat scroll left top #181914; -} - -.hljs-comment, -.hljs-quote { - color: #586e75; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-addition { - color: #b64926; -} - -.hljs-number, -.hljs-string, -.hljs-doctag, -.hljs-regexp { - color: #468966; -} - -.hljs-title, -.hljs-section, -.hljs-built_in, -.hljs-name { - color: #ffb03b; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-class .hljs-title, -.hljs-type, -.hljs-tag { - color: #b58900; -} - -.hljs-attribute { - color: #b89859; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-link, -.hljs-subst, -.hljs-meta { - color: #cb4b16; -} - -.hljs-deletion { - color: #dc322f; -} - -.hljs-selector-id, -.hljs-selector-class { - color: #d3a60c; -} - -.hljs-formula { - background: #073642; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/pojoaque.jpg b/src/ASPNETHandler/DemoApp/styles/pojoaque.jpg deleted file mode 100644 index 9c07d4ab4..000000000 Binary files a/src/ASPNETHandler/DemoApp/styles/pojoaque.jpg and /dev/null differ diff --git a/src/ASPNETHandler/DemoApp/styles/purebasic.css b/src/ASPNETHandler/DemoApp/styles/purebasic.css deleted file mode 100644 index 5ce9b9e07..000000000 --- a/src/ASPNETHandler/DemoApp/styles/purebasic.css +++ /dev/null @@ -1,96 +0,0 @@ -/* - -PureBASIC native IDE style ( version 1.0 - April 2016 ) - -by Tristano Ajmone - -Public Domain - -NOTE_1: PureBASIC code syntax highlighting only applies the following classes: - .hljs-comment - .hljs-function - .hljs-keywords - .hljs-string - .hljs-symbol - - Other classes are added here for the benefit of styling other languages with the look and feel of PureBASIC native IDE style. - If you need to customize a stylesheet for PureBASIC only, remove all non-relevant classes -- PureBASIC-related classes are followed by - a "--- used for PureBASIC ... ---" comment on same line. - -NOTE_2: Color names provided in comments were derived using "Name that Color" online tool: - http://chir.ag/projects/name-that-color -*/ - -.hljs { /* Common set of rules required by highlight.js (don'r remove!) */ - display: block; - overflow-x: auto; - padding: 0.5em; - background: #FFFFDF; /* Half and Half (approx.) */ -/* --- Uncomment to add PureBASIC native IDE styled font! - font-family: Consolas; -*/ -} - -.hljs, /* --- used for PureBASIC base color --- */ -.hljs-type, /* --- used for PureBASIC Procedures return type --- */ -.hljs-function, /* --- used for wrapping PureBASIC Procedures definitions --- */ -.hljs-name, -.hljs-number, -.hljs-attr, -.hljs-params, -.hljs-subst { - color: #000000; /* Black */ -} - -.hljs-comment, /* --- used for PureBASIC Comments --- */ -.hljs-regexp, -.hljs-section, -.hljs-selector-pseudo, -.hljs-addition { - color: #00AAAA; /* Persian Green (approx.) */ -} - -.hljs-title, /* --- used for PureBASIC Procedures Names --- */ -.hljs-tag, -.hljs-variable, -.hljs-code { - color: #006666; /* Blue Stone (approx.) */ -} - -.hljs-keyword, /* --- used for PureBASIC Keywords --- */ -.hljs-class, -.hljs-meta-keyword, -.hljs-selector-class, -.hljs-built_in, -.hljs-builtin-name { - color: #006666; /* Blue Stone (approx.) */ - font-weight: bold; -} - -.hljs-string, /* --- used for PureBASIC Strings --- */ -.hljs-selector-attr { - color: #0080FF; /* Azure Radiance (approx.) */ -} - -.hljs-symbol, /* --- used for PureBASIC Constants --- */ -.hljs-link, -.hljs-deletion, -.hljs-attribute { - color: #924B72; /* Cannon Pink (approx.) */ -} - -.hljs-meta, -.hljs-literal, -.hljs-selector-id { - color: #924B72; /* Cannon Pink (approx.) */ - font-weight: bold; -} - -.hljs-strong, -.hljs-name { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/qtcreator_dark.css b/src/ASPNETHandler/DemoApp/styles/qtcreator_dark.css deleted file mode 100644 index 7aa56a365..000000000 --- a/src/ASPNETHandler/DemoApp/styles/qtcreator_dark.css +++ /dev/null @@ -1,83 +0,0 @@ -/* - -Qt Creator dark color scheme - -*/ - - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #000000; -} - -.hljs, -.hljs-subst, -.hljs-tag, -.hljs-title { - color: #aaaaaa; -} - -.hljs-strong, -.hljs-emphasis { - color: #a8a8a2; -} - -.hljs-bullet, -.hljs-quote, -.hljs-number, -.hljs-regexp, -.hljs-literal { - color: #ff55ff; -} - -.hljs-code -.hljs-selector-class { - color: #aaaaff; -} - -.hljs-emphasis, -.hljs-stronge, -.hljs-type { - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-function, -.hljs-section, -.hljs-symbol, -.hljs-name { - color: #ffff55; -} - -.hljs-attribute { - color: #ff5555; -} - -.hljs-variable, -.hljs-params, -.hljs-class .hljs-title { - color: #8888ff; -} - -.hljs-string, -.hljs-selector-id, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-template-tag, -.hljs-template-variable, -.hljs-addition, -.hljs-link { - color: #ff55ff; -} - -.hljs-comment, -.hljs-meta, -.hljs-deletion { - color: #55ffff; -} diff --git a/src/ASPNETHandler/DemoApp/styles/qtcreator_light.css b/src/ASPNETHandler/DemoApp/styles/qtcreator_light.css deleted file mode 100644 index 1efa2c660..000000000 --- a/src/ASPNETHandler/DemoApp/styles/qtcreator_light.css +++ /dev/null @@ -1,83 +0,0 @@ -/* - -Qt Creator light color scheme - -*/ - - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #ffffff; -} - -.hljs, -.hljs-subst, -.hljs-tag, -.hljs-title { - color: #000000; -} - -.hljs-strong, -.hljs-emphasis { - color: #000000; -} - -.hljs-bullet, -.hljs-quote, -.hljs-number, -.hljs-regexp, -.hljs-literal { - color: #000080; -} - -.hljs-code -.hljs-selector-class { - color: #800080; -} - -.hljs-emphasis, -.hljs-stronge, -.hljs-type { - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-function, -.hljs-section, -.hljs-symbol, -.hljs-name { - color: #808000; -} - -.hljs-attribute { - color: #800000; -} - -.hljs-variable, -.hljs-params, -.hljs-class .hljs-title { - color: #0055AF; -} - -.hljs-string, -.hljs-selector-id, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-template-tag, -.hljs-template-variable, -.hljs-addition, -.hljs-link { - color: #008000; -} - -.hljs-comment, -.hljs-meta, -.hljs-deletion { - color: #008000; -} diff --git a/src/ASPNETHandler/DemoApp/styles/railscasts.css b/src/ASPNETHandler/DemoApp/styles/railscasts.css deleted file mode 100644 index 008cdc5bf..000000000 --- a/src/ASPNETHandler/DemoApp/styles/railscasts.css +++ /dev/null @@ -1,106 +0,0 @@ -/* - -Railscasts-like style (c) Visoft, Inc. (Damien White) - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #232323; - color: #e6e1dc; -} - -.hljs-comment, -.hljs-quote { - color: #bc9458; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag { - color: #c26230; -} - -.hljs-string, -.hljs-number, -.hljs-regexp, -.hljs-variable, -.hljs-template-variable { - color: #a5c261; -} - -.hljs-subst { - color: #519f50; -} - -.hljs-tag, -.hljs-name { - color: #e8bf6a; -} - -.hljs-type { - color: #da4939; -} - - -.hljs-symbol, -.hljs-bullet, -.hljs-built_in, -.hljs-builtin-name, -.hljs-attr, -.hljs-link { - color: #6d9cbe; -} - -.hljs-params { - color: #d0d0ff; -} - -.hljs-attribute { - color: #cda869; -} - -.hljs-meta { - color: #9b859d; -} - -.hljs-title, -.hljs-section { - color: #ffc66d; -} - -.hljs-addition { - background-color: #144212; - color: #e6e1dc; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #600; - color: #e6e1dc; - display: inline-block; - width: 100%; -} - -.hljs-selector-class { - color: #9b703f; -} - -.hljs-selector-id { - color: #8b98ab; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-link { - text-decoration: underline; -} diff --git a/src/ASPNETHandler/DemoApp/styles/rainbow.css b/src/ASPNETHandler/DemoApp/styles/rainbow.css deleted file mode 100644 index 905eb8ef1..000000000 --- a/src/ASPNETHandler/DemoApp/styles/rainbow.css +++ /dev/null @@ -1,85 +0,0 @@ -/* - -Style with support for rainbow parens - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #474949; - color: #d1d9e1; -} - - -.hljs-comment, -.hljs-quote { - color: #969896; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-type, -.hljs-addition { - color: #cc99cc; -} - -.hljs-number, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #f99157; -} - -.hljs-string, -.hljs-doctag, -.hljs-regexp { - color: #8abeb7; -} - -.hljs-title, -.hljs-name, -.hljs-section, -.hljs-built_in { - color: #b5bd68; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-selector-id, -.hljs-class .hljs-title { - color: #ffcc66; -} - -.hljs-section, -.hljs-name, -.hljs-strong { - font-weight: bold; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-subst, -.hljs-meta, -.hljs-link { - color: #f99157; -} - -.hljs-deletion { - color: #dc322f; -} - -.hljs-formula { - background: #eee8d5; -} - -.hljs-attr, -.hljs-attribute { - color: #81a2be; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/routeros.css b/src/ASPNETHandler/DemoApp/styles/routeros.css deleted file mode 100644 index ebe23990d..000000000 --- a/src/ASPNETHandler/DemoApp/styles/routeros.css +++ /dev/null @@ -1,108 +0,0 @@ -/* - - highlight.js style for Microtik RouterOS script - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #F0F0F0; -} - -/* Base color: saturation 0; */ - -.hljs, -.hljs-subst { - color: #444; -} - -.hljs-comment { - color: #888888; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-meta-keyword, -.hljs-doctag, -.hljs-name { - font-weight: bold; -} - -.hljs-attribute { - color: #0E9A00; -} - -.hljs-function { - color: #99069A; -} - -.hljs-builtin-name { - color: #99069A; -} - -/* User color: hue: 0 */ - -.hljs-type, -.hljs-string, -.hljs-number, -.hljs-selector-id, -.hljs-selector-class, -.hljs-quote, -.hljs-template-tag, -.hljs-deletion { - color: #880000; -} - -.hljs-title, -.hljs-section { - color: #880000; - font-weight: bold; -} - -.hljs-regexp, -.hljs-symbol, -.hljs-variable, -.hljs-template-variable, -.hljs-link, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #BC6060; -} - - -/* Language color: hue: 90; */ - -.hljs-literal { - color: #78A960; -} - -.hljs-built_in, -.hljs-bullet, -.hljs-code, -.hljs-addition { - color: #0C9A9A; -} - - -/* Meta color: hue: 200 */ - -.hljs-meta { - color: #1f7199; -} - -.hljs-meta-string { - color: #4d99bf; -} - - -/* Misc effects */ - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/school-book.css b/src/ASPNETHandler/DemoApp/styles/school-book.css deleted file mode 100644 index 964b51d84..000000000 --- a/src/ASPNETHandler/DemoApp/styles/school-book.css +++ /dev/null @@ -1,72 +0,0 @@ -/* - -School Book style from goldblog.com.ua (c) Zaripov Yura - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 15px 0.5em 0.5em 30px; - font-size: 11px; - line-height:16px; -} - -pre{ - background:#f6f6ae url(./school-book.png); - border-top: solid 2px #d2e8b9; - border-bottom: solid 1px #d2e8b9; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal { - color:#005599; - font-weight:bold; -} - -.hljs, -.hljs-subst { - color: #3e5915; -} - -.hljs-string, -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-symbol, -.hljs-bullet, -.hljs-attribute, -.hljs-built_in, -.hljs-builtin-name, -.hljs-addition, -.hljs-variable, -.hljs-template-tag, -.hljs-template-variable, -.hljs-link { - color: #2c009f; -} - -.hljs-comment, -.hljs-quote, -.hljs-deletion, -.hljs-meta { - color: #e60415; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal, -.hljs-doctag, -.hljs-title, -.hljs-section, -.hljs-type, -.hljs-name, -.hljs-selector-id, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/school-book.png b/src/ASPNETHandler/DemoApp/styles/school-book.png deleted file mode 100644 index 956e9790a..000000000 Binary files a/src/ASPNETHandler/DemoApp/styles/school-book.png and /dev/null differ diff --git a/src/ASPNETHandler/DemoApp/styles/solarized-dark.css b/src/ASPNETHandler/DemoApp/styles/solarized-dark.css deleted file mode 100644 index b4c0da1f7..000000000 --- a/src/ASPNETHandler/DemoApp/styles/solarized-dark.css +++ /dev/null @@ -1,84 +0,0 @@ -/* - -Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #002b36; - color: #839496; -} - -.hljs-comment, -.hljs-quote { - color: #586e75; -} - -/* Solarized Green */ -.hljs-keyword, -.hljs-selector-tag, -.hljs-addition { - color: #859900; -} - -/* Solarized Cyan */ -.hljs-number, -.hljs-string, -.hljs-meta .hljs-meta-string, -.hljs-literal, -.hljs-doctag, -.hljs-regexp { - color: #2aa198; -} - -/* Solarized Blue */ -.hljs-title, -.hljs-section, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #268bd2; -} - -/* Solarized Yellow */ -.hljs-attribute, -.hljs-attr, -.hljs-variable, -.hljs-template-variable, -.hljs-class .hljs-title, -.hljs-type { - color: #b58900; -} - -/* Solarized Orange */ -.hljs-symbol, -.hljs-bullet, -.hljs-subst, -.hljs-meta, -.hljs-meta .hljs-keyword, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-link { - color: #cb4b16; -} - -/* Solarized Red */ -.hljs-built_in, -.hljs-deletion { - color: #dc322f; -} - -.hljs-formula { - background: #073642; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/solarized-light.css b/src/ASPNETHandler/DemoApp/styles/solarized-light.css deleted file mode 100644 index fdcfcc72c..000000000 --- a/src/ASPNETHandler/DemoApp/styles/solarized-light.css +++ /dev/null @@ -1,84 +0,0 @@ -/* - -Orginal Style from ethanschoonover.com/solarized (c) Jeremy Hull - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #fdf6e3; - color: #657b83; -} - -.hljs-comment, -.hljs-quote { - color: #93a1a1; -} - -/* Solarized Green */ -.hljs-keyword, -.hljs-selector-tag, -.hljs-addition { - color: #859900; -} - -/* Solarized Cyan */ -.hljs-number, -.hljs-string, -.hljs-meta .hljs-meta-string, -.hljs-literal, -.hljs-doctag, -.hljs-regexp { - color: #2aa198; -} - -/* Solarized Blue */ -.hljs-title, -.hljs-section, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class { - color: #268bd2; -} - -/* Solarized Yellow */ -.hljs-attribute, -.hljs-attr, -.hljs-variable, -.hljs-template-variable, -.hljs-class .hljs-title, -.hljs-type { - color: #b58900; -} - -/* Solarized Orange */ -.hljs-symbol, -.hljs-bullet, -.hljs-subst, -.hljs-meta, -.hljs-meta .hljs-keyword, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-link { - color: #cb4b16; -} - -/* Solarized Red */ -.hljs-built_in, -.hljs-deletion { - color: #dc322f; -} - -.hljs-formula { - background: #eee8d5; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/sunburst.css b/src/ASPNETHandler/DemoApp/styles/sunburst.css deleted file mode 100644 index f56dd5e9b..000000000 --- a/src/ASPNETHandler/DemoApp/styles/sunburst.css +++ /dev/null @@ -1,102 +0,0 @@ -/* - -Sunburst-like style (c) Vasily Polovnyov - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #000; - color: #f8f8f8; -} - -.hljs-comment, -.hljs-quote { - color: #aeaeae; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-type { - color: #e28964; -} - -.hljs-string { - color: #65b042; -} - -.hljs-subst { - color: #daefa3; -} - -.hljs-regexp, -.hljs-link { - color: #e9c062; -} - -.hljs-title, -.hljs-section, -.hljs-tag, -.hljs-name { - color: #89bdff; -} - -.hljs-class .hljs-title, -.hljs-doctag { - text-decoration: underline; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-number { - color: #3387cc; -} - -.hljs-params, -.hljs-variable, -.hljs-template-variable { - color: #3e87e3; -} - -.hljs-attribute { - color: #cda869; -} - -.hljs-meta { - color: #8996a8; -} - -.hljs-formula { - background-color: #0e2231; - color: #f8f8f8; - font-style: italic; -} - -.hljs-addition { - background-color: #253b22; - color: #f8f8f8; -} - -.hljs-deletion { - background-color: #420e09; - color: #f8f8f8; -} - -.hljs-selector-class { - color: #9b703f; -} - -.hljs-selector-id { - color: #8b98ab; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/tomorrow-night-blue.css b/src/ASPNETHandler/DemoApp/styles/tomorrow-night-blue.css deleted file mode 100644 index 78e59cc8c..000000000 --- a/src/ASPNETHandler/DemoApp/styles/tomorrow-night-blue.css +++ /dev/null @@ -1,75 +0,0 @@ -/* Tomorrow Night Blue Theme */ -/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ -/* Original theme - https://github.com/chriskempson/tomorrow-theme */ -/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ - -/* Tomorrow Comment */ -.hljs-comment, -.hljs-quote { - color: #7285b7; -} - -/* Tomorrow Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-deletion { - color: #ff9da4; -} - -/* Tomorrow Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-meta, -.hljs-link { - color: #ffc58f; -} - -/* Tomorrow Yellow */ -.hljs-attribute { - color: #ffeead; -} - -/* Tomorrow Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #d1f1a9; -} - -/* Tomorrow Blue */ -.hljs-title, -.hljs-section { - color: #bbdaff; -} - -/* Tomorrow Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #ebbbff; -} - -.hljs { - display: block; - overflow-x: auto; - background: #002451; - color: white; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/tomorrow-night-bright.css b/src/ASPNETHandler/DemoApp/styles/tomorrow-night-bright.css deleted file mode 100644 index e05af8ae2..000000000 --- a/src/ASPNETHandler/DemoApp/styles/tomorrow-night-bright.css +++ /dev/null @@ -1,74 +0,0 @@ -/* Tomorrow Night Bright Theme */ -/* Original theme - https://github.com/chriskempson/tomorrow-theme */ -/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ - -/* Tomorrow Comment */ -.hljs-comment, -.hljs-quote { - color: #969896; -} - -/* Tomorrow Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-deletion { - color: #d54e53; -} - -/* Tomorrow Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-meta, -.hljs-link { - color: #e78c45; -} - -/* Tomorrow Yellow */ -.hljs-attribute { - color: #e7c547; -} - -/* Tomorrow Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #b9ca4a; -} - -/* Tomorrow Blue */ -.hljs-title, -.hljs-section { - color: #7aa6da; -} - -/* Tomorrow Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #c397d8; -} - -.hljs { - display: block; - overflow-x: auto; - background: black; - color: #eaeaea; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/tomorrow-night-eighties.css b/src/ASPNETHandler/DemoApp/styles/tomorrow-night-eighties.css deleted file mode 100644 index 08fd51c74..000000000 --- a/src/ASPNETHandler/DemoApp/styles/tomorrow-night-eighties.css +++ /dev/null @@ -1,74 +0,0 @@ -/* Tomorrow Night Eighties Theme */ -/* Original theme - https://github.com/chriskempson/tomorrow-theme */ -/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ - -/* Tomorrow Comment */ -.hljs-comment, -.hljs-quote { - color: #999999; -} - -/* Tomorrow Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-deletion { - color: #f2777a; -} - -/* Tomorrow Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-meta, -.hljs-link { - color: #f99157; -} - -/* Tomorrow Yellow */ -.hljs-attribute { - color: #ffcc66; -} - -/* Tomorrow Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #99cc99; -} - -/* Tomorrow Blue */ -.hljs-title, -.hljs-section { - color: #6699cc; -} - -/* Tomorrow Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #cc99cc; -} - -.hljs { - display: block; - overflow-x: auto; - background: #2d2d2d; - color: #cccccc; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/tomorrow-night.css b/src/ASPNETHandler/DemoApp/styles/tomorrow-night.css deleted file mode 100644 index ddd270a4e..000000000 --- a/src/ASPNETHandler/DemoApp/styles/tomorrow-night.css +++ /dev/null @@ -1,75 +0,0 @@ -/* Tomorrow Night Theme */ -/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ -/* Original theme - https://github.com/chriskempson/tomorrow-theme */ -/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ - -/* Tomorrow Comment */ -.hljs-comment, -.hljs-quote { - color: #969896; -} - -/* Tomorrow Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-deletion { - color: #cc6666; -} - -/* Tomorrow Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-meta, -.hljs-link { - color: #de935f; -} - -/* Tomorrow Yellow */ -.hljs-attribute { - color: #f0c674; -} - -/* Tomorrow Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #b5bd68; -} - -/* Tomorrow Blue */ -.hljs-title, -.hljs-section { - color: #81a2be; -} - -/* Tomorrow Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #b294bb; -} - -.hljs { - display: block; - overflow-x: auto; - background: #1d1f21; - color: #c5c8c6; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/tomorrow.css b/src/ASPNETHandler/DemoApp/styles/tomorrow.css deleted file mode 100644 index 026a62fe3..000000000 --- a/src/ASPNETHandler/DemoApp/styles/tomorrow.css +++ /dev/null @@ -1,72 +0,0 @@ -/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ - -/* Tomorrow Comment */ -.hljs-comment, -.hljs-quote { - color: #8e908c; -} - -/* Tomorrow Red */ -.hljs-variable, -.hljs-template-variable, -.hljs-tag, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-regexp, -.hljs-deletion { - color: #c82829; -} - -/* Tomorrow Orange */ -.hljs-number, -.hljs-built_in, -.hljs-builtin-name, -.hljs-literal, -.hljs-type, -.hljs-params, -.hljs-meta, -.hljs-link { - color: #f5871f; -} - -/* Tomorrow Yellow */ -.hljs-attribute { - color: #eab700; -} - -/* Tomorrow Green */ -.hljs-string, -.hljs-symbol, -.hljs-bullet, -.hljs-addition { - color: #718c00; -} - -/* Tomorrow Blue */ -.hljs-title, -.hljs-section { - color: #4271ae; -} - -/* Tomorrow Purple */ -.hljs-keyword, -.hljs-selector-tag { - color: #8959a8; -} - -.hljs { - display: block; - overflow-x: auto; - background: white; - color: #4d4d4c; - padding: 0.5em; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/vs.css b/src/ASPNETHandler/DemoApp/styles/vs.css deleted file mode 100644 index c5d07d311..000000000 --- a/src/ASPNETHandler/DemoApp/styles/vs.css +++ /dev/null @@ -1,68 +0,0 @@ -/* - -Visual Studio-like style based on original C# coloring by Jason Diamond - -*/ -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: white; - color: black; -} - -.hljs-comment, -.hljs-quote, -.hljs-variable { - color: #008000; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-built_in, -.hljs-name, -.hljs-tag { - color: #00f; -} - -.hljs-string, -.hljs-title, -.hljs-section, -.hljs-attribute, -.hljs-literal, -.hljs-template-tag, -.hljs-template-variable, -.hljs-type, -.hljs-addition { - color: #a31515; -} - -.hljs-deletion, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-meta { - color: #2b91af; -} - -.hljs-doctag { - color: #808080; -} - -.hljs-attr { - color: #f00; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-link { - color: #00b0e8; -} - - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/styles/vs2015.css b/src/ASPNETHandler/DemoApp/styles/vs2015.css deleted file mode 100644 index d1d9be3ca..000000000 --- a/src/ASPNETHandler/DemoApp/styles/vs2015.css +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Visual Studio 2015 dark style - * Author: Nicolas LLOBERA - */ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #1E1E1E; - color: #DCDCDC; -} - -.hljs-keyword, -.hljs-literal, -.hljs-symbol, -.hljs-name { - color: #569CD6; -} -.hljs-link { - color: #569CD6; - text-decoration: underline; -} - -.hljs-built_in, -.hljs-type { - color: #4EC9B0; -} - -.hljs-number, -.hljs-class { - color: #B8D7A3; -} - -.hljs-string, -.hljs-meta-string { - color: #D69D85; -} - -.hljs-regexp, -.hljs-template-tag { - color: #9A5334; -} - -.hljs-subst, -.hljs-function, -.hljs-title, -.hljs-params, -.hljs-formula { - color: #DCDCDC; -} - -.hljs-comment, -.hljs-quote { - color: #57A64A; - font-style: italic; -} - -.hljs-doctag { - color: #608B4E; -} - -.hljs-meta, -.hljs-meta-keyword, -.hljs-tag { - color: #9B9B9B; -} - -.hljs-variable, -.hljs-template-variable { - color: #BD63C5; -} - -.hljs-attr, -.hljs-attribute, -.hljs-builtin-name { - color: #9CDCFE; -} - -.hljs-section { - color: gold; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -/*.hljs-code { - font-family:'Monospace'; -}*/ - -.hljs-bullet, -.hljs-selector-tag, -.hljs-selector-id, -.hljs-selector-class, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #D7BA7D; -} - -.hljs-addition { - background-color: #144212; - display: inline-block; - width: 100%; -} - -.hljs-deletion { - background-color: #600; - display: inline-block; - width: 100%; -} diff --git a/src/ASPNETHandler/DemoApp/styles/xcode.css b/src/ASPNETHandler/DemoApp/styles/xcode.css deleted file mode 100644 index 43dddad84..000000000 --- a/src/ASPNETHandler/DemoApp/styles/xcode.css +++ /dev/null @@ -1,93 +0,0 @@ -/* - -XCode style (c) Angel Garcia - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #fff; - color: black; -} - -.hljs-comment, -.hljs-quote { - color: #006a00; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-literal { - color: #aa0d91; -} - -.hljs-name { - color: #008; -} - -.hljs-variable, -.hljs-template-variable { - color: #660; -} - -.hljs-string { - color: #c41a16; -} - -.hljs-regexp, -.hljs-link { - color: #080; -} - -.hljs-title, -.hljs-tag, -.hljs-symbol, -.hljs-bullet, -.hljs-number, -.hljs-meta { - color: #1c00cf; -} - -.hljs-section, -.hljs-class .hljs-title, -.hljs-type, -.hljs-attr, -.hljs-built_in, -.hljs-builtin-name, -.hljs-params { - color: #5c2699; -} - -.hljs-attribute, -.hljs-subst { - color: #000; -} - -.hljs-formula { - background-color: #eee; - font-style: italic; -} - -.hljs-addition { - background-color: #baeeba; -} - -.hljs-deletion { - background-color: #ffc8bd; -} - -.hljs-selector-id, -.hljs-selector-class { - color: #9b703f; -} - -.hljs-doctag, -.hljs-strong { - font-weight: bold; -} - -.hljs-emphasis { - font-style: italic; -} diff --git a/src/ASPNETHandler/DemoApp/styles/xt256.css b/src/ASPNETHandler/DemoApp/styles/xt256.css deleted file mode 100644 index 58df82cb7..000000000 --- a/src/ASPNETHandler/DemoApp/styles/xt256.css +++ /dev/null @@ -1,92 +0,0 @@ - -/* - xt256.css - - Contact: initbar [at] protonmail [dot] ch - : github.com/initbar -*/ - -.hljs { - display: block; - overflow-x: auto; - color: #eaeaea; - background: #000; - padding: 0.5; -} - -.hljs-subst { - color: #eaeaea; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} - -.hljs-builtin-name, -.hljs-type { - color: #eaeaea; -} - -.hljs-params { - color: #da0000; -} - -.hljs-literal, -.hljs-number, -.hljs-name { - color: #ff0000; - font-weight: bolder; -} - -.hljs-comment { - color: #969896; -} - -.hljs-selector-id, -.hljs-quote { - color: #00ffff; -} - -.hljs-template-variable, -.hljs-variable, -.hljs-title { - color: #00ffff; - font-weight: bold; -} - -.hljs-selector-class, -.hljs-keyword, -.hljs-symbol { - color: #fff000; -} - -.hljs-string, -.hljs-bullet { - color: #00ff00; -} - -.hljs-tag, -.hljs-section { - color: #000fff; -} - -.hljs-selector-tag { - color: #000fff; - font-weight: bold; -} - -.hljs-attribute, -.hljs-built_in, -.hljs-regexp, -.hljs-link { - color: #ff00ff; -} - -.hljs-meta { - color: #fff; - font-weight: bolder; -} diff --git a/src/ASPNETHandler/DemoApp/styles/zenburn.css b/src/ASPNETHandler/DemoApp/styles/zenburn.css deleted file mode 100644 index 07be50201..000000000 --- a/src/ASPNETHandler/DemoApp/styles/zenburn.css +++ /dev/null @@ -1,80 +0,0 @@ -/* - -Zenburn style from voldmar.ru (c) Vladimir Epifanov -based on dark.css by Ivan Sagalaev - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #3f3f3f; - color: #dcdcdc; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-tag { - color: #e3ceab; -} - -.hljs-template-tag { - color: #dcdcdc; -} - -.hljs-number { - color: #8cd0d3; -} - -.hljs-variable, -.hljs-template-variable, -.hljs-attribute { - color: #efdcbc; -} - -.hljs-literal { - color: #efefaf; -} - -.hljs-subst { - color: #8f8f8f; -} - -.hljs-title, -.hljs-name, -.hljs-selector-id, -.hljs-selector-class, -.hljs-section, -.hljs-type { - color: #efef8f; -} - -.hljs-symbol, -.hljs-bullet, -.hljs-link { - color: #dca3a3; -} - -.hljs-deletion, -.hljs-string, -.hljs-built_in, -.hljs-builtin-name { - color: #cc9393; -} - -.hljs-addition, -.hljs-comment, -.hljs-quote, -.hljs-meta { - color: #7f9f7f; -} - - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/ASPNETHandler/DemoApp/viewsrc.jpg b/src/ASPNETHandler/DemoApp/viewsrc.jpg deleted file mode 100644 index 98b37e43a..000000000 Binary files a/src/ASPNETHandler/DemoApp/viewsrc.jpg and /dev/null differ diff --git a/src/ASPNETHandler/DemoApp/web.config b/src/ASPNETHandler/DemoApp/web.config deleted file mode 100644 index 3afa63324..000000000 --- a/src/ASPNETHandler/DemoApp/web.config +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ASPNETHandler/Dockerfile b/src/ASPNETHandler/Dockerfile deleted file mode 100644 index 70a939ce7..000000000 --- a/src/ASPNETHandler/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM mono - -MAINTAINER oscript.io team - -RUN apt-get update \ - && apt-get update \ - && apt-get install mono-devel apache2 libapache2-mod-mono mono-apache-server4 -y --no-install-recommends \ - && a2enmod mod_mono \ - && service apache2 stop \ - && apt-get autoremove -y \ - && apt-get clean \ - && rm -rf /var/tmp/* \ - && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /etc/mono/registry /etc/mono/registry/LocalMachine \ - && sed -ri ' \ - s!^(\s*CustomLog)\s+\S+!\1 /proc/self/fd/1!g; \ - s!^(\s*ErrorLog)\s+\S+!\1 /proc/self/fd/2!g; \ - ' /etc/apache2/apache2.conf - -ADD ./nixconfig/apache2-site.conf /etc/apache2/sites-available/default - -RUN mkdir -p /srv/www/mono.localhost - -COPY ./bin/Debug/ /srv/www/mono.localhost -COPY ./nixconfig/Web.config /srv/www/mono.localhost - -RUN ls /srv/www/mono.localhost - -RUN /usr/bin/mod-mono-server4 --version - -WORKDIR /var/www -EXPOSE 80 -CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"] \ No newline at end of file diff --git a/src/ASPNETHandler/HTTPServiceContext.cs b/src/ASPNETHandler/HTTPServiceContext.cs deleted file mode 100644 index 7f7256bac..000000000 --- a/src/ASPNETHandler/HTTPServiceContext.cs +++ /dev/null @@ -1,61 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; - -namespace ScriptEngine.HostedScript.Library.HTTPService -{ - [ContextClass("HTTPСервисКонтекст", "HTTPServiceContext")] - public class HTTPServiceContextImpl : AutoContext - { - System.Web.HttpContext _context; - SessionStateImpl _sessionState; - - public HTTPServiceContextImpl(System.Web.HttpContext context) - { - _context = context; - _sessionState = new SessionStateImpl(_context); - } - - [ContextProperty("ФизическийПуть", "PhysicalPath")] - public IValue PhysicalPath - { - get - { - return ValueFactory.Create(_context.Request.PhysicalPath); - } - } - - [ContextProperty("АдресКлиента", "UserHostAddress")] - public IValue UserHostAddress - { - get - { - return ValueFactory.Create(_context.Request.UserHostAddress); - } - } - - [ContextProperty("Сессия", "Session")] - public SessionStateImpl Session - { - get - { - return _sessionState; - } - } - - } -} diff --git a/src/ASPNETHandler/HTTPServiceRequest.cs b/src/ASPNETHandler/HTTPServiceRequest.cs deleted file mode 100644 index 0b1b61475..000000000 --- a/src/ASPNETHandler/HTTPServiceRequest.cs +++ /dev/null @@ -1,215 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; - -/// -/// -/// - -namespace ScriptEngine.HostedScript.Library.HTTPService -{ - /* HTTPСервисЗапрос - * Свойства: - - +HTTPМетод (HTTPMethod) - +БазовыйURL (BaseURL) - +Заголовки (Headers) - +ОтносительныйURL (RelativeURL) - +ПараметрыURL (URLParameters) - +ПараметрыЗапроса (QueryOptions) - - Методы: - - +ПолучитьТелоКакДвоичныеДанные (GetBodyAsBinaryData) - ПолучитьТелоКакПоток (GetBodyAsStream) - +ПолучитьТелоКакСтроку (GetBodyAsString) - - */ - [ContextClass("HTTPСервисЗапрос", "HTTPServiceRequest")] - public class HTTPServiceRequestImpl : AutoContext - { - System.Web.HttpContext _httpContext; - - FixedMapImpl _headers; - FixedMapImpl _urlParams; - FixedMapImpl _queryOptions; - HTTPServiceContextImpl _context; - - #region Свойства 1C - [ContextProperty("Контекст", "Context")] - public HTTPServiceContextImpl Context - { - get - { - return _context; - } - } - - - [ContextProperty("HTTPМетод", "HTTPMethod")] - public string HTTPMethod - { - get - { - return _httpContext.Request.HttpMethod.ToUpper(); - } - } - - [ContextProperty("БазовыйURL", "BaseURL")] - public string BaseURL - { - get - { - return _httpContext.Request.Url.Host; - } - } - - [ContextProperty("Заголовки", "Headers")] - public FixedMapImpl Headers - { - get - { - return _headers; - } - } - - [ContextProperty("ОтносительныйURL", "RelativeURL")] - public string RelativeURL - { - get - { - return _httpContext.Request.FilePath; - } - } - - [ContextProperty("ПараметрыURL", "URLParameters")] - public FixedMapImpl URLParameters - { - get - { - return _urlParams; - } - } - - [ContextProperty("ПараметрыЗапроса", "QueryOptions")] - public FixedMapImpl QueryOptions - { - get - { - return _queryOptions; - } - } - #endregion - - #region Методы1С - - [ContextMethod("ПолучитьТелоКакДвоичныеДанные", "GetBodyAsBinaryData")] - public BinaryDataContext GetBodyAsBinaryData() - { - System.IO.Stream str = _httpContext.Request.InputStream; - int bytes_count = Convert.ToInt32(str.Length); - byte[] buffer = new byte[bytes_count]; - str.Seek(0, System.IO.SeekOrigin.Begin); - str.Read(buffer, 0, bytes_count); - - return new BinaryDataContext(buffer); - } - - [ContextMethod("ПолучитьТелоКакСтроку", "GetBodyAsString")] - public string GetBodyAsString(IValue encoding = null) - { - // Формируем кодировку как в 1С. Если не указана, смотрим Content-Type. Если там не указана - используем UTF8 - System.Text.Encoding enc = System.Text.Encoding.UTF8; - - if (encoding != null) - enc = TextEncodingEnum.GetEncoding(encoding); - else - { - System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex("charset=([^\\\"']+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase); - string charsetString = regex.Match(_httpContext.Request.ContentType).Value; - - if (charsetString != "") - { - // Что-то нашли - try - { - //charsetString.Substring(8) делает "charset=Кодировка" -> "Кодировка" - enc = TextEncodingEnum.GetEncodingByName(charsetString.Substring(8)); - } - catch - { - // что то не так, осталась UTF8 - } - } - } - - System.IO.Stream str = _httpContext.Request.InputStream; - int bytes_count = Convert.ToInt32(str.Length); - byte[] buffer = new byte[bytes_count]; - - str.Seek(0, System.IO.SeekOrigin.Begin); - str.Read(buffer, 0, bytes_count); - - return enc.GetString(buffer); - } - - //ПолучитьТелоКакПоток(GetBodyAsStream) - [ContextMethod("ПолучитьТелоКакПоток", "GetBodyAsStream")] - public GenericStream GetBodyAsStream() - { - return new GenericStream(_httpContext.Request.InputStream); - } - - #endregion - - public HTTPServiceRequestImpl(System.Web.HttpContext ctx) - { - _httpContext = ctx; - // Инициализируем объект для 1С - // Заголовки - MapImpl headers = new MapImpl(); - - for (int i = 0; i < _httpContext.Request.Headers.Count; i++) - headers.Insert(ValueFactory.Create(_httpContext.Request.Headers.GetKey(i)) - , ValueFactory.Create(_httpContext.Request.Headers.Get(i)) - ); - - this._headers = new FixedMapImpl(headers); - - // ПараметрыURL будут пустыми - _urlParams = new FixedMapImpl(new MapImpl()); - - // Параметры запроса - MapImpl queryOptions = new MapImpl(); - - // Изменено для совместимости. в 1С только параметры командной строки - // Надо перенести в Контекст - //for (int i = 0; i < _httpContext.Request.Params.Count; i++) - // queryOptions.Insert(ValueFactory.Create(_httpContext.Request.Params.GetKey(i)) - // , ValueFactory.Create(_httpContext.Request.Params.Get(i)) - // ); - for (int i = 0; i < _httpContext.Request.QueryString.Count; i++) - queryOptions.Insert(ValueFactory.Create(_httpContext.Request.QueryString.GetKey(i)) - , ValueFactory.Create(_httpContext.Request.QueryString.Get(i)) - ); - - _queryOptions = new FixedMapImpl(queryOptions); - - _context = new HTTPServiceContextImpl(_httpContext); - } - } -} \ No newline at end of file diff --git a/src/ASPNETHandler/HTTPServiceResponse.cs b/src/ASPNETHandler/HTTPServiceResponse.cs deleted file mode 100644 index 4a6b08718..000000000 --- a/src/ASPNETHandler/HTTPServiceResponse.cs +++ /dev/null @@ -1,220 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; - -/// -/// -/// - -namespace ScriptEngine.HostedScript.Library.HTTPService -{ - /* - 8.3.10.2650 - - HTTPСервисОтвет (HTTPServiceResponseImpl) - - Свойства: - - +Заголовки (Headers) - Соответствие - +КодСостояния (StatusCode) - Целое - +Причина (Reason) - Строка - - Методы: - - +ПолучитьИмяФайлаТела (GetBodyFileName) - +ПолучитьТелоКакДвоичныеДанные (GetBodyAsBinaryData) - +ПолучитьТелоКакСтроку (GetBodyAsString) - +ПолучитьТелоКакПоток (GetBodyAsStream) - +УстановитьИмяФайлаТела (SetBodyFileName) - +УстановитьТелоИзДвоичныхДанных (SetBodyFromBinaryData) - +УстановитьТелоИзСтроки (SetBodyFromString) - - Конструкторы: - - +По коду состояния, причине и заголовкам - - ОТЛИЧИЯ: - При возврате потоков в несколько переменных, в 1С не обновляется текущее положение потока, - в настоящей реализации обновляется - */ - - [ContextClass("HTTPСервисОтвет", "HTTPServiceResponse")] - public class HTTPServiceResponseImpl : AutoContext - { - ScriptEngine.HostedScript.Library.MapImpl _headers = new HostedScript.Library.MapImpl(); - string _reason = ""; - int _statusCode = 200; - string _contentCharset = Encoding.UTF8.WebName; - - System.IO.Stream _bodyStream = new System.IO.MemoryStream(); - - public System.IO.Stream BodyStream - { - get - { - return _bodyStream; - } - } - - public string ContentCharset - { - get - { - return _contentCharset; - } - } - - public HTTPServiceResponseImpl() - { - } - - #region Свойства 1C - - [ContextProperty("Заголовки", "Headers")] - public MapImpl Headers - { - get - { - return _headers; - } - set - { - _headers = value; - } - } - - [ContextProperty("Причина", "Reason")] - public string Reason - { - get - { - return _reason; - } - set - { - _reason = value; - } - } - - [ContextProperty("КодСостояния", "StatusCode")] - public int StatusCode - { - get - { - return _statusCode; - } - set - { - _statusCode = value; - } - } - - #endregion - - #region Функции 1С - - [ContextMethod("ПолучитьИмяФайлаТела", "GetBodyFileName")] - public IValue GetBodyFileName() - { - if ((_bodyStream as System.IO.FileStream) == null) - return ValueFactory.Create(); - else - return ValueFactory.Create(((System.IO.FileStream)_bodyStream).Name); - } - - [ContextMethod("ПолучитьТелоКакДвоичныеДанные", "GetBodyAsBinaryData")] - public BinaryDataContext ПолучитьТелоКакДвоичныеДанные() - { - if ((_bodyStream as System.IO.MemoryStream) == null) - return null; - else - return new BinaryDataContext(((System.IO.MemoryStream)_bodyStream).GetBuffer()); - } - - [ContextMethod("ПолучитьТелоКакПоток", "GetBodyAsStream")] - public GenericStream GetBodyAsStream() - { - return new GenericStream(_bodyStream); - } - - [ContextMethod("ПолучитьТелоКакСтроку", "GetBodyAsString")] - public IValue GetBodyAsString() - { - if ((_bodyStream as System.IO.MemoryStream) == null) - return ValueFactory.Create(); - else - { - // Выяснено экспериментальным путем, используется UTF8 (8.3.10.2650) - return ValueFactory.Create(System.Text.Encoding.UTF8.GetString(((System.IO.MemoryStream)_bodyStream).GetBuffer())); - } - } - - [ContextMethod("УстановитьИмяФайлаТела", "SetBodyFileName")] - public void SetBodyFileName(IValue fileName) - { - _contentCharset = Encoding.UTF8.WebName; - _bodyStream = new System.IO.FileStream(fileName.AsString(), System.IO.FileMode.Open, System.IO.FileAccess.Read); - } - - [ContextMethod("УстановитьТелоИзДвоичныхДанных", "SetBodyFromBinaryData")] - public void SetBodyFromBinaryData(BinaryDataContext binaryData) - { - _contentCharset = Encoding.UTF8.WebName; - _bodyStream = new System.IO.MemoryStream(); - _bodyStream.Write(binaryData.Buffer, 0, binaryData.Buffer.Length); - _bodyStream.Seek(0, System.IO.SeekOrigin.Begin); - } - - [ContextMethod("УстановитьТелоИзСтроки", "SetBodyFromString")] - public void SetBodyFromString(string str, IValue encoding = null, IValue useBOM = null) - { - // Получаем кодировку - // useBOM должен иметь тип ИспользованиеByteOrderMark он не реализован. Его не используем - // Из синтаксис-помощника в режиме совместимости Использовать - // Из синтаксис помощника если кодировка не задана используем UTF8 - - System.Text.Encoding enc = System.Text.Encoding.UTF8; - if (encoding != null) - enc = TextEncodingEnum.GetEncoding(encoding); - - _contentCharset = enc.WebName; - - _bodyStream = new System.IO.MemoryStream(); - byte[] buffer = enc.GetBytes(str); - _bodyStream.Write(buffer, 0, buffer.Length); - _bodyStream.Seek(0, System.IO.SeekOrigin.Begin); - } - - [ScriptConstructor(Name = "По коду состояния, причине и заголовкам")] - public static HTTPServiceResponseImpl Constructor(IValue statusCode, IValue reason = null, MapImpl headers = null) - { - var response = new HTTPServiceResponseImpl(); - - response._statusCode = System.Convert.ToInt16(statusCode.AsNumber()); - - if (reason != null) - response._reason = reason.AsString(); - - if (headers != null) - response._headers = headers; - - return response; - } - - #endregion - } -} \ No newline at end of file diff --git a/src/ASPNETHandler/HTTPServices.csproj b/src/ASPNETHandler/HTTPServices.csproj deleted file mode 100644 index b8873cbfe..000000000 --- a/src/ASPNETHandler/HTTPServices.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - - ASPNETHandler - HTTP services for 1Script - ASPNETHandler - net452 - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ASPNETHandler/HttpMeans.cs b/src/ASPNETHandler/HttpMeans.cs deleted file mode 100644 index 6173025cb..000000000 --- a/src/ASPNETHandler/HttpMeans.cs +++ /dev/null @@ -1,44 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System.Web; - -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; - -/// -/// -/// - -namespace ScriptEngine.HostedScript.Library.HTTPService -{ - [ContextClass("СредстваHTTP", "HTTPMeans")] - public class HttpMeansImpl : AutoContext - { - [ContextMethod("ПолучитьФизическийПутьИзВиртуального", "MapPath")] - public string MapPath(string virtualPath) - { - return HttpContext.Current.Server.MapPath(virtualPath); ; - } - - [ScriptConstructor(Name = "Без параметров")] - public static HttpMeansImpl Constructor() - { - return new HttpMeansImpl(); - } - - public HttpMeansImpl() - { - } - } -} diff --git a/src/ASPNETHandler/SessionState.cs b/src/ASPNETHandler/SessionState.cs deleted file mode 100644 index 3658e0f16..000000000 --- a/src/ASPNETHandler/SessionState.cs +++ /dev/null @@ -1,44 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; - -/// -/// -/// - -namespace ScriptEngine.HostedScript.Library.HTTPService -{ - [ContextClass("HTTPСервисПараметрыСессии", "HTTPServiceRequestSessionState")] - public class SessionStateImpl : AutoContext - { - System.Web.HttpContext _context; - - public SessionStateImpl(System.Web.HttpContext context) - { - _context = context; - } - - [ContextProperty("Количество", "Count")] - public IValue Count - { - get - { - return ValueFactory.Create(_context.Session.Count); - } - } - } -} diff --git a/src/ASPNETHandler/check-docker.sh b/src/ASPNETHandler/check-docker.sh deleted file mode 100755 index 1a9439ae2..000000000 --- a/src/ASPNETHandler/check-docker.sh +++ /dev/null @@ -1,3 +0,0 @@ -docker build -t oscript/http-service . - -docker run --rm -it -p 8080:80 oscript/http-service diff --git a/src/ASPNETHandler/nixconfig/Web.config b/src/ASPNETHandler/nixconfig/Web.config deleted file mode 100644 index 6f9b6ae07..000000000 --- a/src/ASPNETHandler/nixconfig/Web.config +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ASPNETHandler/nixconfig/apache2-site.conf b/src/ASPNETHandler/nixconfig/apache2-site.conf deleted file mode 100644 index 3a131d082..000000000 --- a/src/ASPNETHandler/nixconfig/apache2-site.conf +++ /dev/null @@ -1,46 +0,0 @@ - - ServerName mono.localhost - ServerAdmin web-admin@mono.localhost - DocumentRoot /srv/www/mono.localhost - # MonoServerPath can be changed to specify which version of ASP.NET is hosted - # mod-mono-server1 = ASP.NET 1.1 / mod-mono-server2 = ASP.NET 2.0 - # For SUSE Linux Enterprise Mono Extension, uncomment the line below: - # MonoServerPath mono.localhost "/opt/novell/mono/bin/mod-mono-server2" - # For Mono on openSUSE, uncomment the line below instead: - MonoServerPath mono.localhost "/usr/bin/mod-mono-server4" - - # To obtain line numbers in stack traces you need to do two things: - # 1) Enable Debug code generation in your page by using the Debug="true" - # page directive, or by setting in the - # application's Web.config - # 2) Uncomment the MonoDebug true directive below to enable mod_mono debugging - MonoDebug mono.localhost true - - # The MONO_IOMAP environment variable can be configured to provide platform abstraction - # for file access in Linux. Valid values for MONO_IOMAP are: - # case - # drive - # all - # Uncomment the line below to alter file access behavior for the configured application - MonoSetEnv mono.localhost MONO_IOMAP=all - # - # Additional environtment variables can be set for this server instance using - # the MonoSetEnv directive. MonoSetEnv takes a string of 'name=value' pairs - # separated by semicolons. For instance, to enable platform abstraction *and* - # use Mono's old regular expression interpreter (which is slower, but has a - # shorter setup time), uncomment the line below instead: - # MonoSetEnv mono.localhost MONO_IOMAP=all;MONO_OLD_RX=1 - - MonoApplications mono.localhost "/mono:/srv/www/mono.localhost" - - Allow from all - Order allow,deny - MonoSetServerAlias mono.localhost - SetHandler mono - SetOutputFilter DEFLATE - SetEnvIfNoCase Request_URI "\.(?:gif|jpe?g|png)$" no-gzip dont-vary - - - AddOutputFilterByType DEFLATE text/html text/plain text/xml text/javascript - - \ No newline at end of file diff --git a/src/Component/Component.csproj b/src/Component/Component.csproj index b163bdc6b..d9639c318 100644 --- a/src/Component/Component.csproj +++ b/src/Component/Component.csproj @@ -1,35 +1,39 @@ - - - - - - net452 - Debug;Release - AnyCPU - - - - 1Script component example - - - - - - - - - - - - - - - - - - - - xcopy "$(TargetDir)Component.dll" "$(SolutionDir)..\tests\component" /Y /E /D - cp -f "$(TargetDir)Component.dll" "$(SolutionDir)../tests/component" - + + + + + + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug + AnyCPU + + + + 1Script component example + + + + + + + + + + + + + + + + + + + + xcopy "$(TargetDir)Component.dll" "$(SolutionDir)..\tests\component" /Y /E /D + cp -f "$(TargetDir)Component.dll" "$(SolutionDir)../tests/component" + + + true + false + \ No newline at end of file diff --git a/src/Component/ISimple.cs b/src/Component/ISimple.cs index 7dca16f31..fab054ecd 100644 --- a/src/Component/ISimple.cs +++ b/src/Component/ISimple.cs @@ -5,8 +5,6 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; - namespace Component { public interface ISimple diff --git a/src/Component/SimpleClass.cs b/src/Component/SimpleClass.cs index 568a0e75a..54b743579 100644 --- a/src/Component/SimpleClass.cs +++ b/src/Component/SimpleClass.cs @@ -5,8 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; - +using OneScript.Contexts; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; @@ -31,10 +30,10 @@ public static SimpleClass Constructor() } [ScriptConstructor] - public static SimpleClass Constructor(IValue initialProperty) + public static SimpleClass Constructor(int initialProperty) { var result = new SimpleClass(); - result.IntProperty = ContextValuesMarshaller.ConvertParam(initialProperty); + result.IntProperty = initialProperty; return result; } } diff --git a/src/Component/SimpleCollection.cs b/src/Component/SimpleCollection.cs index b14c96098..8b09c5b91 100644 --- a/src/Component/SimpleCollection.cs +++ b/src/Component/SimpleCollection.cs @@ -1,55 +1,43 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections; -using System.Collections.Generic; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace Component -{ - [ContextClass("ПростоКоллекция")] - public sealed class SimpleCollection : AutoContext, ICollectionContext, IEnumerable - { - private readonly List _data = new List(); - - [ContextMethod("Добавить")] - public void Add(SimpleClass item) - { - _data.Add(item); - } - - [ScriptConstructor] - public static IRuntimeContextInstance Constructor() - { - return new SimpleCollection(); - } - - [ContextMethod("Количество")] - public int Count() - { - return _data.Count; - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public IEnumerator GetEnumerator() - { - return ((IEnumerable) _data).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable) _data).GetEnumerator(); - } - } +/*---------------------------------------------------------- +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 OneScript.Contexts; +using ScriptEngine.Machine.Contexts; + +namespace Component +{ + [ContextClass("ПростоКоллекция")] + public sealed class SimpleCollection : AutoCollectionContext + { + private readonly List _data = new List(); + + [ContextMethod("Добавить")] + public void Add(SimpleClass item) + { + _data.Add(item); + } + + [ScriptConstructor] + public static IRuntimeContextInstance Constructor() + { + return new SimpleCollection(); + } + + [ContextMethod("Количество")] + public override int Count() + { + return _data.Count; + } + + public override IEnumerator GetEnumerator() + { + return ((IEnumerable) _data).GetEnumerator(); + } + + } } \ No newline at end of file diff --git a/src/Component/SimpleEnum.cs b/src/Component/SimpleEnum.cs index 8a64a3657..3f7ca2baa 100644 --- a/src/Component/SimpleEnum.cs +++ b/src/Component/SimpleEnum.cs @@ -1,21 +1,19 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; - -using ScriptEngine; - -namespace Component -{ - [EnumerationType("ПростоПеречисление")] - public enum SimpleEnum - { - [EnumItem("Элемент1")] Item1, - - [EnumItem("Элемент2")] Item2 - } +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace Component +{ + [EnumerationType("ПростоПеречисление")] + public enum SimpleEnum + { + [EnumValue("Элемент1")] Item1, + + [EnumValue("Элемент2")] Item2 + } } \ No newline at end of file diff --git a/src/Component/UseLibrary.cs b/src/Component/UseLibrary.cs index eeb109006..1ca7ff8f5 100644 --- a/src/Component/UseLibrary.cs +++ b/src/Component/UseLibrary.cs @@ -5,15 +5,19 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; -using ScriptEngine.HostedScript.Library.Http; -using ScriptEngine.HostedScript.Library.Net; -using ScriptEngine.HostedScript.Library.ValueList; -using ScriptEngine.HostedScript.Library.ValueTable; -using ScriptEngine.HostedScript.Library.ValueTree; -using ScriptEngine.HostedScript.Library.Xml; -using ScriptEngine.HostedScript.Library.Zip; +using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Collections.ValueList; +using OneScript.StandardLibrary.Collections.ValueTable; +using OneScript.StandardLibrary.Collections.ValueTree; +using OneScript.StandardLibrary.Http; +using OneScript.StandardLibrary.Net; +using OneScript.StandardLibrary.Processes; +using OneScript.StandardLibrary.Text; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.Zip; +using OneScript.StandardLibrary.TypeDescriptions; using ScriptEngine.Machine; namespace Component diff --git a/src/Installer/BinaryContent.wxs b/src/Installer/BinaryContent.wxs deleted file mode 100644 index a5791c50f..000000000 --- a/src/Installer/BinaryContent.wxs +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Installer/DocumentsContent.wxs b/src/Installer/DocumentsContent.wxs deleted file mode 100644 index 5a9e577ac..000000000 --- a/src/Installer/DocumentsContent.wxs +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Installer/ExamplesContent.wxs b/src/Installer/ExamplesContent.wxs deleted file mode 100644 index 9b738987e..000000000 --- a/src/Installer/ExamplesContent.wxs +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/Installer/Features.wxs b/src/Installer/Features.wxs deleted file mode 100644 index 9bf4cc664..000000000 --- a/src/Installer/Features.wxs +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Installer/Installer.wixproj b/src/Installer/Installer.wixproj deleted file mode 100644 index 298ae6d3d..000000000 --- a/src/Installer/Installer.wixproj +++ /dev/null @@ -1,92 +0,0 @@ - - - - Debug - x86 - 1.0.0 - $(SolutionDir)..\install\build\ - 3.9 - bbe794a6-b159-422f-b655-b7f03f25f223 - 2.0 - OneScript-$(Version) - Package - $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets - $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets - Installer - - - bin\$(Configuration)\ - obj\$(Configuration)\ - Debug;ProductVersion=$(Version);OutputPathForBuild=$(OutputPathForBuild);LibraryContentPath=$(OutputPathForBuild)lib;DocContentPath=$(OutputPathForBuild)doc - - - -cultures:ru-RU - False - False - True - - - - - bin\$(Configuration)\ - obj\$(Configuration)\ - ProductVersion=$(Version);OutputPathForBuild=$(OutputPathForBuild);LibraryContentPath=$(OutputPathForBuild)lib;DocContentPath=$(OutputPathForBuild)doc - -cultures:ru-RU - True - - - - - - - - - - - - $(WixExtDir)\WixNetFxExtension.dll - WixNetFxExtension - - - $(WixExtDir)\WixUIExtension.dll - WixUIExtension - - - - - - - - - "%25WIX%25\bin\heat.exe" dir "$(OutputPathForBuild)lib" -o "$(ProjectDir)LibraryContent.wxs" -dr LibFolder -cg Library.Content -sfrag -gg -srd -var var.LibraryContentPath -"%25WIX%25\bin\heat.exe" dir "$(OutputPathForBuild)doc" -o "$(ProjectDir)DocumentsContent.wxs" -dr DocFolder -cg Docs.Content -sfrag -gg -srd -var var.DocContentPath - - - obj\$(Configuration)\ - Debug;ProductVersion=$(Version);OutputPathForBuild=$(OutputPathForBuild);LibraryContentPath=$(OutputPathForBuild)lib;DocContentPath=$(OutputPathForBuild)doc - - - -cultures:ru-RU - False - False - True - - - bin\Debug\ - - - obj\$(Configuration)\ - ProductVersion=$(Version);OutputPathForBuild=$(OutputPathForBuild);LibraryContentPath=$(OutputPathForBuild)lib;DocContentPath=$(OutputPathForBuild)doc - -cultures:ru-RU - True - bin\Release\ - - - \ No newline at end of file diff --git a/src/Installer/LibraryContent.wxs b/src/Installer/LibraryContent.wxs deleted file mode 100644 index d03be78d7..000000000 --- a/src/Installer/LibraryContent.wxs +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Installer/Product.wxs b/src/Installer/Product.wxs deleted file mode 100644 index d0106c97d..000000000 --- a/src/Installer/Product.wxs +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Installer/installer_left.bmp b/src/Installer/installer_left.bmp deleted file mode 100644 index d6d64fae8..000000000 Binary files a/src/Installer/installer_left.bmp and /dev/null differ diff --git a/src/Installer/mpl2_license.rtf b/src/Installer/mpl2_license.rtf deleted file mode 100644 index 6ab9027fd..000000000 --- a/src/Installer/mpl2_license.rtf +++ /dev/null @@ -1,550 +0,0 @@ -{\rtf1\adeflang1025\ansi\ansicpg1251\uc1\adeff0\deff0\stshfdbch0\stshfloch31506\stshfhich31506\stshfbi31506\deflang1049\deflangfe1049\themelang1049\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} -{\f2\fbidi \fmodern\fcharset204\fprq1{\*\panose 02070309020205020404}Courier New;}{\f34\fbidi \froman\fcharset204\fprq2{\*\panose 02040503050406030204}Cambria Math;} -{\flomajor\f31500\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} -{\fhimajor\f31502\fbidi \fswiss\fcharset204\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} -{\flominor\f31504\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;} -{\fhiminor\f31506\fbidi \fswiss\fcharset204\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset204\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f41\fbidi \froman\fcharset0\fprq2 Times New Roman;} -{\f39\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} -{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f61\fbidi \fmodern\fcharset0\fprq1 Courier New;} -{\f59\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}{\f62\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f63\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f64\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);} -{\f65\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);}{\f66\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f67\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f381\fbidi \froman\fcharset0\fprq2 Cambria Math;} -{\f379\fbidi \froman\fcharset238\fprq2 Cambria Math CE;}{\f382\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f383\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f386\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} -{\f387\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\flomajor\f31510\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} -{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} -{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} -{\fdbmajor\f31520\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} -{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} -{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31530\fbidi \fswiss\fcharset0\fprq2 Calibri Light;} -{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;} -{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31540\fbidi \froman\fcharset0\fprq2 Times New Roman;} -{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} -{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} -{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31550\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} -{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} -{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} -{\fdbminor\f31560\fbidi \froman\fcharset0\fprq2 Times New Roman;}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} -{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} -{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31570\fbidi \fswiss\fcharset0\fprq2 Calibri;} -{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} -{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31580\fbidi \froman\fcharset0\fprq2 Times New Roman;} -{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} -{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} -{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0; -\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp -\f31506\fs22\lang1049\langfe1033\langfenp1033 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1 -\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1049\langfe1033\cgrid\langnp1049\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive -\ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* -\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 -\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31506\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1049\langfe1033\cgrid\langnp1049\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{ -\s15\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af2\afs20\alang1025 \ltrch\fcs0 -\f2\fs20\lang1049\langfe1049\cgrid\langnp1049\langfenp1049 \sbasedon0 \snext15 \slink16 \ssemihidden \sunhideused \styrsid4543286 HTML Preformatted;}{\*\cs16 \additive \rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\lang0\langfe1049\langnp0\langfenp1049 -\sbasedon10 \slink15 \slocked \ssemihidden \styrsid4543286 \'d1\'f2\'e0\'ed\'e4\'e0\'f0\'f2\'ed\'fb\'e9 HTML \'c7\'ed\'e0\'ea;}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid4543286\rsid6367065 -\rsid6900295}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info -{\author \'ce\'e2\'f1\'ff\'ed\'ea\'e8\'ed \'c0\'ed\'e4\'f0\'e5\'e9 \'c0\'eb\'e5\'ea\'f1\'e0\'ed\'e4\'f0\'ee\'e2\'e8\'f7}{\operator \'ce\'e2\'f1\'ff\'ed\'ea\'e8\'ed \'c0\'ed\'e4\'f0\'e5\'e9 \'c0\'eb\'e5\'ea\'f1\'e0\'ed\'e4\'f0\'ee\'e2\'e8\'f7} -{\creatim\yr2016\mo4\dy8\hr19\min4}{\revtim\yr2016\mo4\dy19\hr13\min56}{\version2}{\edmins11}{\nofpages6}{\nofwords2303}{\nofchars13128}{\*\company Svyaznoy Logistics}{\nofcharsws15401}{\vern57441}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office -/word/2003/wordml}}\paperw11906\paperh16838\margl1701\margr850\margt1134\margb1134\gutter0\ltrsect -\deftab708\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1 -\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1701\dgvorigin1134\dghshow1\dgvshow1 -\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct -\asianbrkrule\rsidroot4543286\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0 -{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\headery708\footery708\colsx708\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 -\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 -\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang -{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\widctlpar -\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid4543286 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 -\f31506\fs22\lang1049\langfe1033\cgrid\langnp1049\langfenp1033 {\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \b\f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid4543286\charrsid4543286 Mozilla Public License, version 2.0 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid4543286\charrsid4543286 -\par 1. Definitions -\par -\par 1.1. "Contributor" -\par -\par means each individual or legal entity that creates, contributes to the -\par creation of, or owns Covered Software. -\par -\par 1.2. "Contributor Version" -\par -\par means the combination of the Contributions of others (if any) used by a -\par Contributor and that particular Contributor's Contribution. -\par -\par 1.3. "Contribution" -\par -\par means Covered Software of a particular Contributor. -\par -\par 1.4. "Covered Software" -\par -\par means Source Code Form to which the initial Contributor has attached the -\par notice in Exhibit A, the Executable Form of such Source Code Form, and -\par Modifications of such Source Code Form, in each case including portions -\par thereof. -\par -\par 1.5. "Incompatible With Secondary Licenses" -\par means -\par -\par a. that the initial Contributor has attached the notice described in -\par Exhibit B to the Covered Software; or -\par -\par b. that the Covered Software was made available under the terms of -\par version 1.1 or earlier of the License, but not also under the terms of -\par a Secondary License. -\par -\par 1.6. "Executable Form" -\par -\par means any form of the work other than Source Code Form. -\par -\par 1.7. "Larger Work" -\par -\par means a work that combines Covered Software with other material, in a -\par separate file or files, that is not Covered Software. -\par -\par 1.8. "License" -\par -\par means this document. -\par -\par 1.9. "Licensable" -\par -\par means having the right to grant, to the maximum extent possible, whether -\par at the time of the initial grant or subsequently, any and all of the -\par rights conveyed by this License. -\par -\par 1.10. "Modifications" -\par -\par means any of the following: -\par -\par a. any file in Source Code Form that results from an addition to, -\par deletion from, or modification of the contents of Covered Software; or -\par -\par b. any new file in Source Code Form that contains any Covered Software. -\par -\par 1.11. "Patent Claims" of a Contributor -\par -\par means any patent claim(s), including without limitation, method, -\par process, and apparatus claims, in any patent Licensable by such -\par Contributor that would be infringed, but for the grant of the License, -\par by the making, using, selling, offering for sale, having made, import, -\par or transfer of either its Contributions or its Contributor Version. -\par -\par 1.12. "Secondary License" -\par -\par means either the GNU General Public License, Version 2.0, the GNU Lesser -\par General Public License, Version 2.1, the GNU Affero General Public -\par License, Version 3.0, or any later versions of those licenses. -\par -\par 1.13. "Source Code Form" -\par -\par means the form of the work preferred for making modifications. -\par -\par 1.14. "You" (or "Your") -\par -\par means an individual or a legal entity exercising rights under this -\par License. For legal entities, "You" includes any entity that controls, is -\par controlled by, or is under common control with You. For purposes of this -\par definition, "control" means (a) the power, direct or indirect, to cause -\par the direction or management of such entity, whether by contract or -\par otherwise, or (b) ownership of more than fifty percent (50%) of the -\par outstanding shares or beneficial ownership of such entity. -\par -\par -\par 2. License Grants and Conditions -\par -\par 2.1. Grants -\par -\par Each Contributor hereby grants You a world-wide, royalty-free, -\par non-exclusive license: -\par -\par a. under intellectual property rights (other than patent or trademark) -\par Licensable by such Contributor to use, reproduce, make available, -\par modify, display, perform, distribute, and otherwise exploit its -\par Contributions, either on an unmodified basis, with Modifications, or -\par as part of a Larger Work; and -\par -\par b. under Patent Claims of such Contributor to make, use, sell, offer for -\par sale, have made, import, and otherwise transfer either its -\par Contributions or its Contributor Version. -\par -\par 2.2. Effective Date -\par -\par The licenses granted in Section 2.1 with respect to any Contribution -\par become effective for each Contribution on the date the Contributor first -\par distributes such Contribution. -\par -\par 2.3. Limitations on Grant Scope -\par -\par The licenses granted in this Section 2 are the only rights granted under -\par this License. No additional rights or licenses will be implied from the -\par distribution or licensing of Covered Software under this License. -\par Notwithstanding Section 2.1(b) above, no patent license is granted by a -\par Contributor: -\par -\par a. for any code that a Contributor has removed from Covered Software; or -\par -\par b. for infringements caused by: (i) Your and any other third party's -\par modifications of Covered Software, or (ii) the combination of its -\par Contributions with other software (except as part of its Contributor -\par Version); or -\par -\par c. under Patent Claims infringed by Covered Software in the absence of -\par its Contributions. -\par -\par This License does not grant any rights in the trademarks, service marks, -\par or logos of any Contributor (except as may be necessary to comply with -\par the notice requirements in Section 3.4). -\par -\par 2.4. Subsequent Licenses -\par -\par No Contributor makes additional grants as a result of Your choice to -\par distribute the Covered Software under a subsequent version of this -\par License (see Section 10.2) or under the terms of a Secondary License (if -\par permitted under the terms of Section 3.3). -\par -\par 2.5. Representation -\par -\par Each Contributor represents that the Contributor believes its -\par Contributions are its original creation(s) or it has sufficient rights to -\par grant the rights to its Contributions conveyed by this License. -\par -\par 2.6. Fair Use -\par -\par This License is not intended to limit any rights You have under -\par applicable copyright doctrines of fair use, fair dealing, or other -\par equivalents. -\par -\par 2.7. Conditions -\par -\par Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in -\par Section 2.1. -\par -\par -\par 3. Responsibilities -\par -\par 3.1. Distribution of Source Form -\par -\par All distribution of Covered Software in Source Code Form, including any -\par Modifications that You create or to which You contribute, must be under -\par the terms of this License. You must inform recipients that the Source -\par Code Form of the Covered Software is governed by the terms of this -\par License, and how they can obtain a copy of this License. You may not -\par attempt to alter or restrict the recipients' rights in the Source Code -\par Form. -\par -\par 3.2. Distribution of Executable Form -\par -\par If You distribute Covered Software in Executable Form then: -\par -\par a. such Covered Software must also be made available in Source Code Form, -\par as described in Section 3.1, and You must inform recipients of the -\par Executable Form how they can obtain a copy of such Source Code Form by -\par reasonable means in a timely manner, at a charge no more than the cost -\par of distribution to the recipient; and -\par -\par b. You may distribute such Executable Form under the terms of this -\par License, or sublicense it under different terms, provided that the -\par license for the Executable Form does not attempt to limit or alter the -\par recipients' rights in the Source Code Form under this License. -\par -\par 3.3. Distribution of a Larger Work -\par -\par You may create and distribute a Larger Work under terms of Your choice, -\par provided that You also comply with the requirements of this License for -\par the Covered Software. If the Larger Work is a combination of Covered -\par Software with a work governed by one or more Secondary Licenses, and the -\par Covered Software is not Incompatible With Secondary Licenses, this -\par License permits You to additionally distribute such Covered Software -\par under the terms of such Secondary License(s), so that the recipient of -\par the Larger Work may, at their option, further distribute the Covered -\par Software under the terms of either this License or such Secondary -\par License(s). -\par -\par 3.4. Notices -\par -\par You may not remove or alter the substance of any license notices -\par (including copyright notices, patent notices, disclaimers of warranty, or -\par limitations of liability) contained within the Source Code Form of the -\par Covered Software, except that You may alter any license notices to the -\par extent required to remedy known factual inaccuracies. -\par -\par 3.5. Application of Additional Terms -\par -\par You may choose to offer, and to charge a fee for, warranty, support, -\par indemnity or liability obligations to one or more recipients of Covered -\par Software. However, You may do so only on Your own behalf, and not on -\par behalf of any Contributor. You must make it absolutely clear that any -\par such warranty, support, indemnity, or liability obligation is offered by -\par You alone, and You hereby agree to indemnify every Contributor for any -\par liability incurred by such Contributor as a result of warranty, support, -\par indemnity or liability terms You offer. You may include additional -\par disclaimers of warranty and limitations of liability specific to any -\par jurisdiction. -\par -\par 4. Inability to Comply Due to Statute or Regulation -\par -\par If it is impossible for You to comply with any of the terms of this License -\par with respect to some or all of the Covered Software due to statute, -\par judicial order, or regulation then You must: (a) comply with the terms of -\par this License to the maximum extent possible; and (b) describe the -\par limitations and the code they affect. Such description must be placed in a -\par text file included with all distributions of the Covered Software under -\par this License. Except to the extent prohibited by statute or regulation, -\par such description must be sufficiently detailed for a recipient of ordinary -\par skill to be able to understand it. -\par -\par 5. Termination -\par -\par 5.1. The rights granted under this License will terminate automatically if You -\par fail to comply with any of its terms. However, if You become compliant, -\par then the rights granted under this License from a particular Contributor -\par are reinstated (a) provisionally, unless and until such Contributor -\par explicitly and finally terminates Your grants, and (b) on an ongoing -\par basis, if such Contributor fails to notify You of the non-compliance by -\par some reasonable means prior to 60 days after You have come back into -\par compliance. Moreover, Your grants from a particular Contributor are -\par reinstated on an ongoing basis if such Contributor notifies You of the -\par non-compliance by some reasonable means, this is the first time You have -\par received notice of non-compliance with this License from such -\par Contributor, and You become compliant prior to 30 days after Your receipt -\par of the notice. -\par -\par 5.2. If You initiate litigation against any entity by asserting a patent -\par infringement claim (excluding declaratory judgment actions, -\par counter-claims, and cross-claims) alleging that a Contributor Version -\par directly or indirectly infringes any patent, then the rights granted to -\par You by any and all Contributors for the Covered Software under Section -\par 2.1 of this License shall terminate. -\par -\par 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user -\par license agreements (excluding distributors and resellers) which have been -\par validly granted by You or Your distributors under this License prior to -\par termination shall survive termination. -\par -\par 6. Disclaimer of Warranty -\par -\par Covered Software is provided under this License on an "as is" basis, -\par without warranty of any kind, either expressed, implied, or statutory, -\par including, without limitation, warranties that the Covered Software is free -\par of defects, merchantable, fit for a particular purpose or non-infringing. -\par The entire risk as to the quality and performance of the Covered Software -\par is with You. Should any Covered Software prove defective in any respect, -\par You (not any Contributor) assume the cost of any necessary servicing, -\par repair, or correction. This disclaimer of warranty constitutes an essential -\par part of this License. No use of any Covered Software is authorized under -\par this License except under this disclaimer. -\par -\par 7. Limitation of Liability -\par -\par Under no circumstances and under no legal theory, whether tort (including -\par negligence), contract, or otherwise, shall any Contributor, or anyone who -\par distributes Covered Software as permitted above, be liable to You for any -\par direct, indirect, special, incidental, or consequential damages of any -\par character including, without limitation, damages for lost profits, loss of -\par goodwill, work stoppage, computer failure or malfunction, or any and all -\par other commercial damages or losses, even if such party shall have been -\par informed of the possibility of such damages. This limitation of liability -\par shall not apply to liability for death or personal injury resulting from -\par such party's negligence to the extent applicable law prohibits such -\par limitation. Some jurisdictions do not allow the exclusion or limitation of -\par incidental or consequential damages, so this exclusion and limitation may -\par not apply to You. -\par -\par 8. Litigation -\par -\par Any litigation relating to this License may be brought only in the courts -\par of a jurisdiction where the defendant maintains its principal place of -\par business and such litigation shall be governed by laws of that -\par jurisdiction, without reference to its conflict-of-law provisions. Nothing -\par in this Section shall prevent a party's ability to bring cross-claims or -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid4543286\charrsid6367065 counter-claims. -\par -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid4543286\charrsid4543286 9. Miscellaneous -\par -\par This License represents the complete agreement concerning the subject -\par matter hereof. If any provision of this License is held to be -\par unenforceable, such provision shall be reformed only to the extent -\par necessary to make it enforceable. Any law or regulation which provides that -\par the language of a contract shall be construed against the drafter shall not -\par be used to construe this License against a Contributor. -\par -\par -\par 10. Versions of the License -\par -\par 10.1. New Versions -\par -\par Mozilla Foundation is the license steward. Except as provided in Section -\par 10.3, no one other than the license steward has the right to modify or -\par publish new versions of this License. Each version will be given a -\par distinguishing version number. -\par -\par 10.2. Effect of New Versions -\par -\par You may distribute the Covered Software under the terms of the version -\par of the License under which You originally received the Covered Software, -\par or under the terms of any subsequent version published by the license -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid4543286\charrsid6367065 steward. -\par -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid4543286\charrsid4543286 10.3. Modified Versions -\par -\par If you create software not governed by this License, and you want to -\par create a new license for such software, you may create and use a -\par modified version of this License if you rename the license and remove -\par any references to the name of the license steward (except to note that -\par such modified license differs from this License). -\par -\par 10.4. Distributing Source Code Form that is Incompatible With Secondary -\par Licenses If You choose to distribute Source Code Form that is -\par Incompatible With Secondary Licenses under the terms of this version of -\par the License, the notice described in Exhibit B of this License must be -\par attached. -\par -\par Exhibit A - Source Code Form License Notice -\par -\par This Source Code Form is subject to the -\par terms of the Mozilla Public License, v. -\par 2.0. If a copy of the MPL was not -\par distributed with this file, You can -\par obtain one at -\par }\pard \ltrpar\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6367065 {\rtlch\fcs1 -\af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid4543286\charrsid4543286 }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid6367065 http://mozilla.org/MPL/2.0/.} -{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf1\lang1033\langfe1049\langnp1033\langfenp1049\insrsid6900295\charrsid6367065 -\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a -9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad -5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 -b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 -0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 -a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f -c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 -0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 -a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 -6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b -4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b -4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100863ae0a7f0060000be1a0000160000007468656d652f7468656d652f -7468656d65312e786d6cec59dd6e134714beafd47758edbdf1dfae7f221c64af6d682180b0a1e272628fbd436677ac9d71828590105c566a559556bd2852db9b -5e546d2341d51b7887f00c69a95a2af10a3d336baf67ec49091195a28a444ad6e3ef9cf9e69cb3df99dd397bee76449d5d9c70c2e2865b3c53701d1c0fd890c4 -e3867bbddfcdd55c870b140f1165316eb833ccdd739befbf77166d881047d801fb986fa0861b0a31d9c8e7f90086113fc3263886ef462c8990808fc9383f4cd0 -1ef88d68be542854f21122b1ebc42802b707df1dfc72f0f460dfb9321a9101763717fe3b142689059703039af4a477bc30faf6f9fd83fd8367074f0ef69fdf83 -eb67f0ff53653bdc294a0b3ee3014d9c5d441b2e4c3d647b7d7c5bb80e455cc0170db7a07edcfce6d93cda981b517184ad66d7553f73bbb9c170a7a4e64cc6db -d9a49ee77b9566e65f01a858c775aa9d4aa792f953003418c0ca532eba4fbf556fb5fd395603a59716dfed6abb5c34f09afff21ae7a62f7f0dbc02a5febd357c -b71b40140dbc02a5787f0def79d552e01978054af195357cb5d06c7b5503af402125f1ce1abae057cac162b51964c4e8052bbcee7bdd6a69ee7c89826ac8aa4d -4e3162b1386eed45e8164bba60200d29122476c46c82476800851e204ab613e25c22e3100a718262c661b8502a740b65f82b7f3d75a522843630d2ac254f60c6 -d786243f870f1232110df743f0ea6a90574f7f7cf5f4b17378ffc9e1fd5f0f1f3c38bcff73eac8b0ba80e2b16ef5f2fbcffe7e74cff9ebf1372f1f7e61c7731d -fffb4f1ffff6ec733b1056ba0cc18b2ff7ff78b2ffe2ab4ffefce1a105de4cd0b60eef930873e732de73aeb10816a6426032c7dbc99b59f44344748b663ce628 -4672168bff8e080df4e519a2c8826b6133823712901c1bf0fcf49641b8172653412c1e2f869101dc628cb658628dc245399716e6fe341edb274fa63aee1a42bb -b6b903141bf9ed4c27a0bdc4e63208b141f32a45b140631c63e1c8efd80ec696d5dd24c488eb1619248cb391706e12a7858835247db26d54d3d2e80289202f33 -1b41c8b7119bad1b4e8b51dbaadb78d744c25d81a8857c1f53238ce7d154a0c8e6b28f22aa07fc1212a18d646f960c745c870bc8f41853e6748698739bcd9504 -d6ab25fd22c88b3ded5b741699c844901d9bcf4b88311dd9663b4188a2890ddb2371a8633fe03b50a2c8b9ca840dbec5cc3b447e863ca0f8c874df20d848f7eb -d5e03a28ab4e695920f29b6962c9e579cc8cfaedcde808612535d0080c3d8f48fc5a715f9175ffbf957510d2175f3fb2aceab40a7a3321d63beac28a8c1f855b -15ef80254372fab5bb8da6f1550cb7cb7a037b27ddefa4dbfddf4bf751f7f3db17eca546837ccbad62ba75571bf9e8d8fbf811a1b42766145fe26a2bcfa1530d -bb3028fda8c75c9c3de74d42b89477364c68e0c60952364ec2c4474484bd104d60bf5f74a593319fbb1e7367c2383c06a861ab6f89a7d3688b0dd3c7d962513e -baa662c291588e17fc6c1c1e3d448aae54978f68997bc576ac1ead1704a4ed9b90d0263349942d24aa8b411924f5200f41b390502b7b2b2cea161635e97e91aa -3516402dcb0a6ca51cd880355cdf03133082272c44f150e6294df522bb2a996f33d34705d3a800d8572c2a6099e9bae47ae4f2e4ead2523b46a60d125ab99924 -5464544fe3211ae27975cad1e3d078d35cd7972935e8c950a8f9a0b49634aab57f6371d25c83ddaa36d058570a1a3b7b0db752f6a1640668d27047f01a002ea3 -09d40e975b6044c7f0ba6d2092f4863f89b24c122eda888769c095e8a46a101181138792a8e1cae56769a0b1d210c5ad58024138b5e4ea202ba78d1c24dd4c32 -1e8df040e869d74664a4d38fa0f0a95658bf55e627074b4b368574f7c2e19eb34da7c9350425e6578b328043c2e16d50318de690c0ebce4cc896f5b7d298e6b2 -abbf6f5435948e233a09d1bca3e8629ec295946774d4a72c06daa7f99a21a05a48e68d707b2c1bac1e54a39b665d23e57064d77dbd918c9c269acb9e69a88aec -9a7615336658b48195589eacc96bac1621064dd33b7c2addab925b5f68ddca3e21eb1210f02c7e96ae7b8c86a0515b4e6650938cd765586af67cd4ec1d8b05be -86da719a84a6fa9585db95b8653dc23a1d0c9ea8f383dd6ad5c2d068b1cf5491564725fa5106dbbe05e2d18697c2532ab84a251c4c240836443db5274965036e -91db627e6bc095334d48c3bd53f09b5e50f2835ca1e677725ed92be46a7eb39c6bfa7eb9d8f18b8576ab74171a8b08a3a29f1ed374e1d5149dcd0f6bd4f8da81 -4db478fb7666c0a23c53e73079455c1dd8144bc6814d7a4ee3f4e5718ceb10109d3b9552b75eaeb72ab97ab9d9cd79ed562d570f2aad5cbb1254dbdd76e0d7ea -ddbbaeb3abc05eb31c78954e2d57290641ceab1424fd5a3d57f54aa5a6576dd63a5ef3ee7c1b032b4fe5631e0b08afe2b5f90f000000ffff0300504b03041400 -06000800000021000dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c7384 -8f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16d -b8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017c -c524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d001400060008 -0000002100e9de0fbfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600 -080000002100a5d6a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b -799616830000008a0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014 -000600080000002100863ae0a7f0060000be1a00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01 -022d00140006000800000021000dd1909fb60000001b0100002700000000000000000000000000fa0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000f50a00000000} -{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d -617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 -6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 -656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} -{\*\latentstyles\lsdstimax371\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; -\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; -\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; -\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; -\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; -\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; -\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; -\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; -\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; -\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; -\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; -\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; -\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; -\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; -\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; -\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; -\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; -\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; -\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; -\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; -\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; -\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; -\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; -\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; -\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; -\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; -\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; -\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; -\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; -\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; -\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; -\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; -\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; -\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; -\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; -\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; -\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; -\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; -\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; -\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; -\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; -\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; -\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; -\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; -\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; -\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;}}{\*\datastore 010500000200000018000000 -4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 -d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000705e -62142a9ad101feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/src/NUnitTests/CompilerTests.cs b/src/NUnitTests/CompilerTests.cs deleted file mode 100644 index 8e7932e06..000000000 --- a/src/NUnitTests/CompilerTests.cs +++ /dev/null @@ -1,182 +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.IO; -using NUnit.Framework; -using ScriptEngine.Compiler; - -namespace NUnitTests -{ - [TestFixture] - public class CompilerTests - { - private EngineWrapperNUnit host; - - [OneTimeSetUp] - public void Init() - { - host = new EngineWrapperNUnit(); - host.StartEngine(); - var solutionRoot = Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", ".."); - host.Engine.InitExternalLibraries(Path.Combine(solutionRoot, "oscript-library", "src"), null); - } - - [Test] - public void TestNoSemicolonBeforeEndProcedure() - { - var moduleSource = host.Engine.Loader.FromString( - @"Процедура Проц1() - Возврат - КонецПроцедуры"); - - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - - [Test] - public void TestNoSemicolonBeforeEndFunction() - { - var moduleSource = host.Engine.Loader.FromString( - @"Функция Фун1() - Возврат 4 - КонецФункции"); - - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - - [Test] - public void TestNoSemicolonBeforeEndDo() - { - var moduleSource = host.Engine.Loader.FromString( - @"Для Инд = 1 По 10 Цикл - Прервать - КонецЕсли"); - - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - - [Test] - public void TestNoSemicolonBeforeEndIf() - { - var moduleSource = host.Engine.Loader.FromString( - @"Если Истина Тогда - Ф = 1 - ИначеЕсли Истина Тогда - Ф = 2 - Иначе - Ф = 3 - КонецЕсли"); - - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - - - [Test] - public void TestEndFunctionDoesNotEndIf() - { - var moduleSource = host.Engine.Loader.FromString( - @"Если Истина Тогда - Ф = 1 - КонецФункции"); - - bool exceptionThrown = false; - try - { - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - catch (CompilerException) - { - exceptionThrown = true; - } - Assert.IsTrue(exceptionThrown, "КонецФункции закрыл Если!!!"); - } - - [Test] - public void TestEndDoDoesNotEndIf() - { - var moduleSource = host.Engine.Loader.FromString( - @"Если Истина Тогда - Ф = 1 - КонецЦикла"); - - bool exceptionThrown = false; - try - { - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - catch (CompilerException) - { - exceptionThrown = true; - } - Assert.IsTrue(exceptionThrown, "КонецЦикла закрыл Если!!!"); - } - - [Test] - public void TestEndIfDoesNotEndDo() - { - var moduleSource = host.Engine.Loader.FromString( - @"Пока Истина Цикл - Ф = 1 - КонецЕсли"); - - bool exceptionThrown = false; - try - { - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - catch (CompilerException) - { - exceptionThrown = true; - } - Assert.IsTrue(exceptionThrown, "КонецЕсли закрыл Пока!!!"); - } - - [Test(Description = "Компилируется вызов метода с пропуском параметров")] - public void TestCompileMethodCallWithoutAllParams() - { - var moduleSource = host.Engine.Loader.FromString( - @"Функция Ф3(П1, П2, П3) - Возврат """" + П1 + П2 + П3 - КонецФункции - Функция Ф2(П1, П2, П3 = Неопределено) - Возврат """" + П1 + П2 + П3 - КонецФункции - Функция Ф1(П1, П2 = Неопределено, П3 = Неопределено) - Возврат """" + П1 + П2 + П3 - КонецФункции - Р = Ф3(,,); - Р = Ф2(,) + Ф2(,,); - Р = Ф1(,,) + Ф1(,); - "); - - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - - [Test(Description = "Не компилируется вызов метода вообще без параметров")] - public void TestCantCompileCallWithoutParams() - { - var moduleSource = host.Engine.Loader.FromString( - @"Функция Ф1(П1) - Возврат П1; - КонецФункции - Р = Ф1(); - "); - - bool exceptionThrown = false; - try - { - var module = host.Engine.GetCompilerService().Compile(moduleSource); - } - catch (CompilerException) - { - exceptionThrown = true; - } - Assert.IsTrue(exceptionThrown, "Не должно было скомпилироваться!"); - } - - - } -} \ No newline at end of file diff --git a/src/NUnitTests/DebugVariablesTest.cs b/src/NUnitTests/DebugVariablesTest.cs deleted file mode 100644 index fa9b9cbdb..000000000 --- a/src/NUnitTests/DebugVariablesTest.cs +++ /dev/null @@ -1,205 +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.Linq; -using NUnit.Framework; -using NUnit.Framework.Constraints; -using OneScript.DebugServices; -using ScriptEngine; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.ValueTree; -using ScriptEngine.Machine; - -namespace NUnitTests -{ - // TODO стандартный визуализатор ничего не знает про особенности коллекций - // После завершения ветки breaking-refactory визуализатор, возможно переедет в другую сборку - // пока оставляю так, что IVariableVisualizer живет в DebugServices - [TestFixture] - public class DebugVariablesTest - { - [OneTimeSetUp] - public void GlobalSetup() - { - // для инициализации долбаного глобального TypeManager - var e = new ScriptingEngine(); - e.AttachAssembly(typeof(FileContext).Assembly); - } - - [SetUp] - public void Init() - { - Visualizer = new DefaultVariableVisualizer(); - } - - public IVariableVisualizer Visualizer { get; set; } - - [Test] - public void SimpleValuePresentation() - { - var str = ValueFactory.Create("string value"); - - var debuggerVar = Visualizer.GetVariable(Variable.Create(str,"myString")); - Assert.That(debuggerVar.Name, Is.EqualTo("myString")); - Assert.That(debuggerVar.TypeName, Is.EqualTo("Строка")); - Assert.That(debuggerVar.Presentation, Is.EqualTo("string value")); - Assert.That(debuggerVar.IsStructured, Is.False); - - var number = ValueFactory.Create(27.2m); - debuggerVar = Visualizer.GetVariable(Variable.Create(number,"myInt")); - Assert.That(debuggerVar.Name, Is.EqualTo("myInt")); - Assert.That(debuggerVar.TypeName, Is.EqualTo("Число")); - Assert.That(debuggerVar.Presentation, Is.EqualTo("27.2")); - Assert.That(debuggerVar.IsStructured, Is.False); - } - - [Test] - public void ObjectPresentation() - { - var obj = new FileContext("somefile.txt"); - - var debuggerVar = Visualizer.GetVariable(Variable.Create(obj,"myFile")); - Assert.That(debuggerVar.Name, Is.EqualTo("myFile")); - Assert.That(debuggerVar.TypeName, Is.EqualTo("Файл")); - Assert.That(debuggerVar.Presentation, Is.EqualTo("Файл")); - Assert.That(debuggerVar.IsStructured, Is.True); - - var children = Visualizer.GetChildVariables(obj) - .Select(x => Visualizer.GetVariable(x)); - Assert.That(children, Is.Not.Empty); - Assert.That(children.All(x => x.IsStructured == false), Is.True); - - var childrenMap = children.ToDictionary(x => x.Name); - Assert.That(childrenMap["Имя"].IsStructured, Is.False); - Assert.That(childrenMap["Имя"].Presentation, Is.EqualTo("somefile.txt")); - Assert.That(childrenMap["Расширение"].IsStructured, Is.False); - Assert.That(childrenMap["Расширение"].Presentation, Is.EqualTo(".txt")); - } - - [Test] - public void ArrayPresentation() - { - var obj = new ArrayImpl(); - obj.Add(ValueFactory.Create(1)); - obj.Add(ValueFactory.Create(2)); - - var debuggerVar = Visualizer.GetVariable(Variable.Create(obj, "myArray")); - Assert.That(debuggerVar.Presentation, Is.EqualTo("Массив")); - Assert.That(debuggerVar.IsStructured, Is.True); - - var items = Visualizer.GetChildVariables(obj).ToArray(); - Assert.That(items, Has.Length.EqualTo(2)); - Assert.That(items[0].Name, Is.EqualTo("0")); - Assert.That(items[1].Name, Is.EqualTo("1")); - } - - [Test] - public void StructurePresentation() - { - var obj = new StructureImpl(); - obj.Insert("first", ValueFactory.Create(1)); - obj.Insert("second", ValueFactory.Create(2)); - - var debuggerVar = Visualizer.GetVariable(Variable.Create(obj, "myVar")); - Assert.That(debuggerVar.Presentation, Is.EqualTo("Структура")); - Assert.That(debuggerVar.IsStructured, Is.True); - - var items = Visualizer - .GetChildVariables(obj) - .Select(x => Visualizer.GetVariable(x)) - .ToArray(); - - Assert.That(items, Has.Length.EqualTo(2)); - Assert.That(items[0].Name, Is.EqualTo("first")); - Assert.That(items[0].TypeName, Is.EqualTo("Число")); - Assert.That(items[0].Presentation, Is.EqualTo("1")); - Assert.That(items[0].IsStructured, Is.False); - - Assert.That(items[1].Name, Is.EqualTo("second")); - Assert.That(items[1].TypeName, Is.EqualTo("Число")); - Assert.That(items[1].Presentation, Is.EqualTo("2")); - Assert.That(items[1].IsStructured, Is.False); - } - - [Test] - public void MapPresentation() - { - var obj = new MapImpl(); - obj.Insert(ValueFactory.Create("first"), ValueFactory.Create(1)); - obj.Insert(ValueFactory.Create("second"), ValueFactory.Create(2)); - - var debuggerVar = Visualizer.GetVariable(Variable.Create(obj, "myVar")); - Assert.That(debuggerVar.Presentation, Is.EqualTo("Соответствие")); - Assert.That(debuggerVar.IsStructured, Is.True); - - var items = Visualizer.GetChildVariables(obj) - .Select(x => Visualizer.GetVariable(x)) - .ToArray(); - Assert.That(items, Has.Length.EqualTo(2)); - Assert.That(items[0].Name, Is.EqualTo("0")); - Assert.That(items[0].TypeName, Is.EqualTo("КлючИЗначение")); - Assert.That(items[0].Presentation, Is.EqualTo("КлючИЗначение")); - Assert.That(items[0].IsStructured, Is.True); - - Assert.That(items[1].Name, Is.EqualTo("1")); - Assert.That(items[1].TypeName, Is.EqualTo("КлючИЗначение")); - Assert.That(items[1].Presentation, Is.EqualTo("КлючИЗначение")); - Assert.That(items[1].IsStructured, Is.True); - - var keyValue = Visualizer.GetChildVariables(obj.First()) - .Select(x => Visualizer.GetVariable(x)) - .ToArray(); - - Assert.That(keyValue[0].Name, Is.EqualTo("Ключ")); - Assert.That(keyValue[0].Presentation, Is.EqualTo("first")); - Assert.That(keyValue[1].Name, Is.EqualTo("Значение")); - Assert.That(keyValue[1].Presentation, Is.EqualTo("1")); - } - - [Test] - public void ValueTreePresentation() - { - var obj = new ValueTree(); - obj.Columns.Add("first"); - obj.Columns.Add("second"); - - var row = obj.Rows.Add(); - row.Set(0, ValueFactory.Create("val1")); - row.Set(1, ValueFactory.Create("val2")); - - var variables = Visualizer.GetChildVariables(obj) - .Select(x => Visualizer.GetVariable(x)) - .ToDictionary(x => x.Name); - - Assert.That(variables, Has.Count.EqualTo(2)); - Assert.That(variables["Строки"].IsStructured); - Assert.That(variables["Колонки"].IsStructured); - - var rows = Visualizer.GetChildVariables(obj.Rows) - .Select(x => Visualizer.GetVariable(x)) - .ToArray(); - - Assert.That(rows, Has.Length.EqualTo(2)); - Assert.That(rows[0].Name, Is.EqualTo("Родитель")); - Assert.That(rows[0].IsStructured, Is.False); - Assert.That(rows[1].Name, Is.EqualTo("0")); - Assert.That(rows[1].TypeName, Is.EqualTo("СтрокаДереваЗначений")); - Assert.That(rows[1].IsStructured); - - var rowData = Visualizer.GetChildVariables(row) - .Select(x => Visualizer.GetVariable(x)) - .ToArray(); - Assert.That(rowData, Has.Length.EqualTo(4)); - Assert.That(rowData[0].Name, Is.EqualTo("Родитель")); - Assert.That(rowData[1].Name, Is.EqualTo("Строки")); - Assert.That(rowData[2].Name, Is.EqualTo("first")); - Assert.That(rowData[2].Presentation, Is.EqualTo("val1")); - Assert.That(rowData[3].Name, Is.EqualTo("second")); - Assert.That(rowData[3].Presentation, Is.EqualTo("val2")); - } - } -} \ No newline at end of file diff --git a/src/NUnitTests/DynamicObjectTest.cs b/src/NUnitTests/DynamicObjectTest.cs deleted file mode 100644 index 09193d610..000000000 --- a/src/NUnitTests/DynamicObjectTest.cs +++ /dev/null @@ -1,74 +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.Dynamic; -using NUnit.Framework; -using ScriptEngine.HostedScript.Library; - -namespace NUnitTests -{ - [TestFixture] - public class DynamicObjectTest - { - [Test] - public void CanCallMethodsOfStruct() - { - var structImpl = new StructureImpl(); - dynamic dyn = structImpl; - - dyn.Insert("Свойство", 1); - dyn.Вставить("Свойство2", "Привет"); - - Assert.AreEqual(2, structImpl.Count()); - Assert.True(structImpl.HasProperty("Свойство")); - Assert.True(structImpl.HasProperty("Свойство2")); - } - - [Test] - public void CanAccessPropertiesOfStruct() - { - var structImpl = new StructureImpl(); - dynamic dyn = structImpl; - - dyn.Вставить("Свойство", 1); - dyn.Вставить("Свойство2", "Привет"); - - Assert.AreEqual(1, dyn.Свойство); - Assert.AreEqual("Привет",dyn.Свойство2); - } - - [Test] - public void CanAccessIndexOfStruct() - { - var structImpl = new StructureImpl(); - dynamic dyn = structImpl; - - dyn.Вставить("Свойство", 1); - dyn.Вставить("Свойство2", "Привет"); - - Assert.AreEqual(1, dyn["Свойство"]); - Assert.AreEqual("Привет",dyn["Свойство2"]); - } - - [Test] - public void CanAccessArraysByIndex() - { - var arr = new ArrayImpl(); - dynamic Массив = new ArrayImpl(); - - Массив.Добавить(1); - Массив.Добавить(2); - Массив.Добавить("Привет"); - - Assert.AreEqual(3, Массив.Количество()); - Assert.AreEqual(1, Массив[0]); - Assert.AreEqual(2, Массив[1]); - Assert.AreEqual("Привет", Массив[2]); - } - - } -} \ No newline at end of file diff --git a/src/NUnitTests/EngineWrapperNUnit.cs b/src/NUnitTests/EngineWrapperNUnit.cs deleted file mode 100644 index bd5fc51e2..000000000 --- a/src/NUnitTests/EngineWrapperNUnit.cs +++ /dev/null @@ -1,90 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using ScriptEngine; -using ScriptEngine.Environment; -using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; - -namespace NUnitTests -{ - internal class EngineWrapperNUnit : IHostApplication - { - private string[] commandLineArgs; - - public HostedScriptEngine Engine { get; private set; } - - public HostedScriptEngine StartEngine() - { - Engine = new HostedScriptEngine(); - Engine.Initialize(); - - commandLineArgs = new string[] - { - }; - - return Engine; - } - - public int RunTestString(string source) - { - var process = Engine.CreateProcess(this, Engine.Loader.FromString(source)); - return process.Start(); - } - - private int RunTestScript(ICodeSource source, string resourceName) - { - var module = Engine.GetCompilerService().Compile(source); - - Engine.LoadUserScript(new UserAddedScript - { - Type = UserAddedScriptType.Class, - Image = module, - Symbol = resourceName - }); - - var process = Engine.CreateProcess(this, source); - return process.Start(); - } - - internal int RunTestScriptFromPath(string scriptFilePath, string argsScript = "") - { - if (argsScript != "") - commandLineArgs = argsScript.Split(' '); - - var sourceToCompile = Engine.Loader.FromFile(scriptFilePath); - - return RunTestScript(sourceToCompile, scriptFilePath); - } - - public string[] GetCommandLineArguments() - { - return commandLineArgs; - } - - public bool InputString(out string result, int maxLen) - { - result = ""; - return false; - } - - public void ShowExceptionInfo(Exception exc) - { - throw exc; - } - - public void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary) - { - Console.WriteLine(str); - } - } -} \ No newline at end of file diff --git a/src/NUnitTests/Properties/AssemblyInfo.cs b/src/NUnitTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 4916ea8d8..000000000 --- a/src/NUnitTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,42 +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.Reflection; -using System.Runtime.InteropServices; - -// Управление общими сведениями о сборке осуществляется посредством следующего -// набора атрибутов. Измените значения этих атрибутов, чтобы изменить сведения, -// связанные со сборкой. -[assembly: AssemblyTitle("NUnitTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NUnitTests")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Задание значения false для атрибута ComVisible приведет к тому, что типы из этой сборки станут невидимыми -// для COM-компонентов. Если к одному из типов этой сборки необходимо обращаться из -// модели COM, задайте для атрибута ComVisible этого типа значение true. -[assembly: ComVisible(false)] - -// Если данный проект доступен для модели COM, следующий GUID используется в качестве идентификатора библиотеки типов -[assembly: Guid("93acc849-e7e1-4695-b59d-54b3737e48a6")] - -// Сведения о версии сборки состоят из следующих четырех значений: -// -// Основной номер версии -// Дополнительный номер версии -// Номер сборки -// Редакция -// -// Можно задать все значения или принять номера сборки и редакции по умолчанию -// используя "*", как показано ниже: -// [сборка: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/NUnitTests/TestContextClass.cs b/src/NUnitTests/TestContextClass.cs deleted file mode 100644 index f4ccf2033..000000000 --- a/src/NUnitTests/TestContextClass.cs +++ /dev/null @@ -1,38 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace NUnitTests -{ - [ContextClass("ТестовыйКласс", "TestClass")] - public class TestContextClass : AutoContext - { - - [ContextMethod("УстаревшийМетод", "ObsoleteMethod", IsDeprecated = true, ThrowOnUse = false)] - public void ObsoleteMethod() - { - // Do nothing - } - - [ContextMethod("ХорошийМетод", "GoodMethod")] - [ContextMethod("ObsoleteAlias", IsDeprecated = true, ThrowOnUse = false)] - [ContextMethod("VeryObsoleteAlias", IsDeprecated = true, ThrowOnUse = true)] - public void GoodMethod() - { - // Do nothing - } - - [ScriptConstructor] - public static TestContextClass Constructor() - { - return new TestContextClass(); - } - - } -} \ No newline at end of file diff --git a/src/NUnitTests/TestNullConversion.cs b/src/NUnitTests/TestNullConversion.cs deleted file mode 100644 index df366bb8f..000000000 --- a/src/NUnitTests/TestNullConversion.cs +++ /dev/null @@ -1,297 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; -using NUnit.Framework; - -namespace NUnitTests -{ - [ContextClass("ТестNullПреобразования", "TestNullConversion")] - class TestNullConversion : AutoContext - { - IValue _pIValue; - string _pString; - TestNullClass _pClass; - - - public TestNullConversion() - { - - } - - [ScriptConstructor(Name = "Без параметров")] - public static TestNullConversion Constructor() - { - return new TestNullConversion(); - } - - [ContextMethod("ТестIValue", "IValueTest")] - public IValue TestIValue(IValue arg) - { - if (arg.GetType() != typeof(IValue)) - Assert.Fail("Test IValue Func(IValue) -> Func(IValue): argument type is different from IValue."); - - return arg; - } - - [ContextMethod("ТестIValueНеопределено", "IValueNullTest")] - public IValue TestIValueNull(IValue arg) - { - if (arg != ValueFactory.Create()) - Assert.Fail("Test IValue Func(IValue) -> Func(Unknown): argument value is different from null."); - - return arg; - } - - [ContextMethod("ТестКласс", "ClassTest")] - public TestNullClass TestClass(TestNullClass arg) - { - if (arg.GetType() != typeof(TestNullClass)) - Assert.Fail("Test Class Func(Class) -> Func(Class): argument type is different from Class."); - - return arg; - } - - [ContextMethod("ТестClassНеопределено", "ClassNullTest")] - public TestNullClass TestClassNull(TestNullClass arg) - { - if (arg != null) - Assert.Fail("Test Class Func(Class) -> Func(Unknown): argument value is different from null."); - - return arg; - } - - - - [ContextMethod("ТестString", "StringTest")] - public string TestString(string arg) - { - if (arg.GetType() != typeof(System.String)) - Assert.Fail("Test string Func(string) -> Func(string): argument type is different from string."); - - return arg; - } - - [ContextMethod("ТестNullString", "StringNullTest")] - public string TestStringNull(string arg) - { - if (arg != null) - Assert.Fail("Test string Func(string) -> Func(Unknown): argument value is different from null."); - - return arg; - } - - [ContextMethod("ТестInt", "IntTest")] - public int TestInt(int arg) - { - return arg; - } - - [ContextMethod("ТестUInt", "UIntTest")] - public uint TestUInt(uint arg) - { - return arg; - } - - [ContextMethod("ТестLong", "LongTest")] - public long TestLong(long arg) - { - return arg; - } - - [ContextMethod("ТестULong", "ULongTest")] - public ulong TestULong(ulong arg) - { - return arg; - } - - [ContextMethod("ТестDouble", "DoubleTest")] - public double TestDouble(double arg) - { - return arg; - } - - [ContextMethod("ТестDateTime", "DateTimeTest")] - public DateTime TestDateTime(DateTime arg) - { - return arg; - } - - [ContextMethod("ТестBool", "BoolTest")] - public bool TestBool(bool arg) - { - return arg; - } - - - [ContextProperty("ПInt", "PInt")] - public int PInt - { - get;set; - } - - [ContextProperty("ПUint", "PUint")] - public uint PUint - { - get;set; - } - - [ContextProperty("ПLong", "PLong")] - public long PLong - { - get;set; - } - - [ContextProperty("ПUlong", "PUlong")] - public ulong PUlong - { - get;set; - } - - [ContextProperty("ПDouble", "PDouble")] - public double PDouble - { - get;set; - } - - [ContextProperty("ПDateTime", "PDateTime")] - public DateTime PDateTime - { - get;set; - } - - [ContextProperty("ПBool", "PBool")] - public bool PBool - { - get;set; - } - - [ContextProperty("ПString", "PString")] - public string PString - { - get - { - return _pString; - } - set - { - if (value.GetType() != typeof(System.String)) - Assert.Fail("Test string Property = string: value type is different from string."); - - _pString = value; - } - } - - [ContextProperty("ПNullString", "PNullString")] - public string PNullString - { - get - { - return _pString; - } - set - { - if (value != null) - Assert.Fail("Test string Property = Unknown: value value is different from null."); - - _pString = value; - } - } - - [ContextProperty("ПIValue", "PIValue")] - public IValue PIValue - { - get - { - return _pIValue; - } - set - { - if (value.GetType() != typeof(IValue)) - Assert.Fail("Test IValue Property = IValue: value type is different from IValue."); - - _pIValue = value; - } - } - - [ContextProperty("ПNullIValue", "PNullIValue")] - public IValue PNullIValue - { - get - { - return _pIValue; - } - set - { - if (value != ValueFactory.Create()) - Assert.Fail("Test IValue Property = Unknown: value value is different from Unknown."); - - _pIValue = value; - } - } - - - - [ContextProperty("ПClass", "PClass")] - public TestNullClass PClass - { - get - { - return _pClass; - } - set - { - if (value.GetType() != typeof(TestNullClass)) - Assert.Fail("Test TestNullClass Property = TestNullClass: value type is different from TestNullClass."); - - _pClass = value; - } - } - - [ContextProperty("ПNullClass", "PNullClass")] - public TestNullClass PNullClass - { - get - { - return _pClass; - } - set - { - if (value != null) - Assert.Fail("Test TestNullClass Property = Unknown: value value is different from null."); - _pIValue = value; - } - } - - } - - [ContextClass("ТестNullКласс", "TestNullClass")] - class TestNullClass : AutoContext - { - public TestNullClass() - { - - } - - [ScriptConstructor(Name = "Без параметров")] - public static TestNullClass Constructor() - { - return new TestNullClass(); - } - - } - -} diff --git a/src/NUnitTests/packages.config b/src/NUnitTests/packages.config deleted file mode 100644 index 4a0e8ab39..000000000 --- a/src/NUnitTests/packages.config +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/OneScript.Core.Tests/ModuleSerializationTest.cs b/src/OneScript.Core.Tests/ModuleSerializationTest.cs deleted file mode 100644 index d9572ffce..000000000 --- a/src/OneScript.Core.Tests/ModuleSerializationTest.cs +++ /dev/null @@ -1,45 +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.IO; -using System.Runtime.Serialization.Formatters.Binary; -using OneScript.Language.LexicalAnalysis; -using ScriptEngine; -using ScriptEngine.Environment; -using Xunit; - -namespace OneScript.Core.Tests -{ - public class ModuleSerializationTest - { - [Fact] - public void TestThatModuleInfoIsSerialized() - { - var mi = new ModuleImage - { - ModuleInfo = new ModuleInformation - { - Origin = "AAA", - ModuleName = "BBB", - CodeIndexer = new SourceCodeIterator() - } - }; - - var formatter = new BinaryFormatter(); - using (var ms = new MemoryStream()) - { - formatter.Serialize(ms, mi); - ms.Position = 0; - - var obj = (ModuleImage)formatter.Deserialize(ms); - Assert.Equal("AAA", obj.ModuleInfo.Origin); - Assert.Equal("BBB", obj.ModuleInfo.ModuleName); - Assert.Null(obj.ModuleInfo.CodeIndexer); - } - } - } -} diff --git a/src/OneScript.Core.Tests/OneScript.Core.Tests.csproj b/src/OneScript.Core.Tests/OneScript.Core.Tests.csproj deleted file mode 100644 index e4d659f64..000000000 --- a/src/OneScript.Core.Tests/OneScript.Core.Tests.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - netcoreapp3.1 - - false - - - - - - - - - - - - - - diff --git a/src/OneScript.Core.Tests/TypeReflectionTests.cs b/src/OneScript.Core.Tests/TypeReflectionTests.cs deleted file mode 100644 index 11e54014a..000000000 --- a/src/OneScript.Core.Tests/TypeReflectionTests.cs +++ /dev/null @@ -1,245 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Reflection; -using ScriptEngine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Reflection; -using Xunit; - -namespace OneScript.Core.Tests -{ - public class TypeReflectionTests - { - private ScriptingEngine host; - - public TypeReflectionTests() - { - host = new ScriptingEngine(); - host.Environment = new RuntimeEnvironment(); - } - - private LoadedModule LoadFromString(string code) - { - var codeSrc = host.Loader.FromString(code); - var cmp = host.GetCompilerService(); - var image = cmp.Compile(codeSrc); - var module = host.LoadModuleImage(image); - - return module; - } - - [Fact] - public void CheckIfTypeHasReflectedWithName() - { - string script = "Перем А;"; - - var reflected = CreateDummyType(script); - Assert.Equal("Dummy", reflected.Name); - Assert.Equal("ScriptEngine.Machine.Reflection.dyn.Dummy", reflected.FullName); - - } - - private Type CreateDummyType(string script) - { - var module = LoadFromString(script); - var builder = new ClassBuilder(); - var reflected = builder.SetModule(module) - .SetTypeName("Dummy") - .ExportDefaults() - .Build(); - - return reflected; - } - - [Fact] - public void CheckNonExportVarsArePrivateFields() - { - string script = "Перем А; Перем Б Экспорт;"; - - var reflected = CreateDummyType(script); - - var props = reflected.GetFields(BindingFlags.NonPublic); - Assert.Single(props); - Assert.Equal("А", props[0].Name); - Assert.Equal(typeof(IValue), props[0].FieldType); - } - - [Fact] - public void CheckExportVarsArePublicFields() - { - string script = "Перем А; Перем Б Экспорт;"; - - var reflected = CreateDummyType(script); - - var props = reflected.GetFields(BindingFlags.Public); - Assert.Single(props); - Assert.Equal("Б", props[0].Name); - Assert.Equal(typeof(IValue), props[0].FieldType); - - } - - [Fact] - public void CheckDefaultGetMethodsArePublic() - { - string script = "Процедура Внутренняя()\n" + - "КонецПроцедуры\n\n" + - "Процедура Внешняя() Экспорт\n" + - "КонецПроцедуры"; - - var reflected = CreateDummyType(script); - - var defaultGet = reflected.GetMethods(); - Assert.Single(defaultGet); - Assert.Equal("Внешняя", defaultGet[0].Name); - } - - [Fact] - public void CheckExplicitPublicMethodsCanBeRetrieved() - { - string script = "Процедура Внутренняя()\n" + - "КонецПроцедуры\n\n" + - "Процедура Внешняя() Экспорт\n" + - "КонецПроцедуры"; - - var reflected = CreateDummyType(script); - - var defaultGet = reflected.GetMethods(BindingFlags.Public); - Assert.Single(defaultGet); - Assert.Equal("Внешняя", defaultGet[0].Name); - } - - [Fact] - public void CheckPrivateMethodsCanBeRetrieved() - { - string script = "Процедура Внутренняя()\n" + - "КонецПроцедуры\n\n" + - "Процедура Внешняя() Экспорт\n" + - "КонецПроцедуры"; - - var reflected = CreateDummyType(script); - - var defaultGet = reflected.GetMethods(BindingFlags.NonPublic); - Assert.Single(defaultGet); - Assert.Equal("Внутренняя", defaultGet[0].Name); - } - - [Fact] - public void CheckAllMethodsCanBeRetrieved() - { - string script = "Процедура Внутренняя()\n" + - "КонецПроцедуры\n\n" + - "Процедура Внешняя() Экспорт\n" + - "КонецПроцедуры"; - - var reflected = CreateDummyType(script); - - var defaultGet = reflected.GetMethods(BindingFlags.Public|BindingFlags.NonPublic); - Assert.Equal(2, defaultGet.Length); - } - - [Fact] - public void ClassCanBeCreatedViaConstructor() - { - var cb = new ClassBuilder(); - var module = LoadFromString(""); - cb.SetTypeName("testDrive") - .SetModule(module) - .ExportDefaults() - .ExportConstructor((parameters => new UserScriptContextInstance(module))); - var type = cb.Build(); - - var instance = type.GetConstructors()[0].Invoke(new object[0]); - Assert.IsAssignableFrom(instance); - } - - [Fact] - public void ClassCanExposeNativeMethodByName() - { - var cb = new ClassBuilder(); - var module = LoadFromString(""); - cb.SetTypeName("testDrive") - .SetModule(module) - .ExportClassMethod("GetMethodsCount") - .ExportConstructor((parameters => new UserScriptContextInstance(module))); - var type = cb.Build(); - - Assert.NotNull(type.GetMethod("GetMethodsCount")); - } - - [Fact] - public void ClassCanExposeNativeMethodDirectly() - { - var cb = new ClassBuilder(); - var module = LoadFromString(""); - var nativeMethod = typeof(UserScriptContextInstance).GetMethod("AddProperty", - BindingFlags.Public | BindingFlags.Instance, - null, - new Type[] - { - typeof(string), - typeof(IValue) - }, null); - cb.SetTypeName("testDrive") - .SetModule(module) - .ExportClassMethod(nativeMethod) - .ExportConstructor((parameters => new UserScriptContextInstance(module))); - var type = cb.Build(); - - Assert.NotNull(type.GetMethod("AddProperty")); - } - - [Fact] - public void CheckMethodBodyIsNotReflected() - { - string script = "Процедура Внутренняя()\n" + - "КонецПроцедуры\n\n" + - "Процедура Внешняя() Экспорт\n" + - "КонецПроцедуры\n" + - "ТелоМодуля = 2;"; - - var reflected = CreateDummyType(script); - - var defaultGet = reflected.GetMethods(BindingFlags.Public | BindingFlags.NonPublic); - Assert.Equal(2, defaultGet.Length); - } - - [Fact] - public void CheckMethodAnnotationsReflected() - { - string script = "&Аннотация\n" + - "&ДругаяАннотация\n" + - "Процедура Внешняя() Экспорт\n" + - "КонецПроцедуры"; - - var reflected = CreateDummyType(script); - var method = reflected.GetMethod("Внешняя"); - Assert.NotNull(method); - Assert.Equal(3, method.GetCustomAttributes(false).Length); - Assert.Equal(2, method.GetCustomAttributes(typeof(UserAnnotationAttribute), false).Length); - - var first = (UserAnnotationAttribute)method.GetCustomAttributes(typeof(UserAnnotationAttribute), false)[0]; - Assert.Equal("Аннотация", first.Annotation.Name); - } - - [Fact] - public void CheckParametersAnnotationsReflected() - { - string script = "Процедура Внешняя(&Аннотация Параметр, ПараметрБезАннотации) Экспорт\n" + - "КонецПроцедуры"; - - var reflected = CreateDummyType(script); - var method = reflected.GetMethod("Внешняя"); - Assert.NotNull(method); - var param = method.GetParameters()[0]; - - var first = (UserAnnotationAttribute)param.GetCustomAttributes(typeof(UserAnnotationAttribute), false)[0]; - Assert.Equal("Аннотация", first.Annotation.Name); - } - } -} diff --git a/src/OneScript.Core.Tests/ValuesTest.cs b/src/OneScript.Core.Tests/ValuesTest.cs deleted file mode 100644 index cba54da36..000000000 --- a/src/OneScript.Core.Tests/ValuesTest.cs +++ /dev/null @@ -1,243 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using ScriptEngine; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Values; -using Xunit; - -namespace OneScript.Core.Tests -{ - public class ValuesTest - { - [Fact] - public void BooleanEquality() - { - Assert.True(BooleanValue.True.AsBoolean()); - Assert.False(BooleanValue.False.AsBoolean()); - - Assert.Same(BooleanValue.True, ValueFactory.Create(true)); - Assert.Same(BooleanValue.False, ValueFactory.Create(false)); - Assert.NotEqual(BooleanValue.False, BooleanValue.True); - Assert.Equal(0, BooleanValue.False.AsNumber()); - Assert.Equal(1, BooleanValue.True.AsNumber()); - - Assert.True(BooleanValue.True.CompareTo(BooleanValue.False) > 0); - - Assert.Throws(() => BooleanValue.True.AsDate()); - Assert.Throws(() => BooleanValue.True.AsObject()); - } - - [Theory] - [InlineData("ru", "Да", "Нет")] - [InlineData("en", "True", "False")] - [InlineData("jp", "True", "False")] - public void BooleanStringLocales(string locale, string trueString, string falseString) - { - Locale.SystemLanguageISOName = locale; - Assert.Equal(trueString, BooleanValue.True.AsString()); - Assert.Equal(falseString, BooleanValue.False.AsString()); - } - - [Fact] - public void NumbersEquality() - { - var num1 = NumberValue.Create(12.5); - var num2 = NumberValue.Create(12.5); - var num3 = NumberValue.Create(7); - var num4 = NumberValue.Create(0); - - Assert.Equal(num1, num2); - Assert.True(num1.Equals(num2)); - Assert.NotEqual(num1, num3); - - Assert.Equal(0, num4.AsNumber()); - Assert.Equal(7, num3.AsNumber()); - - Assert.True(num1.AsBoolean()); - Assert.True(num2.AsBoolean()); - Assert.True(num3.AsBoolean()); - Assert.False(num4.AsBoolean()); - - Assert.True(num1.CompareTo(num2) == 0); - Assert.True(num1.CompareTo(num3) > 0); - Assert.True(num4.CompareTo(num3) < 0); - - Assert.Equal("12.5", num1.AsString()); - Assert.Throws(() => num1.AsDate()); - Assert.Throws(() => num1.AsObject()); - } - - [Fact] - public void PopularNumbersReferenceEquality() - { - for (int i = 0; i < 10; i++) - { - var n1 = ValueFactory.Create(i); - var n2 = ValueFactory.Create(i); - - Assert.Same(n1, n2); - } - } - - [Fact] - public void StringValueTests() - { - var trueString = ValueFactory.Create("ИстИНа"); - Assert.True(trueString.DataType == DataType.String); - Assert.True(trueString.AsBoolean()); - Assert.True(trueString.AsString() == "ИстИНа"); - - var falseString = ValueFactory.Create("лОжЬ"); - Assert.False(falseString.AsBoolean()); - Assert.True(falseString.AsString() == "лОжЬ"); - - var dateString = ValueFactory.Create("20140101"); - DateTime jan_01_14 = new DateTime(2014,01,01); - Assert.True(dateString.AsDate() == jan_01_14); - - var numString = ValueFactory.Create("012.12"); - Assert.True(numString.AsNumber() == 12.12m); - - Assert.Throws(() => dateString.AsObject()); - Assert.Throws(() => trueString.AsNumber()); - } - - [Fact] - public void Undefined_Value_Test() - { - var value = ValueFactory.Create(); - Assert.True(value.DataType == DataType.Undefined); - Assert.True(value.AsString() == ""); - - Assert.Throws(() => value.AsNumber()); - Assert.Throws(() => value.AsBoolean()); - Assert.Throws(() => value.AsObject()); - Assert.Throws(() => value.AsDate()); - } - - [Fact] - public void Null_Value_Test() - { - var value = ValueFactory.CreateNullValue(); - Assert.True(value.DataType == DataType.GenericValue); - Assert.True(value.AsString() == ""); - - Assert.Throws(() => value.AsNumber()); - Assert.Throws(() => value.AsBoolean()); - Assert.Throws(() => value.AsObject()); - Assert.Throws(() => value.AsDate()); - } - - [Fact] - public void Type_Value_Test() - { - var typeValue = new TypeTypeValue(new TypeDescriptor - { - Name = "Строка", - ID = 1899 - }); - Assert.True(typeValue.DataType == DataType.Type); - Assert.True(typeValue.AsString() == "Строка"); - - Assert.Throws(() => typeValue.AsNumber()); - Assert.Throws(() => typeValue.AsBoolean()); - Assert.Throws(() => typeValue.AsObject()); - Assert.Throws(() => typeValue.AsDate()); - - } - - [Fact] - public void Number_To_Number_Comparison() - { - var num1 = ValueFactory.Create(1); - var num2 = ValueFactory.Create(2); - var num3 = ValueFactory.Create(1); - - Assert.True(num1.CompareTo(num2) < 0); - Assert.True(num1.CompareTo(num3) == 0); - Assert.True(num2.CompareTo(num1) > 0); - Assert.True(num3.CompareTo(num2) < 0); - - } - - [Fact] - public void Invalid_Comparison_Num_And_String() - { - var num1 = ValueFactory.Create(1); - var num2 = ValueFactory.Create("2"); - - Assert.Throws(() => num1.CompareTo(num2)); - Assert.Throws(() => num2.CompareTo(num1)); - } - - [Fact] - public void Invalid_Comparison_Undefined() - { - var v1 = ValueFactory.Create(); - var v2 = ValueFactory.Create(true); - - try - { - v1.CompareTo(v2); - } - catch(RuntimeException e) - { - var validExc = RuntimeException.ComparisonNotSupportedException(); - Assert.True(e.Message == validExc.Message); - return; - } - - throw new Exception("No exception thrown"); - } - - [Fact] - public void String_To_String_Comparison() - { - var str1 = ValueFactory.Create("АБВ"); - var str2 = ValueFactory.Create("ВГД"); - - Assert.True(str1.CompareTo(str2) < 0); - Assert.True(str2.CompareTo(str1) > 0); - Assert.True(str1.CompareTo(ValueFactory.Create("абв")) != 0); - } - - [Fact] - public void Boolean_Comparison() - { - var v1 = ValueFactory.Create(true); - var v2 = ValueFactory.Create(false); - var num1 = ValueFactory.Create(1); - var num0 = ValueFactory.Create(0); - - Assert.True(v1.CompareTo(v2) > 0); - Assert.True(v2.CompareTo(v1) < 0); - Assert.True(v1.CompareTo(num1) == 0); - Assert.True(v2.CompareTo(num0) == 0); - Assert.True(v1.CompareTo(num0) > 0); - Assert.True(v2.CompareTo(num1) < 0); - Assert.True(num1.CompareTo(v1) == 0); - Assert.True(num1.CompareTo(v2) > 0); - } - - [Theory] - [InlineData("Null", DataType.GenericValue)] - [InlineData("Истина", DataType.Boolean)] - [InlineData("Ложь", DataType.Boolean)] - [InlineData("True", DataType.Boolean)] - [InlineData("False", DataType.Boolean)] - [InlineData("20140105", DataType.Date)] - [InlineData("20140105010101", DataType.Date)] - [InlineData("Неопределено", DataType.Undefined)] - [InlineData("Undefined", DataType.Undefined)] - public void ValueFactory_Parse(string literal, DataType type) - { - var value = ValueFactory.Parse(literal, type); - Assert.True(value.DataType == type); - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Environment/FileOpener.cs b/src/OneScript.Core/Commons/FileOpener.cs similarity index 75% rename from src/ScriptEngine/Environment/FileOpener.cs rename to src/OneScript.Core/Commons/FileOpener.cs index a6d90d829..aab42118a 100644 --- a/src/ScriptEngine/Environment/FileOpener.cs +++ b/src/OneScript.Core/Commons/FileOpener.cs @@ -1,109 +1,110 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.IO; -using System.Text; - -namespace ScriptEngine.Environment -{ - public static class FileOpener - { - public static StreamReader OpenReader(string filename) - { - return OpenReader(filename, FileShare.ReadWrite); - } - - public static StreamReader OpenReader(string filename, FileShare shareMode, Encoding encoding = null) - { - var input = new FileStream(filename, FileMode.Open, FileAccess.Read, shareMode); - - if (encoding == null) - { - var enc = AssumeEncoding(input); - return new StreamReader(input, enc, true); - } - - return new StreamReader(input, encoding); - } - - public static StreamReader OpenReader(string filename, Encoding encoding) - { - return OpenReader(filename, FileShare.ReadWrite, encoding); - } - - public static StreamReader OpenReader(Stream stream, Encoding encoding = null) - { - if(encoding == null) - { - var enc = AssumeEncoding(stream); - return new StreamReader(stream, enc, true); - } - return new StreamReader(stream, encoding); - } - - public static StreamWriter OpenWriter(string filename) - { - var utf8BOMEncoding = new UTF8Encoding(true); - return new StreamWriter(filename, false, utf8BOMEncoding); - } - - public static StreamWriter OpenWriter(string filename, Encoding encoding) - { - return new StreamWriter(filename, false, encoding); - } - - public static StreamWriter OpenWriter(string filename, Encoding encoding, bool append) - { - return new StreamWriter(filename, append, encoding); - } - - public static Encoding AssumeEncoding(Stream inputStream) - { - return AssumeEncoding(inputStream, SystemSpecificEncoding()); - } - - public static Encoding SystemSpecificEncoding() - { - if(System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == PlatformID.MacOSX) - { - return Encoding.UTF8; - } - else - { - return Encoding.Default; - } - } - - public static Encoding AssumeEncoding(Stream inputStream, Encoding fallbackEncoding) - { - var enc = fallbackEncoding; - - // *** Detect byte order mark if any - otherwise assume default - byte[] buffer = new byte[5]; - - inputStream.Read(buffer, 0, 5); - inputStream.Position = 0; - - if (buffer [0] == 0xef && buffer [1] == 0xbb && buffer [2] == 0xbf) - enc = Encoding.UTF8; - else if (buffer [0] == 0xfe && buffer [1] == 0xff) - enc = Encoding.Unicode; - else if (buffer [0] == 0 && buffer [1] == 0 && buffer [2] == 0xfe && buffer [3] == 0xff) - enc = Encoding.UTF32; - else if (buffer [0] == 0x2b && buffer [1] == 0x2f && buffer [2] == 0x76) - enc = Encoding.UTF7; - else if (buffer [0] == '#' && buffer [1] == '!') - { - /* Если в начале файла присутствует shebang, считаем, что файл в UTF-8*/ - enc = Encoding.UTF8; - } - - return enc; - } - - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Text; + +namespace OneScript.Commons +{ + public static class FileOpener + { + public static StreamReader OpenReader(string filename) + { + return OpenReader(filename, FileShare.ReadWrite); + } + + public static StreamReader OpenReader(string filename, FileShare shareMode, Encoding encoding = null) + { + var input = new FileStream(filename, FileMode.Open, FileAccess.Read, shareMode); + + if (encoding == null) + { + var enc = AssumeEncoding(input); + return new StreamReader(input, enc, true); + } + + return new StreamReader(input, encoding); + } + + public static StreamReader OpenReader(string filename, Encoding encoding) + { + return OpenReader(filename, FileShare.ReadWrite, encoding); + } + + public static StreamReader OpenReader(Stream stream, Encoding encoding = null) + { + if(encoding == null) + { + var enc = AssumeEncoding(stream); + return new StreamReader(stream, enc, true); + } + return new StreamReader(stream, encoding); + } + + public static StreamWriter OpenWriter(string filename) + { + var utf8BOMEncoding = new UTF8Encoding(true); + return new StreamWriter(filename, false, utf8BOMEncoding); + } + + public static StreamWriter OpenWriter(string filename, Encoding encoding) + { + return new StreamWriter(filename, false, encoding); + } + + public static StreamWriter OpenWriter(string filename, Encoding encoding, bool append) + { + return new StreamWriter(filename, append, encoding); + } + + public static Encoding AssumeEncoding(Stream inputStream) + { + return AssumeEncoding(inputStream, SystemSpecificEncoding()); + } + + public static Encoding SystemSpecificEncoding() + { + if(Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) + { + return Encoding.UTF8; + } + + return Encoding.Default; + } + + public static Encoding AssumeEncoding(Stream inputStream, Encoding fallbackEncoding) + { + var enc = fallbackEncoding; + + // *** Detect byte order mark if any - otherwise assume default + byte[] buffer = new byte[4]; + + inputStream.Read(buffer, 0, 4); + inputStream.Position = 0; + + if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf) + enc = Encoding.UTF8; + else if (buffer[0] == 0xff && buffer[1] == 0xfe && buffer[2] == 0 && buffer[3] == 0) + enc = Encoding.UTF32; // UTF32LE + else if (buffer[0] == 0xff && buffer[1] == 0xfe) + enc = Encoding.Unicode; // UTF16LE + else if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff) + enc = new UTF32Encoding(true, true); // UTF32BE with BOM; + else if (buffer[0] == 0xfe && buffer[1] == 0xff) + enc = Encoding.BigEndianUnicode; // UTF16BE + else if (buffer[0] == '#' && buffer[1] == '!') + { + /* Если в начале файла присутствует shebang, считаем, что файл в UTF-8*/ + enc = Encoding.UTF8; + } + + return enc; + } + + } +} diff --git a/src/ScriptEngine/FormatParametersList.cs b/src/OneScript.Core/Commons/FormatParametersList.cs similarity index 98% rename from src/ScriptEngine/FormatParametersList.cs rename to src/OneScript.Core/Commons/FormatParametersList.cs index a0e75d639..df025d069 100644 --- a/src/ScriptEngine/FormatParametersList.cs +++ b/src/OneScript.Core/Commons/FormatParametersList.cs @@ -10,7 +10,8 @@ This Source Code Form is subject to the terms of the using System.Linq; using System.Text; -namespace ScriptEngine +// ReSharper disable once CheckNamespace +namespace OneScript.Commons { public class FormatParametersList { diff --git a/src/OneScript.Core/Commons/IObjectWrapper.cs b/src/OneScript.Core/Commons/IObjectWrapper.cs new file mode 100644 index 000000000..e837f9b69 --- /dev/null +++ b/src/OneScript.Core/Commons/IObjectWrapper.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace OneScript.Commons +{ + public interface IObjectWrapper + { + object UnderlyingObject { get; } + } + + public static class WrapperHelper + { + public static T GetUnderlying(this IObjectWrapper wrapper) + { + return (T) wrapper.UnderlyingObject; + } + } +} diff --git a/src/OneScript.Core/Commons/IndexedNameValueCollection.cs b/src/OneScript.Core/Commons/IndexedNameValueCollection.cs new file mode 100644 index 000000000..f840a75e4 --- /dev/null +++ b/src/OneScript.Core/Commons/IndexedNameValueCollection.cs @@ -0,0 +1,163 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace OneScript.Commons +{ + /// + /// Универсальный класс для коллекций доступных по имени и номеру. + /// + /// + public class IndexedNameValueCollection : IEnumerable + { + private readonly Dictionary _nameIndex = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly List _values = new List(); + + public int Add(T item, string name) + { + if (_nameIndex.ContainsKey(name)) + throw new InvalidOperationException($"Name {name} already registered"); + + var idx = _values.Count; + _values.Add(item); + _nameIndex[name] = idx; + return idx; + } + + public int Add(T item, string name, string alias) + { + var index = _values.Count; + Add(item, name); + + if(!string.IsNullOrEmpty(alias)) + AddName(index, alias); + + return index; + } + + public T this[int index] + { + get => _values[index]; + set => _values[index] = value; + } + + public T this[string name] + { + get => this[IndexOf(name)]; + set => this[IndexOf(name)] = value; + } + + public void AddName(int index, string name) + { + if (index < 0 || index >= _values.Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + _nameIndex[name] = index; + } + + public int Count => _values.Count; + + public int IndexOf(string name) + { + if (!_nameIndex.TryGetValue(name, out var index)) + return -1; + + return index; + } + + public string NameOf(int index) + { + return _nameIndex.First(pair => pair.Value == index).Key; + } + + public bool TryGetValue(string name, out T result) + { + int idx; + if(_nameIndex.TryGetValue(name, out idx)) + { + result = _values[idx]; + return true; + } + else + { + result = default(T); + return false; + } + } + + public IEnumerator GetEnumerator() + { + return _values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public IEnumerable> GetIndex() + { + return _nameIndex.AsEnumerable(); + } + + public void RemoveValue(string name) + { + var idx = IndexOf(name); + if (idx == -1) + { + throw new InvalidOperationException("Name is not belong to index"); + } + + var indices = _nameIndex + .Where(x => x.Value == idx) + .Select(x => x.Key); + + foreach (var referencedName in indices) + { + _nameIndex.Remove(referencedName); + } + + Reindex(); + } + + private void Reindex() + { + var nameMap = _nameIndex + .OrderBy(x => x.Value) + .Select(x => new + { + x.Key, + Value = _values[x.Value] + }).ToList(); + + Clear(); + + foreach (var item in nameMap) + { + var newIndex = _values.Count; + _values.Add(item.Value); + _nameIndex.Add(item.Key, newIndex); + } + } + + public void RemoveName(string name) + { + _nameIndex.Remove(name); + Reindex(); + } + + public void Clear() + { + _nameIndex.Clear(); + _values.Clear(); + } + } +} diff --git a/src/ScriptEngine/Machine/IndexedNamesCollection.cs b/src/OneScript.Core/Commons/IndexedNamesCollection.cs similarity index 97% rename from src/ScriptEngine/Machine/IndexedNamesCollection.cs rename to src/OneScript.Core/Commons/IndexedNamesCollection.cs index ec9c11bbf..7cf9cff43 100644 --- a/src/ScriptEngine/Machine/IndexedNamesCollection.cs +++ b/src/OneScript.Core/Commons/IndexedNamesCollection.cs @@ -4,10 +4,11 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Collections.Generic; -namespace ScriptEngine.Machine +namespace OneScript.Commons { public class IndexedNamesCollection { diff --git a/src/ScriptEngine/Locale.cs b/src/OneScript.Core/Commons/Locale.cs similarity index 88% rename from src/ScriptEngine/Locale.cs rename to src/OneScript.Core/Commons/Locale.cs index eebfa385c..27875fd22 100644 --- a/src/ScriptEngine/Locale.cs +++ b/src/OneScript.Core/Commons/Locale.cs @@ -6,9 +6,11 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Globalization; using System.Linq; -namespace ScriptEngine +// ReSharper disable once CheckNamespace +namespace OneScript.Commons { public static class Locale @@ -21,10 +23,14 @@ public static string SystemLanguageISOName set { _actualLocaleName = value; + var ci = CultureInfo.CreateSpecificCulture(_actualLocaleName); + CultureInfo.CurrentCulture = ci; SystemLocaleChanged?.Invoke(); } } + public static bool UseAliasedPresentations => SystemLanguageISOName != default && SystemLanguageISOName != "ru"; + public static event Action SystemLocaleChanged; /// diff --git a/src/OneScript.Core/Commons/LruCache.cs b/src/OneScript.Core/Commons/LruCache.cs new file mode 100644 index 000000000..2e623c28a --- /dev/null +++ b/src/OneScript.Core/Commons/LruCache.cs @@ -0,0 +1,72 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace OneScript.Commons +{ + public class LruCache + { + private readonly int _capacity; + + private readonly Dictionary> _index = + new Dictionary>(); + + private readonly LinkedList _list = new LinkedList(); + + public LruCache(int capacity) + { + _capacity = capacity; + } + + public bool IsEmpty() => _index.Count == 0; + + public void Clear() + { + _index.Clear(); + _list.Clear(); + } + + public TValue GetOrAdd(TKey key, Func factory) + { + if (_index.TryGetValue(key, out var listNode)) + { + _list.Remove(listNode); + _list.AddFirst(listNode); + return listNode.Value.Value; + } + + if (_index.Count == _capacity) + { + var keyOfOld = _list.Last.Value.Key; + _index.Remove(keyOfOld); + _list.RemoveLast(); + } + + var newItem = _list.AddFirst(CacheItem.Create(key, factory(key))); + _index[key] = newItem; + return newItem.Value.Value; + } + + private class CacheItem + { + public static CacheItem Create(TKey key, TValue value) => + new CacheItem(key, value); + + private CacheItem(TKey key, TValue value) + { + Key = key; + Value = value; + } + + public TKey Key { get; } + + public TValue Value { get; } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Commons/Utils.cs b/src/OneScript.Core/Commons/Utils.cs new file mode 100644 index 000000000..e64603519 --- /dev/null +++ b/src/OneScript.Core/Commons/Utils.cs @@ -0,0 +1,55 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; + +namespace OneScript.Commons +{ + public static class Utils + { + public static bool IsValidIdentifier(string name) + { + if (name == null || name.Length == 0) + return false; + + if (!(Char.IsLetter(name[0]) || name[0] == '_')) + return false; + + for (int i = 1; i < name.Length; i++) + { + if (!(Char.IsLetterOrDigit(name[i]) || name[i] == '_')) + return false; + } + + return true; + } + + public static void ForEach(this IEnumerable input, Action action) + { + foreach (var data in input) + { + action(data); + } + } + public static string NameAndValuePresentation(string name, object value) + { + var list = new List(); + if (!string.IsNullOrEmpty(name)) + { + list.Add(name); + } + if (value != null) + { + list.Add(value.ToString()); + } + return string.Join("=", list); + } + + public static bool IsMonoRuntime => Type.GetType("Mono.Runtime") != null; + + } +} diff --git a/src/ScriptEngine/Machine/ValueFormatter.cs b/src/OneScript.Core/Commons/ValueFormatter.cs similarity index 85% rename from src/ScriptEngine/Machine/ValueFormatter.cs rename to src/OneScript.Core/Commons/ValueFormatter.cs index d03e3d037..638c9e32e 100644 --- a/src/ScriptEngine/Machine/ValueFormatter.cs +++ b/src/OneScript.Core/Commons/ValueFormatter.cs @@ -1,65 +1,67 @@ /*---------------------------------------------------------- -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 +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; -using System.Globalization; +using OneScript.Values; -namespace ScriptEngine.Machine +namespace OneScript.Commons { public static class ValueFormatter { - static readonly string[] BOOLEAN_FALSE = { "БЛ", "BF" }; - static readonly string[] BOOLEAN_TRUE = { "БИ", "BT" }; - static readonly string[] LOCALE = { "Л", "L" }; - static readonly string[] NUM_MAX_SIZE = { "ЧЦ", "ND" }; - static readonly string[] NUM_DECIMAL_SIZE = { "ЧДЦ", "NFD" }; - static readonly string[] NUM_FRACTION_DELIMITER = { "ЧРД", "NDS" }; - static readonly string[] NUM_GROUPS_DELIMITER = { "ЧРГ", "NGS" }; - static readonly string[] NUM_ZERO_APPEARANCE = { "ЧН", "NZ" }; - static readonly string[] NUM_GROUPING = { "ЧГ", "NG" }; - static readonly string[] NUM_LEADING_ZERO = { "ЧВН", "NLZ" }; - static readonly string[] NUM_NEGATIVE_APPEARANCE = { "ЧО", "NN" }; - static readonly string[] DATE_EMPTY = { "ДП", "DE" }; - static readonly string[] DATE_FORMAT = { "ДФ", "DF" }; - static readonly string[] DATE_LOCAL_FORMAT = { "ДЛФ", "DLF" }; - - const int MAX_DECIMAL_ROUND = 28; - - public static string Format(IValue value, string format) + private static readonly string[] BOOLEAN_FALSE = { "БЛ", "BF" }; + private static readonly string[] BOOLEAN_TRUE = { "БИ", "BT" }; + private static readonly string[] LOCALE = { "Л", "L" }; + private static readonly string[] NUM_MAX_SIZE = { "ЧЦ", "ND" }; + private static readonly string[] NUM_DECIMAL_SIZE = { "ЧДЦ", "NFD" }; + private static readonly string[] NUM_FRACTION_DELIMITER = { "ЧРД", "NDS" }; + private static readonly string[] NUM_GROUPS_DELIMITER = { "ЧРГ", "NGS" }; + private static readonly string[] NUM_ZERO_APPEARANCE = { "ЧН", "NZ" }; + private static readonly string[] NUM_GROUPING = { "ЧГ", "NG" }; + private static readonly string[] NUM_LEADING_ZERO = { "ЧВН", "NLZ" }; + private static readonly string[] NUM_NEGATIVE_APPEARANCE = { "ЧО", "NN" }; + private static readonly string[] DATE_EMPTY = { "ДП", "DE" }; + private static readonly string[] DATE_FORMAT = { "ДФ", "DF" }; + private static readonly string[] DATE_LOCAL_FORMAT = { "ДЛФ", "DLF" }; + + private const int MAX_DECIMAL_ROUND = 28; + + public static string Format(BslValue value, string format) { - var formatParameters = ParseParameters(format); + var formatParameters = new FormatParametersList(format); string formattedValue; - switch(value.DataType) + switch(value.GetRawValue()) { - case DataType.Boolean: - formattedValue = FormatBoolean(value.AsBoolean(), formatParameters); + case BslBooleanValue bslBool: + formattedValue = FormatBoolean((bool)bslBool, formatParameters); break; - case DataType.Number: - formattedValue = FormatNumber(value.AsNumber(), formatParameters); + case BslNumericValue bslNum: + formattedValue = FormatNumber((decimal)bslNum, formatParameters); break; - case DataType.Date: - formattedValue = FormatDate(value.AsDate(), formatParameters); + case BslDateValue bslDate: + formattedValue = FormatDate((DateTime)bslDate, formatParameters); break; default: - formattedValue = DefaultFormat(value, formatParameters); + formattedValue = DefaultFormat(value); break; } return formattedValue; } - - private static string FormatBoolean(bool p, FormatParametersList formatParameters) + + public static string FormatBoolean(bool value, FormatParametersList formatParameters) { - if(p) + if(value) { var truePresentation = formatParameters.GetParamValue(BOOLEAN_TRUE); if (truePresentation != null) @@ -72,18 +74,17 @@ private static string FormatBoolean(bool p, FormatParametersList formatParameter return falsePresentation; } - return ValueFactory.Create(p).AsString(); + return BslBooleanValue.Create(value).ToString(); } - #region Number formatting - - private static int ParseUnsignedParam(string param) + public static string DefaultFormat(BslValue value) { - int paramToInt; - return (Int32.TryParse(param, out paramToInt) && paramToInt > 0) ? paramToInt : 0; + return value.ToString(); } + + #region Number formatting - private static string FormatNumber(decimal num, FormatParametersList formatParameters) + public static string FormatNumber(decimal num, FormatParametersList formatParameters) { int[] numberGroupSizes = null; @@ -247,6 +248,12 @@ private static string FormatNumber(decimal num, FormatParametersList formatParam return num.ToString(formatBuilder.ToString(), nf); } + private static int ParseUnsignedParam(string param) + { + int paramToInt; + return (Int32.TryParse(param, out paramToInt) && paramToInt > 0) ? paramToInt : 0; + } + private static int[] ParseGroupSizes(string param) { List sizes = new List(); @@ -386,9 +393,9 @@ private static int GetDecimalPrecision(int[] bits) return (int)power; } -#endregion + #endregion -#region Date formatting + #region Date formatting private static string FormatDate(DateTime dateTime, FormatParametersList formatParameters) { @@ -402,11 +409,8 @@ private static string FormatDate(DateTime dateTime, FormatParametersList formatP } else { - var currentDF= DateTimeFormatInfo.CurrentInfo; - if(currentDF == null) - df = new DateTimeFormatInfo(); - else - df = (DateTimeFormatInfo)currentDF.Clone(); + var currentDf= DateTimeFormatInfo.CurrentInfo; + df = (DateTimeFormatInfo)currentDf.Clone(); } string param; @@ -518,26 +522,13 @@ private static string ProcessLocalDateFormat(string param) } } -#endregion - - private static string DefaultFormat(IValue value, FormatParametersList formatParameters) - { - throw new NotImplementedException(); - } - - private static FormatParametersList ParseParameters(string format) - { - return new FormatParametersList(format); - } - + #endregion + private static CultureInfo CreateCulture(string locale) { locale = locale.Replace('_', '-'); - var culture = System.Globalization.CultureInfo.CreateSpecificCulture(locale); + var culture = CultureInfo.CreateSpecificCulture(locale); return culture; } - - - } -} +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/AliasedVariableSymbol.cs b/src/OneScript.Core/Compilation/Binding/AliasedVariableSymbol.cs new file mode 100644 index 000000000..bca92f5e2 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/AliasedVariableSymbol.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Values; + +namespace OneScript.Compilation.Binding +{ + public class AliasedVariableSymbol : IVariableSymbol + { + public AliasedVariableSymbol(string name, string alias, Type type) + { + Name = name; + Alias = alias; + Type = type; + } + + public AliasedVariableSymbol(string name, string alias) + { + Name = name; + Alias = alias; + Type = typeof(BslValue); + } + + public string Name { get; } + + public string Alias { get; } + + public Type Type { get; } + } +} diff --git a/src/OneScript.Core/Compilation/Binding/BindingException.cs b/src/OneScript.Core/Compilation/Binding/BindingException.cs new file mode 100644 index 000000000..96c59591a --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/BindingException.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Language; + +namespace OneScript.Compilation.Binding +{ + public class BindingException : ApplicationException + { + public CodeError CodeError { get; } + + public BindingException(CodeError codeError) : base(codeError.Description) + { + CodeError = codeError; + } + } +} diff --git a/src/OneScript.Core/Compilation/Binding/BindingExtensions.cs b/src/OneScript.Core/Compilation/Binding/BindingExtensions.cs new file mode 100644 index 000000000..8d062edd9 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/BindingExtensions.cs @@ -0,0 +1,59 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Contexts; +using OneScript.Runtime.Binding; +using OneScript.Values; + +namespace OneScript.Compilation.Binding +{ + public static class BindingExtensions + { + /// + /// Добавляет в таблицу символов область видимости объекта, который может быть присоединён к рантайму. + /// + /// Таблица символов, в которую нужно добавить область. + /// Объект, чьи методы/свойства требуется отобразить в символы. + /// + /// Пользовательский дескриптор привязки. Если не указан, область считается статической и привязанной к самому объекту. + /// + /// Выбрасывается, если объект не реализует . + public static SymbolScope PushObject(this SymbolTable table, BslObjectValue target, + ScopeBindingDescriptor? descriptor = null) + { + if (!(target is IAttachableContext attachable)) + throw new ArgumentException("Target must implement IAttachableContext", nameof(target)); + + var scope = SymbolScope.FromObject(target); + table.PushScope(scope, descriptor ?? ScopeBindingDescriptor.Static(attachable)); + return scope; + } + + public static SymbolScope PushContext(this SymbolTable table, IAttachableContext target, + ScopeBindingDescriptor? descriptor = null) + { + var scope = SymbolScope.FromContext(target); + table.PushScope(scope, descriptor ?? ScopeBindingDescriptor.Static(target)); + return scope; + } + + public static IMethodSymbol ToSymbol(this BslMethodInfo info) + { + return new BslMethodSymbol { Method = info }; + } + + public static IPropertySymbol ToSymbol(this BslPropertyInfo info) + { + return new BslPropertySymbol { Property = info }; + } + + public static IFieldSymbol ToSymbol(this BslFieldInfo info) + { + return new BslFieldSymbol { Field = info }; + } + } +} diff --git a/src/OneScript.Core/Compilation/Binding/BslFieldSymbol.cs b/src/OneScript.Core/Compilation/Binding/BslFieldSymbol.cs new file mode 100644 index 000000000..0cc635ae4 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/BslFieldSymbol.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; + +namespace OneScript.Compilation.Binding +{ + internal class BslFieldSymbol : IFieldSymbol + { + public BslFieldInfo Field { get; set; } + + public string Name => Field.Name; + + public string Alias => Field.Alias; + + public Type Type => Field.FieldType; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/BslMethodSymbol.cs b/src/OneScript.Core/Compilation/Binding/BslMethodSymbol.cs new file mode 100644 index 000000000..0d828f275 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/BslMethodSymbol.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Compilation.Binding; +using OneScript.Contexts; + +namespace OneScript.Runtime.Binding +{ + internal class BslMethodSymbol : IMethodSymbol + { + public string Name => Method?.Name; + public string Alias => Method?.Alias; + public BslMethodInfo Method { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/BslPropertySymbol.cs b/src/OneScript.Core/Compilation/Binding/BslPropertySymbol.cs new file mode 100644 index 000000000..c2909dd9c --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/BslPropertySymbol.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; + +namespace OneScript.Compilation.Binding +{ + internal class BslPropertySymbol : IPropertySymbol + { + public string Name => Property?.Name; + public string Alias => Property?.Alias; + + public Type Type => Property.PropertyType; + + public BslPropertyInfo Property { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/IFieldSymbol.cs b/src/OneScript.Core/Compilation/Binding/IFieldSymbol.cs new file mode 100644 index 000000000..1c77a6412 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/IFieldSymbol.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; + +namespace OneScript.Compilation.Binding +{ + public interface IFieldSymbol : IVariableSymbol + { + BslFieldInfo Field { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/IMethodSymbol.cs b/src/OneScript.Core/Compilation/Binding/IMethodSymbol.cs new file mode 100644 index 000000000..fee5a7fa8 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/IMethodSymbol.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; + +namespace OneScript.Compilation.Binding +{ + public interface IMethodSymbol : ISymbol + { + BslMethodInfo Method { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/IPackageSymbol.cs b/src/OneScript.Core/Compilation/Binding/IPackageSymbol.cs new file mode 100644 index 000000000..cbc6bdd19 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/IPackageSymbol.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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.Compilation.Binding +{ + /// + /// Символ, который импортирован из внешнего пакета + /// + public interface IPackageSymbol + { + PackageInfo GetPackageInfo(); + } +} diff --git a/src/OneScript.Core/Compilation/Binding/IPropertySymbol.cs b/src/OneScript.Core/Compilation/Binding/IPropertySymbol.cs new file mode 100644 index 000000000..39894f229 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/IPropertySymbol.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; + +namespace OneScript.Compilation.Binding +{ + public interface IPropertySymbol : IVariableSymbol + { + BslPropertyInfo Property { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/ISymbol.cs b/src/OneScript.Core/Compilation/Binding/ISymbol.cs new file mode 100644 index 000000000..c6d20ce31 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/ISymbol.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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.Compilation.Binding +{ + public interface ISymbol + { + string Name { get; } + + string Alias { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/IVariableSymbol.cs b/src/OneScript.Core/Compilation/Binding/IVariableSymbol.cs new file mode 100644 index 000000000..9a25a0973 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/IVariableSymbol.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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.Compilation.Binding +{ + public interface IVariableSymbol : ISymbol + { + Type Type { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/LocalVariableSymbol.cs b/src/OneScript.Core/Compilation/Binding/LocalVariableSymbol.cs new file mode 100644 index 000000000..df426f534 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/LocalVariableSymbol.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Values; + +namespace OneScript.Compilation.Binding +{ + public class LocalVariableSymbol : IVariableSymbol + { + public LocalVariableSymbol(string name, Type type) + { + Name = name; + Type = type; + } + + public LocalVariableSymbol(string name) + { + Name = name; + Type = typeof(BslValue); + } + + public string Name { get; } + + public string Alias => null; + + public Type Type { get; } + } +} diff --git a/src/OneScript.Core/Compilation/Binding/ScopeBindingDescriptor.cs b/src/OneScript.Core/Compilation/Binding/ScopeBindingDescriptor.cs new file mode 100644 index 000000000..c8127c84d --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/ScopeBindingDescriptor.cs @@ -0,0 +1,51 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; + +namespace OneScript.Compilation.Binding +{ + public enum ScopeBindingKind + { + Static, + ThisScope, + FrameScope + } + + public readonly struct ScopeBindingDescriptor + { + private ScopeBindingDescriptor(ScopeBindingKind kind, IAttachableContext target, int scopeIndex) + { + if (kind == ScopeBindingKind.FrameScope && scopeIndex < 0) + throw new ArgumentOutOfRangeException(nameof(scopeIndex)); + + Kind = kind; + Target = target; + ScopeIndex = scopeIndex; + } + + public ScopeBindingKind Kind { get; } + + public IAttachableContext Target { get; } + + /// + /// Индекс области видимости в списке ExecutionFrame.Scopes. Используется для ScopeBindingKind.FrameScope. + /// + public int ScopeIndex { get; } + + public static ScopeBindingDescriptor Static(IAttachableContext target) + => new ScopeBindingDescriptor(ScopeBindingKind.Static, target, -1); + + public static ScopeBindingDescriptor ThisScope() + => new ScopeBindingDescriptor(ScopeBindingKind.ThisScope, null, -1); + + public static ScopeBindingDescriptor FrameScope(int index) + => new ScopeBindingDescriptor(ScopeBindingKind.FrameScope, null, index); + } +} + diff --git a/src/OneScript.Core/Compilation/Binding/SymbolBinding.cs b/src/OneScript.Core/Compilation/Binding/SymbolBinding.cs new file mode 100644 index 000000000..a98942106 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/SymbolBinding.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +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.Compilation.Binding +{ + [Serializable] + public struct SymbolBinding : IEquatable + { + public int ScopeNumber { get; set; } + + public int MemberNumber { get; set; } + + public bool Equals(SymbolBinding other) + { + return ScopeNumber == other.ScopeNumber && MemberNumber == other.MemberNumber; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/SymbolScope.cs b/src/OneScript.Core/Compilation/Binding/SymbolScope.cs new file mode 100644 index 000000000..9bcf11bc1 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/SymbolScope.cs @@ -0,0 +1,110 @@ +/*---------------------------------------------------------- +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.Reflection; +using OneScript.Contexts; +using OneScript.Runtime.Binding; +using OneScript.Values; + +namespace OneScript.Compilation.Binding +{ + public class SymbolScope + { + public SymbolsCollection Methods { get; } = + new SymbolsCollection(); + + public SymbolsCollection Variables { get; } = + new SymbolsCollection(); + + + public int DefineVariable(IVariableSymbol symbol) + { + return Variables.Add(symbol); + } + + public int DefineMethod(IMethodSymbol symbol) + { + return Methods.Add(symbol); + } + + #region Static part + + public static SymbolScope FromObject(BslObjectValue target) + { + var scope = new SymbolScope(); + + var type = target.GetType(); + foreach (var info in type.GetMethods()) + { + var attr = info.GetCustomAttribute(); + if(attr == null) + continue; + + var symbol = new BslMethodSymbol + { + Method = new ContextMethodInfo(info), + }; + + scope.Methods.Add(symbol); + } + + foreach (var info in type.GetProperties()) + { + var attr = info.GetCustomAttribute(); + if(attr == null) + continue; + + var symbol = new BslPropertySymbol + { + Property = new ContextPropertyInfo(info), + }; + + scope.Variables.Add(symbol); + } + + return scope; + } + + public static SymbolScope FromContext(IRuntimeContextInstance target) + { + var scope = new SymbolScope(); + for (int i = 0; i < target.GetPropCount(); i++) + { + var targetProp = target.GetPropertyInfo(i); + scope.Variables.Add(new BslPropertySymbol { Property = targetProp }); + } + + for (int i = 0; i < target.GetMethodsCount(); i++) + { + var targetMeth = target.GetMethodInfo(i); + scope.Methods.Add(new BslMethodSymbol { Method = targetMeth }); + } + + return scope; + } + + public static SymbolScope FromMethodsAndProperties(IEnumerable methods, + IEnumerable properties) + { + var scope = new SymbolScope(); + foreach (var methodInfo in methods) + { + scope.Methods.Add(methodInfo.ToSymbol()); + } + + foreach (var propertyInfo in properties) + { + scope.Variables.Add(propertyInfo.ToSymbol()); + } + + return scope; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/Binding/SymbolTable.cs b/src/OneScript.Core/Compilation/Binding/SymbolTable.cs new file mode 100644 index 000000000..855879886 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/SymbolTable.cs @@ -0,0 +1,141 @@ +/*---------------------------------------------------------- +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; +using System.Collections.Generic; + +namespace OneScript.Compilation.Binding +{ + public class SymbolTable : IEnumerable + { + private class BindingRecord + { + public SymbolScope Scope; + public ScopeBindingDescriptor Descriptor; + } + + private readonly List _bindings = new List(); + + public SymbolScope GetScope(int index) => _bindings[index].Scope; + + public ScopeBindingDescriptor GetBinding(int scopeIndex) => _bindings[scopeIndex].Descriptor; + + public int ScopeCount => _bindings.Count; + + public int PushScope(SymbolScope scope, ScopeBindingDescriptor descriptor) + { + var idx = _bindings.Count; + _bindings.Add(new BindingRecord + { + Scope = scope, + Descriptor = descriptor + }); + + return idx; + } + + public void PopScope() + { + _bindings.RemoveAt(_bindings.Count - 1); + } + + public bool FindVariable(string name, out SymbolBinding binding) + { + for (int i = _bindings.Count - 1; i >= 0; i--) + { + var scope = _bindings[i].Scope; + var idx = scope.Variables.IndexOf(name); + if (idx >= 0) + { + binding = new SymbolBinding + { + ScopeNumber = i, + MemberNumber = idx + }; + return true; + } + } + + binding = default; + return false; + } + + public bool TryFindMethodBinding(string name, out SymbolBinding binding) + { + for (int i = _bindings.Count - 1; i >= 0; i--) + { + var scope = _bindings[i].Scope; + var idx = scope.Methods.IndexOf(name); + if (idx >= 0) + { + binding = new SymbolBinding + { + ScopeNumber = i, + MemberNumber = idx + }; + return true; + } + } + + binding = default; + return false; + } + + public bool TryFindMethod(string name, out IMethodSymbol method) + { + if (TryFindMethodBinding(name, out var binding)) + { + method = GetMethod(binding); + return true; + } + + method = default; + return false; + } + + public SymbolBinding DefineMethod(IMethodSymbol symbol) + { + var index = _bindings[ScopeCount - 1].Scope.DefineMethod(symbol); + return new SymbolBinding + { + ScopeNumber = ScopeCount - 1, + MemberNumber = index + }; + } + + public SymbolBinding DefineVariable(IVariableSymbol symbol) + { + var index = _bindings[ScopeCount - 1].Scope.DefineVariable(symbol); + return new SymbolBinding + { + ScopeNumber = ScopeCount - 1, + MemberNumber = index + }; + } + + public IVariableSymbol GetVariable(SymbolBinding binding) + { + return GetScope(binding.ScopeNumber).Variables[binding.MemberNumber]; + } + + public IMethodSymbol GetMethod(SymbolBinding binding) + { + return GetScope(binding.ScopeNumber).Methods[binding.MemberNumber]; + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < ScopeCount; i++) + yield return _bindings[i].Scope; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/OneScript.Core/Compilation/Binding/SymbolsCollection.cs b/src/OneScript.Core/Compilation/Binding/SymbolsCollection.cs new file mode 100644 index 000000000..b2aae71a4 --- /dev/null +++ b/src/OneScript.Core/Compilation/Binding/SymbolsCollection.cs @@ -0,0 +1,88 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections; +using System.Collections.Generic; +using OneScript.Commons; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis; + +namespace OneScript.Compilation.Binding +{ + public class SymbolsCollection : IReadOnlyList + where T : ISymbol + { + private readonly IndexedNameValueCollection _storage = new IndexedNameValueCollection(); + + public int Add(T symbol) + { + CodeError SelectMessage(T item, string text) + { + return item switch + { + IVariableSymbol _ => LocalizedErrors.DuplicateVarDefinition(text), + IMethodSymbol _ => LocalizedErrors.DuplicateMethodDefinition(text), + _ => throw new InvalidOperationException() + }; + } + + var index = AddItem(symbol); + if (index == -1) + throw new BindingException(SelectMessage(symbol, symbol.Name)); + + if (!AddAlias(index, symbol)) + throw new BindingException(SelectMessage(symbol, symbol.Alias)); + + return index; + } + + public IEnumerator GetEnumerator() => _storage.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int Count => _storage.Count; + + public T this[int index] => _storage[index]; + + public T this[string index] => _storage[index]; + + public int IndexOf(string name) => _storage.IndexOf(name); + + public bool IsDefined(string name) => _storage.IndexOf(name) >= 0; + + private int AddItem(T item) + { + try + { + return _storage.Add(item, item.Name); + } + catch (InvalidOperationException) + { + return -1; + } + } + + private bool AddAlias(int index, T item) + { + try + { + if (string.IsNullOrWhiteSpace(item.Alias)) + return true; + + _storage.AddName(index, item.Alias); + return true; + } + catch (InvalidOperationException) + { + return false; + } + } + } +} diff --git a/src/OneScript.Core/Compilation/CompilerException.cs b/src/OneScript.Core/Compilation/CompilerException.cs new file mode 100644 index 000000000..f2f28d8ec --- /dev/null +++ b/src/OneScript.Core/Compilation/CompilerException.cs @@ -0,0 +1,48 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Language; + +namespace OneScript.Compilation +{ + public class CompilerException : ScriptException + { + public CompilerException(string message) : base(message) + { + } + + public CompilerException(string message, ErrorPositionInfo errorInfo, Exception innerException = null) : base(errorInfo, message, innerException) + { + } + + public CompilerException(ErrorPositionInfo errorInfo, Exception innerException) : base(errorInfo, innerException) + { + } + + public CompilerException(Exception innerException) : base(innerException) + { + } + + public static CompilerException FromCodeError(CodeError error) + { + var exc = new CompilerException(error.Description); + if (error.Position != default) + AppendCodeInfo(exc, error.Position); + + return exc; + } + + public static void AppendCodeInfo(CompilerException exc, ErrorPositionInfo errorPosInfo) + { + exc.LineNumber = errorPosInfo.LineNumber; + exc.ColumnNumber = errorPosInfo.ColumnNumber; + exc.Code = errorPosInfo.Code; + exc.ModuleName = errorPosInfo.ModuleName; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/CompilerFrontendBase.cs b/src/OneScript.Core/Compilation/CompilerFrontendBase.cs new file mode 100644 index 000000000..a3272ebe0 --- /dev/null +++ b/src/OneScript.Core/Compilation/CompilerFrontendBase.cs @@ -0,0 +1,162 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using OneScript.Compilation.Binding; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Sources; + +namespace OneScript.Compilation +{ + public abstract class CompilerFrontendBase : ICompilerFrontend + { + protected CompilerFrontendBase( + PreprocessorHandlers handlers, + IErrorSink errorSink, + IServiceContainer services) + { + PreprocessorHandlers = handlers; + ErrorSink = errorSink; + Services = services; + } + + public IErrorSink ErrorSink { get; } + + protected IServiceContainer Services { get; } + + private PreprocessorHandlers PreprocessorHandlers { get; } + + public bool GenerateDebugCode { get; set; } + + public bool GenerateCodeStat { get; set; } + + public IList PreprocessorDefinitions { get; } = new List(); + + public SymbolTable SharedSymbols { get; set; } + + public SymbolScope FillSymbols(Type targetType) + { + var symbolsProvider = Services.Resolve(); + var typeSymbols = symbolsProvider.Get(targetType); + ModuleSymbols = new SymbolScope(); + typeSymbols.FillSymbols(ModuleSymbols); + + return ModuleSymbols; + } + + private SymbolScope ModuleSymbols { get; set; } + + public IExecutableModule Compile(SourceCode source, IBslProcess process, Type classType = null) + { + var lexer = CreatePreprocessor(source); + var symbols = PrepareSymbols(); + var parsedModule = ParseSyntaxConstruction(lexer, source, p => p.ParseStatefulModule()); + + return CompileInternal(symbols, parsedModule, classType, process); + } + + public IExecutableModule CompileExpression(SourceCode source) + { + var lexer = new DefaultLexer + { + Iterator = source.CreateIterator() + }; + var symbols = PrepareSymbols(); + var parsedModule = ParseSyntaxConstruction(lexer, source, p => p.ParseExpression()); + + return CompileExpressionInternal(symbols, parsedModule); + } + + public IExecutableModule CompileBatch(SourceCode source) + { + var lexer = CreatePreprocessor(source); + var symbols = PrepareSymbols(); + var parsedModule = ParseSyntaxConstruction(lexer, source, p => p.ParseStatefulModule()); + + return CompileBatchInternal(symbols, parsedModule); + } + + protected abstract IExecutableModule CompileInternal(SymbolTable symbols, ModuleNode parsedModule, Type classType, IBslProcess process); + + protected abstract IExecutableModule CompileExpressionInternal(SymbolTable symbols, ModuleNode parsedModule); + + protected abstract IExecutableModule CompileBatchInternal(SymbolTable symbols, ModuleNode parsedModule); + + private SymbolTable PrepareSymbols() + { + var actualTable = new SymbolTable(); + if (SharedSymbols != default) + { + for (int i = 0; i < SharedSymbols.ScopeCount; i++) + { + var descriptor = SharedSymbols.GetBinding(i); + actualTable.PushScope(SharedSymbols.GetScope(i), descriptor); + } + } + + ModuleSymbols ??= new SymbolScope(); + actualTable.PushScope(ModuleSymbols, ScopeBindingDescriptor.ThisScope()); + + return actualTable; + } + + private ModuleNode ParseSyntaxConstruction( + ILexer lexer, + SourceCode source, + Func action) + { + var parser = new DefaultBslParser( + lexer, + ErrorSink, + PreprocessorHandlers); + + ModuleNode moduleNode; + + try + { + moduleNode = (ModuleNode) action(parser); + } + catch (SyntaxErrorException e) + { + e.ModuleName ??= source.Name; + throw; + } + + return moduleNode; + } + + private ILexer CreatePreprocessor( + SourceCode source) + { + var baseLexer = new DefaultLexer + { + Iterator = source.CreateIterator() + }; + + var conditionals = PreprocessorHandlers?.Get(); + if (conditionals != default) + { + foreach (var constant in PreprocessorDefinitions) + { + conditionals.Define(constant); + } + } + + var lexer = new PreprocessingLexer(baseLexer) + { + Handlers = PreprocessorHandlers, + ErrorSink = ErrorSink + }; + return lexer; + } + } +} diff --git a/src/OneScript.Core/Compilation/ICompileTimeDependencyResolver.cs b/src/OneScript.Core/Compilation/ICompileTimeDependencyResolver.cs new file mode 100644 index 000000000..292eb4754 --- /dev/null +++ b/src/OneScript.Core/Compilation/ICompileTimeDependencyResolver.cs @@ -0,0 +1,26 @@ +/*---------------------------------------------------------- +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/. +----------------------------------------------------------*/ + +#nullable enable + +using OneScript.Execution; +using OneScript.Sources; + +namespace OneScript.Compilation +{ + public interface ICompileTimeDependencyResolver + { + /// + /// Загрузить библиотеку для модуля + /// + /// Модуль в котором объявлен импорт + /// имя библиотеки + /// + /// Информация о загруженном пакете или null + PackageInfo? Resolve(SourceCode module, string libraryName, IBslProcess process); + } +} diff --git a/src/OneScript.Core/Compilation/ICompilerBackend.cs b/src/OneScript.Core/Compilation/ICompilerBackend.cs new file mode 100644 index 000000000..8b5d2ba19 --- /dev/null +++ b/src/OneScript.Core/Compilation/ICompilerBackend.cs @@ -0,0 +1,26 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + + +using System; +using OneScript.Compilation.Binding; +using OneScript.Execution; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Compilation +{ + public interface ICompilerBackend + { + bool GenerateDebugCode { get; set; } + + bool GenerateCodeStat { get; set; } + + public SymbolTable Symbols { get; set; } + + IExecutableModule Compile(ModuleNode parsedModule, Type classType, IBslProcess process); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Compilation/ICompilerFrontend.cs b/src/OneScript.Core/Compilation/ICompilerFrontend.cs new file mode 100644 index 000000000..809acac6a --- /dev/null +++ b/src/OneScript.Core/Compilation/ICompilerFrontend.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using OneScript.Compilation.Binding; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Sources; + +namespace OneScript.Compilation +{ + public interface ICompilerFrontend + { + bool GenerateDebugCode { get; set; } + + bool GenerateCodeStat { get; set; } + + IList PreprocessorDefinitions { get; } + + SymbolTable SharedSymbols { get; set; } + + SymbolScope FillSymbols(Type targetType); + + IErrorSink ErrorSink { get; } + + IExecutableModule Compile(SourceCode source, IBslProcess process, Type classType = null); + + IExecutableModule CompileExpression(SourceCode source); + + IExecutableModule CompileBatch(SourceCode source); + } +} diff --git a/src/OneScript.Core/Compilation/ITypeSymbolsProvider.cs b/src/OneScript.Core/Compilation/ITypeSymbolsProvider.cs new file mode 100644 index 000000000..13fcb632e --- /dev/null +++ b/src/OneScript.Core/Compilation/ITypeSymbolsProvider.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Compilation.Binding; + +namespace OneScript.Compilation +{ + /// + /// Провайдер дополнительных внешних символов для класса при компиляции. + /// + public interface ITypeSymbolsProvider + { + void FillSymbols(SymbolScope moduleScope); + } +} diff --git a/src/OneScript.Core/Compilation/PackageInfo.cs b/src/OneScript.Core/Compilation/PackageInfo.cs new file mode 100644 index 000000000..24e95fa52 --- /dev/null +++ b/src/OneScript.Core/Compilation/PackageInfo.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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.Compilation +{ + /// + /// Информация о внешнем пакете (библиотеке). + /// + public sealed class PackageInfo + { + public PackageInfo(string id, string shortName) + { + Id = id; + ShortName = shortName; + } + + /// + /// Уникальный идентификатор пакета (обычно путь к каталогу) + /// + public string Id { get; } + + /// + /// Короткое имя для отображения в сообщениях + /// + public string ShortName { get; } + } +} diff --git a/src/OneScript.Core/Compilation/SymbolsProviderAttribute.cs b/src/OneScript.Core/Compilation/SymbolsProviderAttribute.cs new file mode 100644 index 000000000..9f125f6e1 --- /dev/null +++ b/src/OneScript.Core/Compilation/SymbolsProviderAttribute.cs @@ -0,0 +1,15 @@ +/*---------------------------------------------------------- +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.Compilation +{ + [AttributeUsage(AttributeTargets.Method)] + public class SymbolsProviderAttribute : Attribute + { + } +} diff --git a/src/OneScript.Core/Compilation/TypeSymbolsProviderFactory.cs b/src/OneScript.Core/Compilation/TypeSymbolsProviderFactory.cs new file mode 100644 index 000000000..0b73108c2 --- /dev/null +++ b/src/OneScript.Core/Compilation/TypeSymbolsProviderFactory.cs @@ -0,0 +1,81 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; +using OneScript.Compilation.Binding; + +namespace OneScript.Compilation +{ + /// + /// Фабрика провайдеров дополнительных внешних символов для класса при компиляции. + /// + public class TypeSymbolsProviderFactory + { + private delegate void Filler(TypeSymbolsProviderFactory providerFactory, SymbolScope scope); + + private readonly ConcurrentDictionary _providers = + new ConcurrentDictionary(); + + public ITypeSymbolsProvider Get() + { + return Get(typeof(T)); + } + + public ITypeSymbolsProvider Get(Type type) + { + return _providers.GetOrAdd(type, CreateProvider); + } + + private SymbolProvider CreateProvider(Type type) + { + var filler = FindFillerMethod(type); + return new SymbolProvider(this, filler); + } + + private static Filler FindFillerMethod(Type type) + { + var filler = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) + .Where(m => m.GetCustomAttribute() != default) + .Where(IsFiller) + .Select(m => (Filler)m.CreateDelegate(typeof(Filler))) + .SingleOrDefault() ?? DoNothing; + + return filler; + } + + private static void DoNothing(TypeSymbolsProviderFactory providerFactory, SymbolScope scope) + { + } + + private static bool IsFiller(MethodInfo methodInfo) + { + var parameters = methodInfo.GetParameters(); + return parameters.Length == 2 + && parameters[0].ParameterType == typeof(TypeSymbolsProviderFactory) + && parameters[1].ParameterType == typeof(SymbolScope); + } + + private class SymbolProvider : ITypeSymbolsProvider + { + private readonly TypeSymbolsProviderFactory _providerFactory; + private readonly Filler _filler; + + public SymbolProvider(TypeSymbolsProviderFactory providerFactory, Filler filler) + { + _providerFactory = providerFactory; + _filler = filler; + } + + public void FillSymbols(SymbolScope moduleScope) + { + _filler(_providerFactory, moduleScope); + } + } + } +} diff --git a/src/OneScript.Core/Contexts/AnnotationHolder.cs b/src/OneScript.Core/Contexts/AnnotationHolder.cs new file mode 100644 index 000000000..0eaf8cdfe --- /dev/null +++ b/src/OneScript.Core/Contexts/AnnotationHolder.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Reflection; + +namespace OneScript.Contexts +{ + public class AnnotationHolder : ICustomAttributeProvider + { + private readonly object[] _annotations; + + public AnnotationHolder(object[] annotations) + { + _annotations = annotations; + } + + public object[] GetCustomAttributes(bool inherit) + { + return _annotations ?? Array.Empty(); + } + + public object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return GetCustomAttributes(true).Where(x => attributeType.IsAssignableFrom(attributeType)).ToArray(); + } + + public bool IsDefined(Type attributeType, bool inherit) + { + return _annotations != default && + _annotations.Any(x => x.GetType() == attributeType); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslAnnotationAttribute.cs b/src/OneScript.Core/Contexts/BslAnnotationAttribute.cs new file mode 100644 index 000000000..eb32971e8 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslAnnotationAttribute.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Commons; +using OneScript.Values; +using System; +using System.Collections.Generic; + +namespace OneScript.Contexts +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Parameter)] + public class BslAnnotationAttribute : Attribute + { + public BslAnnotationAttribute(string name) + { + Name = name; + } + + public BslAnnotationAttribute(string name, IEnumerable parameters) + { + Name = name; + Parameters = parameters; + } + + public string Name { get; } + + public IEnumerable Parameters { get; private set; } + + public void SetParameters(IEnumerable parameters) + { + Parameters = new List(parameters); + } + } + + public class BslAnnotationParameter + { + public BslAnnotationParameter(string name, BslPrimitiveValue value = null) + { + Name = name; + Value = value; + } + + public string Name { get; } + + public BslPrimitiveValue Value { get; } + + public override string ToString() + { + return Utils.NameAndValuePresentation(Name, Value); + } + + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslConstructorInfo.cs b/src/OneScript.Core/Contexts/BslConstructorInfo.cs new file mode 100644 index 000000000..e2d4d98b4 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslConstructorInfo.cs @@ -0,0 +1,62 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; + +namespace OneScript.Contexts +{ + public abstract class BslConstructorInfo : ConstructorInfo + { + #region Attributes Infrastructure + + private AnnotationHolder _annotations; + + private AnnotationHolder Annotations + { + get + { + if (_annotations == default) + _annotations = RetrieveAnnotations(); + + return _annotations; + } + } + + protected void SetAnnotations(AnnotationHolder annotations) + { + _annotations = annotations; + } + + protected virtual AnnotationHolder RetrieveAnnotations() + { + return new AnnotationHolder(Array.Empty()); + } + + public override object[] GetCustomAttributes(bool inherit) + { + return Annotations.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return Annotations.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return Annotations.IsDefined(attributeType, inherit); + } + + #endregion + + public override MethodImplAttributes GetMethodImplementationFlags() => MethodImplAttributes.Managed; + + public override MethodAttributes Attributes => MethodAttributes.Public; + public override RuntimeMethodHandle MethodHandle => throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslFieldBuilder.cs b/src/OneScript.Core/Contexts/BslFieldBuilder.cs new file mode 100644 index 000000000..fecc6198d --- /dev/null +++ b/src/OneScript.Core/Contexts/BslFieldBuilder.cs @@ -0,0 +1,80 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using OneScript.Contexts.Internal; + +namespace OneScript.Contexts +{ + public class BslFieldBuilder + { + private readonly IBuildableMember _member; + + private BslFieldBuilder() + { + _member = new BslScriptFieldInfo(null); + } + + public BslFieldBuilder Name(string name) + { + _member.SetName(name); + return this; + } + + public BslFieldBuilder Alias(string name) + { + _member.SetAlias(name); + return this; + } + + public BslFieldBuilder DeclaringType(Type type) + { + _member.SetDeclaringType(type); + return this; + } + + public BslFieldBuilder ValueType(Type type) + { + _member.SetDataType(type); + return this; + } + + public BslFieldBuilder IsExported(bool exportFlag) + { + _member.SetExportFlag(exportFlag); + return this; + } + + public BslFieldBuilder SetAnnotations(IEnumerable annotations) + { + _member.SetAnnotations(annotations); + return this; + } + + public BslFieldBuilder SetDispatchingIndex(int index) + { + _member.SetDispatchIndex(index); + return this; + } + + public BslScriptFieldInfo Build() + { + var scriptField = _member as BslScriptFieldInfo; + Debug.Assert(scriptField != null); + if (scriptField.Name == default) + { + throw new InvalidOperationException("No name specified"); + } + + return scriptField; + } + + public static BslFieldBuilder Create() => new BslFieldBuilder(); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslFieldInfo.cs b/src/OneScript.Core/Contexts/BslFieldInfo.cs new file mode 100644 index 000000000..e6105a928 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslFieldInfo.cs @@ -0,0 +1,58 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; + +namespace OneScript.Contexts +{ + /// + /// Поле объекта, доступное в языке. + /// + public abstract class BslFieldInfo : FieldInfo, INameAndAliasProvider + { + private AnnotationHolder _annotations; + + public string Alias => null; + + private AnnotationHolder Annotations + { + get + { + if (_annotations == default) + _annotations = RetrieveAnnotations(); + + return _annotations; + } + } + + protected virtual AnnotationHolder RetrieveAnnotations() + { + return new AnnotationHolder(Array.Empty()); + } + + protected void SetAnnotations(AnnotationHolder holder) + { + _annotations = holder; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return Annotations.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return Annotations.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return Annotations.IsDefined(attributeType, inherit); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslLambdaConstructorInfo.cs b/src/OneScript.Core/Contexts/BslLambdaConstructorInfo.cs new file mode 100644 index 000000000..4d32faae8 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslLambdaConstructorInfo.cs @@ -0,0 +1,96 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; +using OneScript.Types; +using OneScript.Values; + +namespace OneScript.Contexts +{ + public delegate BslValue InstanceConstructor(TypeActivationContext context, BslValue[] arguments); + + public class BslLambdaConstructorInfo : BslConstructorInfo, IBuildableMethod + { + private readonly InstanceConstructor _implementation; + private Type _declaringType; + + internal BslLambdaConstructorInfo(InstanceConstructor implementation) + { + _implementation = implementation; + } + + internal List Parameters { get; } = new List(); + + public override Type DeclaringType => _declaringType; + public override string Name => ConstructorName; + public override Type ReflectedType => _declaringType; + + public override ParameterInfo[] GetParameters() + { + return Parameters.Cast().ToArray(); + } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + if (parameters.Length > 1) + { + if (parameters[0] is TypeActivationContext activationContext) + { + return _implementation(activationContext, parameters.Skip(1).Cast().ToArray()); + } + } + + var context = new TypeActivationContext(); + return _implementation(context, parameters.Cast().ToArray()); + } + + void IBuildableMember.SetDeclaringType(Type type) + { + _declaringType = type; + } + + void IBuildableMember.SetName(string name) + { + } + + void IBuildableMember.SetAlias(string alias) + { + } + + void IBuildableMember.SetExportFlag(bool isExport) + { + } + + void IBuildableMember.SetDataType(Type type) + { + } + + void IBuildableMember.SetAnnotations(IEnumerable annotations) + { + } + + void IBuildableMember.SetDispatchIndex(int index) + { + } + + void IBuildableMethod.SetParameters(IEnumerable parameters) + { + Parameters.Clear(); + Parameters.AddRange(parameters); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslMethodBuilder.cs b/src/OneScript.Core/Contexts/BslMethodBuilder.cs new file mode 100644 index 000000000..818be5b59 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslMethodBuilder.cs @@ -0,0 +1,100 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; + +namespace OneScript.Contexts +{ + public static class BslMethodBuilder + { + public static BslMethodBuilder Create() => + new BslMethodBuilder(BslScriptMethodInfo.Create(), () => new BslParameterInfo()); + } + + public class BslMethodBuilder where T : BslScriptMethodInfo + { + private readonly IBuildableMethod _member; + private readonly Func _parameterFactory; + + private readonly List _parametersToBuild = new List(); + + internal BslMethodBuilder(IBuildableMethod member, Func parameterFactory) + { + _member = member; + _parameterFactory = parameterFactory; + } + + public BslMethodBuilder SetNames(string methodNameRu, string methodNameEn) + { + _member.SetName(methodNameRu); + _member.SetAlias(methodNameEn); + return this; + } + + public T Build() + { + var parameters = _parametersToBuild.Select(x => x.Build()); + _member.SetParameters(parameters); + return (T)_member; + } + + public BslMethodBuilder Name(string name) + { + _member.SetName(name); + return this; + } + + public BslMethodBuilder Alias(string name) + { + _member.SetAlias(name); + return this; + } + + public BslMethodBuilder DeclaringType(Type type) + { + _member.SetDeclaringType(type); + return this; + } + + public BslMethodBuilder ReturnType(Type type) + { + _member.SetDataType(type); + return this; + } + + public BslMethodBuilder IsExported(bool exportFlag) + { + _member.SetExportFlag(exportFlag); + return this; + } + + public BslMethodBuilder SetAnnotations(IEnumerable annotations) + { + _member.SetAnnotations(annotations); + return this; + } + + public BslMethodBuilder SetDispatchingIndex(int dispId) + { + _member.SetDispatchIndex(dispId); + return this; + } + + public BslParameterBuilder NewParameter() + { + var parameter = _parameterFactory(); + parameter.SetOwner((MemberInfo)_member); + var builder = new BslParameterBuilder(parameter); + _parametersToBuild.Add(builder); + return builder; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslMethodInfo.cs b/src/OneScript.Core/Contexts/BslMethodInfo.cs new file mode 100644 index 000000000..0232a37b0 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslMethodInfo.cs @@ -0,0 +1,64 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Reflection; + +namespace OneScript.Contexts +{ + /// + /// Информация о методе, который может быть вызван из 1Script + /// + public abstract class BslMethodInfo : MethodInfo, INameAndAliasProvider + { + private AnnotationHolder _annotations; + + public abstract string Alias { get; } + + protected AnnotationHolder Annotations + { + get + { + if (_annotations == default) + _annotations = RetrieveAnnotations(); + + return _annotations; + } + } + + protected void SetAnnotations(AnnotationHolder annotations) + { + _annotations = annotations; + } + + protected virtual AnnotationHolder RetrieveAnnotations() + { + return new AnnotationHolder(Array.Empty()); + } + + public override object[] GetCustomAttributes(bool inherit) + { + return Annotations.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return Annotations.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return Annotations.IsDefined(attributeType, inherit); + } + + public override string ToString() + { + return $"{ReturnType} {Name}(${string.Join(',', GetParameters().Select(x=>x.ParameterType))})"; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslMethodInfoFactory.cs b/src/OneScript.Core/Contexts/BslMethodInfoFactory.cs new file mode 100644 index 000000000..07f60ffa3 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslMethodInfoFactory.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +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.Contexts +{ + public class BslMethodInfoFactory where TMethodInfo : BslScriptMethodInfo + { + private readonly Func _factory; + private readonly Func _parameterFactory; + + public BslMethodInfoFactory(Func methodFactory) + : this(methodFactory, () => new BslParameterInfo()) + { + } + + public BslMethodInfoFactory(Func methodFactory, Func paramFactory) + { + _factory = methodFactory; + _parameterFactory = paramFactory; + } + + public BslMethodBuilder NewMethod() => new BslMethodBuilder(_factory(), _parameterFactory); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslParameterBuilder.cs b/src/OneScript.Core/Contexts/BslParameterBuilder.cs new file mode 100644 index 000000000..e13a48484 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslParameterBuilder.cs @@ -0,0 +1,70 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Contexts.Internal; +using OneScript.Values; + +namespace OneScript.Contexts +{ + public class BslParameterBuilder + { + private readonly BslParameterInfo _parameter; + + internal BslParameterBuilder(BslParameterInfo info) + { + _parameter = info; + } + + public BslParameterBuilder() + { + _parameter = new BslParameterInfo(); + } + + public BslParameterBuilder Name(string name) + { + ((IBuildableMember)_parameter).SetName(name); + return this; + } + + public BslParameterBuilder ByValue(bool isByValue) + { + _parameter.ByValue(isByValue); + return this; + } + + public BslParameterBuilder DefaultValue(BslPrimitiveValue value) + { + _parameter.SetDefaultValue(value); + return this; + } + + public BslParameterBuilder CompileTimeBslConstant(int constant) + { + _parameter.ConstantValueIndex = constant; + return this; + } + + public BslParameterBuilder SetAnnotations(IEnumerable annotations) + { + ((IBuildableMember)_parameter).SetAnnotations(annotations); + return this; + } + + public BslParameterBuilder ParameterType(Type type) + { + ((IBuildableMember)_parameter).SetDataType(type); + return this; + } + + public BslParameterInfo Build() + { + return _parameter; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslParameterInfo.cs b/src/OneScript.Core/Contexts/BslParameterInfo.cs new file mode 100644 index 000000000..893af58cb --- /dev/null +++ b/src/OneScript.Core/Contexts/BslParameterInfo.cs @@ -0,0 +1,107 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; +using OneScript.Values; + +namespace OneScript.Contexts +{ + public class BslParameterInfo : ParameterInfo, IBuildableMember + { + private AnnotationHolder _annotations; + + internal BslParameterInfo() + { + AttrsImpl = ParameterAttributes.In; + ClassImpl = typeof(BslValue); + } + + #region Attributes Infrastructure + + private AnnotationHolder Annotations => _annotations ??= new AnnotationHolder(Array.Empty()); + + public override object[] GetCustomAttributes(bool inherit) + { + return Annotations.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return Annotations.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return Annotations.IsDefined(attributeType, inherit); + } + + #endregion + + public override object DefaultValue => DefaultValueImpl; + + public override bool HasDefaultValue => DefaultValue != null || ConstantValueIndex != -1; + + public virtual bool ExplicitByVal { get; protected set; } + + public int ConstantValueIndex { get; set; } = -1; + + void IBuildableMember.SetDeclaringType(Type type) + { + MemberImpl = type; + } + + void IBuildableMember.SetName(string name) + { + NameImpl = name; + } + + void IBuildableMember.SetAlias(string alias) + { + throw new NotSupportedException(); + } + + void IBuildableMember.SetExportFlag(bool isExport) + { + throw new NotSupportedException(); + } + + void IBuildableMember.SetDataType(Type type) + { + ClassImpl = type; + } + + void IBuildableMember.SetAnnotations(IEnumerable annotations) + { + _annotations = new AnnotationHolder(annotations.ToArray()); + } + + void IBuildableMember.SetDispatchIndex(int index) + { + PositionImpl = index; + } + + internal void ByValue(bool byVal) + { + ExplicitByVal = byVal; + } + + internal void SetDefaultValue(BslPrimitiveValue val) + { + DefaultValueImpl = val; + AttrsImpl |= ParameterAttributes.HasDefault | ParameterAttributes.Optional; + } + + internal void SetOwner(MemberInfo parent) + { + MemberImpl = parent; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslPropertyBuilder.cs b/src/OneScript.Core/Contexts/BslPropertyBuilder.cs new file mode 100644 index 000000000..d58495a0d --- /dev/null +++ b/src/OneScript.Core/Contexts/BslPropertyBuilder.cs @@ -0,0 +1,95 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Contexts.Internal; + +namespace OneScript.Contexts +{ + public static class BslPropertyBuilder + { + public static BslPropertyBuilder Create() => + new BslPropertyBuilder(BslScriptPropertyInfo.Create()); + } + + public class BslPropertyBuilder where T : BslScriptPropertyInfo + { + private readonly IBuildableProperty _member; + + internal BslPropertyBuilder(IBuildableProperty member) + { + _member = member; + } + + public BslPropertyBuilder SetNames(string methodNameRu, string methodNameEn) + { + _member.SetName(methodNameRu); + _member.SetAlias(methodNameEn); + return this; + } + + public T Build() + { + return (T)_member; + } + + public BslPropertyBuilder Name(string name) + { + _member.SetName(name); + return this; + } + + public BslPropertyBuilder Alias(string name) + { + _member.SetAlias(name); + return this; + } + + public BslPropertyBuilder DeclaringType(Type type) + { + _member.SetDeclaringType(type); + return this; + } + + public BslPropertyBuilder ReturnType(Type type) + { + _member.SetDataType(type); + return this; + } + + public BslPropertyBuilder IsExported(bool exportFlag) + { + _member.SetExportFlag(exportFlag); + return this; + } + + public BslPropertyBuilder SetAnnotations(IEnumerable annotations) + { + _member.SetAnnotations(annotations); + return this; + } + + public BslPropertyBuilder SetDispatchingIndex(int index) + { + _member.SetDispatchIndex(index); + return this; + } + + public BslPropertyBuilder CanRead(bool canRead) + { + _member.CanRead(canRead); + return this; + } + + public BslPropertyBuilder CanWrite(bool canWrite) + { + _member.CanWrite(canWrite); + return this; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslPropertyInfo.cs b/src/OneScript.Core/Contexts/BslPropertyInfo.cs new file mode 100644 index 000000000..103f863d0 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslPropertyInfo.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; + +namespace OneScript.Contexts +{ + public abstract class BslPropertyInfo : PropertyInfo, INameAndAliasProvider, IEquatable + { + public abstract string Alias { get; } + + private AnnotationHolder _annotations; + + private AnnotationHolder Annotations + { + get + { + if (_annotations == default) + _annotations = RetrieveAnnotations(); + + return _annotations; + } + } + + protected void SetAnnotations(AnnotationHolder annotations) + { + _annotations = annotations; + } + + protected virtual AnnotationHolder RetrieveAnnotations() + { + return new AnnotationHolder(Array.Empty()); + } + + public override object[] GetCustomAttributes(bool inherit) + { + return Annotations.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return Annotations.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return Annotations.IsDefined(attributeType, inherit); + } + + public abstract bool Equals(BslPropertyInfo other); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslScriptFieldInfo.cs b/src/OneScript.Core/Contexts/BslScriptFieldInfo.cs new file mode 100644 index 000000000..9cc7ca115 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslScriptFieldInfo.cs @@ -0,0 +1,88 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; +using OneScript.Values; + +namespace OneScript.Contexts +{ + /// + /// Поле в BSL модуле. Технически, это переменные уровня bsl-модуля, объявленные в этом модуле. + /// + public class BslScriptFieldInfo : BslFieldInfo, IBuildableMember + { + private Type _declaringType; + private bool _isExported; + private string _name; + private Type _dataType = typeof(BslValue); + private int _dispId = -1; + + internal BslScriptFieldInfo(string name) + { + _name = name; + } + + public override Type DeclaringType => _declaringType; + public override string Name => _name; + public override Type ReflectedType => _declaringType; + + public int DispatchId => _dispId; + + public override object GetValue(object obj) + { + throw new NotImplementedException(); + } + + public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override FieldAttributes Attributes => _isExported ? FieldAttributes.Public : FieldAttributes.Private; + public override RuntimeFieldHandle FieldHandle => default; + public override Type FieldType => _dataType; + + void IBuildableMember.SetDeclaringType(Type type) + { + _declaringType = type; + } + + void IBuildableMember.SetName(string name) + { + _name = name; + } + + void IBuildableMember.SetAlias(string alias) + { + } + + void IBuildableMember.SetExportFlag(bool isExport) + { + _isExported = isExport; + } + + void IBuildableMember.SetDataType(Type type) + { + _dataType = type; + } + + void IBuildableMember.SetAnnotations(IEnumerable annotations) + { + SetAnnotations(new AnnotationHolder(annotations.ToArray())); + } + + void IBuildableMember.SetDispatchIndex(int index) + { + _dispId = index; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs b/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs new file mode 100644 index 000000000..89ede2c03 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs @@ -0,0 +1,129 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; +using OneScript.Localization; + +namespace OneScript.Contexts +{ + /// + /// Информация о методе, который объявлен в пользовательском скриптовом коде + /// + public class BslScriptMethodInfo : BslMethodInfo, IBuildableMethod + { + private Type _declaringType; + private Type _returnType = typeof(void); + private string _name; + private bool _isPrivate = true; + protected BslParameterInfo[] _parameters = Array.Empty(); + + protected BslScriptMethodInfo() + { + } + + internal static BslScriptMethodInfo Create() + { + return new BslScriptMethodInfo(); + } + + void IBuildableMember.SetDispatchIndex(int index) + { + DispatchId = index; + } + + void IBuildableMember.SetDeclaringType(Type type) + { + _declaringType = type; + } + + void IBuildableMember.SetName(string name) + { + _name = name; + } + + void IBuildableMember.SetAlias(string alias) + { + } + + void IBuildableMember.SetExportFlag(bool isExport) + { + _isPrivate = !isExport; + } + + void IBuildableMember.SetDataType(Type type) + { + _returnType = type; + } + + void IBuildableMember.SetAnnotations(IEnumerable annotations) + { + SetAnnotations(new AnnotationHolder(annotations.ToArray())); + } + + void IBuildableMethod.SetParameters(IEnumerable parameters) + { + _parameters = parameters.ToArray(); + } + + public override Type ReturnType => _returnType; + + public override Type DeclaringType => _declaringType; + + public override string Name => _name; + + public override string Alias => default; + + public override Type ReflectedType => _declaringType; + + public int DispatchId { get; private set; } = -1; + + public override MethodImplAttributes GetMethodImplementationFlags() => MethodImplAttributes.Managed; + + // ReSharper disable once CoVariantArrayConversion + public override ParameterInfo[] GetParameters() => CopyParams(); + + public BslParameterInfo[] GetBslParameters() => CopyParams(); + + private BslParameterInfo[] CopyParams() + { + if (_parameters.Length == 0) + return _parameters; + + var dest = new BslParameterInfo[_parameters.Length]; + _parameters.CopyTo(dest, 0); + return dest; + } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + throw new NotSupportedException(); + } + + public override MethodAttributes Attributes => _isPrivate ? MethodAttributes.Private : MethodAttributes.Public; + + public override RuntimeMethodHandle MethodHandle => throw new NotSupportedException(); + + public override MethodInfo GetBaseDefinition() + { + return this; + } + + public override ICustomAttributeProvider ReturnTypeCustomAttributes => throw new NotImplementedException(); + + public bool HasBslAnnotation(BilingualString name) + { + return Annotations.GetCustomAttributes(typeof(BslAnnotationAttribute), false) + .Cast() + .Any(anno => name.HasName(anno.Name)); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/BslScriptPropertyInfo.cs b/src/OneScript.Core/Contexts/BslScriptPropertyInfo.cs new file mode 100644 index 000000000..83a33f466 --- /dev/null +++ b/src/OneScript.Core/Contexts/BslScriptPropertyInfo.cs @@ -0,0 +1,140 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; +using OneScript.Values; + +namespace OneScript.Contexts +{ + /// + /// Свойство BSL-объекта, объявленное в модуле. Экспортные переменные также создают одноименное свойство. + /// + public class BslScriptPropertyInfo : BslPropertyInfo, IBuildableProperty + { + private Type _declaringType; + private Type _propertyType = typeof(BslValue); + private string _name; + private string _alias; + private bool _isPrivate = true; + private bool _canRead = true; + private bool _canWrite = true; + + protected BslScriptPropertyInfo() + { + } + + internal static BslScriptPropertyInfo Create() + { + return new BslScriptPropertyInfo(); + } + + public override Type DeclaringType => _declaringType; + public override string Name => _name; + public override string Alias => _alias; + public override bool Equals(BslPropertyInfo other) + { + if (!(other is BslScriptPropertyInfo scriptProp)) + return false; + + return scriptProp._declaringType == _declaringType + && scriptProp._name == _name + && scriptProp._alias == _alias + && scriptProp.DispatchId == DispatchId; + } + + public override Type ReflectedType => _declaringType; + + public int DispatchId { get; private set; } = -1; + + public bool IsExported => !_isPrivate; + + public override MethodInfo[] GetAccessors(bool nonPublic) + { + throw new NotImplementedException(); + } + + public override MethodInfo GetGetMethod(bool nonPublic) + { + throw new NotImplementedException(); + } + + public override ParameterInfo[] GetIndexParameters() + { + throw new NotImplementedException(); + } + + public override MethodInfo GetSetMethod(bool nonPublic) + { + throw new NotImplementedException(); + } + + public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override PropertyAttributes Attributes => PropertyAttributes.None; + public override bool CanRead => _canRead; + public override bool CanWrite => _canWrite; + public override Type PropertyType => _propertyType; + + void IBuildableMember.SetDeclaringType(Type type) + { + _declaringType = type; + } + + void IBuildableMember.SetName(string name) + { + _name = name; + } + + void IBuildableMember.SetAlias(string alias) + { + _alias = alias; + } + + void IBuildableMember.SetExportFlag(bool isExport) + { + _isPrivate = !isExport; + } + + void IBuildableMember.SetDataType(Type type) + { + _propertyType = type; + } + + void IBuildableMember.SetAnnotations(IEnumerable annotations) + { + SetAnnotations(new AnnotationHolder(annotations.ToArray())); + } + + void IBuildableMember.SetDispatchIndex(int index) + { + DispatchId = index; + } + + void IBuildableProperty.CanRead(bool canRead) + { + _canRead = canRead; + } + + void IBuildableProperty.CanWrite(bool canWrite) + { + _canWrite = canWrite; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ByRefAttribute.cs b/src/OneScript.Core/Contexts/ByRefAttribute.cs new file mode 100644 index 000000000..86e9a29cd --- /dev/null +++ b/src/OneScript.Core/Contexts/ByRefAttribute.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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.Contexts +{ + [AttributeUsage(AttributeTargets.Parameter)] + public class ByRefAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ClassBuilder.cs b/src/OneScript.Core/Contexts/ClassBuilder.cs new file mode 100644 index 000000000..3b7839c83 --- /dev/null +++ b/src/OneScript.Core/Contexts/ClassBuilder.cs @@ -0,0 +1,209 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; +using OneScript.Execution; + +namespace OneScript.Contexts +{ + public class ClassBuilder + { + private readonly Type _classType; + private readonly List _methods = new List(); + private readonly List _properties = new List(); + private readonly List _fields = new List(); + private readonly List _constructors = new List(); + + public ClassBuilder(Type classType) + { + _classType = classType; + } + + public string TypeName { get; set; } + + public IExecutableModule Module { get; set; } + + public ClassBuilder SetTypeName(string typeName) + { + TypeName = typeName; + return this; + } + + public ClassBuilder SetModule(IExecutableModule module) + { + Module = module; + return this; + } + + public ClassBuilder ExportClassMethod(string methodName) + { + var mi = _classType.GetMethod(methodName); + if(mi == null) + throw new InvalidOperationException($"Method '{methodName}' not found in {_classType}"); + + ExportClassMethod(mi); + return this; + } + + public ClassBuilder ExportClassMethod(MethodInfo nativeMethod) + { + if(nativeMethod == null) + throw new ArgumentNullException(nameof(nativeMethod)); + + if (nativeMethod.ReflectedType != _classType) + throw new InvalidOperationException($"Method '{nativeMethod.Name}' not found in {_classType}"); + + if (MarkedAsContextMethod(nativeMethod, true)) + { + _methods.Add(new ContextMethodInfo(nativeMethod)); + } + else + { + var markup = new ContextMethodAttribute(nativeMethod.Name, null); // no alias + _methods.Add(new ContextMethodInfo(nativeMethod, markup)); + } + + return this; + } + + public ClassBuilder ExportProperty(string propName) + { + var info = _classType.GetProperty(propName); + if (info == null) + throw new InvalidOperationException($"Property '{propName}' not found in {_classType}"); + + _properties.Add(new ContextPropertyInfo(info)); + + return this; + } + + public ClassBuilder ExportMethods(bool includeDeprecations = false) + { + var methods = _classType.GetMethods() + .Where(x => MarkedAsContextMethod(x, includeDeprecations)) + .Select(x=>new ContextMethodInfo(x)); + + _methods.AddRange(methods); + return this; + } + + public ClassBuilder ExportProperties(bool includeDeprecations = false) + { + var props = _classType.GetProperties() + .Where(prop => MarkedAsContextProperty(prop, includeDeprecations)) + .Select(prop => new ContextPropertyInfo(prop)); + + _properties.AddRange(props); + return this; + } + + private static bool MarkedAsContextMethod(MemberInfo member, bool includeDeprecations) + { + return member + .GetCustomAttributes(typeof(ContextMethodAttribute), false) + .Any(x => includeDeprecations || (x as ContextMethodAttribute)?.IsDeprecated == false); + } + + private static bool MarkedAsContextProperty(PropertyInfo member, bool includeDeprecations = false) + { + return member.GetCustomAttributes(typeof(ContextPropertyAttribute), false) + .Any(x => includeDeprecations || (x as ContextPropertyAttribute)?.IsDeprecated == false); + } + + public ClassBuilder ExportConstructor(MethodInfo info) + { + if (info.DeclaringType != _classType) + { + throw new ArgumentException("info must belong to the current class"); + } + + _constructors.Add(new ContextConstructorInfo(info)); + return this; + } + + public ClassBuilder ExportConstructor(InstanceConstructor creator) + { + IBuildableMember info = new BslLambdaConstructorInfo(creator); + info.SetDeclaringType(_classType); + _constructors.Add((BslLambdaConstructorInfo)info); + return this; + } + + public ClassBuilder ExportScriptVariables() + { + if(Module == null) + throw new InvalidOperationException("Module is not set"); + + foreach (var variable in Module.Fields) + { + _fields.Add(variable); + } + + return this; + } + + public ClassBuilder ExportScriptMethods() + { + if (Module == null) + throw new InvalidOperationException("Module is not set"); + + foreach (var methodDescriptor in Module.Methods) + { + if(methodDescriptor.Name == IExecutableModule.BODY_METHOD_NAME) + continue; + + _methods.Add(methodDescriptor); + } + + return this; + } + + public ClassBuilder ExportScriptConstructors() + { + var statics = _classType.GetMethods(BindingFlags.Static | BindingFlags.Public) + .Where(x => x.GetCustomAttributes(false).Any(y => y is ScriptConstructorAttribute)); + + foreach (var staticConstructor in statics) + { + ExportConstructor(staticConstructor); + } + + return this; + } + + public ClassBuilder ExportDefaults() + { + ExportMethods(); + ExportProperties(); + if (Module != null) + { + ExportScriptVariables(); + ExportScriptMethods(); + ExportScriptConstructors(); + } + + return this; + } + + public Type Build() + { + var classDelegator = new ReflectedClassType(_classType); + classDelegator.SetName(TypeName); + classDelegator.SetFields(_fields); + classDelegator.SetProperties(_properties); + classDelegator.SetMethods(_methods); + classDelegator.SetConstructors(_constructors); + + return classDelegator; + } + + } + +} diff --git a/src/OneScript.Core/Contexts/ContextClassAttribute.cs b/src/OneScript.Core/Contexts/ContextClassAttribute.cs new file mode 100644 index 000000000..be3819813 --- /dev/null +++ b/src/OneScript.Core/Contexts/ContextClassAttribute.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Commons; + +namespace OneScript.Contexts +{ + [AttributeUsage(AttributeTargets.Class)] + public class ContextClassAttribute : Attribute, INameAndAliasProvider + { + public ContextClassAttribute(string typeName, string typeAlias = "") + { + if (!Utils.IsValidIdentifier(typeName)) + throw new ArgumentException("Name must be a valid identifier"); + + if (!string.IsNullOrEmpty(typeAlias) && !Utils.IsValidIdentifier(typeAlias)) + throw new ArgumentException("Alias must be a valid identifier"); + + Name = typeName; + Alias = typeAlias; + } + + public string Name { get; } + + public string Alias { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ContextConstructorInfo.cs b/src/OneScript.Core/Contexts/ContextConstructorInfo.cs new file mode 100644 index 000000000..a4d6be6af --- /dev/null +++ b/src/OneScript.Core/Contexts/ContextConstructorInfo.cs @@ -0,0 +1,55 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Globalization; +using System.Reflection; +using OneScript.Commons; +using OneScript.Values; + +namespace OneScript.Contexts +{ + public class ContextConstructorInfo : BslConstructorInfo, IObjectWrapper + { + private readonly MethodInfo _factoryMethod; + + internal ContextConstructorInfo(MethodInfo staticConstructor) + { + CheckMethod(staticConstructor); + _factoryMethod = staticConstructor; + } + + private static void CheckMethod(MethodInfo staticConstructor) + { + if (!staticConstructor.IsStatic) + throw new ArgumentException("Method must be static"); + + if (!typeof(BslObjectValue).IsAssignableFrom(staticConstructor.ReturnType)) + throw new ArgumentException("Return type must inherit BslObjectValue"); + } + + public object UnderlyingObject => _factoryMethod; + public override Type DeclaringType => _factoryMethod.DeclaringType; + public override string Name => ConstructorName; + public override Type ReflectedType => _factoryMethod.DeclaringType; + public override ParameterInfo[] GetParameters() + { + return _factoryMethod.GetParameters(); + } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return _factoryMethod.Invoke(obj, invokeAttr, binder, parameters, culture); + } + + public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return _factoryMethod.Invoke(null, invokeAttr, binder, parameters, culture) ?? + throw new InvalidOperationException("Constructors should not return null"); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ContextMethodAttribute.cs b/src/OneScript.Core/Contexts/ContextMethodAttribute.cs new file mode 100644 index 000000000..498c53e2d --- /dev/null +++ b/src/OneScript.Core/Contexts/ContextMethodAttribute.cs @@ -0,0 +1,50 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.CompilerServices; +using OneScript.Commons; + +namespace OneScript.Contexts +{ + [AttributeUsage(AttributeTargets.Method)] + public class ContextMethodAttribute : Attribute, INameAndAliasProvider + { + private readonly string _name; + private readonly string _alias; + + public ContextMethodAttribute(string name, string alias) + { + if (!Utils.IsValidIdentifier(name)) + throw new ArgumentException($"Name '{name}' must be a valid identifier"); + + if (!string.IsNullOrEmpty(alias) && !Utils.IsValidIdentifier(alias)) + throw new ArgumentException($"Alias '{alias}' must be a valid identifier"); + + _name = name; + _alias = alias; + } + + public ContextMethodAttribute(string name, string _ = null, + [CallerMemberName] string nativeMethodName = null) + : this(name, nativeMethodName) + { + } + + public bool IsDeprecated { get; set; } + + public bool ThrowOnUse { get; set; } + + /// + /// Данный метод не будет обработан генератором документации при обходе типов + /// + public bool SkipForDocumenter { get; set; } + + public string Name => _name; + public string Alias => _alias; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ContextMethodInfo.cs b/src/OneScript.Core/Contexts/ContextMethodInfo.cs new file mode 100644 index 000000000..c656f2fd0 --- /dev/null +++ b/src/OneScript.Core/Contexts/ContextMethodInfo.cs @@ -0,0 +1,104 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; +using OneScript.Commons; +using OneScript.Execution; + +namespace OneScript.Contexts +{ + /// + /// Информация о методе, объявленном в классе .NET и помеченном атрибутом ContextMethod + /// + public sealed class ContextMethodInfo : BslMethodInfo, IObjectWrapper, ISupportsDeprecation + { + private readonly MethodInfo _realMethod; + + public ContextMethodInfo(MethodInfo realMethod) + : this(realMethod, realMethod.GetCustomAttribute(false) + ?? throw new ArgumentException("Method is not marked with ContextMethodAttribute")) + { + } + + public ContextMethodInfo(MethodInfo realMethod, ContextMethodAttribute binding) + { + _realMethod = realMethod; + InjectsProcess = _realMethod.GetParameters().FirstOrDefault()?.ParameterType == typeof(IBslProcess); + IsDeprecated = binding.IsDeprecated; + IsForbiddenToUse = binding.ThrowOnUse; + Name = binding.Name; + Alias = binding.Alias; + } + + public override Type ReturnType => _realMethod.ReturnType; + + public override ParameterInfo ReturnParameter => _realMethod.ReturnParameter; + + public bool IsDeprecated { get; } + + public bool IsForbiddenToUse { get; } + + public override object[] GetCustomAttributes(bool inherit) + { + return _realMethod.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return _realMethod.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return _realMethod.IsDefined(attributeType, inherit); + } + + public override ParameterInfo[] GetParameters() + { + return _realMethod.GetParameters(); + } + + public override MethodImplAttributes GetMethodImplementationFlags() + { + return _realMethod.GetMethodImplementationFlags(); + } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return _realMethod.Invoke(obj, invokeAttr, binder, parameters, culture); + } + + public override MethodInfo GetBaseDefinition() + { + return _realMethod.GetBaseDefinition(); + } + + public override ICustomAttributeProvider ReturnTypeCustomAttributes => _realMethod.ReturnTypeCustomAttributes; + + public override string Name { get; } + + public override string Alias { get; } + + public override Type DeclaringType => _realMethod.DeclaringType; + + public override Type ReflectedType => _realMethod.ReflectedType; + + public override RuntimeMethodHandle MethodHandle => _realMethod.MethodHandle; + + public override MethodAttributes Attributes => _realMethod.Attributes; + + + public object UnderlyingObject => _realMethod; + + public bool InjectsProcess { get; } + + public MethodInfo GetWrappedMethod() => _realMethod; + } +} diff --git a/src/OneScript.Core/Contexts/ContextPropertyAttribute.cs b/src/OneScript.Core/Contexts/ContextPropertyAttribute.cs new file mode 100644 index 000000000..32f34e2e9 --- /dev/null +++ b/src/OneScript.Core/Contexts/ContextPropertyAttribute.cs @@ -0,0 +1,48 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Commons; + +namespace OneScript.Contexts +{ + [AttributeUsage(AttributeTargets.Property)] + public class ContextPropertyAttribute : Attribute, INameAndAliasProvider + { + private readonly string _name; + private readonly string _alias; + + public ContextPropertyAttribute(string name, string alias = "") + { + if (!Utils.IsValidIdentifier(name)) + throw new ArgumentException("Name must be a valid identifier"); + + if (!string.IsNullOrEmpty(alias) && !Utils.IsValidIdentifier(alias)) + throw new ArgumentException("Alias must be a valid identifier"); + + _name = name; + _alias = alias; + CanRead = true; + CanWrite = true; + } + + public bool CanRead { get; set; } + public bool CanWrite { get; set; } + + /// + /// Данное свойство не будет обработано генератором документации при обходе типов + /// + public bool SkipForDocumenter { get; set; } + + public string Name => _name; + public string Alias => _alias; + + public bool IsDeprecated { get; set; } + + public bool ThrowOnUse { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ContextPropertyInfo.cs b/src/OneScript.Core/Contexts/ContextPropertyInfo.cs new file mode 100644 index 000000000..72ce6d4d4 --- /dev/null +++ b/src/OneScript.Core/Contexts/ContextPropertyInfo.cs @@ -0,0 +1,107 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Globalization; +using System.Reflection; +using OneScript.Commons; + +namespace OneScript.Contexts +{ + /// + /// Свойство, объявленое в CLR-классе через атрибут ContextProperty + /// + public class ContextPropertyInfo : BslPropertyInfo, IObjectWrapper, ISupportsDeprecation + { + private readonly PropertyInfo _realProperty; + private readonly ContextPropertyAttribute _scriptMark; + + public ContextPropertyInfo(PropertyInfo wrappedInfo) + : this(wrappedInfo, wrappedInfo.GetCustomAttribute(false)) + { + } + + public ContextPropertyInfo(PropertyInfo wrappedInfo, ContextPropertyAttribute binding) + { + _realProperty = wrappedInfo; + _scriptMark = binding; + } + + public override object[] GetCustomAttributes(bool inherit) + { + return _realProperty.GetCustomAttributes(inherit); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + return _realProperty.GetCustomAttributes(attributeType, inherit); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + return _realProperty.IsDefined(attributeType, inherit); + } + + public override bool Equals(BslPropertyInfo other) + { + return (other is ContextPropertyInfo ctxProp) && ctxProp._realProperty.Equals(_realProperty); + } + + public override Type DeclaringType => _realProperty.DeclaringType; + public override Type ReflectedType => _realProperty.ReflectedType; + + public override string Name => _scriptMark.Name; + + public override string Alias => _scriptMark.Alias; + + public override MethodInfo[] GetAccessors(bool nonPublic) + { + var getter = GetGetMethod(nonPublic); + var setter = GetSetMethod(nonPublic); + + if (getter != null && setter != null) + { + return new [] {getter, setter}; + } + + return setter == null ? new [] {getter} : new [] {setter}; + } + + public override MethodInfo GetGetMethod(bool nonPublic) + { + return CanRead ? _realProperty.GetGetMethod(nonPublic) : null; + } + + public override ParameterInfo[] GetIndexParameters() + { + return Array.Empty(); + } + + public override MethodInfo GetSetMethod(bool nonPublic) + { + return CanWrite ? _realProperty.GetSetMethod(nonPublic) : null; + } + + public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override PropertyAttributes Attributes => _realProperty.Attributes; + public override bool CanRead => _scriptMark.CanRead; + public override bool CanWrite => _scriptMark.CanWrite; + public override Type PropertyType => _realProperty.PropertyType; + public object UnderlyingObject => _realProperty; + public bool IsDeprecated => _scriptMark.IsDeprecated; + public bool IsForbiddenToUse => _scriptMark.ThrowOnUse; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/DeprecatedNameAttribute.cs b/src/OneScript.Core/Contexts/DeprecatedNameAttribute.cs new file mode 100644 index 000000000..0a8c413d0 --- /dev/null +++ b/src/OneScript.Core/Contexts/DeprecatedNameAttribute.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; + +namespace OneScript.Contexts +{ + /// + /// Позволяет объявить терм (имя или алиас), как устаревший. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Enum, + AllowMultiple = true)] + public class DeprecatedNameAttribute : Attribute + { + public DeprecatedNameAttribute(string name, bool throwOnUse = false) + { + Name = name; + ThrowOnUse = throwOnUse; + } + + public string Name { get; } + public bool ThrowOnUse { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/DocumentationProviderAttribute.cs b/src/OneScript.Core/Contexts/DocumentationProviderAttribute.cs new file mode 100644 index 000000000..a8313e963 --- /dev/null +++ b/src/OneScript.Core/Contexts/DocumentationProviderAttribute.cs @@ -0,0 +1,26 @@ +/*---------------------------------------------------------- +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.Contexts +{ + /// + /// Атрибут обозначает класс, который сам по себе не является типом или контекстом BSL, + /// однако имеет методы, которые предоставляют xml-документацию для генерации синтакс-помощника + /// + [AttributeUsage(AttributeTargets.Class)] + public class DocumentationProviderAttribute : Attribute + { + public DocumentationProviderAttribute(string category) + { + Category = category; + } + + public string Category { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/DocumentedMemberAttribute.cs b/src/OneScript.Core/Contexts/DocumentedMemberAttribute.cs new file mode 100644 index 000000000..065263878 --- /dev/null +++ b/src/OneScript.Core/Contexts/DocumentedMemberAttribute.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +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.Contexts +{ + /// + /// Атрибут обозначает свойство или метод, которое не является членом контекста + /// однако, предоставляет xml-документацию для генерации синтакс-помощника. + /// Используется для генерации перегрузок методов в документации совместно с флагом + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] + public class DocumentedMemberAttribute : Attribute, INameAndAliasProvider + { + public DocumentedMemberAttribute(string name, string alias = null) + { + Name = name; + Alias = alias; + } + + public string Name { get; } + public string Alias { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/Enums/EnumValueAttribute.cs b/src/OneScript.Core/Contexts/Enums/EnumValueAttribute.cs new file mode 100644 index 000000000..22de7d463 --- /dev/null +++ b/src/OneScript.Core/Contexts/Enums/EnumValueAttribute.cs @@ -0,0 +1,35 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Commons; + +namespace OneScript.Contexts.Enums +{ + /// + /// Атрибут для задания имени или алиаса для значения перечисления + /// А также пометки свойства или поля clr-enum, как значения bsl-перечисления + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class EnumValueAttribute : Attribute, INameAndAliasProvider + { + public EnumValueAttribute (string name, string alias = null) + { + if (!Utils.IsValidIdentifier(name)) + throw new ArgumentException("Name must be a valid identifier"); + + if (!string.IsNullOrEmpty(alias) && !Utils.IsValidIdentifier(alias)) + throw new ArgumentException("Alias must be a valid identifier"); + + Name = name; + Alias = alias; + } + + public string Name { get; } + public string Alias { get; } + } +} diff --git a/src/OneScript.Core/Contexts/Enums/EnumerationTypeAttribute.cs b/src/OneScript.Core/Contexts/Enums/EnumerationTypeAttribute.cs new file mode 100644 index 000000000..3cb4dcb74 --- /dev/null +++ b/src/OneScript.Core/Contexts/Enums/EnumerationTypeAttribute.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +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.Contexts.Enums +{ + /// + /// Атрибут для простых перечислений, являющихся обычными Clr-перечислениями + /// + [AttributeUsage(AttributeTargets.Enum)] + public class EnumerationTypeAttribute : Attribute, IEnumMetadataProvider + { + public EnumerationTypeAttribute (string name, string alias = null, bool createProperty = true) + { + Name = name; + Alias = alias; + CreateGlobalProperty = createProperty; + } + + public string Name { get; } + public string Alias { get; } + public bool CreateGlobalProperty { get; } + + public string TypeUUID { get; set; } + + public string ValueTypeUUID { get; set; } + } +} diff --git a/src/OneScript.Core/Contexts/Enums/IEnumMetadataProvider.cs b/src/OneScript.Core/Contexts/Enums/IEnumMetadataProvider.cs new file mode 100644 index 000000000..5b54f2e6e --- /dev/null +++ b/src/OneScript.Core/Contexts/Enums/IEnumMetadataProvider.cs @@ -0,0 +1,15 @@ +/*---------------------------------------------------------- +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.Contexts.Enums +{ + public interface IEnumMetadataProvider : INameAndAliasProvider + { + public string TypeUUID { get; set; } + + public string ValueTypeUUID { get; set; } + } +} diff --git a/src/OneScript.Core/Contexts/Enums/SystemEnumAttribute.cs b/src/OneScript.Core/Contexts/Enums/SystemEnumAttribute.cs new file mode 100644 index 000000000..f09989eda --- /dev/null +++ b/src/OneScript.Core/Contexts/Enums/SystemEnumAttribute.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; + +namespace OneScript.Contexts.Enums +{ + [AttributeUsage(AttributeTargets.Class)] + public class SystemEnumAttribute : Attribute, IEnumMetadataProvider + { + public SystemEnumAttribute(string name, string alias = "") + { + Name = name; + Alias = alias; + } + + public string Name { get; } + public string Alias { get; } + + public string TypeUUID { get; set; } + + public string ValueTypeUUID { get; set; } + } +} diff --git a/src/ScriptEngine/Machine/Contexts/GlobalContextAttribute.cs b/src/OneScript.Core/Contexts/GlobalContextAttribute.cs similarity index 93% rename from src/ScriptEngine/Machine/Contexts/GlobalContextAttribute.cs rename to src/OneScript.Core/Contexts/GlobalContextAttribute.cs index 7c71c0386..fa05142aa 100644 --- a/src/ScriptEngine/Machine/Contexts/GlobalContextAttribute.cs +++ b/src/OneScript.Core/Contexts/GlobalContextAttribute.cs @@ -4,9 +4,10 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; -namespace ScriptEngine.Machine.Contexts +namespace OneScript.Contexts { [AttributeUsage(AttributeTargets.Class)] public class GlobalContextAttribute : Attribute diff --git a/src/OneScript.Core/Contexts/IAttachableContext.cs b/src/OneScript.Core/Contexts/IAttachableContext.cs new file mode 100644 index 000000000..86ce3e70e --- /dev/null +++ b/src/OneScript.Core/Contexts/IAttachableContext.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Execution; + +namespace OneScript.Contexts +{ + /// + /// Присоединяемый контекст. Методы и свойства этого контекста становятся глобальными методами и свойствами. + /// Каждый скрипт также является присоединяемым, за счет чего его методы и свойства доступны в модуле, как глобальные. + /// + public interface IAttachableContext : IRuntimeContextInstance + { + IVariable GetVariable(int index); + BslMethodInfo GetMethod(int index); + + int VariablesCount { get; } + int MethodsCount { get; } + } + + /// + /// Выполняемое. Имеет bsl-модуль и может присоединяться к машине + /// + public interface IRunnable : IAttachableContext + { + IExecutableModule Module { get; } + } +} diff --git a/src/OneScript.Core/Contexts/ICollectionContext.cs b/src/OneScript.Core/Contexts/ICollectionContext.cs new file mode 100644 index 000000000..7f05d329d --- /dev/null +++ b/src/OneScript.Core/Contexts/ICollectionContext.cs @@ -0,0 +1,26 @@ +/*---------------------------------------------------------- +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 OneScript.Execution; +using ScriptEngine.Machine; + +namespace OneScript.Contexts +{ + public interface ICollectionContext : IEnumerable + where T : IValue + { + int Count(IBslProcess process); + + IEnumerator GetEnumerator(IBslProcess process) + { + // ReSharper disable once NotDisposedResourceIsReturned + return GetEnumerator(); + } + + } +} diff --git a/src/OneScript.Core/Contexts/IIndexedContext.cs b/src/OneScript.Core/Contexts/IIndexedContext.cs new file mode 100644 index 000000000..32a702555 --- /dev/null +++ b/src/OneScript.Core/Contexts/IIndexedContext.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Values; + +namespace OneScript.Contexts +{ + public interface IIndexedContext + { + TValue GetIndexedValue(TIndex index); + + void SetIndexedValue(TIndex index, TValue value); + } + + public interface IIndexedContext : IIndexedContext + { + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/INameAndAliasProvider.cs b/src/OneScript.Core/Contexts/INameAndAliasProvider.cs new file mode 100644 index 000000000..5bbd9ac50 --- /dev/null +++ b/src/OneScript.Core/Contexts/INameAndAliasProvider.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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.Contexts +{ + public interface INameAndAliasProvider + { + string Name { get; } + + string Alias { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/IPredefinedInterfaceChecker.cs b/src/OneScript.Core/Contexts/IPredefinedInterfaceChecker.cs new file mode 100644 index 000000000..7491fc76a --- /dev/null +++ b/src/OneScript.Core/Contexts/IPredefinedInterfaceChecker.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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 OneScript.Execution; + +namespace OneScript.Contexts +{ + /// + /// Постобработчик построения модуля, регистрирующий специальные аннотации, позволяющий во время компиляции проверить + /// что модуль соответствует тому или иному интерфейсу, содержит корректные методы и пр. + /// + public interface IPredefinedInterfaceChecker + { + IEnumerable GetRegistrations(); + + public void Validate(IExecutableModule module); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/IRuntimeContextInstance.cs b/src/OneScript.Core/Contexts/IRuntimeContextInstance.cs new file mode 100644 index 000000000..65a5440ca --- /dev/null +++ b/src/OneScript.Core/Contexts/IRuntimeContextInstance.cs @@ -0,0 +1,54 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Execution; +using ScriptEngine.Machine; + +namespace OneScript.Contexts +{ + public interface IRuntimeContextInstance + { + bool IsIndexed { get; } + bool DynamicMethodSignatures { get; } + + IValue GetIndexedValue(IValue index); + void SetIndexedValue(IValue index, IValue val); + + int GetPropertyNumber(string name); + bool IsPropReadable(int propNum); + bool IsPropWritable(int propNum); + IValue GetPropValue(int propNum); + void SetPropValue(int propNum, IValue newVal); + + int GetPropCount(); + string GetPropName(int propNum); + + int GetMethodNumber(string name); + int GetMethodsCount(); + + BslMethodInfo GetMethodInfo(int methodNumber); + + BslPropertyInfo GetPropertyInfo(int propertyNumber); + + void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process); + + void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process); + + [Obsolete("Use function with IBslProcess parameter")] + void CallAsProcedure(int methodNumber, IValue[] arguments) + { + CallAsProcedure(methodNumber, arguments, ForbiddenBslProcess.Instance); + } + + [Obsolete("Use function with IBslProcess parameter")] + void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) + { + CallAsFunction(methodNumber, arguments, out retValue, ForbiddenBslProcess.Instance); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/IRuntimeEnvironment.cs b/src/OneScript.Core/Contexts/IRuntimeEnvironment.cs new file mode 100644 index 000000000..33fd05208 --- /dev/null +++ b/src/OneScript.Core/Contexts/IRuntimeEnvironment.cs @@ -0,0 +1,83 @@ +/*---------------------------------------------------------- +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 OneScript.Compilation; +using OneScript.Compilation.Binding; +using ScriptEngine.Machine; + +namespace OneScript.Contexts +{ + /// + /// Программное окружение BSL-процесса. То, что "видит" скриптовый код. + /// Используется для инжекта внешних символов в процесс. + /// + public interface IRuntimeEnvironment + { + /// + /// Инжект объекта в глобальное пространство имен. Методы и свойства объекта станут глобальными методами и свойствами. + /// + /// + void InjectObject(IAttachableContext context); + + /// + /// Инжект глобального свойства + /// + /// Значение свойства + /// Идентификатор + /// Псевдоним + /// Флаг доступности свойства только для чтения + void InjectGlobalProperty(IValue value, string identifier, string alias, bool readOnly); + + /// + /// Инжект глобального свойства + /// + /// Значение свойства + /// Идентификатор + /// Флаг доступности свойства только для чтения + void InjectGlobalProperty(IValue value, string identifier, bool readOnly); + + /// + /// Инжект глобального свойства + /// + /// Значение свойства + /// Идентификатор + /// Пакет, предоставивший это свойство + void InjectGlobalProperty(IValue value, string identifier, PackageInfo ownerPackage); + + /// + /// Инжект глобального свойства с помощью описания свойства. + /// + /// Значение свойства + /// Определение свойства + void InjectGlobalProperty(IValue value, BslPropertyInfo definition); + + /// + /// Установить новое значение глобального свойства + /// + /// Имя свойства + /// Значение свойства + void SetGlobalProperty(string propertyName, IValue value); + + /// + /// Прочитать значение глобального свойства + /// + /// Имя свойства + /// + IValue GetGlobalProperty(string propertyName); + + /// + /// Получить таблицу символов для видимого контекста + /// + SymbolTable GetSymbolTable(); + + /// + /// Список подключенных внешних контекстов (слоев), доступных всегда в рамках данного окружения + /// + IReadOnlyList AttachedContexts { get; } + } +} diff --git a/src/OneScript.Core/Contexts/IScriptInformationFactory.cs b/src/OneScript.Core/Contexts/IScriptInformationFactory.cs new file mode 100644 index 000000000..23b8b053a --- /dev/null +++ b/src/OneScript.Core/Contexts/IScriptInformationFactory.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Sources; +using OneScript.Values; + +namespace OneScript.Contexts +{ + public interface IScriptInformationFactory + { + BslObjectValue GetInfo(SourceCode source); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ISupportsDeprecation.cs b/src/OneScript.Core/Contexts/ISupportsDeprecation.cs new file mode 100644 index 000000000..0631834cc --- /dev/null +++ b/src/OneScript.Core/Contexts/ISupportsDeprecation.cs @@ -0,0 +1,14 @@ +/*---------------------------------------------------------- +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.Contexts +{ + public interface ISupportsDeprecation + { + bool IsDeprecated { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/IValueArray.cs b/src/OneScript.Core/Contexts/IValueArray.cs new file mode 100644 index 000000000..95c8f4c6c --- /dev/null +++ b/src/OneScript.Core/Contexts/IValueArray.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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 ScriptEngine.Machine; + +namespace OneScript.Contexts +{ + public interface IValueArray : IEnumerable + { + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/IVariable.cs b/src/OneScript.Core/Contexts/IVariable.cs new file mode 100644 index 000000000..48b91e25e --- /dev/null +++ b/src/OneScript.Core/Contexts/IVariable.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Contexts +{ + public interface IVariable : IValue, IValueReference + { + string Name { get; } + } +} diff --git a/src/OneScript.Core/Contexts/Internal/IBuildableMember.cs b/src/OneScript.Core/Contexts/Internal/IBuildableMember.cs new file mode 100644 index 000000000..49341fbe9 --- /dev/null +++ b/src/OneScript.Core/Contexts/Internal/IBuildableMember.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace OneScript.Contexts.Internal +{ + internal interface IBuildableMember + { + void SetDeclaringType(Type type); + void SetName(string name); + void SetAlias(string alias); + void SetExportFlag(bool isExport); + void SetDataType(Type type); + void SetAnnotations(IEnumerable annotations); + void SetDispatchIndex(int index); + } + + internal interface IBuildableMethod : IBuildableMember + { + void SetParameters(IEnumerable parameters); + } + + internal interface IBuildableProperty : IBuildableMember + { + void CanRead(bool canRead); + void CanWrite(bool canWrite); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/MemberInfoExtensions.cs b/src/OneScript.Core/Contexts/MemberInfoExtensions.cs new file mode 100644 index 000000000..f6cfb006e --- /dev/null +++ b/src/OneScript.Core/Contexts/MemberInfoExtensions.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +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.Reflection; +using System.Runtime.CompilerServices; + +namespace OneScript.Contexts +{ + public static class MemberInfoExtensions + { + public static bool IsByRef(this ParameterInfo parameter) + { + if (parameter is BslParameterInfo bslParam) + return !bslParam.ExplicitByVal; + + return parameter.ParameterType.IsByRef || parameter.IsDefined(typeof(ByRefAttribute)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFunction(this MethodInfo method) + { + return method.ReturnType != typeof(void); + } + + public static ParameterInfo[] GetBslParameters(this BslMethodInfo methodInfo) + { + return methodInfo is ContextMethodInfo { InjectsProcess: true } ? + methodInfo.GetParameters()[1..]: + methodInfo.GetParameters(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/PredefinedInterfaceRegistration.cs b/src/OneScript.Core/Contexts/PredefinedInterfaceRegistration.cs new file mode 100644 index 000000000..9987b609f --- /dev/null +++ b/src/OneScript.Core/Contexts/PredefinedInterfaceRegistration.cs @@ -0,0 +1,82 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Localization; + +namespace OneScript.Contexts +{ + /// + /// Опции регистрации предопределенных аннотаций + /// + public class PredefinedInterfaceRegistration : IEquatable + { + public PredefinedInterfaceRegistration( + MarkerLocation location, + BilingualString annotation, + BilingualString methodName) + { + Location = location; + Annotation = annotation; + MethodName = methodName; + } + + public PredefinedInterfaceRegistration(MarkerLocation location, BilingualString annotation) : this(location, annotation, null) + { + } + + public MarkerLocation Location { get; } + public BilingualString Annotation { get; } + public BilingualString MethodName { get; } + + + /// + /// Хелпер для регистрации аннотаций модуля + /// + /// Регистрируемая аннотация + public static PredefinedInterfaceRegistration OnModule(BilingualString annotation) + { + return new PredefinedInterfaceRegistration(MarkerLocation.ModuleAnnotation, annotation); + } + + /// + /// Хелпер для регистрации аннотаций на методах + /// + /// Регистрируемая аннотация + /// Метод на котором должна быть расположена аннотация + public static PredefinedInterfaceRegistration OnMethod(BilingualString annotation, BilingualString methodName) + { + return new PredefinedInterfaceRegistration(MarkerLocation.SpecificMethodAnnotation, annotation, methodName); + } + + public virtual bool Equals(PredefinedInterfaceRegistration other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Location == other.Location && Equals(Annotation, other.Annotation) && Equals(MethodName, other.MethodName); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((PredefinedInterfaceRegistration)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine((int)Location, Annotation, MethodName); + } + } + + public enum MarkerLocation + { + ModuleAnnotation, + SpecificMethodAnnotation + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/PredefinedInterfaceResolver.cs b/src/OneScript.Core/Contexts/PredefinedInterfaceResolver.cs new file mode 100644 index 000000000..329fe2423 --- /dev/null +++ b/src/OneScript.Core/Contexts/PredefinedInterfaceResolver.cs @@ -0,0 +1,81 @@ +/*---------------------------------------------------------- +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 OneScript.Commons; +using OneScript.Execution; + +namespace OneScript.Contexts +{ + // ReSharper disable once ClassNeverInstantiated.Global + public class PredefinedInterfaceResolver + { + private readonly ISet _checkers; + + private class CheckerData : PredefinedInterfaceRegistration + { + public IPredefinedInterfaceChecker Checker { get; } + + public CheckerData( + IPredefinedInterfaceChecker checker, + PredefinedInterfaceRegistration source) + : base(source.Location, source.Annotation, source.MethodName) + { + Checker = checker; + } + } + + public PredefinedInterfaceResolver(IEnumerable checkers) + { + _checkers = MapCheckers(checkers); + } + + public void Resolve(IExecutableModule module) + { + var triggeredCheckers = new HashSet(); + + _checkers + .Where(check => !triggeredCheckers.Contains(check.Checker)) + .Where(check => + check.Location == MarkerLocation.ModuleAnnotation + && module.ModuleAttributes.Any(attr => check.Annotation.HasName(attr.Name))) + .ForEach(check => + { + triggeredCheckers.Add(check.Checker); + check.Checker.Validate(module); + }); + + foreach (var methodInfo in module.Methods.Where(m => m is BslScriptMethodInfo).Cast()) + { + _checkers + .Where(check => !triggeredCheckers.Contains(check.Checker)) + .Where(check => + check.Location == MarkerLocation.SpecificMethodAnnotation && + check.MethodName.HasName(methodInfo.Name) && + methodInfo.HasBslAnnotation(check.Annotation)) + .ForEach(check => + { + triggeredCheckers.Add(check.Checker); + check.Checker.Validate(module); + }); + } + } + + private static ISet MapCheckers(IEnumerable checkers) + { + var result = new HashSet(); + + foreach (var sourceCheck in checkers) + { + sourceCheck.GetRegistrations().ForEach(x => result.Add(new CheckerData(sourceCheck, x))); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ReflectedClassType.cs b/src/OneScript.Core/Contexts/ReflectedClassType.cs new file mode 100644 index 000000000..08fb9a307 --- /dev/null +++ b/src/OneScript.Core/Contexts/ReflectedClassType.cs @@ -0,0 +1,188 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Contexts.Internal; + +namespace OneScript.Contexts +{ + /// + /// Обертка для типов 1Script, которая предоставляет рефлексию класса + /// как он виден в языке. С именами и составом полей, видимых из скриптового кода. + /// Позволяет представить тип 1Script как системный тип + /// + public class ReflectedClassType : TypeDelegator + { + private string _typeName; + private BslPropertyInfo[] _properties; + private BslMethodInfo[] _methods; + private BslFieldInfo[] _fields; + private ConstructorInfo[] _constructors; + + private readonly Type _underlyingType; + + internal ReflectedClassType(Type classType) + :base(classType) + { + _underlyingType = classType; + } + + public void SetName(string name) + { + _typeName = name; + } + + public void SetFields(IEnumerable source) + { + _fields = source.Select(x => + { + if (x is IBuildableMember refl) + { + refl.SetDeclaringType(this); + } + + return x; + }).ToArray(); + } + + public void SetProperties(IEnumerable source) + { + _properties = source.ToArray(); + } + + public void SetMethods(IEnumerable source) + { + _methods = source.Select(x => + { + if (x is IBuildableMethod refl) + { + refl.SetDeclaringType(this); + } + + return x; + }).ToArray(); + } + + public void SetConstructors(IEnumerable source) + { + _constructors = source.Select(x => + { + if(x is IBuildableMethod refl) + refl.SetDeclaringType(this); + return x; + + }).ToArray(); + } + + public override string Name => _typeName; + public override string FullName => Namespace + "." + Name; + public override Assembly Assembly => Assembly.GetExecutingAssembly(); + public override bool ContainsGenericParameters => false; + public override string AssemblyQualifiedName => Assembly.CreateQualifiedName(Assembly.FullName, Name); + public override Type UnderlyingSystemType => _underlyingType; + public override Type BaseType => _underlyingType.BaseType; + public override string Namespace => GetType().Namespace + ".dyn"; + + public override FieldInfo[] GetFields(BindingFlags bindingAttr) + { + IEnumerable result; + bool showPublic = bindingAttr.HasFlag(BindingFlags.Public); + bool showPrivate = bindingAttr.HasFlag(BindingFlags.NonPublic); + if (showPublic && showPrivate) + result = _fields; + else + result = _fields.Where(x => x.IsPublic && showPublic || x.IsPrivate && showPrivate); + + return result.ToArray(); + } + + public override FieldInfo GetField(string name, BindingFlags bindingAttr) + { + if (bindingAttr.HasFlag(BindingFlags.Public)) + { + return _fields.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0 || x.IsPublic); + } + + return _fields.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0); + } + + protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + return _methods.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0); + } + + public override MethodInfo[] GetMethods(BindingFlags bindingAttr) + { + bool showPrivate = bindingAttr.HasFlag(BindingFlags.NonPublic); + bool showPublic = bindingAttr.HasFlag(BindingFlags.Public); + return _methods.Where(x=>x.IsPublic && showPublic || x.IsPrivate && showPrivate).ToArray(); + } + + public override IList GetCustomAttributesData() + { + return Array.Empty(); + } + + public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) + { + bool showPrivate = bindingAttr.HasFlag(BindingFlags.NonPublic); + bool showPublic = bindingAttr.HasFlag(BindingFlags.Public); + + return _properties.Where(x => + { + var isPublic = (x.GetMethod?.IsPublic) == true || (x.SetMethod?.IsPublic) == true; + return isPublic && showPublic || !isPublic && showPrivate; + + }) + .Cast() + .ToArray(); + } + + protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + return _properties.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0); + } + + public override MemberInfo[] GetMembers(BindingFlags bindingAttr) + { + var mems = new MemberInfo[_properties.Length + _fields.Length + _methods.Length]; + + Array.Copy(_fields, mems, _fields.Length); + Array.Copy(_properties, 0, mems, _fields.Length, _properties.Length); + Array.Copy(_methods, 0, mems, _properties.Length + _fields.Length, _methods.Length); + + return mems; + } + + public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) + { + if(name == null) + throw new ArgumentNullException(nameof(name)); + + switch (type) + { + case MemberTypes.Field: + return new MemberInfo[] { GetField(name, bindingAttr) }; + case MemberTypes.Method: + return new MemberInfo[] { GetMethod(name, bindingAttr) }; + case MemberTypes.Property: + return new MemberInfo[] { GetProperty(name, bindingAttr) }; + default: + return Array.Empty(); + } + } + + public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) + { + return _constructors; + } + + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/ScriptConstructorAttribute.cs b/src/OneScript.Core/Contexts/ScriptConstructorAttribute.cs new file mode 100644 index 000000000..d4927ab66 --- /dev/null +++ b/src/OneScript.Core/Contexts/ScriptConstructorAttribute.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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.Contexts +{ + /// + /// Помечает статический метод класса, как Bsl-конструктор. + /// Должен располагаться на статических методах. + /// Поддерживает маршаллинг аргументов, т.е. можно указывать не только IValue, но и просто типы C# + /// Первым параметром метода может быть указан TypeActivationContext, тогда он будет инжектиться в вызов. + /// + [AttributeUsage(AttributeTargets.Method)] + public class ScriptConstructorAttribute : Attribute + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Contexts/Variable.cs b/src/OneScript.Core/Contexts/Variable.cs new file mode 100644 index 000000000..0b071ce4a --- /dev/null +++ b/src/OneScript.Core/Contexts/Variable.cs @@ -0,0 +1,168 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Contexts +{ + public sealed class Variable : IVariable + { + public string Name { get; private set; } + + private Variable() + { + + } + + #region Factory + + public static IVariable Create(IValue val, string symbol) + { + return new Variable + { + Value = val, + Name = symbol + }; + } + + public static IVariable CreateReference(IVariable variable, string refName) + { + if (variable is VariableReference vref && vref._reference is IndexedValueReference iv) + { + _ = iv.Value; // проверить правильность индекса через его чтение + } + + return new VariableReference(variable, refName); + } + + public static IVariable CreateContextPropertyReference(IRuntimeContextInstance context, int propertyNumber, string refName) + { + return new VariableReference(context, propertyNumber, refName); + } + + public static IVariable CreateIndexedPropertyReference(IRuntimeContextInstance context, IValue index, string refName) + { + return new VariableReference(context, index, refName); + } + + #endregion + + #region IVariable Members + + public IValue Value { get; set; } + + #endregion + + #region IValue Members + + public TypeDescriptor SystemType => Value.SystemType; + + #endregion + + #region IComparable Members + + public int CompareTo(IValue other) + { + return Value.CompareTo(other); + } + + #endregion + + #region IEquatable Members + + public bool Equals(IValue other) + { + return Value.Equals(other); + } + + #endregion + + #region Reference + + private sealed class VariableReference : IVariable + { + public readonly IValueReference _reference; + + public string Name { get; } + + public VariableReference(IVariable var, string name) + { + _reference = new ValueReference(() => (BslValue)var.Value, v => var.Value = v); + Name = name; + } + + public VariableReference(IRuntimeContextInstance context, int propertyNumber, string name) + { + _reference = new PropertyValueReference(context, propertyNumber); + Name = name; + } + + public VariableReference(IRuntimeContextInstance context, IValue index, string name) + { + _reference = new IndexedValueReference(context, (BslValue)index); + Name = name; + } + + #region IVariable Members + + public IValue Value + { + get => _reference.Value; + set => _reference.Value = (BslValue)value; + } + + #endregion + + #region IValue Members + + public TypeDescriptor SystemType => Value.SystemType; + + #endregion + + #region IComparable Members + + public int CompareTo(IValue other) + { + return Value.CompareTo(other); + } + + #endregion + + #region IEquatable Members + + public bool Equals(IValue other) + { + return Value.Equals(other); + } + + #endregion + + public bool Equals(IValueReference other) + { + return _reference.Equals(other); + } + + public override string ToString() + { + return Value.ToString(); + } + } + + #endregion + + public bool Equals(IValueReference other) + { + return ReferenceEquals(this, other); + } + + public override string ToString() + { + return Value.ToString(); + } + } +} diff --git a/src/OneScript.Core/DependencyInjection/IServiceContainer.cs b/src/OneScript.Core/DependencyInjection/IServiceContainer.cs new file mode 100644 index 000000000..feaff4baf --- /dev/null +++ b/src/OneScript.Core/DependencyInjection/IServiceContainer.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace OneScript.DependencyInjection +{ + public interface IServiceContainer : IDisposable + { + object Resolve(Type type); + + T Resolve() where T : class; + + object TryResolve(Type type); + + T TryResolve() where T : class; + + IEnumerable ResolveEnumerable() where T : class; + + IServiceContainer CreateScope(); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/DependencyInjection/IServiceDefinitions.cs b/src/OneScript.Core/DependencyInjection/IServiceDefinitions.cs new file mode 100644 index 000000000..a96b48d1a --- /dev/null +++ b/src/OneScript.Core/DependencyInjection/IServiceDefinitions.cs @@ -0,0 +1,42 @@ +/*---------------------------------------------------------- +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.DependencyInjection +{ + public interface IServiceDefinitions + { + IServiceContainer CreateContainer(); + + void Register(Type knownType); + void Register(Type interfaceType, Type implementation); + void Register() where T : class; + void Register(T instance) where T : class; + void Register() + where T : class + where TImpl : class, T; + + void Register(Func factory) where T : class; + + void RegisterSingleton(Type knownType); + void RegisterSingleton(Type interfaceType, Type implementation); + void RegisterSingleton() where T : class; + void RegisterSingleton(T instance) where T : class; + void RegisterSingleton() + where T : class + where TImpl : class, T; + + void RegisterSingleton(Func factory) where T : class; + + void RegisterScoped() where T : class; + + void RegisterEnumerable() + where T : class + where TImpl : class,T; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/BslCoreException.cs b/src/OneScript.Core/Exceptions/BslCoreException.cs new file mode 100644 index 000000000..5de478ca8 --- /dev/null +++ b/src/OneScript.Core/Exceptions/BslCoreException.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Language; +using OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class BslCoreException : ScriptException + { + public BslCoreException(BilingualString message) : base(message.ToString()) + { + } + + public BslCoreException(BilingualString message, Exception innerException) : base(message.ToString(), innerException) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/BslExceptions.cs b/src/OneScript.Core/Exceptions/BslExceptions.cs new file mode 100644 index 000000000..1bccf03a7 --- /dev/null +++ b/src/OneScript.Core/Exceptions/BslExceptions.cs @@ -0,0 +1,56 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Localization; +using OneScript.Values; + +namespace OneScript.Exceptions +{ + public static class BslExceptions + { + private static string SourceTypeInfo(object source) + { + if (source == null) return "(null)"; + var typeDescription = source.GetType().ToString(); + var presentation = source.ToString(); + if (source is BslValue bslValue) + { + presentation = bslValue.ToString(); + } + return $"{typeDescription}: {presentation}"; + } + + public static RuntimeException ConvertToNumberException(object source = null) + { + var sourceTypeInfo = SourceTypeInfo(source); + return new TypeConversionException(new BilingualString( + $"{sourceTypeInfo} Преобразование к типу 'Число' не поддерживается", + $"{sourceTypeInfo} Conversion to type 'Number' is not supported")); + } + + public static RuntimeException ConvertToBooleanException() + { + return new TypeConversionException(new BilingualString( + "Преобразование к типу 'Булево' не поддерживается", + "Conversion to type 'Boolean' is not supported")); + } + + public static RuntimeException ConvertToDateException() + { + return new TypeConversionException(new BilingualString( + "Преобразование к типу 'Дата' не поддерживается", + "Conversion to type 'Date' is not supported")); + } + + public static RuntimeException ValueIsNotObjectException() + { + return new TypeConversionException(new BilingualString( + "Значение не является значением объектного типа", + "Value is not of object type")); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/ComparisonException.cs b/src/OneScript.Core/Exceptions/ComparisonException.cs new file mode 100644 index 000000000..ad47c325e --- /dev/null +++ b/src/OneScript.Core/Exceptions/ComparisonException.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class ComparisonException : RuntimeException + { + public ComparisonException(BilingualString message) : base(message) + { + } + + public static ComparisonException NotSupported() + { + return new ComparisonException(new BilingualString( + $"Сравнение на больше/меньше для данного типа не поддерживается", + $"Greater-than/Less-than comparison operations are not supported")); + } + + public static ComparisonException NotSupported(string type) + { + return new ComparisonException(new BilingualString( + $"Сравнение на больше/меньше для типа '{type}' не поддерживается", + $"Greater-than/Less-than comparison operations are not supported for '{type}'")); + } + + public static ComparisonException NotSupported(string type1, string type2) + { + return new ComparisonException(new BilingualString( + $"Сравнение на больше/меньше типов '{type1}' и '{type2}' не поддерживается", + $"Greater-than/Less-than comparison operations are not supported for '{type1}' and '{type2}'")); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/DependencyResolveException.cs b/src/OneScript.Core/Exceptions/DependencyResolveException.cs new file mode 100644 index 000000000..8d101f87d --- /dev/null +++ b/src/OneScript.Core/Exceptions/DependencyResolveException.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/. +----------------------------------------------------------*/ + +using OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class DependencyResolveException : RuntimeException + { + public DependencyResolveException(BilingualString message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/IExceptionInfoFactory.cs b/src/OneScript.Core/Exceptions/IExceptionInfoFactory.cs new file mode 100644 index 000000000..c623b4c0a --- /dev/null +++ b/src/OneScript.Core/Exceptions/IExceptionInfoFactory.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; +using OneScript.Contexts; +using OneScript.Values; + +namespace OneScript.Exceptions +{ + /// + /// Фабрика объекта ИнформацияОбОшибке по исключению. + /// + public interface IExceptionInfoFactory + { + BslObjectValue GetExceptionInfo(Exception exception); + + string GetExceptionDescription(IRuntimeContextInstance exceptionInfo); + + Exception Raise(object raiseValue); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/InterfaceCheckException.cs b/src/OneScript.Core/Exceptions/InterfaceCheckException.cs new file mode 100644 index 000000000..d591996b4 --- /dev/null +++ b/src/OneScript.Core/Exceptions/InterfaceCheckException.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class InterfaceCheckException : RuntimeException + { + public InterfaceCheckException(BilingualString message, Exception innerException) : base(message, innerException) + { + } + + public InterfaceCheckException(BilingualString message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/PropertyAccessException.cs b/src/OneScript.Core/Exceptions/PropertyAccessException.cs new file mode 100644 index 000000000..9cafc07fe --- /dev/null +++ b/src/OneScript.Core/Exceptions/PropertyAccessException.cs @@ -0,0 +1,44 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class PropertyAccessException : RuntimeException + { + public PropertyAccessException(BilingualString message, Exception innerException) : base(message, innerException) + { + } + + public PropertyAccessException(BilingualString message) : base(message) + { + } + + public static PropertyAccessException PropIsNotReadableException(string prop) + { + return new PropertyAccessException(new BilingualString( + $"Свойство '{prop}' недоступно для чтения", + $"Property '{prop}' is not readable")); + } + + public static PropertyAccessException PropIsNotWritableException(string prop) + { + return new PropertyAccessException(new BilingualString( + $"Свойство '{prop}' недоступно для записи", + $"Property '{prop}' is not writable")); + } + + public static PropertyAccessException PropNotFoundException(string prop) + { + return new PropertyAccessException(new BilingualString( + $"Свойство объекта не обнаружено ({prop})", + $"Object property is not found ({prop})")); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/RuntimeException.cs b/src/OneScript.Core/Exceptions/RuntimeException.cs new file mode 100644 index 000000000..82b470b11 --- /dev/null +++ b/src/OneScript.Core/Exceptions/RuntimeException.cs @@ -0,0 +1,210 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class RuntimeException : BslCoreException + { + public RuntimeException(BilingualString message, Exception innerException) : base(message, innerException) + { + } + + public RuntimeException(BilingualString message) : base(message) + { + } + + public RuntimeException(string message_ru, string message_en) + : base(new BilingualString(message_ru, message_en)) + { + } + + #region Static Factory Methods + + public static RuntimeException DeprecatedMethodCall(string name) + { + return new RuntimeException( + $"Вызов безнадёжно устаревшего метода {name}", + $"Call to deprecated method {name}"); + } + + public static RuntimeException DeprecatedPropertyAccess(string name) + { + return new RuntimeException( + $"Обращение к безнадёжно устаревшему свойству {name}", + $"Access to deprecated property {name}"); + } + + public static RuntimeException MethodNotFoundException(string methodName) + { + return new RuntimeException( + $"Метод объекта не обнаружен ({methodName})", + $"Method not found ({methodName})"); + } + + public static RuntimeException MethodNotFoundException(string methodName, string objectName) + { + return new RuntimeException( + $"Метод объекта не обнаружен ({{{objectName}}}::{methodName})", + $"Method not found ({{{objectName}}}::{methodName})"); + } + + public static RuntimeException TooManyArgumentsPassed() + { + return new RuntimeException( + "Слишком много фактических параметров", + "Too many arguments were passed"); + } + + public static RuntimeException TooFewArgumentsPassed() + { + return new RuntimeException( + "Недостаточно фактических параметров", + "Too few arguments were passed"); + } + + public static RuntimeException MissedArgument() + { + return new RuntimeException( + "Пропущен обязательный параметр", + "Missed mandatory argument"); + } + + public static RuntimeException InvalidArgumentType() + { + return new RuntimeException( + "Неверный тип аргумента", + "Invalid type of argument"); + } + + public static RuntimeException InvalidArgumentType(string argName) + { + return new RuntimeException( + $"Неверный тип аргумента '{argName}'", + $"Invalid type of argument '{argName}'"); + } + + public static RuntimeException InvalidArgumentType(int argNum, string argName) + { + return new RuntimeException( + $"Неверный тип аргумента номер {argNum} '{argName}'", + $"Invalid type of argument number {argNum} '{argName}'"); + } + + public static RuntimeException InvalidNthArgumentType(int argNum) + { + return new RuntimeException( + $"Неверный тип аргумента номер {argNum}", + $"Invalid type of argument number {argNum}"); + } + + public static RuntimeException InvalidArgumentValue() + { + return new RuntimeException( + "Неверное значение аргумента", + "Invalid argument value"); + } + + public static RuntimeException InvalidArgumentValue(object value) + { + return new RuntimeException( + $"Неверное значение аргумента {value}", + $"Invalid value for argument {value}"); + } + + public static RuntimeException InvalidNthArgumentValue(int argNum) + { + return new RuntimeException( + $"Неверное значение аргумента номер {argNum}", + $"Invalid value for argument number {argNum}"); + } + + public static RuntimeException IndexedAccessIsNotSupportedException() + { + return new RuntimeException( + "Объект не поддерживает доступ по индексу", + "Indexed access is not supported"); + } + + public static RuntimeException IteratorIsNotDefined() + { + return new RuntimeException("Итератор не определен","Iterator is not defined"); + } + + public static RuntimeException UseProcAsAFunction() + { + return new RuntimeException( + "Использование процедуры как функции", + "Procedure called as function"); + } + + public static RuntimeException DivideByZero() + { + return new RuntimeException("Деление на ноль", "Divide by zero"); + } + + public static RuntimeException ConstructorNotFound(string typeName) + { + return new RuntimeException( + $"Конструктор не найден ({typeName})", + $"Constructor not found ({typeName})"); + } + + public static RuntimeException TypeIsNotDefined(string typeName) + { + return new RuntimeException( + $"Тип не определен. Конструктор не найден ({typeName})", // для совместимости с v1 + $"Type is not defined ({typeName})"); + } + + public static RuntimeException TypeIsNotRegistered(string typeName) + { + return new RuntimeException( + $"Тип не зарегистрирован ({typeName})", + $"Type is not registered ({typeName})"); + } + + public static RuntimeException InvalidEncoding(string encoding) + { + return new RuntimeException( + $"Неправильное имя кодировки '{encoding}'", + $"Invalid encoding name '{encoding}'"); + } + + public static RuntimeException IncorrectOffset() + { + return new RuntimeException( + "Неправильное смещение внутри коллекции", + "Incorrect offset within collection"); + } + + public static RuntimeException IndexOutOfRange() + { + return new RuntimeException( + "Значение индекса выходит за пределы диапазона", + "Index is out of range"); + } + + public static RuntimeException ClosedStream() + { + return new RuntimeException( + "Ошибка обращения к закрытому потоку", + "Cannot access a closed stream"); + } + + public static RuntimeException NonWritableStream() + { + return new RuntimeException( + "Попытка записи в поток не поддерживающий запись", + "Cannot write to a stream that does not support writing"); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Exceptions/TypeConversionException.cs b/src/OneScript.Core/Exceptions/TypeConversionException.cs new file mode 100644 index 000000000..d5fa80e3c --- /dev/null +++ b/src/OneScript.Core/Exceptions/TypeConversionException.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 OneScript.Localization; + +namespace OneScript.Exceptions +{ + public class TypeConversionException : RuntimeException + { + public TypeConversionException(BilingualString message) : base(message) + { + } + + public TypeConversionException() + : base(new BilingualString( + "Несоответствие типов", + "Type mismatch")) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Execution/ForbiddenBslProcess.cs b/src/OneScript.Core/Execution/ForbiddenBslProcess.cs new file mode 100644 index 000000000..73fb76973 --- /dev/null +++ b/src/OneScript.Core/Execution/ForbiddenBslProcess.cs @@ -0,0 +1,35 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Execution +{ + /// + /// Класс процесса который не может выполняться. + /// Используется в API, требующих процесс, когда реальное исполнение bsl-кода не предполагается. + /// При попытке запустить в таком процессе BSL-код будет выдано исключение. + /// + public class ForbiddenBslProcess : IBslProcess + { + public static readonly IBslProcess Instance = new ForbiddenBslProcess(); + + private ForbiddenBslProcess() + {} + + public BslValue Run(BslObjectValue target, IExecutableModule module, BslScriptMethodInfo method, IValue[] arguments) + => throw new NotSupportedException("BslProcess required"); + + public IServiceContainer Services => throw new NotSupportedException("BslProcess required"); + + public int VirtualThreadId => -1; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Execution/IBslProcess.cs b/src/OneScript.Core/Execution/IBslProcess.cs new file mode 100644 index 000000000..2ef3b4233 --- /dev/null +++ b/src/OneScript.Core/Execution/IBslProcess.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +#nullable enable + +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Execution +{ + /// + /// Готовый к исполнению bsl-процесс, с настроенным окружением + /// + public interface IBslProcess + { + /// + /// Запустить метод в текущем процессе + /// + /// целевой объект + /// модуль bsl-кода, который запускается + /// bsl-метод, который запускается + /// аргументы метода + /// Возвращаемое значение. default если вызывалась процедура + public BslValue? Run(BslObjectValue target, IExecutableModule module, BslScriptMethodInfo method, IValue[] arguments); + + /// + /// Сервисы текущего процесса + /// + public IServiceContainer Services { get; } + + public int VirtualThreadId { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Execution/IBslProcessFactory.cs b/src/OneScript.Core/Execution/IBslProcessFactory.cs new file mode 100644 index 000000000..e2679a94f --- /dev/null +++ b/src/OneScript.Core/Execution/IBslProcessFactory.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.Execution +{ + /// + /// Фабрика запуска новых bsl-потоков внутри потока C# + /// + public interface IBslProcessFactory + { + // Создать новый bsl-процесс с пустым стеком вызовов + IBslProcess NewProcess(); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Execution/IExecutableModule.cs b/src/OneScript.Core/Execution/IExecutableModule.cs new file mode 100644 index 000000000..a66829b31 --- /dev/null +++ b/src/OneScript.Core/Execution/IExecutableModule.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Sources; + +namespace OneScript.Execution +{ + public interface IExecutableModule + { + IList ModuleAttributes { get; } + + IList Fields { get; } + + IList Properties { get; } + + IList Methods { get; } + + BslScriptMethodInfo ModuleBody { get; } + + SourceCode Source { get; } + + IDictionary Interfaces { get; } + + const string BODY_METHOD_NAME = "$entry"; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Execution/IExecutorProvider.cs b/src/OneScript.Core/Execution/IExecutorProvider.cs new file mode 100644 index 000000000..c656e5e91 --- /dev/null +++ b/src/OneScript.Core/Execution/IExecutorProvider.cs @@ -0,0 +1,37 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Execution +{ + public delegate BslValue Invoker( + IBslProcess process, + BslObjectValue target, + IExecutableModule module, + BslScriptMethodInfo method, + IValue[] arguments); + + public interface IExecutorProvider + { + Type SupportedModuleType { get; } + + Invoker GetInvokeDelegate(); + + void BeforeProcessStart(IBslProcess process) + { + } + + void AfterProcessExit(IBslProcess process) + { + } + + } +} \ No newline at end of file diff --git a/src/OneScript.Core/OneScript.Core.csproj b/src/OneScript.Core/OneScript.Core.csproj new file mode 100644 index 000000000..d69d06de6 --- /dev/null +++ b/src/OneScript.Core/OneScript.Core.csproj @@ -0,0 +1,22 @@ + + + + $(TargetFrameworkVersion) + OneScript + Debug;Release;LinuxDebug + AnyCPU + + + + + + + OneScript.CoreLib + + + + true + false + + + diff --git a/src/ScriptEngine/Machine/Rcw/DispatchUtility.cs b/src/OneScript.Core/Rcw/DispatchUtility.cs similarity index 88% rename from src/ScriptEngine/Machine/Rcw/DispatchUtility.cs rename to src/OneScript.Core/Rcw/DispatchUtility.cs index 04f8524a4..08dd51882 100644 --- a/src/ScriptEngine/Machine/Rcw/DispatchUtility.cs +++ b/src/OneScript.Core/Rcw/DispatchUtility.cs @@ -18,12 +18,12 @@ This Source Code Form is subject to the terms of the #endregion -namespace ScriptEngine.Machine.Rcw +namespace OneScript.Rcw { /// /// Provides helper methods for working with COM IDispatch objects that have a registered type library. /// - static class DispatchUtility + public static class DispatchUtility { #region Private Constants @@ -63,15 +63,10 @@ public static int GetTypeInfoCount(object obj) [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public static ITypeInfo GetITypeInfo(object obj) { -#if NETSTANDARD - throw new PlatformNotSupportedException(); -#else - RequireReference(obj, "obj"); var dispatch = (IDispatchInfo) obj; dispatch.GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, out var typeInfo); return typeInfo; -#endif } /// @@ -103,9 +98,27 @@ public static bool TryGetDispId(object obj, string name, out int dispId) /// This can invoke a method or a property get accessor. /// public static object Invoke(object obj, int dispId, object[] args) + { + object result = Invoke(obj, dispId, args, null); + return result; + } + + /// + /// Invokes a member by DISPID. + /// + /// An object that implements IDispatch. + /// The DISPID of a member. This can be obtained using + /// . + /// The arguments to pass to the member. + /// ByRef modifiers + /// The member's return value. + /// + /// This can invoke a method or a property get accessor. + /// + public static object Invoke(object obj, int dispId, object[] args, ParameterModifier[] modifiers) { string memberName = "[DispId=" + dispId + "]"; - object result = Invoke(obj, memberName, args); + object result = Invoke(obj, memberName, args, modifiers); return result; } @@ -119,12 +132,12 @@ public static object Invoke(object obj, int dispId, object[] args) /// /// This can invoke a method or a property get accessor. /// - public static object Invoke(object obj, string memberName, object[] args) + public static object Invoke(object obj, string memberName, object[] args, ParameterModifier[] modifiers) { RequireReference(obj, "obj"); Type type = obj.GetType(); object result = type.InvokeMember(memberName, BindingFlags.InvokeMethod | BindingFlags.GetProperty, - null, obj, args, null); + null, obj, args, modifiers, null, null); return result; } diff --git a/src/ScriptEngine/Machine/Rcw/IDispatchInfo.cs b/src/OneScript.Core/Rcw/IDispatchInfo.cs similarity index 96% rename from src/ScriptEngine/Machine/Rcw/IDispatchInfo.cs rename to src/OneScript.Core/Rcw/IDispatchInfo.cs index 3cbee5a58..4db57edab 100644 --- a/src/ScriptEngine/Machine/Rcw/IDispatchInfo.cs +++ b/src/OneScript.Core/Rcw/IDispatchInfo.cs @@ -9,7 +9,7 @@ This Source Code Form is subject to the terms of the using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; -namespace ScriptEngine.Machine.Rcw +namespace OneScript.Rcw { /// /// A partial declaration of IDispatch used to lookup Type information and DISPIDs. @@ -47,11 +47,8 @@ public interface IDispatchInfo /// /// http://msdn.microsoft.com/en-us/library/cc1ec9aa-6c40-4e70-819c-a7c6dd6b8c99(VS.85) /// -#if NETSTANDARD - void GetTypeInfo(int typeInfoIndex, int lcid, out Type typeInfo); -#else void GetTypeInfo(int typeInfoIndex, int lcid, out ITypeInfo typeInfo); -#endif + /// /// Gets the DISPID of the specified member name. /// diff --git a/src/OneScript.Core/Rcw/RcwMemberMetadata.cs b/src/OneScript.Core/Rcw/RcwMemberMetadata.cs new file mode 100644 index 000000000..2aff2f090 --- /dev/null +++ b/src/OneScript.Core/Rcw/RcwMemberMetadata.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace OneScript.Rcw +{ + public abstract class RcwMemberMetadata + { + public int DispatchId { get; } + + public string Name { get; } + + protected RcwMemberMetadata(string name, int dispId) + { + Name = name; + DispatchId = dispId; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Rcw/RcwMembersMetadataCollection.cs b/src/OneScript.Core/Rcw/RcwMembersMetadataCollection.cs new file mode 100644 index 000000000..5e7825485 --- /dev/null +++ b/src/OneScript.Core/Rcw/RcwMembersMetadataCollection.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace OneScript.Rcw +{ + public class RcwMembersMetadataCollection where T : RcwMemberMetadata + { + private readonly List _collection = new List(); + private readonly Dictionary _indexByDispId = new Dictionary(); + private readonly Dictionary _indexByName = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public IReadOnlyDictionary ByDispatchId => _indexByDispId; + + public IReadOnlyDictionary ByName => _indexByName; + + public T this[int index] => _collection[index]; + + public int IndexOf(T item) => _collection.IndexOf(item); + + public void Add(T item) + { + if (_indexByDispId.TryGetValue(item.DispatchId, out var method)) + { + if (method.Name != item.Name) + { + // добавляют метод, который известен нам, с тем же dispId, но который имеет другое имя + _indexByName.Remove(method.Name); // известное нам старое имя этого dispId - инвалидируем + + // добавляемый метод поместим в индекс диспатчей и имен + _indexByDispId[item.DispatchId] = item; + _indexByName[item.Name] = item; + } + else + { + _indexByName[item.Name] = item; + } + + return; + } + + _collection.Add(item); + _indexByDispId.Add(item.DispatchId, item); + _indexByName.Add(item.Name, item); + } + + public bool Contains(T item) => _collection.Contains(item); + + public int Count => _collection.Count; + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Rcw/RcwMetadata.cs b/src/OneScript.Core/Rcw/RcwMetadata.cs similarity index 96% rename from src/ScriptEngine/Machine/Rcw/RcwMetadata.cs rename to src/OneScript.Core/Rcw/RcwMetadata.cs index 7ab5b8056..be04a8e5b 100644 --- a/src/ScriptEngine/Machine/Rcw/RcwMetadata.cs +++ b/src/OneScript.Core/Rcw/RcwMetadata.cs @@ -13,9 +13,8 @@ This Source Code Form is subject to the terms of the using FUNCFLAGS = System.Runtime.InteropServices.ComTypes.FUNCFLAGS; using TYPEATTR = System.Runtime.InteropServices.ComTypes.TYPEATTR; using INVOKEKIND = System.Runtime.InteropServices.ComTypes.INVOKEKIND; -using VARDESC = System.Runtime.InteropServices.ComTypes.VARDESC; -namespace ScriptEngine.Machine.Rcw +namespace OneScript.Rcw { public class RcwMetadata { @@ -60,7 +59,7 @@ private void LoadMetadata(object instance) LoadVars(typeInfo, typeAttr); LoadFuncs(typeInfo, typeAttr); - + typeInfo.ReleaseTypeAttr(ptAttr); } @@ -129,7 +128,7 @@ private void LoadFuncs(ITypeInfo typeInfo, TYPEATTR typeAttr) private RcwPropertyMetadata GetOrAddProperty(string propName, int dispId) { - if (Properties.Names.TryGetValue(propName, out var md)) return md; + if (Properties.ByName.TryGetValue(propName, out var md)) return md; md = new RcwPropertyMetadata(propName, dispId); Properties.Add(md); diff --git a/src/OneScript.Core/Rcw/RcwMethodMetadata.cs b/src/OneScript.Core/Rcw/RcwMethodMetadata.cs new file mode 100644 index 000000000..53f806010 --- /dev/null +++ b/src/OneScript.Core/Rcw/RcwMethodMetadata.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +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.Rcw +{ + public class RcwMethodMetadata : RcwMemberMetadata + { + public bool? IsFunction { get; } + + public RcwMethodMetadata(string name, int dispId, bool? isFunc) : base(name, dispId) + { + IsFunction = isFunc; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Rcw/RcwPropertyMetadata.cs b/src/OneScript.Core/Rcw/RcwPropertyMetadata.cs new file mode 100644 index 000000000..b2790a3e1 --- /dev/null +++ b/src/OneScript.Core/Rcw/RcwPropertyMetadata.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace OneScript.Rcw +{ + public class RcwPropertyMetadata : RcwMemberMetadata + { + public bool IsReadable { get; internal set; } + + public bool IsWritable { get; internal set; } + + public RcwPropertyMetadata(string name, int dispId) : base(name, dispId) + { + IsReadable = true; + IsWritable = true; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Sources/EmptyModule.cs b/src/OneScript.Core/Sources/EmptyModule.cs new file mode 100644 index 000000000..5c5be93bd --- /dev/null +++ b/src/OneScript.Core/Sources/EmptyModule.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using OneScript.Contexts; +using OneScript.Execution; + +namespace OneScript.Sources +{ + public sealed class EmptyModule : IExecutableModule + { + public static readonly IExecutableModule Instance = new EmptyModule(); + + private EmptyModule() + { + ModuleBody = BslScriptMethodInfo.Create(); + Source = SourceCodeBuilder.Create().FromString("").Build(); + } + + public IList ModuleAttributes => Array.Empty(); + public IList Fields => Array.Empty(); + + public IList Properties => Array.Empty(); + public IList Methods => Array.Empty(); + public BslScriptMethodInfo ModuleBody { get; } + public SourceCode Source { get; } + + public IDictionary Interfaces => new ReadOnlyDictionary(new Dictionary()); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Sources/FileCodeSource.cs b/src/OneScript.Core/Sources/FileCodeSource.cs new file mode 100644 index 000000000..64abaa918 --- /dev/null +++ b/src/OneScript.Core/Sources/FileCodeSource.cs @@ -0,0 +1,69 @@ +/*---------------------------------------------------------- +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.IO; +using System.Text; +using OneScript.Commons; +using System; +using OneScript.Language.Sources; + +namespace OneScript.Sources +{ + public class FileCodeSource : ICodeSource + { + private readonly string _path; + private readonly Encoding _noBomEncoding; + + public FileCodeSource(string path, Encoding defaultEncoding) + { + _path = Path.GetFullPath(path); + _noBomEncoding = defaultEncoding; + } + + public FileCodeSource(string path) + { + _path = path; + _noBomEncoding = Encoding.UTF8; + } + + public string GetSourceCode() + { + using (var fStream = new FileStream(_path, FileMode.Open, FileAccess.Read)) + { + var buf = new byte[2]; + fStream.Read(buf, 0, 2); + Encoding enc; + var skipShebang = false; + if (IsLinuxScript(buf)) + { + enc = Encoding.UTF8; // скрипты с shebang считать в формате UTF-8 + skipShebang = true; + } + else + { + fStream.Position = 0; + enc = FileOpener.AssumeEncoding(fStream, _noBomEncoding); + } + + using (var reader = new StreamReader(fStream, enc)) + { + if (skipShebang) + reader.ReadLine(); + + return reader.ReadToEnd(); + } + } + } + + private static bool IsLinuxScript(byte[] buf) + { + return buf[0] == '#' && buf[1] == '!'; + } + + public string Location => string.Concat(_path[0].ToString().ToUpperInvariant(), _path.AsSpan(1)); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Sources/SourceCodeBuilderExtensions.cs b/src/OneScript.Core/Sources/SourceCodeBuilderExtensions.cs new file mode 100644 index 000000000..f40d22e78 --- /dev/null +++ b/src/OneScript.Core/Sources/SourceCodeBuilderExtensions.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +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.Text; + +namespace OneScript.Sources +{ + public static class SourceCodeBuilderExtensions + { + public static SourceCodeBuilder FromString(this SourceCodeBuilder builder, string code) + { + builder.FromSource(new StringCodeSource(code)); + return builder; + } + + public static SourceCodeBuilder FromFile(this SourceCodeBuilder builder, string path) + { + builder.FromSource(new FileCodeSource(path)); + return builder; + } + + public static SourceCodeBuilder FromFile(this SourceCodeBuilder builder, string path, Encoding encoding) + { + builder.FromSource(new FileCodeSource(path, encoding)); + return builder; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Sources/StringCodeSource.cs b/src/OneScript.Core/Sources/StringCodeSource.cs new file mode 100644 index 000000000..c274a5464 --- /dev/null +++ b/src/OneScript.Core/Sources/StringCodeSource.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.Sources; + +namespace OneScript.Sources +{ + public class StringCodeSource : ICodeSource + { + private readonly string _src; + + public StringCodeSource(string src) + { + _src = src; + } + + public string Location => $"" ; + + public string GetSourceCode() + { + return _src; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/TypeUtils.cs b/src/OneScript.Core/TypeUtils.cs new file mode 100644 index 000000000..c742a45ac --- /dev/null +++ b/src/OneScript.Core/TypeUtils.cs @@ -0,0 +1,44 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Values; + +namespace OneScript +{ + public static class TypeUtils + { + public static bool IsNumeric(this Type type) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Int32: + return !type.IsEnum; + case TypeCode.Decimal: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.Int16: + case TypeCode.Double: + case TypeCode.Single: + return true; + default: + return false; + } + } + + public static bool IsValue(this Type type) => typeof(BslValue).IsAssignableFrom(type); + + public static bool IsContext(this Type type) => typeof(IRuntimeContextInstance).IsAssignableFrom(type); + + public static bool IsObjectValue(this Type type) => typeof(BslObjectValue).IsAssignableFrom(type); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Types/BasicTypes.cs b/src/OneScript.Core/Types/BasicTypes.cs new file mode 100644 index 000000000..03118a38e --- /dev/null +++ b/src/OneScript.Core/Types/BasicTypes.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Values; + +namespace OneScript.Types +{ + public static class BasicTypes + { + public static TypeDescriptor Number { get; } + public static TypeDescriptor String { get; } + public static TypeDescriptor Date { get; } + public static TypeDescriptor Boolean { get; } + public static TypeDescriptor Undefined { get; } + public static TypeDescriptor Type { get; } + public static TypeDescriptor Null { get; } + + /// + /// Тип, устанавливаемый объектам, которые не задали свой тип + /// Если значение этого типа возвращается функцией ТипЗнч - это ошибка автора объекта. + /// + public static TypeDescriptor UnknownType { get; } + + static BasicTypes() + { + Number = new TypeDescriptor(typeof(BslNumericValue), "Число", "Number"); + String = new TypeDescriptor(typeof(BslStringValue), "Строка", "String"); + Date = new TypeDescriptor(typeof(BslDateValue), "Дата", "Date"); + Boolean = new TypeDescriptor(typeof(BslBooleanValue), "Булево", "Boolean"); + Undefined = new TypeDescriptor(typeof(BslUndefinedValue), "Неопределено", "Undefined"); + Type = new TypeDescriptor(typeof(BslTypeValue), "Тип", "Type"); + Null = new TypeDescriptor(typeof(BslNullValue), "Null", "Null"); + UnknownType = new TypeDescriptor(typeof(BslValue), "$UnknownType$", "$UnknownType$"); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Types/ITypeFactory.cs b/src/OneScript.Core/Types/ITypeFactory.cs new file mode 100644 index 000000000..8b11274b6 --- /dev/null +++ b/src/OneScript.Core/Types/ITypeFactory.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/. +----------------------------------------------------------*/ + +using ScriptEngine.Machine; + +namespace OneScript.Types +{ + public interface ITypeFactory + { + //todo сейчас работает в лоб, через текущую фабрику. Сам не вызывает статически методы-конструкторы + + IValue Activate(TypeActivationContext context, IValue[] arguments); + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Types/ITypeManager.cs b/src/OneScript.Core/Types/ITypeManager.cs new file mode 100644 index 000000000..79b21490a --- /dev/null +++ b/src/OneScript.Core/Types/ITypeManager.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace OneScript.Types +{ + public interface ITypeManager + { + TypeDescriptor GetTypeByName(string name); + TypeDescriptor GetTypeByFrameworkType(Type type); + + bool TryGetType(string name, out TypeDescriptor type); + bool TryGetType(Type frameworkType, out TypeDescriptor type); + + TypeDescriptor RegisterType(string name, string alias, Type implementingClass); + + void RegisterType(TypeDescriptor typeDescriptor); + + ITypeFactory GetFactoryFor(TypeDescriptor type); + + bool IsKnownType(Type type); + bool IsKnownType(string typeName); + + IReadOnlyList RegisteredTypes(); + + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Types/TypeActivationContext.cs b/src/OneScript.Core/Types/TypeActivationContext.cs new file mode 100644 index 000000000..7ae2b7147 --- /dev/null +++ b/src/OneScript.Core/Types/TypeActivationContext.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.DependencyInjection; +using OneScript.Execution; + +namespace OneScript.Types +{ + public struct TypeActivationContext + { + public string TypeName { get; set; } + + public ITypeManager TypeManager { get; set; } + + public IBslProcess CurrentProcess { get; set; } + + public IServiceContainer Services { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Types/TypeDescriptor.cs b/src/OneScript.Core/Types/TypeDescriptor.cs new file mode 100644 index 000000000..a3fff44ed --- /dev/null +++ b/src/OneScript.Core/Types/TypeDescriptor.cs @@ -0,0 +1,80 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Localization; +using OneScript.Values; + +namespace OneScript.Types +{ + public sealed class TypeDescriptor : IEquatable + { + [Obsolete("Use string id version")] + public TypeDescriptor(Guid id, string typeName, string alias = default, Type implementingClass = default) + : this(id.ToString(), typeName, alias, implementingClass) + { + } + + public TypeDescriptor(Type implementingClass, string typeName, string alias = default) + : this($"{typeName}@{implementingClass.Name}", typeName, alias, implementingClass) + { + } + + public TypeDescriptor(String id, string typeName, string alias = default, Type implementingClass = default) + { + Id = id; + Name = typeName; + Alias = alias; + ImplementingClass = implementingClass ?? typeof(BslValue); + } + + public string Name { get; } + + public string Alias { get; } + + public string Id { get; } + + public Type ImplementingClass { get; } + + public override string ToString() + { + return BilingualString.UseRussianLocale || Alias == default? Name : Alias; + } + + public bool Equals(TypeDescriptor other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Id.Equals(other.Id); + } + + public override bool Equals(object obj) + { + return ReferenceEquals(this, obj) || obj is TypeDescriptor other && Equals(other); + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + private static bool StaticEquals(TypeDescriptor left, TypeDescriptor right) + { + return left?.Equals(right) ?? ReferenceEquals(right, null); + } + + public static bool operator ==(TypeDescriptor left, TypeDescriptor right) + { + return StaticEquals(left, right); + } + + public static bool operator !=(TypeDescriptor left, TypeDescriptor right) + { + return !StaticEquals(left, right); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslAnnotationValue.cs b/src/OneScript.Core/Values/BslAnnotationValue.cs new file mode 100644 index 000000000..88b89b997 --- /dev/null +++ b/src/OneScript.Core/Values/BslAnnotationValue.cs @@ -0,0 +1,58 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Localization; +using OneScript.Types; +using System.Collections.Generic; +using System.Text; + +namespace OneScript.Values +{ + public sealed class BslAnnotationValue : BslPrimitiveValue + { + public BslAnnotationValue(string name) { + Name = name; + } + + public string Name { get; } + + public List Parameters { get; } = new List(); + + + public override int CompareTo(BslValue other) { + var msg = new BilingualString("Сравнение на больше/меньше для данного типа не поддерживается", + "Comparison for less/greater is not supported for this type"); + + throw new RuntimeException(msg); + } + + public override bool Equals(BslValue other) { + return ReferenceEquals(this, other); + } + + public override string ToString() + { + var sb = new StringBuilder("&"); + sb.Append(Name); + if (Parameters.Count != 0) + { + var prefix = "("; + foreach (var parameter in Parameters) + { + sb.Append(prefix); + sb.Append(parameter); + prefix = ","; + } + sb.Append(")"); + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslBooleanValue.cs b/src/OneScript.Core/Values/BslBooleanValue.cs new file mode 100644 index 000000000..c87ccd889 --- /dev/null +++ b/src/OneScript.Core/Values/BslBooleanValue.cs @@ -0,0 +1,85 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Exceptions; +using OneScript.Localization; +using OneScript.Types; + +namespace OneScript.Values +{ + public sealed class BslBooleanValue : BslPrimitiveValue, IBslComparable + { + public static readonly BslBooleanValue True = new BslBooleanValue(true); + public static readonly BslBooleanValue False = new BslBooleanValue(false); + + private static readonly BilingualString _stringTrue = new BilingualString("Да", "True"); + private static readonly BilingualString _stringFalse = new BilingualString("Нет", "False"); + + private readonly bool _flag; + + private BslBooleanValue(bool flag) + { + _flag = flag; + } + + public override TypeDescriptor SystemType => BasicTypes.Boolean; + + public static explicit operator decimal(BslBooleanValue value) + { + return value._flag ? 1 : 0; + } + + public static explicit operator bool(BslBooleanValue value) => value._flag; + + public static BslBooleanValue Parse(string presentation) + { + if (String.Compare(presentation, "истина", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(presentation, "true", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(presentation, "да", StringComparison.OrdinalIgnoreCase) == 0) + return True; + else if (String.Compare(presentation, "ложь", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(presentation, "false", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(presentation, "нет", StringComparison.OrdinalIgnoreCase) == 0) + return False; + else + throw BslExceptions.ConvertToBooleanException(); + } + + public override string ToString() + { + return _flag? _stringTrue.ToString() : _stringFalse.ToString(); + } + + public override bool Equals(BslValue other) + { + if (ReferenceEquals(this, other)) return true; + + return other switch + { + BslNumericValue num => num == ((decimal)this), + BslBooleanValue boolean => _flag == boolean._flag, + _ => false + }; + } + + public override int CompareTo(BslValue other) + { + if (other is BslNumericValue || other is BslBooleanValue) + { + return ((decimal)this).CompareTo((decimal)other); + } + + return base.CompareTo(other); + } + + public static BslValue Create(bool boolean) + { + return boolean ? True : False; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslDateValue.cs b/src/OneScript.Core/Values/BslDateValue.cs new file mode 100644 index 000000000..91ac459a0 --- /dev/null +++ b/src/OneScript.Core/Values/BslDateValue.cs @@ -0,0 +1,174 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Globalization; +using OneScript.Exceptions; +using OneScript.Types; + +namespace OneScript.Values +{ + public sealed class BslDateValue : BslPrimitiveValue, IBslComparable + { + private readonly DateTime _value; + + private BslDateValue(DateTime value) + { + _value = value; + } + + public static BslDateValue Create(DateTime value) => new BslDateValue(value); + + public override int CompareTo(BslValue other) + { + if(other is BslDateValue d) + return _value.CompareTo(d._value); + + return base.CompareTo(other); + } + + public override bool Equals(BslValue other) + { + if (other == null) + return false; + + if(other is BslDateValue d) + return _value.Equals(d._value); + + return base.Equals(other); + } + + public override int GetHashCode() => _value.GetHashCode(); + + public override bool Equals(object obj) + { + if (obj is BslValue bsl) + return Equals(bsl); + + return false; + } + + public override TypeDescriptor SystemType => BasicTypes.Date; + + #region Conversions + + public static explicit operator DateTime(BslDateValue date) => date._value; + + public override string ToString() + { + return _value.ToString(CultureInfo.CurrentCulture); + } + + #endregion + + #region Date Arithmetics + + public static DateTime operator +(BslDateValue left, decimal right) + { + return left._value.AddSeconds((double) right); + } + + public static DateTime operator -(BslDateValue left, decimal right) + { + return left._value.AddSeconds(-(double) right); + } + + public static decimal operator -(BslDateValue left, DateTime right) + { + var span = left._value - right; + return (decimal) span.TotalSeconds; + } + + public static decimal operator -(BslDateValue left, BslDateValue right) + { + var span = left._value - right._value; + return (decimal) span.TotalSeconds; + } + + #endregion + + #region Date Comparisons + + public static bool operator >(BslDateValue left, DateTime right) + { + return left._value > right; + } + + public static bool operator <(BslDateValue left, DateTime right) + { + return left._value < right; + } + + public static bool operator >=(BslDateValue left, DateTime right) + { + return left._value >= right; + } + + public static bool operator <=(BslDateValue left, DateTime right) + { + return left._value <= right; + } + + public static bool operator >(DateTime left, BslDateValue right) + { + return left > right._value; + } + + public static bool operator <(DateTime left, BslDateValue right) + { + return left < right._value; + } + + public static bool operator >=(DateTime left, BslDateValue right) + { + return left >= right._value; + } + + public static bool operator <=(DateTime left, BslDateValue right) + { + return left <= right._value; + } + + #endregion + + public static BslDateValue Parse(string presentation) + { + BslDateValue result; + string format; + if (presentation.Length == 14) + format = "yyyyMMddHHmmss"; + else if (presentation.Length == 8) + format = "yyyyMMdd"; + else if (presentation.Length == 12) + format = "yyyyMMddHHmm"; + else + throw BslExceptions.ConvertToDateException(); + + if (presentation == "00000000" + || presentation == "000000000000" + || presentation == "00000000000000") + { + result = new BslDateValue(new DateTime()); + } + else + { + try + { + var date = DateTime.ParseExact(presentation, format, + System.Globalization.CultureInfo.InvariantCulture); + result = new BslDateValue(date); + } + catch (FormatException) + { + throw BslExceptions.ConvertToDateException(); + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslNullValue.cs b/src/OneScript.Core/Values/BslNullValue.cs new file mode 100644 index 000000000..8cda6f263 --- /dev/null +++ b/src/OneScript.Core/Values/BslNullValue.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Types; + +namespace OneScript.Values +{ + public sealed class BslNullValue : BslPrimitiveValue + { + public static BslNullValue Instance { get; } = new BslNullValue(); + + private BslNullValue() + { + } + + public override TypeDescriptor SystemType => BasicTypes.Null; + + public override bool Equals(BslValue other) + { + return ReferenceEquals(Instance, other); + } + + public override string ToString() => string.Empty; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslNumericValue.cs b/src/OneScript.Core/Values/BslNumericValue.cs new file mode 100644 index 000000000..8d3464fd0 --- /dev/null +++ b/src/OneScript.Core/Values/BslNumericValue.cs @@ -0,0 +1,336 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Dynamic; +using System.Globalization; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; + +namespace OneScript.Values +{ + public sealed class BslNumericValue : BslPrimitiveValue, IEquatable, IBslComparable + { + private static readonly BslNumericValue[] _popularValues = new BslNumericValue[10]; + static BslNumericValue() + { + for (int i = 0; i < 10; i++) + { + _popularValues[i] = new BslNumericValue(i); + } + } + + + private readonly decimal _value; + + private BslNumericValue(decimal value) + { + _value = value; + } + + public static BslNumericValue Create(decimal value) + { + switch (value) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return _popularValues[(int)value]; + default: + return new BslNumericValue(value); + } + } + + public override TypeDescriptor SystemType => BasicTypes.Number; + + public override bool TryConvert(ConvertBinder binder, out object result) + { + if (!binder.Type.IsNumeric()) + { + if (binder.Type == typeof(string)) + { + result = ToString(ForbiddenBslProcess.Instance); + } + throw new InvalidOperationException($"Conversion from Number to {binder.Type} is not supported"); + } + + result = Convert.ToDecimal(_value); + return true; + } + + public override string ToString() + { + return _value.ToString(NumberFormatInfo.InvariantInfo); + } + + public bool Equals(BslNumericValue other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return _value == other._value; + } + + public override bool Equals(BslValue other) + { + if (ReferenceEquals(this, other)) return true; + + return other switch + { + BslNumericValue num => Equals(num), + BslBooleanValue boolean => _value == (decimal) boolean, + _ => false + }; + } + + public override int CompareTo(BslValue other) + { + return other switch + { + BslNumericValue num => _value.CompareTo(num._value), + BslBooleanValue boolean => _value.CompareTo((decimal) boolean), + _ => base.CompareTo(other) + }; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((BslNumericValue) obj); + } + + public override int GetHashCode() + { + return _value.GetHashCode(); + } + + #region Arithmetics for BslNumeric on Right + + public static decimal operator +(decimal left, BslNumericValue right) + { + return left + right._value; + } + + public static decimal operator -(decimal left, BslNumericValue right) + { + return left - right._value; + } + + public static decimal operator *(decimal left, BslNumericValue right) + { + return left * right._value; + } + + public static decimal operator /(decimal left, BslNumericValue right) + { + return left / right._value; + } + + public static decimal operator %(decimal left, BslNumericValue right) + { + return left % right._value; + } + + #endregion + + #region Arithmetics for BslNumeric on Left + + public static decimal operator +(BslNumericValue left, decimal right) + { + return left._value + right; + } + + public static decimal operator -(BslNumericValue left, decimal right) + { + return left._value - right; + } + + public static decimal operator *(BslNumericValue left, decimal right) + { + return left._value * right; + } + + public static decimal operator /(BslNumericValue left, decimal right) + { + return left._value / right; + } + + public static decimal operator %(BslNumericValue left, decimal right) + { + return left._value % right; + } + + #endregion + + #region Arithmetics for both BslNumeric + + public static decimal operator +(BslNumericValue left, BslNumericValue right) + { + return left._value + right._value; + } + + public static decimal operator -(BslNumericValue left, BslNumericValue right) + { + return left._value - right._value; + } + + public static decimal operator *(BslNumericValue left, BslNumericValue right) + { + return left._value * right._value; + } + + public static decimal operator /(BslNumericValue left, BslNumericValue right) + { + return left._value / right._value; + } + + public static decimal operator %(BslNumericValue left, BslNumericValue right) + { + return left._value % right._value; + } + + #endregion + + #region Unary operators + + public static decimal operator -(BslNumericValue number) + { + return -number._value; + } + + public static decimal operator +(BslNumericValue number) + { + return number._value; + } + + public static explicit operator bool(BslNumericValue numVal) + { + return numVal._value != 0; + } + + public static implicit operator decimal(BslNumericValue numVal) + { + return numVal._value; + } + + public static implicit operator int(BslNumericValue numVal) + { + return (int)numVal._value; + } + + public static implicit operator long(BslNumericValue numVal) + { + return (long)numVal._value; + } + + public static explicit operator BslNumericValue(decimal value) + { + return Create(value); + } + + public static explicit operator BslNumericValue(int value) + { + return Create(value); + } + + public static explicit operator BslNumericValue(double value) + { + return Create((decimal)value); + } + + #endregion + + public static bool operator ==(BslNumericValue left, BslNumericValue right) + { + return left?.Equals(right) ?? ReferenceEquals(right, null); + } + + public static bool operator !=(BslNumericValue left, BslNumericValue right) + { + if (ReferenceEquals(left, null)) + return ReferenceEquals(right, null); + + return !left.Equals(right); + } + + public static bool operator ==(BslNumericValue left, decimal right) + { + if (ReferenceEquals(left, null)) + return right == 0; + + return left._value == right; + } + + public static bool operator !=(BslNumericValue left, decimal right) + { + if (ReferenceEquals(left, null)) + return right != 0; + + return left._value != right; + } + + public static bool operator >(BslNumericValue left, decimal right) + { + if (ReferenceEquals(left, null)) + return right > 0; + + return left._value > right; + } + + public static bool operator <(BslNumericValue left, decimal right) + { + if (ReferenceEquals(left, null)) + return right < 0; + + return left._value < right; + } + + public static bool operator >=(BslNumericValue left, decimal right) + { + if (ReferenceEquals(left, null)) + return right >= 0; + + return left._value >= right; + } + + public static bool operator <=(BslNumericValue left, decimal right) + { + if (ReferenceEquals(left, null)) + return right <= 0; + + return left._value <= right; + } + + public static BslNumericValue Parse(string presentation) + { + var numInfo = NumberFormatInfo.InvariantInfo; + var numStyle = NumberStyles.AllowDecimalPoint + |NumberStyles.AllowLeadingSign + |NumberStyles.AllowLeadingWhite + |NumberStyles.AllowTrailingWhite; + + try + { + var number = decimal.Parse(presentation, numStyle, numInfo); + return new BslNumericValue(number); + } + catch (FormatException) + { + throw BslExceptions.ConvertToNumberException(); + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslObjectValue.cs b/src/OneScript.Core/Values/BslObjectValue.cs new file mode 100644 index 000000000..e94171f09 --- /dev/null +++ b/src/OneScript.Core/Values/BslObjectValue.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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.Values +{ + public abstract class BslObjectValue : BslValue + { + public override bool Equals(BslValue other) + { + return ReferenceEquals(this, other); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslPrimitiveValue.cs b/src/OneScript.Core/Values/BslPrimitiveValue.cs new file mode 100644 index 000000000..33a990ade --- /dev/null +++ b/src/OneScript.Core/Values/BslPrimitiveValue.cs @@ -0,0 +1,14 @@ +/*---------------------------------------------------------- +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.Values +{ + public abstract class BslPrimitiveValue : BslValue + { + public override bool Equals(BslValue other) => false; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslSkippedParameterValue.cs b/src/OneScript.Core/Values/BslSkippedParameterValue.cs new file mode 100644 index 000000000..00e6a9503 --- /dev/null +++ b/src/OneScript.Core/Values/BslSkippedParameterValue.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/. +----------------------------------------------------------*/ + +namespace OneScript.Values +{ + public sealed class BslSkippedParameterValue : BslPrimitiveValue + { + public static BslSkippedParameterValue Instance { get; } = new BslSkippedParameterValue(); + + private BslSkippedParameterValue() + { + } + + public override bool Equals(BslValue other) + { + return ReferenceEquals(Instance, other); + } + + public override string ToString() => string.Empty; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslStringValue.cs b/src/OneScript.Core/Values/BslStringValue.cs new file mode 100644 index 000000000..6dbbd86e0 --- /dev/null +++ b/src/OneScript.Core/Values/BslStringValue.cs @@ -0,0 +1,69 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Types; + +namespace OneScript.Values +{ + public sealed class BslStringValue : BslPrimitiveValue, IBslComparable + { + private readonly string _value; + + public static BslStringValue Empty { get; } = new BslStringValue(String.Empty); + + public static BslStringValue Create(string value) + { + return value == string.Empty ? Empty : new BslStringValue(value); + } + + private BslStringValue(string value) + { + _value = value ?? throw new ArgumentNullException(); + } + + public override TypeDescriptor SystemType => BasicTypes.String; + + public override string ToString() => _value; + + public static explicit operator decimal(BslStringValue value) => BslNumericValue.Parse(value._value); + + public static implicit operator string(BslStringValue value) => value._value; + + public static explicit operator DateTime(BslStringValue value) => (DateTime)BslDateValue.Parse(value._value); + + public static explicit operator bool(BslStringValue value) => (bool)BslBooleanValue.Parse(value._value); + + public static string operator +(BslStringValue value, object other) => value._value + other; + + public override bool Equals(BslValue other) + { + return other is BslStringValue sv ? _value.Equals(sv._value) : base.Equals(other); + } + + public override int GetHashCode() => _value.GetHashCode(); + + public override bool Equals(object obj) + { + if (obj is BslValue bsl) + return Equals(bsl); + + return false; + } + + public override int CompareTo(BslValue other) + { + if (ReferenceEquals(this, other)) + return 0; + + if (other is BslStringValue s) + return String.Compare(_value, s._value, StringComparison.InvariantCulture); + + return base.CompareTo(other); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslTypeValue.cs b/src/OneScript.Core/Values/BslTypeValue.cs new file mode 100644 index 000000000..dab5b65a0 --- /dev/null +++ b/src/OneScript.Core/Values/BslTypeValue.cs @@ -0,0 +1,55 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Types; + +namespace OneScript.Values +{ + public sealed class BslTypeValue : BslPrimitiveValue + { + private readonly TypeDescriptor _type; + + public BslTypeValue(TypeDescriptor type) + { + _type = type; + } + + public static BslTypeValue Create(TypeDescriptor value) => new BslTypeValue(value); + + public override TypeDescriptor SystemType => BasicTypes.Type; + + public override string ToString() + { + return _type.ToString(); + } + + public override bool Equals(BslValue other) + { + if(other?.SystemType == BasicTypes.Type) + { + var otherVal = other.GetRawValue() as BslTypeValue; + return otherVal._type == this._type; + } + + return false; + } + + public override bool Equals(object obj) + { + if (obj is BslTypeValue value) + return Equals(value); + return false; + } + + public override int GetHashCode() + { + return _type.GetHashCode(); + } + + public TypeDescriptor TypeValue => _type; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslUndefinedValue.cs b/src/OneScript.Core/Values/BslUndefinedValue.cs new file mode 100644 index 000000000..95324e00a --- /dev/null +++ b/src/OneScript.Core/Values/BslUndefinedValue.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Types; + +namespace OneScript.Values +{ + public sealed class BslUndefinedValue : BslPrimitiveValue + { + public static BslUndefinedValue Instance { get; } = new BslUndefinedValue(); + + private BslUndefinedValue() + { + } + + public override TypeDescriptor SystemType => BasicTypes.Undefined; + + public override bool Equals(BslValue other) + { + return ReferenceEquals(Instance, other); + } + + public override int GetHashCode() + { + return 42; // Константа для синглтона + } + + public override string ToString() => string.Empty; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/BslValue.cs b/src/OneScript.Core/Values/BslValue.cs new file mode 100644 index 000000000..14d1596b9 --- /dev/null +++ b/src/OneScript.Core/Values/BslValue.cs @@ -0,0 +1,112 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Dynamic; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using ScriptEngine.Machine; + +namespace OneScript.Values +{ + public abstract class BslValue : DynamicObject, IComparable, IEquatable, IValue + { + /// + /// Bsl-представление объекта. Имеет смысл переопределять, если представление при вызове + /// из BSL должно отличаться от стандартного метода ToString, либо, если формирование + /// представления требует выполнения Bsl-кода. + /// + /// Текущий процесс, в котором вызван данный метод + public virtual string ToString(IBslProcess process) + { + return ToString(); + } + + public virtual int CompareTo(BslValue other) + { + if (other is null) + return -1; + + string typeOfThis = null; + string typeOfOther = null; + + try + { + typeOfThis = this.SystemType.Name; + typeOfOther = other.SystemType.Name; + } + catch (InvalidOperationException) // если тип не зарегистрирован + { + typeOfThis ??= this.GetType().ToString(); + typeOfOther ??= other.GetType().ToString(); + } + + if (typeOfThis == typeOfOther) + throw ComparisonException.NotSupported(typeOfThis); + else + throw ComparisonException.NotSupported(typeOfThis, typeOfOther); + } + + public abstract bool Equals(BslValue other); + + public static explicit operator bool(BslValue target) => + target switch + { + BslBooleanValue v => (bool) v, + BslNumericValue nv => (bool) nv, + BslStringValue sv => (bool) sv, + _ => throw BslExceptions.ConvertToBooleanException() + }; + + public static explicit operator decimal(BslValue target) => + target switch + { + BslNumericValue v => (decimal) v, + BslStringValue sv => (decimal) sv, + BslBooleanValue bv => (decimal) bv, + _ => throw BslExceptions.ConvertToNumberException(target) + }; + + public static explicit operator int(BslValue target) => + target switch + { + BslNumericValue v => (int) (decimal) v, + BslStringValue sv => (int) (decimal) sv, + BslBooleanValue bv => (int) (decimal) bv, + _ => throw BslExceptions.ConvertToNumberException() + }; + + public static explicit operator DateTime(BslValue target) => + target switch + { + BslDateValue v => (DateTime) v, + BslStringValue sv => (DateTime) sv, + _ => throw BslExceptions.ConvertToDateException() + }; + + #region Stack Runtime Bridge + + public virtual TypeDescriptor SystemType => BasicTypes.UnknownType; + + public int CompareTo(IValue other) => CompareTo(UnwrapReference(other)); + + public bool Equals(IValue other) => Equals(UnwrapReference(other)); + + public virtual IValue GetRawValue() => this; + + private static BslValue UnwrapReference(IValue v) + { + if (v is IValueReference r) + return r.BslValue; + + return (BslValue)v; + } + + #endregion + } +} diff --git a/src/OneScript.Core/Values/ClrEnumValueWrapper.cs b/src/OneScript.Core/Values/ClrEnumValueWrapper.cs new file mode 100644 index 000000000..95970adf1 --- /dev/null +++ b/src/OneScript.Core/Values/ClrEnumValueWrapper.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Commons; +using OneScript.Types; + +namespace OneScript.Values +{ + public class ClrEnumValueWrapper : EnumerationValue, IObjectWrapper where T :struct + { + private readonly T _realValue; + + public ClrEnumValueWrapper(TypeDescriptor systemType, T realValue, string name, string alias) + : base (systemType, name, alias) + { + _realValue = realValue; + } + + public object UnderlyingObject => _realValue; + + public T UnderlyingValue => _realValue; + + public override bool Equals(BslValue other) + { + if (!(other?.GetRawValue() is ClrEnumValueWrapper otherWrapper)) + return false; + + return UnderlyingValue.Equals(otherWrapper.UnderlyingValue); + } + + public bool Equals(ClrEnumValueWrapper otherWrapper) + { + return UnderlyingValue.Equals(otherWrapper.UnderlyingValue); + } + } +} diff --git a/src/OneScript.Core/Values/EnumerationValue.cs b/src/OneScript.Core/Values/EnumerationValue.cs new file mode 100644 index 000000000..5a3f324e0 --- /dev/null +++ b/src/OneScript.Core/Values/EnumerationValue.cs @@ -0,0 +1,49 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Commons; +using OneScript.Localization; +using OneScript.Types; +using System; +using ScriptEngine.Machine; + +namespace OneScript.Values +{ + public abstract class EnumerationValue : BslValue + { + protected EnumerationValue(TypeDescriptor systemType, string name, string alias) + { + if (!Utils.IsValidIdentifier(name)) + throw new ArgumentException("Name must be a valid identifier", nameof(name)); + + if(alias != null && !Utils.IsValidIdentifier(alias)) + throw new ArgumentException("Name must be a valid identifier", nameof(alias)); + + SystemType = systemType; + Name = name; + Alias = alias; + } + + public string Name { get; } + + public string Alias { get; } + + public override TypeDescriptor SystemType { get; } + + public override string ToString() + { + return BilingualString.Localize(Name, Alias); + } + + public override IValue GetRawValue() => this; + + public override bool Equals(BslValue other) + { + return ReferenceEquals(other?.GetRawValue(), this); + } + } +} diff --git a/src/OneScript.Core/Values/IBslComparable.cs b/src/OneScript.Core/Values/IBslComparable.cs new file mode 100644 index 000000000..4075a214b --- /dev/null +++ b/src/OneScript.Core/Values/IBslComparable.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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.Values +{ + /// + /// Интерфейс-маркер для примитивных типов, допускающих сравнение на больше/меньше. + /// Используется GenericIValueComparer для определения возможности сравнения. + /// + public interface IBslComparable + { + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/IEmptyValueCheck.cs b/src/OneScript.Core/Values/IEmptyValueCheck.cs new file mode 100644 index 000000000..63c8f8d42 --- /dev/null +++ b/src/OneScript.Core/Values/IEmptyValueCheck.cs @@ -0,0 +1,14 @@ +/*---------------------------------------------------------- +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.Values +{ + public interface IEmptyValueCheck + { + bool IsEmpty { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/IValue.cs b/src/OneScript.Core/Values/IValue.cs new file mode 100644 index 000000000..2987876a2 --- /dev/null +++ b/src/OneScript.Core/Values/IValue.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Types; + +namespace ScriptEngine.Machine +{ + public interface IValue : IComparable, IEquatable + { + TypeDescriptor SystemType { get; } + } + + public static class IValueExtension + { + public static bool StrictEquals(this IValue value, IValue other) + => value.Equals(other) && value.SystemType == other.SystemType; + } +} \ No newline at end of file diff --git a/src/OneScript.Core/Values/IValueReference.cs b/src/OneScript.Core/Values/IValueReference.cs new file mode 100644 index 000000000..2e419f966 --- /dev/null +++ b/src/OneScript.Core/Values/IValueReference.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using ScriptEngine.Machine; + +namespace OneScript.Values +{ + public interface IValueReference : IEquatable + { + IValue Value { get; set; } + + BslValue BslValue + { + get => (BslValue)Value; + set => Value = value; + } + } +} diff --git a/src/OneScript.Core/Values/IndexedValueReference.cs b/src/OneScript.Core/Values/IndexedValueReference.cs new file mode 100644 index 000000000..2414cbe69 --- /dev/null +++ b/src/OneScript.Core/Values/IndexedValueReference.cs @@ -0,0 +1,53 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Contexts; +using ScriptEngine.Machine; + +namespace OneScript.Values +{ + public class IndexedValueReference : IValueReference + { + private readonly IRuntimeContextInstance _context; + private readonly BslValue _index; + + public IndexedValueReference(IRuntimeContextInstance context, BslValue index) + { + _context = context; + _index = index; + } + + public IValue Value + { + get => (BslValue)_context.GetIndexedValue(_index); + set => _context.SetIndexedValue(_index, value); + } + + public bool Equals(IValueReference other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!(other is IndexedValueReference idxRef)) + return false; + + return Equals(_context, idxRef._context) && Equals(_index, idxRef._index); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((IndexedValueReference)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(_context, _index); + } + } +} diff --git a/src/OneScript.Core/Values/PropertyValueReference.cs b/src/OneScript.Core/Values/PropertyValueReference.cs new file mode 100644 index 000000000..ec3789b28 --- /dev/null +++ b/src/OneScript.Core/Values/PropertyValueReference.cs @@ -0,0 +1,72 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Contexts; +using OneScript.Exceptions; +using ScriptEngine.Machine; + +namespace OneScript.Values +{ + public class PropertyValueReference : IValueReference + { + private readonly IRuntimeContextInstance _context; + private readonly int _contextPropertyNumber; + + public PropertyValueReference(IRuntimeContextInstance context, string propertyName) + { + _context = context; + _contextPropertyNumber = context.GetPropertyNumber(propertyName); + } + + public PropertyValueReference(IRuntimeContextInstance context, int propertyId) + { + _context = context; + _contextPropertyNumber = propertyId; + } + + public bool Equals(IValueReference other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + if (!(other is PropertyValueReference prop)) + return false; + return _context.Equals(prop._context) && _contextPropertyNumber == prop._contextPropertyNumber; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == this.GetType() && Equals((PropertyValueReference)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(_context, _contextPropertyNumber); + } + + public IValue Value + { + get + { + if (_context.IsPropReadable(_contextPropertyNumber)) + return (BslValue)_context.GetPropValue(_contextPropertyNumber); + + throw PropertyAccessException.PropIsNotReadableException(PropertyName()); + } + set + { + if(_context.IsPropWritable(_contextPropertyNumber)) + _context.SetPropValue(_contextPropertyNumber, value); + else + throw PropertyAccessException.PropIsNotWritableException(PropertyName()); + } + } + + private string PropertyName() => _context.GetPropertyInfo(_contextPropertyNumber).Name; + } +} diff --git a/src/OneScript.Core/Values/ValueReference.cs b/src/OneScript.Core/Values/ValueReference.cs new file mode 100644 index 000000000..8e0130bc9 --- /dev/null +++ b/src/OneScript.Core/Values/ValueReference.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using ScriptEngine.Machine; + +namespace OneScript.Values +{ + public class ValueReference : IValueReference + { + private readonly Func _getter; + private readonly Action _setter; + + public ValueReference(Func getter, Action setter) + { + _getter = getter; + _setter = setter; + } + + public IValue Value + { + get => _getter(); + set => _setter(value); + } + + public bool Equals(IValueReference other) + { + return ReferenceEquals(this, other); + } + } +} diff --git a/src/OneScript.DebugProtocol/Abstractions/CommunicationEventArgs.cs b/src/OneScript.DebugProtocol/Abstractions/CommunicationEventArgs.cs index 76874469e..c62fb169b 100644 --- a/src/OneScript.DebugProtocol/Abstractions/CommunicationEventArgs.cs +++ b/src/OneScript.DebugProtocol/Abstractions/CommunicationEventArgs.cs @@ -11,7 +11,7 @@ namespace OneScript.DebugProtocol.Abstractions { public class CommunicationEventArgs : EventArgs { - public ICommunicationChannel Channel { get; set; } + public IMessageChannel Channel { get; set; } public object Data { get; set; } diff --git a/src/OneScript.DebugProtocol/Abstractions/ICommunicationChannel.cs b/src/OneScript.DebugProtocol/Abstractions/ICommunicationChannel.cs deleted file mode 100644 index d449f81cf..000000000 --- a/src/OneScript.DebugProtocol/Abstractions/ICommunicationChannel.cs +++ /dev/null @@ -1,21 +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.DebugProtocol.Abstractions -{ - public interface ICommunicationChannel : IDisposable - { - void Write(object data); - - T Read(); - - object Read(); - - } -} \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/Abstractions/ICommunicationServer.cs b/src/OneScript.DebugProtocol/Abstractions/ICommunicationServer.cs index b964431db..3ecb7f1f1 100644 --- a/src/OneScript.DebugProtocol/Abstractions/ICommunicationServer.cs +++ b/src/OneScript.DebugProtocol/Abstractions/ICommunicationServer.cs @@ -9,6 +9,9 @@ This Source Code Form is subject to the terms of the namespace OneScript.DebugProtocol.Abstractions { + /// + /// Сервис прослушивания сетевого канала и отправки в него сообщений + /// public interface ICommunicationServer { void Start(); @@ -16,5 +19,6 @@ public interface ICommunicationServer void Stop(); event EventHandler DataReceived; + event EventHandler OnError; } } \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/Abstractions/IDebuggerClient.cs b/src/OneScript.DebugProtocol/Abstractions/IDebuggerClient.cs new file mode 100644 index 000000000..8e54657eb --- /dev/null +++ b/src/OneScript.DebugProtocol/Abstractions/IDebuggerClient.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; + +namespace OneScript.DebugProtocol.Abstractions +{ + public interface IDebuggerClient : IDisposable + { + bool Connected { get; } + + Stream GetDataStream(); + } +} \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/Abstractions/IMessageChannel.cs b/src/OneScript.DebugProtocol/Abstractions/IMessageChannel.cs new file mode 100644 index 000000000..6d8c2c816 --- /dev/null +++ b/src/OneScript.DebugProtocol/Abstractions/IMessageChannel.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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.DebugProtocol.Abstractions +{ + public interface IMessageChannel : IDisposable + { + void Write(object data); + + T Read(); + + object Read(); + + bool Connected { get; } + + } +} \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/Binder.cs b/src/OneScript.DebugProtocol/Binder.cs deleted file mode 100644 index 47d70292c..000000000 --- a/src/OneScript.DebugProtocol/Binder.cs +++ /dev/null @@ -1,36 +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/. -----------------------------------------------------------*/ - -#if NETFRAMEWORK -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.Text; - -namespace OneScript.DebugProtocol -{ - public static class Binder - { - public static Uri GetDebuggerUri(int port) - { - var builder = new UriBuilder(); - builder.Scheme = "net.tcp"; - builder.Port = port; - builder.Host = "localhost"; - - return builder.Uri; - } - - public static Binding GetBinding() - { - return new NetTcpBinding(SecurityMode.None); - } - } -} -#endif diff --git a/src/OneScript.DebugProtocol/Breakpoint.cs b/src/OneScript.DebugProtocol/Breakpoint.cs index 699797c5b..8064d7274 100644 --- a/src/OneScript.DebugProtocol/Breakpoint.cs +++ b/src/OneScript.DebugProtocol/Breakpoint.cs @@ -15,6 +15,7 @@ public class Breakpoint public int Id { get; set; } public string Source { get; set; } public int Line { get; set; } + public string Condition { get; set; } = string.Empty; } } diff --git a/src/OneScript.DebugProtocol/DebuggerSettings.cs b/src/OneScript.DebugProtocol/DebuggerSettings.cs index c89fe6025..a9494893a 100644 --- a/src/OneScript.DebugProtocol/DebuggerSettings.cs +++ b/src/OneScript.DebugProtocol/DebuggerSettings.cs @@ -4,6 +4,7 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + namespace OneScript.DebugProtocol { public static class DebuggerSettings diff --git a/src/OneScript.DebugProtocol/EvaluatedVariableLocator.cs b/src/OneScript.DebugProtocol/EvaluatedVariableLocator.cs deleted file mode 100644 index 399731b3f..000000000 --- a/src/OneScript.DebugProtocol/EvaluatedVariableLocator.cs +++ /dev/null @@ -1,97 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OneScript.DebugProtocol -{ - public class EvaluatedVariableLocator : IVariableLocator - { - - private int[] _path; - private readonly int _stackFrameIndex; - private readonly string _expression; - - private Variable[] _variables; - - private EvaluatedVariableLocator(EvaluatedVariableLocator parent, int variableIndex) - { - _stackFrameIndex = parent._stackFrameIndex; - _path = new int[parent._path.Length + 1]; - Array.Copy(parent._path, _path, parent._path.Length); - _path[parent._path.Length] = variableIndex; - _expression = parent._expression; - } - - public EvaluatedVariableLocator(string expression, int stackFrameIndex) - { - _stackFrameIndex = stackFrameIndex; - _path = new int[0]; - _expression = expression; - } - - public EvaluatedVariableLocator(string expression, int stackFrameIndex, int variableIndex) - { - _stackFrameIndex = stackFrameIndex; - _path = new int[] { variableIndex }; - _expression = expression; - } - - public Variable this[int index] - { - get - { - ThrowIfNoVariables(); - - return _variables[index]; - } - } - - public int Count - { - get - { - ThrowIfNoVariables(); - return _variables.Length; - } - } - - public IEnumerator GetEnumerator() - { - ThrowIfNoVariables(); - - return ((IEnumerable) _variables).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - private void ThrowIfNoVariables() - { - if (_variables == null) - throw new InvalidOperationException("No variables aquired yet"); - } - - void IVariableLocator.Hydrate(IDebuggerService process) - { - if(_variables != null) - return; - var variables = process.GetEvaluatedVariables(_expression, 1, _stackFrameIndex, _path); - _variables = variables; - } - - IVariableLocator IVariableLocator.CreateChildLocator(int variableIndex) - { - return new EvaluatedVariableLocator(this, variableIndex); - } - } -} diff --git a/src/OneScript.DebugProtocol/ExceptionBreakpointFilter.cs b/src/OneScript.DebugProtocol/ExceptionBreakpointFilter.cs new file mode 100644 index 000000000..0cb7e506e --- /dev/null +++ b/src/OneScript.DebugProtocol/ExceptionBreakpointFilter.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/. +----------------------------------------------------------*/ +using System; + +namespace OneScript.DebugProtocol +{ + [Serializable] + public class ExceptionBreakpointFilter + { + public string Id { get; set; } + + public string Condition { get; set; } + } +} diff --git a/src/OneScript.DebugProtocol/IDebugEventListener.cs b/src/OneScript.DebugProtocol/IDebugEventListener.cs index e22ef3ac2..215f2a33c 100644 --- a/src/OneScript.DebugProtocol/IDebugEventListener.cs +++ b/src/OneScript.DebugProtocol/IDebugEventListener.cs @@ -4,22 +4,18 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -#if NETFRAMEWORK -using System.ServiceModel; -#endif namespace OneScript.DebugProtocol { + /// + /// Интерфейс слушателя событий отладки (сообщений, инициируемых со стороны BSL) + /// public interface IDebugEventListener { -#if NETFRAMEWORK - [OperationContract(IsOneWay = true)] -#endif void ThreadStopped(int threadId, ThreadStopReason reason); + + void ThreadStoppedEx(int threadId, ThreadStopReason reason, string errorMessage); -#if NETFRAMEWORK - [OperationContract(IsOneWay = true)] -#endif void ProcessExited(int exitCode); } } \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/IDebuggerService.cs b/src/OneScript.DebugProtocol/IDebuggerService.cs index 91468ed79..366c7ce59 100644 --- a/src/OneScript.DebugProtocol/IDebuggerService.cs +++ b/src/OneScript.DebugProtocol/IDebuggerService.cs @@ -6,48 +6,43 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -#if NETFRAMEWORK -using System.ServiceModel; -#endif namespace OneScript.DebugProtocol { -#if NETFRAMEWORK - [ServiceContract( - Namespace = "http://oscript.io/services/debugger", - SessionMode = SessionMode.Required, - CallbackContract = typeof(IDebugEventListener))] -#endif + /// + /// Сервис непосредственной работы с командами отладки, шагами, брейкпоинтами и пр. + /// public interface IDebuggerService { /// /// Разрешает потоку виртуальной машины начать выполнение скрипта /// Все точки останова уже установлены, все настройки сделаны /// -#if NETFRAMEWORK - [OperationContract(IsOneWay = true)] -#endif void Execute(int threadId); + + /// + /// Добавление фильтров точек останова для исключений + /// + /// + [Obsolete("Используется только для совместимости нового адаптера со старыми binary версиями движка")] + void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters); + /// + /// Добавление фильтров точек останова для исключений + /// + /// + void SetExceptionBreakpoints(ExceptionBreakpointFilter[] filters); + /// /// Установка точек остановки /// /// /// Возвращает установленные точки (те, которые смог установить) -#if NETFRAMEWORK - [OperationContract] -#endif Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet); /// /// Запрашивает состояние кадров стека вызовов /// -#if NETFRAMEWORK - [OperationContract] -#endif StackFrame[] GetStackFrames(int threadId); /// @@ -56,10 +51,16 @@ public interface IDebuggerService /// /// /// -#if NETFRAMEWORK - [OperationContract] -#endif Variable[] GetVariables(int threadId, int frameIndex, int[] path); + + /// + /// Получает переменные модуля для указанного фрейма + /// + /// + /// + /// + /// + Variable[] GetModuleVariables(int threadId, int frameIndex, int[] path); /// /// Получает значения переменных вычисленного выражения @@ -68,9 +69,6 @@ public interface IDebuggerService /// /// /// -#if NETFRAMEWORK - [OperationContract] -#endif Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path); /// @@ -80,34 +78,22 @@ public interface IDebuggerService /// Кадр стека, относительно которого вычисляем /// Выражение /// Переменная с результатом -#if NETFRAMEWORK - [OperationContract] -#endif Variable Evaluate(int threadId, int contextFrame, string expression); -#if NETFRAMEWORK - [OperationContract(IsOneWay = true)] -#endif void Next(int threadId); -#if NETFRAMEWORK - [OperationContract(IsOneWay = true)] -#endif void StepIn(int threadId); -#if NETFRAMEWORK - [OperationContract(IsOneWay = true)] -#endif void StepOut(int threadId); -#if NETFRAMEWORK - [OperationContract] -#endif + /// + /// Отключение сеанса отладки по инициативе IDE + /// + /// + void Disconnect(bool terminate); + int[] GetThreads(); -#if NETFRAMEWORK - [OperationContract] -#endif int GetProcessId(); } } diff --git a/src/OneScript.DebugProtocol/Internal/BinaryFormatWriter.cs b/src/OneScript.DebugProtocol/Internal/BinaryFormatWriter.cs new file mode 100644 index 000000000..3159210da --- /dev/null +++ b/src/OneScript.DebugProtocol/Internal/BinaryFormatWriter.cs @@ -0,0 +1,105 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Reflection; +using System.Text; + +namespace OneScript.DebugProtocol.Internal +{ + internal class BinaryFormatWriter : IDisposable + { + private readonly BinaryWriter _writer; + + public BinaryFormatWriter(Stream target) + { + _writer = new BinaryWriter(target, Encoding.UTF8, leaveOpen: true); + } + + public void WriteHeader(int rootId, int headerId) + { + WriteRecordType(0); + _writer.Write(rootId); + _writer.Write(headerId); + _writer.Write(1); + _writer.Write(0); + } + + public void WriteStringRecord(int objectId, string data) + { + WriteRecordType(6); + _writer.Write(objectId); + + WriteStringValue(data); + } + + private void WriteStringValue(string data) + { + var bytes = Encoding.UTF8.GetBytes(data); + if (bytes.Length > 127) + throw new ArgumentOutOfRangeException(nameof(data), "Length encoding not supported for strings more than 127 bytes"); + + _writer.Write((byte)bytes.Length); + _writer.Write(bytes, 0, bytes.Length); + } + + public void WriteLibrary(Assembly assembly, int libraryId) + { + WriteRecordType(12); + _writer.Write(libraryId); + WriteStringValue(assembly.GetName().Name); + } + + public void WriteClassWithNoFields(Type type, int classId, int libraryId) + { + WriteRecordType(5); + + _writer.Write(classId); + WriteStringValue(type.FullName); + _writer.Write(0); // properties count + + _writer.Write(libraryId); + } + + private void WriteObjectArray(int objectId, object[] arr) + { + if (arr.Length != 0) + throw new ArgumentException("Only empty arrays mapping supported"); + + WriteRecordType(16); + _writer.Write(objectId); + _writer.Write(0); + } + + private void WriteNull() + { + WriteRecordType(10); + } + + public void WriteEnd() + { + WriteRecordType(11); + } + + public void Close() + { + Dispose(); + } + + private void WriteRecordType(byte type) + { + _writer.Write(type); + } + + public void Dispose() + { + _writer.Flush(); + _writer.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/OneScript.DebugProtocol.csproj b/src/OneScript.DebugProtocol/OneScript.DebugProtocol.csproj index ed890ffb6..0dcac9887 100644 --- a/src/OneScript.DebugProtocol/OneScript.DebugProtocol.csproj +++ b/src/OneScript.DebugProtocol/OneScript.DebugProtocol.csproj @@ -4,16 +4,19 @@ OneScript.DebugProtocol - Debug;Release + Debug;Release;LinuxDebug 1Script Debug Protocol common objects and DTO's AnyCPU - net452;netstandard2.0 + + net48;$(TargetFrameworkVersion) + + + + true + false - - - - + \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/StackFrame.cs b/src/OneScript.DebugProtocol/StackFrame.cs index 1003be16d..52fa60c6f 100644 --- a/src/OneScript.DebugProtocol/StackFrame.cs +++ b/src/OneScript.DebugProtocol/StackFrame.cs @@ -5,16 +5,13 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Runtime.Serialization; -using System.Text; +using Newtonsoft.Json; namespace OneScript.DebugProtocol { - [DataContract, Serializable] - public class StackFrame : IVariableLocator + [DataContract, JsonObject, Serializable] + public class StackFrame { [DataMember] public int Index { get; set; } @@ -28,59 +25,6 @@ public class StackFrame : IVariableLocator [DataMember] public string Source { get; set; } - [DataMember] - public Variable[] Variables { get; set; } - public int ThreadId { get; set; } - - int IVariableLocator.Count - { - get - { - ThrowIfNoVariables(); - - return Variables.Length; - } - } - - private void ThrowIfNoVariables() - { - if (Variables == null) - throw new InvalidOperationException("No variables aquired yet"); - } - - Variable IVariableLocator.this[int index] - { - get - { - ThrowIfNoVariables(); - return Variables[index]; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - ThrowIfNoVariables(); - - return ((IEnumerable)Variables).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IVariableLocator) this).GetEnumerator(); - } - - void IVariableLocator.Hydrate(IDebuggerService process) - { - if (Variables != null) - return; - var variables = process.GetVariables(ThreadId, Index, new int[0]); - Variables = variables; - } - - public IVariableLocator CreateChildLocator(int variableIndex) - { - return new VariableLocator(ThreadId, Index, variableIndex); - } } } diff --git a/src/OneScript.DebugProtocol/TcpServer/BinaryChannel.cs b/src/OneScript.DebugProtocol/TcpServer/BinaryChannel.cs index 0fc4195d9..5b910121a 100644 --- a/src/OneScript.DebugProtocol/TcpServer/BinaryChannel.cs +++ b/src/OneScript.DebugProtocol/TcpServer/BinaryChannel.cs @@ -6,31 +6,51 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.IO; using System.Net.Sockets; using System.Runtime.Serialization; +#if NET48 using System.Runtime.Serialization.Formatters.Binary; +#endif using OneScript.DebugProtocol.Abstractions; +// ReSharper disable once CheckNamespace namespace OneScript.DebugProtocol { - public class BinaryChannel : ICommunicationChannel, IDisposable + /// + /// TCP-канал, использующий стандартную Binary-сериализацию .NET + /// + [Obsolete("Используется только со стороны адаптера, для работы со старыми версиями 1Script")] + public sealed class BinaryChannel : IMessageChannel { private readonly TcpClient _client; private readonly NetworkStream _clientStream; +#if NET48 private readonly BinaryFormatter _serializer; +#else + private readonly IFormatter _serializer = null; +#endif + + private bool _enabled; public BinaryChannel(TcpClient client) { _client = client; _clientStream = _client.GetStream(); + _enabled = true; +#if NET48 _serializer = new BinaryFormatter(); +#else + throw new NotSupportedException("Binary channel should be used only in .NET 48."); +#endif } - public bool Connected => _client.Connected; + public bool Connected => _enabled && _client.Connected; public void Write(object data) { + if (!_enabled) + throw new ObjectDisposedException(nameof(BinaryChannel)); + _serializer.Serialize(_clientStream, data); } @@ -41,17 +61,16 @@ public T Read() public object Read() { + if (!_enabled) + throw new ObjectDisposedException(nameof(BinaryChannel)); + try { return _serializer.Deserialize(_clientStream); } catch (SerializationException e) { - throw new ChannelException("Serialization Exception occured", Connected, e); - } - catch (IOException e) - { - throw new ChannelException("Network exception occured", true, e); + throw new ChannelException("Serialization Exception occured", !Connected, e); } } @@ -59,6 +78,7 @@ public void Dispose() { _clientStream.Dispose(); _client.Close(); + _enabled = false; } } } \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/TcpServer/DefaultMessageServer.cs b/src/OneScript.DebugProtocol/TcpServer/DefaultMessageServer.cs index b7430c2a3..ad86b4636 100644 --- a/src/OneScript.DebugProtocol/TcpServer/DefaultMessageServer.cs +++ b/src/OneScript.DebugProtocol/TcpServer/DefaultMessageServer.cs @@ -11,16 +11,28 @@ This Source Code Form is subject to the terms of the namespace OneScript.DebugProtocol.TcpServer { + /// + /// Читает сообщения из канала и вызывает обработчики сообщений. + /// Управляет жизненным циклом канала и освобождает его ресурсы при завершении работы сервера. + /// + /// public class DefaultMessageServer : ICommunicationServer { - private readonly ICommunicationChannel _protocolChannel; + private readonly IMessageChannel _protocolChannel; + private readonly object _disposeLock = new object(); private Thread _messageThread; private volatile bool _serverStopped; + private bool _channelDisposed; - public DefaultMessageServer(ICommunicationChannel protocolChannel) + public DefaultMessageServer(IMessageChannel protocolChannel) { _protocolChannel = protocolChannel; } + + /// + /// Имя, назначаемое потоку сервера. Полезно для отладки и диагностики. + /// + public string ServerThreadName { get; set; } public void Start() { @@ -42,18 +54,25 @@ private void RunCommandsLoop() Data = data, Channel = _protocolChannel, }; - + DataReceived?.Invoke(this, eventData); } catch (ChannelException e) { + if (e.StopChannel) + { + // критичные исключения сразу должны завершать сервер + _serverStopped = true; + break; + } + var eventData = new CommunicationEventArgs { Data = null, Channel = _protocolChannel, Exception = e }; - + try { DataReceived?.Invoke(this, eventData); @@ -62,34 +81,79 @@ private void RunCommandsLoop() { // один из обработчиков выбросил исключение // мы все равно не знаем что с ним делать. - + // Считаем, что факап подписчика - его проблемы. } - - // свойство в исключении может быть утановлено в обработчике евента + + // свойство в исключении может быть уcтановлено в обработчике евента _serverStopped = e.StopChannel; } - catch (Exception) + catch (ObjectDisposedException) + { + _serverStopped = true; + } + catch (ThreadInterruptedException) { + // Сервер принудительно остановлен _serverStopped = true; } + catch (Exception e) + { + if (OnError == null) + { + _serverStopped = true; + break; + } + + var eventData = new CommunicationEventArgs + { + Data = null, + Channel = _protocolChannel, + Exception = new ChannelException("Unhandled error in message handler", true, e) + }; + + OnError?.Invoke(this, eventData); + } } + + DisposeChannel(); }); + + _messageThread.IsBackground = true; + if (ServerThreadName != default) + { + _messageThread.Name = ServerThreadName; + } _messageThread.Start(); } + private void DisposeChannel() + { + lock (_disposeLock) + { + if (_channelDisposed) + return; + + _channelDisposed = true; + _protocolChannel.Dispose(); + } + } + public void Stop() { + if (_serverStopped) + return; + _serverStopped = true; if (_messageThread?.IsAlive == true) { - _protocolChannel.Dispose(); _messageThread.Interrupt(); } } public event EventHandler DataReceived; + public event EventHandler OnError; } } \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/TcpServer/DispatchingServer.cs b/src/OneScript.DebugProtocol/TcpServer/DispatchingServer.cs index 13cace284..6069d72f0 100644 --- a/src/OneScript.DebugProtocol/TcpServer/DispatchingServer.cs +++ b/src/OneScript.DebugProtocol/TcpServer/DispatchingServer.cs @@ -10,6 +10,10 @@ This Source Code Form is subject to the terms of the namespace OneScript.DebugProtocol.TcpServer { + /// + /// Диспетчер обработки сообщений. Получает сообщения от сервера и передает их в обработчик. + /// + /// public class DispatchingService { private readonly ICommunicationServer _server; @@ -50,13 +54,13 @@ private void OnDataReceived(object sender, CommunicationEventArgs e) } } - private void ProcessSuccess(RpcCall message, ICommunicationChannel responseChannel) + private void ProcessSuccess(RpcCall message, IMessageChannel responseChannel) { RpcCallResult callResult = null; try { var methodResult = _requestProcessor.Dispatch(_requestService, message.Id, message.Parameters); - if(methodResult != null) + if (methodResult != null) callResult = RpcCallResult.Respond(message, methodResult); } catch (Exception e) diff --git a/src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs b/src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs new file mode 100644 index 000000000..99fa26ef3 --- /dev/null +++ b/src/OneScript.DebugProtocol/TcpServer/FormatReconcileUtils.cs @@ -0,0 +1,100 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.IO; +using System.Text; +using OneScript.DebugProtocol.Internal; + +namespace OneScript.DebugProtocol.TcpServer +{ + public static class FormatReconcileUtils + { + /// + /// Специальный пакет, который будет принят двоичным десериализатором старого движка, но который потом + /// упадет на приведении типов. + /// В новом движке он будет принят, как команда для обмена версией формата. + /// + public static byte[] GetReconcileMagic() + { + using (var memoryStream = new MemoryStream(256)) + { + using (var writer = new BinaryFormatWriter(memoryStream)) + { + 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(); + } + + return memoryStream.ToArray(); + } + } + + /// + /// Ответ на запрос формата + /// + public static readonly byte[] FORMAT_RECONCILE_RESPONSE_PREFIX = + { + 0x1C, + 0x1C, + 0x1C, + 0x1C + }; + + public static readonly TimeSpan FORMAT_RECONCILE_TIMEOUT = TimeSpan.FromMilliseconds(500); + + public static bool CheckReconcilePrefix(byte[] data) + { + for (int i = 0; i < FORMAT_RECONCILE_RESPONSE_PREFIX.Length; i++) + { + if (data[i] != FORMAT_RECONCILE_RESPONSE_PREFIX[i]) + return false; + } + + return true; + } + + public static bool CheckReconcileRequest(Stream stream) + { + var magic = GetReconcileMagic(); + var buffer = new byte[magic.Length]; + StreamUtils.ReadStream(stream, buffer, buffer.Length); + for (int i = 0; i < magic.Length; i++) + { + if (buffer[i] != magic[i]) + return false; + } + + return true; + } + + public static void WriteReconcileResponse(Stream target, short transport, short version) + { + var formatInfo = EncodeFormatMarker(transport, version); + + using var binaryWriter = new BinaryWriter(target, Encoding.UTF8, true); + binaryWriter.Write(FORMAT_RECONCILE_RESPONSE_PREFIX); + binaryWriter.Write(formatInfo); + binaryWriter.Flush(); + } + + public static int EncodeFormatMarker(short transport, short dataVersion) + { + var marker = transport << 16; + return marker | dataVersion; + } + + public static (int, int) DecodeFormatMarker(int marker) + { + var transport = marker >> 16; + var dataVersion = marker & 0x0000FFFF; + + return (transport, dataVersion); + } + } +} diff --git a/src/OneScript.DebugProtocol/TcpServer/JsonDtoChannel.cs b/src/OneScript.DebugProtocol/TcpServer/JsonDtoChannel.cs new file mode 100644 index 000000000..2b0b98d73 --- /dev/null +++ b/src/OneScript.DebugProtocol/TcpServer/JsonDtoChannel.cs @@ -0,0 +1,96 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Text; +using Newtonsoft.Json; +using OneScript.DebugProtocol.Abstractions; + +namespace OneScript.DebugProtocol.TcpServer +{ + public sealed class JsonDtoChannel : IMessageChannel + { + private readonly IDebuggerClient _client; + private readonly Stream _dataStream; + + private bool _enabled = true; + + public JsonDtoChannel(IDebuggerClient client) + { + _client = client; + _dataStream = client.GetDataStream(); + } + + public JsonDtoChannel(Stream dataStream) + { + _client = null; + _dataStream = dataStream; + } + + public void Dispose() + { + _dataStream.Dispose(); + _client?.Dispose(); + _enabled = false; + } + + public void Write(object data) + { + if (!_enabled) + throw new ObjectDisposedException(nameof(JsonDtoChannel)); + + var content = JsonConvert.SerializeObject(data); + var contentBytes = Encoding.UTF8.GetBytes(content); + + using (var bufferedStream = new MemoryStream(contentBytes.Length + sizeof(int))) + { + using (var writer = new BinaryWriter(bufferedStream, Encoding.UTF8)) + { + writer.Write(contentBytes.Length); + writer.Write(contentBytes, 0, contentBytes.Length); + + bufferedStream.Position = 0; + bufferedStream.CopyTo(_dataStream); + } + } + } + + public T Read() + { + return (T)Read(); + } + + public object Read() + { + if (!_enabled) + throw new ObjectDisposedException(nameof(JsonDtoChannel)); + + try + { + using (var socketReader = new BinaryReader(_dataStream, Encoding.UTF8, true)) + { + var contentLength = socketReader.ReadInt32(); + var contentBuffer = new byte[contentLength]; + StreamUtils.ReadStream(socketReader.BaseStream, contentBuffer, contentLength); + + using (var textReader = new StreamReader(new MemoryStream(contentBuffer), Encoding.UTF8, false)) + { + using var reader = new JsonTextReader(textReader); + return JsonSerializer.CreateDefault().Deserialize(reader); + } + } + } + catch (Exception ex) + { + throw new ChannelException("Channel read exception", ex); + } + } + + public bool Connected => _enabled && (_client?.Connected ?? true); + } +} \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/TcpServer/JsonDtoConverter.cs b/src/OneScript.DebugProtocol/TcpServer/JsonDtoConverter.cs new file mode 100644 index 000000000..7a6aa34de --- /dev/null +++ b/src/OneScript.DebugProtocol/TcpServer/JsonDtoConverter.cs @@ -0,0 +1,326 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Newtonsoft.Json; +namespace OneScript.DebugProtocol.TcpServer +{ + public class JsonDtoConverter : JsonConverter + { + private const string TYPE_PROPERTY_NAME = "$type"; + private const string VALUE_PROPERTY_NAME = "$value"; + + public override void WriteJson(JsonWriter writer, TcpProtocolDtoBase value, JsonSerializer serializer) + { + switch (value) + { + case RpcCall rpcCall: + WriteCall(writer, rpcCall, serializer); + break; + case RpcCallResult rpcCallResult: + WriteCallResult(writer, rpcCallResult, serializer); + break; + default: + throw new ArgumentException($"Unknown type {value.GetType()}"); + } + } + + public override TcpProtocolDtoBase ReadJson(JsonReader reader, Type objectType, TcpProtocolDtoBase existingValue, + bool hasExistingValue, JsonSerializer serializer) + { + CheckExpectedToken(reader, JsonToken.StartObject, $"ReadObject {objectType}"); + + AdvanceReader(reader); + var typeName = ReadExpectedProperty(TYPE_PROPERTY_NAME, reader, serializer); + AdvanceReader(reader); + + Type requiredType; + if (objectType == typeof(TcpProtocolDtoBase)) + { + // нам не важен тип, прочитать надо тот, который указан в потоке. + requiredType = typeName switch + { + nameof(RpcCall) => typeof(RpcCall), + nameof(RpcCallResult) => typeof(RpcCallResult), + _ => throw new JsonSerializationException( + $"Unexpected object type in Json. Expected one of {objectType.Name}, got {typeName}") + }; + } + else if (typeName != objectType.Name) + { + // нам важен тип и он в потоке не такой + throw new JsonSerializationException($"Unexpected object type in Json. Expected {objectType.Name}, got {typeName}"); + } + else + { + requiredType = objectType; + } + + TcpProtocolDtoBase readResult; + if (requiredType == typeof(RpcCall)) + { + readResult = ReadRpcCall(reader, serializer); + } + else if (requiredType == typeof(RpcCallResult)) + { + readResult = ReadRpcCallResult(reader, serializer); + } + else + { + throw new InvalidOperationException($"Unknown type {requiredType}"); + } + + return readResult; + } + + private RpcCall ReadRpcCall(JsonReader reader, JsonSerializer serializer) + { + var value = new RpcCall(); + FillRpcDto(value, reader, serializer, (jsonReader, jsonSerializer, prop) => + { + if (prop == nameof(RpcCall.Parameters)) + { + value.Parameters = DeserializeTypedArray(reader, serializer); + } + }); + + return value; + } + + private RpcCallResult ReadRpcCallResult(JsonReader reader, JsonSerializer serializer) + { + var value = new RpcCallResult(); + FillRpcDto(value, reader, serializer, (jsonReader, jsonSerializer, prop) => + { + if (prop == nameof(RpcCallResult.ReturnValue)) + { + value.ReturnValue = ReadTypedValue(reader, serializer); + } + }); + + return value; + } + + private void FillRpcDto( + TcpProtocolDtoBase value, + JsonReader reader, + JsonSerializer serializer, + Action readFunction, + [CallerMemberName] string caller = "" + ) + { + bool hasId = false, hasServiceName = false; + while (reader.TokenType != JsonToken.EndObject) + { + CheckExpectedToken(reader, JsonToken.PropertyName, caller); + + var propName = (string)reader.Value; + AdvanceReader(reader); + switch (propName) + { + case nameof(RpcCallResult.Id): + value.Id = serializer.Deserialize(reader); + hasId = true; + break; + case nameof(RpcCallResult.ServiceName): + value.ServiceName = serializer.Deserialize(reader); + hasServiceName = true; + break; + default: + readFunction(reader, serializer, propName); + break; + } + AdvanceReader(reader); + } + + if (!hasId || !hasServiceName) + { + throw new JsonSerializationException($"Required properties missing ({caller})"); + } + } + + private object[] DeserializeTypedArray(JsonReader reader, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + CheckExpectedToken(reader, JsonToken.StartArray, "TypedArray"); + + var data = new List(); + while (reader.TokenType != JsonToken.EndArray) + { + AdvanceReader(reader); + if (reader.TokenType == JsonToken.EndArray) + break; + + var item = ReadTypedValue(reader, serializer); + data.Add(item); + } + + return data.ToArray(); + } + + private object ReadTypedValue(JsonReader reader, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + + CheckExpectedToken(reader, JsonToken.StartObject, "TypedValue"); + + AdvanceReader(reader); + var typeName = ReadExpectedProperty(TYPE_PROPERTY_NAME, reader, serializer); + AdvanceReader(reader); + + var type = GetSupportedType(typeName); + var value = ReadExpectedProperty(type, VALUE_PROPERTY_NAME, reader, serializer); + AdvanceReader(reader); + + if (reader.TokenType != JsonToken.EndObject) + { + throw new JsonSerializationException("Unexpected token parsing TypedValue " + reader.TokenType); + } + + return value; + } + + private void CheckExpectedToken(JsonReader reader, JsonToken expectedToken, string context) + { + if (reader.TokenType != expectedToken) + { + throw new JsonReaderException($"Unexpected token {reader.TokenType}, ({context}). Expected {expectedToken}"); + } + } + + private Type GetSupportedType(string typeName) + { + bool isArray = false; + if (typeName.EndsWith("[]", StringComparison.Ordinal)) + { + isArray = true; + typeName = typeName.Substring(0, typeName.Length - 2); + } + + var baseType = typeName switch + { + nameof(Breakpoint) => typeof(Breakpoint), + nameof(StackFrame) => typeof(StackFrame), + nameof(ThreadStopReason) => typeof(ThreadStopReason), + nameof(Variable) => typeof(Variable), + nameof(ExceptionBreakpointFilter) => typeof(ExceptionBreakpointFilter), + nameof(RpcExceptionDto) => typeof(RpcExceptionDto), + nameof(String) => typeof(string), + nameof(Int32) => typeof(int), + nameof(Boolean) => typeof(bool), + _ => throw new JsonSerializationException($"Unexpected type name for typed value {typeName}") + }; + + return isArray ? baseType.MakeArrayType() : baseType; + } + + private T ReadExpectedProperty(string propertyName, JsonReader reader, JsonSerializer serializer) + { + return (T)ReadExpectedProperty(typeof(T), propertyName, reader, serializer); + } + + private object ReadExpectedProperty(Type targetType, string propertyName, JsonReader reader, JsonSerializer serializer) + { + CheckExpectedToken(reader, JsonToken.PropertyName, $"reading expected property {propertyName}"); + + var propNameInStream = (string)reader.Value; + if (propNameInStream != propertyName) + { + throw new JsonSerializationException($"Unexpected property {propNameInStream}, expected {propertyName}"); + } + + AdvanceReader(reader); + var value = serializer.Deserialize(reader, targetType); + if (value != null && value.GetType() != targetType) + { + throw new JsonSerializationException($"Unexpected object type returned: {value.GetType()}, Expected {targetType}"); + } + + return value; + } + + private static void AdvanceReader(JsonReader reader) + { + if (!reader.Read()) + { + throw new JsonReaderException($"Unexpected end of Json"); + } + } + + private void WriteCall(JsonWriter writer, RpcCall rpcCall, JsonSerializer serializer) + { + writer.WriteStartObject(); + WriteBaseClass(writer, rpcCall); + writer.WritePropertyName(nameof(rpcCall.Parameters)); + + if (rpcCall.Parameters != null) + { + writer.WriteStartArray(); + foreach (var callParameter in rpcCall.Parameters) + { + WriteTypedValue(writer, serializer, callParameter); + } + writer.WriteEndArray(); + } + else + { + writer.WriteNull(); + } + + writer.WriteEndObject(); + } + + private void WriteCallResult(JsonWriter writer, RpcCallResult rpcCallResult, JsonSerializer serializer) + { + writer.WriteStartObject(); + + WriteBaseClass(writer, rpcCallResult); + writer.WritePropertyName(nameof(rpcCallResult.ReturnValue)); + WriteTypedValue(writer, serializer, rpcCallResult.ReturnValue); + + writer.WriteEndObject(); + } + + private static void WriteTypedValue(JsonWriter writer, JsonSerializer serializer, object valueToWrite) + { + if (valueToWrite == null) + { + writer.WriteNull(); + return; + } + + writer.WriteStartObject(); + + writer.WritePropertyName(TYPE_PROPERTY_NAME); + writer.WriteValue(valueToWrite.GetType().Name); + + writer.WritePropertyName(VALUE_PROPERTY_NAME); + serializer.Serialize(writer, valueToWrite); + + writer.WriteEndObject(); + } + + private void WriteBaseClass(JsonWriter writer, TcpProtocolDtoBase dto) + { + writer.WritePropertyName(TYPE_PROPERTY_NAME); + writer.WriteValue(dto.GetType().Name); + + writer.WritePropertyName(nameof(dto.Id)); + writer.WriteValue(dto.Id); + + writer.WritePropertyName(nameof(dto.ServiceName)); + writer.WriteValue(dto.ServiceName); + } + } +} \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/TcpServer/RpcCall.cs b/src/OneScript.DebugProtocol/TcpServer/RpcCall.cs index fb7ea0aed..09bdea77b 100644 --- a/src/OneScript.DebugProtocol/TcpServer/RpcCall.cs +++ b/src/OneScript.DebugProtocol/TcpServer/RpcCall.cs @@ -6,10 +6,12 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using Newtonsoft.Json; namespace OneScript.DebugProtocol.TcpServer { [Serializable] + [JsonConverter(typeof(JsonDtoConverter))] public class RpcCall : TcpProtocolDtoBase { public object[] Parameters { get; set; } diff --git a/src/OneScript.DebugProtocol/TcpServer/RpcCallResult.cs b/src/OneScript.DebugProtocol/TcpServer/RpcCallResult.cs index 9e8742ed9..a9d75c854 100644 --- a/src/OneScript.DebugProtocol/TcpServer/RpcCallResult.cs +++ b/src/OneScript.DebugProtocol/TcpServer/RpcCallResult.cs @@ -6,10 +6,12 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using Newtonsoft.Json; namespace OneScript.DebugProtocol.TcpServer { [Serializable] + [JsonConverter(typeof(JsonDtoConverter))] public class RpcCallResult : TcpProtocolDtoBase { public object ReturnValue { get; set; } diff --git a/src/OneScript.DebugProtocol/TcpServer/StreamUtils.cs b/src/OneScript.DebugProtocol/TcpServer/StreamUtils.cs new file mode 100644 index 000000000..e2dc7b088 --- /dev/null +++ b/src/OneScript.DebugProtocol/TcpServer/StreamUtils.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +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.IO; + +namespace OneScript.DebugProtocol.TcpServer +{ + public static class StreamUtils + { + public static void ReadStream(Stream stream, byte[] buffer, int length) + { + int readPosition = 0; + int bytesReceived = 0; + + while (bytesReceived < length) + { + var portionSize = stream.Read(buffer, readPosition, length - bytesReceived); + if (portionSize == 0) + throw new IOException("Unexpected end of stream"); + + readPosition += portionSize; + bytesReceived += portionSize; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.DebugProtocol/TcpServer/TcpProtocolDtoBase.cs b/src/OneScript.DebugProtocol/TcpServer/TcpProtocolDtoBase.cs index b8d24f612..060fefaf6 100644 --- a/src/OneScript.DebugProtocol/TcpServer/TcpProtocolDtoBase.cs +++ b/src/OneScript.DebugProtocol/TcpServer/TcpProtocolDtoBase.cs @@ -7,10 +7,12 @@ This Source Code Form is subject to the terms of the using System; +using Newtonsoft.Json; namespace OneScript.DebugProtocol.TcpServer { [Serializable] + [JsonConverter(typeof(JsonDtoConverter))] public abstract class TcpProtocolDtoBase { public string Id { get; set; } diff --git a/src/OneScript.DebugProtocol/ThreadStopReason.cs b/src/OneScript.DebugProtocol/ThreadStopReason.cs index f5b019cc5..33125436e 100644 --- a/src/OneScript.DebugProtocol/ThreadStopReason.cs +++ b/src/OneScript.DebugProtocol/ThreadStopReason.cs @@ -5,9 +5,6 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace OneScript.DebugProtocol { diff --git a/src/OneScript.DebugProtocol/Variable.cs b/src/OneScript.DebugProtocol/Variable.cs index 82c0ad12b..11ff6ee16 100644 --- a/src/OneScript.DebugProtocol/Variable.cs +++ b/src/OneScript.DebugProtocol/Variable.cs @@ -5,10 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.Serialization; -using System.Text; namespace OneScript.DebugProtocol { @@ -27,6 +24,7 @@ public class Variable [DataMember] public string TypeName { get; set; } + [DataMember] public int ChildrenHandleID { get; set; } } } diff --git a/src/OneScript.DebugProtocol/VariableLocator.cs b/src/OneScript.DebugProtocol/VariableLocator.cs deleted file mode 100644 index 624a6c164..000000000 --- a/src/OneScript.DebugProtocol/VariableLocator.cs +++ /dev/null @@ -1,101 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace OneScript.DebugProtocol -{ - public interface IVariableLocator : IEnumerable - { - int Count { get; } - - Variable this[int index] { get; } - - void Hydrate(IDebuggerService process); - - IVariableLocator CreateChildLocator(int variableIndex); - } - - public class VariableLocator : IVariableLocator - { - - private int[] _path; - private readonly int _threadId; - private readonly int _stackFrameIndex; - - private Variable[] _variables; - - private VariableLocator(VariableLocator parent, int variableIndex) - { - _stackFrameIndex = parent._stackFrameIndex; - _threadId = parent._threadId; - _path = new int[parent._path.Length + 1]; - Array.Copy(parent._path, _path, parent._path.Length); - _path[parent._path.Length] = variableIndex; - } - - public VariableLocator(int threadId, int stackFrameIndex, int variableIndex) - { - _threadId = threadId; - _stackFrameIndex = stackFrameIndex; - _path = new int[] { variableIndex }; - } - - public Variable this[int index] - { - get - { - ThrowIfNoVariables(); - - return _variables[index]; - } - } - - public int Count - { - get - { - ThrowIfNoVariables(); - return _variables.Length; - } - } - - public IEnumerator GetEnumerator() - { - ThrowIfNoVariables(); - - return ((IEnumerable) _variables).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - private void ThrowIfNoVariables() - { - if (_variables == null) - throw new InvalidOperationException("No variables aquired yet"); - } - - void IVariableLocator.Hydrate(IDebuggerService process) - { - if(_variables != null) - return; - var variables = process.GetVariables(_threadId, _stackFrameIndex, _path); - _variables = variables; - } - - IVariableLocator IVariableLocator.CreateChildLocator(int variableIndex) - { - return new VariableLocator(this, variableIndex); - } - } -} diff --git a/src/OneScript.DebugServices/BinaryTcpDebugServer.cs b/src/OneScript.DebugServices/BinaryTcpDebugServer.cs deleted file mode 100644 index be9716461..000000000 --- a/src/OneScript.DebugServices/BinaryTcpDebugServer.cs +++ /dev/null @@ -1,41 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Net.Sockets; -using OneScript.DebugProtocol; -using OneScript.DebugProtocol.Abstractions; -using OneScript.DebugProtocol.TcpServer; -using ScriptEngine; -using ScriptEngine.Machine; - -namespace OneScript.DebugServices -{ - public class BinaryTcpDebugServer - { - private readonly int _port; - - public BinaryTcpDebugServer(int port) - { - _port = port; - } - - public IDebugController CreateDebugController() - { - var listener = TcpListener.Create(_port); - var channel = new DelayedConnectionChannel(listener); - var ipcServer = new DefaultMessageServer(channel); - var callback = new TcpEventCallbackChannel(channel); - var threadManager = new ThreadManager(); - var breakpoints = new DefaultBreakpointManager(); - var debuggerService = new DefaultDebugService(breakpoints, threadManager, new DefaultVariableVisualizer()); - var controller = new DefaultDebugController(ipcServer, debuggerService, callback, threadManager, breakpoints); - - return controller; - } - } -} \ No newline at end of file diff --git a/src/OneScript.DebugServices/BreakpointDescriptor.cs b/src/OneScript.DebugServices/BreakpointDescriptor.cs index 11db98d67..a228c576b 100644 --- a/src/OneScript.DebugServices/BreakpointDescriptor.cs +++ b/src/OneScript.DebugServices/BreakpointDescriptor.cs @@ -13,6 +13,8 @@ internal class BreakpointDescriptor public int LineNumber; + public string Condition; + public BreakpointDescriptor(int id) { BreakpointId = id; diff --git a/src/OneScript.DebugServices/DefaultBreakpointManager.cs b/src/OneScript.DebugServices/DefaultBreakpointManager.cs index 82745fdbf..e9661a655 100644 --- a/src/OneScript.DebugServices/DefaultBreakpointManager.cs +++ b/src/OneScript.DebugServices/DefaultBreakpointManager.cs @@ -7,30 +7,63 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Linq; +using OneScript.Commons; using ScriptEngine.Machine; namespace OneScript.DebugServices { public class DefaultBreakpointManager : IBreakpointManager { + private readonly Dictionary _exceptionBreakpointsFilters = new Dictionary(); private readonly List _breakpoints = new List(); private int _idsGenerator; - public void SetLineStops(string module, int[] lines) + public void SetExceptionBreakpoints((string Id, string Condition)[] filters) + { + _exceptionBreakpointsFilters?.Clear(); + filters?.ForEach(c =>_exceptionBreakpointsFilters.Add(c.Id, c.Condition)); + } + + public void SetBreakpoints(string module, (int Line, string Condition)[] breakpoints) { var cleaned = _breakpoints.Where(x => x.Module != module) .ToList(); - var range = lines.Select(x => new BreakpointDescriptor(_idsGenerator++) { LineNumber = x, Module = module }); + var range = breakpoints.Select(x => new BreakpointDescriptor(_idsGenerator++) { LineNumber = x.Line, Module = module, Condition = x.Condition }); cleaned.AddRange(range); _breakpoints.Clear(); _breakpoints.AddRange(cleaned); } - public bool Find(string module, int line) + public bool FindBreakpoint(string module, int line) + => _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line) != null; + + public string GetCondition(string module, int line) + => _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line).Condition; + + public void Clear() + { + _breakpoints.Clear(); + _exceptionBreakpointsFilters.Clear(); + } + + public bool StopOnAnyException(string message) + => NeedStopOnException("all", message); + + public bool StopOnUncaughtException(string message) + => NeedStopOnException("uncaught", message); + + private bool NeedStopOnException(string filterId, string message) { - var found = _breakpoints.Find(x => x.Module.Equals(module) && x.LineNumber == line); - return found != null; + if (_exceptionBreakpointsFilters?.TryGetValue(filterId, out var condition) == true) + { + if (string.IsNullOrEmpty(condition)) + return true; + else + return message.Contains(condition); + } + + return false; } } } \ No newline at end of file diff --git a/src/OneScript.DebugServices/DefaultDebugController.cs b/src/OneScript.DebugServices/DefaultDebugController.cs deleted file mode 100644 index b757b4bf1..000000000 --- a/src/OneScript.DebugServices/DefaultDebugController.cs +++ /dev/null @@ -1,97 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using OneScript.DebugProtocol; -using OneScript.DebugProtocol.Abstractions; -using OneScript.DebugProtocol.TcpServer; -using ScriptEngine.Machine; - -namespace OneScript.DebugServices -{ - public class DefaultDebugController : IDebugController - { - private readonly ICommunicationServer _server; - private readonly IDebuggerService _debugger; - private readonly IDebugEventListener _callbackService; - private readonly ThreadManager _threadManager; - - public DefaultDebugController(ICommunicationServer ipcServer, - IDebuggerService debugger, - IDebugEventListener callbackService, - ThreadManager threadManager, - IBreakpointManager breakpointManager) - { - _server = ipcServer; - _debugger = debugger; - _callbackService = callbackService; - _threadManager = threadManager; - BreakpointManager = breakpointManager; - } - - public void Dispose() - { - _server.Stop(); - _threadManager.Dispose(); - } - - public void Init() - { - _threadManager.ThreadStopped += ThreadManagerOnThreadStopped; - var dispatcher = new DispatchingService(_server, _debugger); - dispatcher.Start(); - } - - private void ThreadManagerOnThreadStopped(object sender, ThreadStoppedEventArgs e) - { - var token = _threadManager.GetTokenForThread(e.ThreadId); - token.Reset(); - _callbackService.ThreadStopped(e.ThreadId, ConvertStopReason(e.StopReason)); - token.Wait(); - } - - public void Wait() - { - _threadManager.GetTokenForCurrentThread().Wait(); - } - - public void NotifyProcessExit(int exitCode) - { - _threadManager.DetachFromCurrentThread(); - _callbackService.ProcessExited(exitCode); - _server.Stop(); - - } - - public void AttachToThread() - { - _threadManager.AttachToCurrentThread(); - } - - public void DetachFromThread() - { - _threadManager.DetachFromCurrentThread(); - } - - public IBreakpointManager BreakpointManager { get; } - - private ThreadStopReason ConvertStopReason(MachineStopReason reason) - { - switch(reason) - { - case MachineStopReason.Breakpoint: - return ThreadStopReason.Breakpoint; - case MachineStopReason.Step: - return ThreadStopReason.Step; - case MachineStopReason.Exception: - return ThreadStopReason.Exception; - default: - throw new NotImplementedException(); - } - } - } -} \ No newline at end of file diff --git a/src/OneScript.DebugServices/DefaultDebugService.cs b/src/OneScript.DebugServices/DefaultDebugService.cs deleted file mode 100644 index 4aea0d8ae..000000000 --- a/src/OneScript.DebugServices/DefaultDebugService.cs +++ /dev/null @@ -1,221 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using OneScript.DebugProtocol; -using OneScript.Language; -using ScriptEngine.Machine; -using StackFrame = OneScript.DebugProtocol.StackFrame; -using Variable = OneScript.DebugProtocol.Variable; -using MachineVariable = ScriptEngine.Machine.Variable; - -namespace OneScript.DebugServices -{ - public class DefaultDebugService : IDebuggerService - { - private readonly IBreakpointManager _breakpointManager; - private readonly IVariableVisualizer _visualizer; - private ThreadManager _threadManager { get; } - - public DefaultDebugService(IBreakpointManager breakpointManager, ThreadManager threads, IVariableVisualizer visualizer) - { - _breakpointManager = breakpointManager; - _visualizer = visualizer; - _threadManager = threads; - } - - public void Execute(int threadId) - { - if (threadId > 0) - { - var token = _threadManager.GetTokenForThread(threadId); - token.Machine.PrepareDebugContinuation(); - token.Set(); - } - else - { - var tokens = _threadManager.GetAllTokens(); - foreach (var token in tokens) - { - token.Machine.PrepareDebugContinuation(); - token.Set(); - } - } - } - - public virtual Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet) - { - var confirmedBreakpoints = new List(); - - var grouped = breaksToSet.GroupBy(g => g.Source); - - foreach (var item in grouped) - { - var lines = item - .Where(x => x.Line != 0) - .Select(x => x.Line) - .ToArray(); - - _breakpointManager.SetLineStops(item.Key, lines); - foreach (var line in lines) - { - confirmedBreakpoints.Add(new Breakpoint() - { - Line = line, - Source = item.Key - }); - } - - } - - // Уведомить все потоки о новых точках остановки - foreach (var machine in _threadManager.GetAllTokens().Select(x=>x.Machine)) - { - machine.SetDebugMode(_breakpointManager); - } - - return confirmedBreakpoints.ToArray(); - } - - public virtual StackFrame[] GetStackFrames(int threadId) - { - var machine = _threadManager.GetTokenForThread(threadId).Machine; - var frames = machine.GetExecutionFrames(); - var result = new StackFrame[frames.Count]; - int index = 0; - foreach (var frameInfo in frames) - { - var frame = new StackFrame(); - frame.LineNumber = frameInfo.LineNumber; - frame.Index = index++; - frame.MethodName = frameInfo.MethodName; - frame.Source = frameInfo.Source; - result[frame.Index] = frame; - - } - return result; - } - - private MachineInstance GetMachine(int threadId) - { - return _threadManager.GetTokenForThread(threadId).Machine; - } - - public virtual Variable[] GetVariables(int threadId, int frameIndex, int[] path) - { - var machine = _threadManager.GetTokenForThread(threadId).Machine; - var locals = machine.GetFrameLocals(frameIndex); - - foreach (var step in path) - { - var variable = locals[step]; - locals = GetChildVariables(variable); - } - - return GetDebugVariables(locals); - } - - public virtual Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path) - { - IValue value; - - try - { - value = GetMachine(threadId).Evaluate(expression, true); - } - catch (Exception e) - { - value = ValueFactory.Create(e.Message); - } - - var locals = GetChildVariables(MachineVariable.Create(value, "$eval")); - - foreach (var step in path) - { - var variable = locals[step]; - locals = GetChildVariables(variable); - } - - return GetDebugVariables(locals); - } - - public virtual Variable Evaluate(int threadId, int contextFrame, string expression) - { - try - { - var value = GetMachine(threadId) - .Evaluate(expression, true) - .GetRawValue(); - - var variable = _visualizer.GetVariable(MachineVariable.Create(value, "$evalResult")); - return variable; - } - catch (ScriptException e) - { - return CreateDebuggerVariable("$evalFault", e.ErrorDescription, - "Ошибка"); - } - } - - public virtual void Next(int threadId) - { - var t = _threadManager.GetTokenForThread(threadId); - t.Machine.StepOver(); - t.Set(); - } - - public virtual void StepIn(int threadId) - { - var t = _threadManager.GetTokenForThread(threadId); - t.Machine.StepIn(); - t.Set(); - } - - public virtual void StepOut(int threadId) - { - var t = _threadManager.GetTokenForThread(threadId); - t.Machine.StepOut(); - t.Set(); - } - - public virtual int[] GetThreads() - { - return _threadManager.GetAllThreadIds(); - } - - public int GetProcessId() - { - return System.Diagnostics.Process.GetCurrentProcess().Id; - } - - private Variable[] GetDebugVariables(IList machineVariables) - { - return machineVariables.Select(x => _visualizer.GetVariable(x)) - .ToArray(); - } - - public Variable CreateDebuggerVariable(string name, string presentation, string typeName) - { - if (presentation.Length > DebuggerSettings.MAX_PRESENTATION_LENGTH) - presentation = presentation.Substring(0, DebuggerSettings.MAX_PRESENTATION_LENGTH) + "..."; - - return new Variable() - { - Name = name, - Presentation = presentation, - TypeName = typeName - }; - } - - private IList GetChildVariables(IVariable src) - { - return _visualizer.GetChildVariables(src).ToList(); - } - } -} \ No newline at end of file diff --git a/src/OneScript.DebugServices/DefaultDebugger.cs b/src/OneScript.DebugServices/DefaultDebugger.cs new file mode 100644 index 000000000..44471ac9d --- /dev/null +++ b/src/OneScript.DebugServices/DefaultDebugger.cs @@ -0,0 +1,116 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.DebugProtocol.Abstractions; +using OneScript.DebugProtocol.TcpServer; +using OneScript.DebugServices.Internal; +using ScriptEngine.Machine.Debugger; + +namespace OneScript.DebugServices +{ + /// + /// Простой отладчик, поддерживающий одновременно только одну сессию отладки. + /// + public class DefaultDebugger : IDebugger + { + // NB! должен быть согласован с перечислением TransportProtocols в адаптере + private const short JSON_FORMAT_MARKER = 2; + // NB! должен быть согласован с файлом ProtocolVersions в адаптере + private const short SUPPORTED_FORMAT_VERSION = 4; + + private readonly IDebugServer _transport; + private IDebugSession _session; + + private readonly object _sessionRecycleLock = new object(); + + public DefaultDebugger(IDebugServer transport) + { + _transport = transport; + } + + public bool IsEnabled => true; + public bool AttachMode { get; set; } + + public void Start() + { + _transport.OnClientConnected += TransportOnOnClientConnected; + _transport.OnListenException += ListenerException; + _transport.Listen(); + } + + private void ListenerException(object sender, ListenerErrorEventArgs e) + { + e.StopServer = true; + } + + private void TransportOnOnClientConnected(object sender, IDebuggerClient debuggerClient) + { + lock (_sessionRecycleLock) + { + if (_session?.IsActive == true) + { + // Если есть активная сессия, то не начинаем новую + debuggerClient.Dispose(); + return; + } + } + + var dataStream = debuggerClient.GetDataStream(); + if (FormatReconcileUtils.CheckReconcileRequest(dataStream)) + { + // Да, это наш фейковый заголовок + FormatReconcileUtils.WriteReconcileResponse(dataStream, JSON_FORMAT_MARKER, SUPPORTED_FORMAT_VERSION); + } + else + { + // Не поддерживаем старый формат протокола отладки + debuggerClient.Dispose(); + return; + } + + lock (_sessionRecycleLock) + { + var session = new DebugSession(debuggerClient, AttachMode); + session.OnClose += OnSessionClose; + + if (_session is ConnectableSessionProxy proxy) + { + proxy.Connect(session); + } + else + { + _session = session; + } + } + } + + private void OnSessionClose(DebugSession session) + { + lock (_sessionRecycleLock) + { + session.OnClose -= OnSessionClose; + _session = null; + } + } + + public IDebugSession GetSession() + { + lock (_sessionRecycleLock) + { + return _session ??= new ConnectableSessionProxy(AttachMode); + } + } + + public void NotifyProcessExit(int exitCode) + { + _transport.OnClientConnected-=TransportOnOnClientConnected; + _transport.Stop(); + + _session?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/DefaultVariableVisualizer.cs b/src/OneScript.DebugServices/DefaultVariableVisualizer.cs index 31f6bf509..03cb5f2cc 100644 --- a/src/OneScript.DebugServices/DefaultVariableVisualizer.cs +++ b/src/OneScript.DebugServices/DefaultVariableVisualizer.cs @@ -7,12 +7,11 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; -using System.Linq; +using OneScript.Contexts; using OneScript.DebugProtocol; +using OneScript.Execution; using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; using Variable = OneScript.DebugProtocol.Variable; -using MachineVariable = ScriptEngine.Machine.Variable; namespace OneScript.DebugServices { @@ -33,7 +32,8 @@ public Variable GetVariable(IVariable value) try { - presentation = value.AsString(); + // FIXME: В отладочном представлении не вызываются кастомные bsl-представления + presentation = value.ToString(); } catch (Exception e) { @@ -65,7 +65,7 @@ public IEnumerable GetChildVariables(IValue value) { var presenter = new DefaultValueVisitor(); - if (VariableHasType(value, DataType.Object)) + if (value is IRuntimeContextInstance) { var objectValue = value.AsObject(); if (objectValue is IDebugPresentationAcceptor customPresenter) @@ -79,7 +79,7 @@ public IEnumerable GetChildVariables(IValue value) presenter.ShowProperties(objectValue); } - if (HasIndexes(objectValue as ICollectionContext)) + if (HasIndexes(objectValue as ICollectionContext)) { var context = value.AsObject(); if (context is IEnumerable collection) @@ -95,24 +95,27 @@ public IEnumerable GetChildVariables(IValue value) private bool IsStructured(IVariable variable) { - var rawValue = variable?.GetRawValue(); + var rawValue = variable?.Value; return HasProperties(rawValue as IRuntimeContextInstance) - || HasIndexes(rawValue as ICollectionContext); + || HasIndexes(rawValue as ICollectionContext); } - private bool HasIndexes(ICollectionContext collection) + private bool HasIndexes(ICollectionContext collection) { - return collection?.Count() > 0; + try + { + return collection?.Count(ForbiddenBslProcess.Instance) > 0; + } + catch (NotSupportedException) + { + // TODO разобраться с bsl-процессом для вычисления пользовательских скриптовых коллекций + return false; + } } private static bool HasProperties(IRuntimeContextInstance value) { return value?.GetPropCount() > 0; } - - private static bool VariableHasType(IValue variable, DataType type) - { - return variable.GetRawValue() != null && variable.DataType == type; - } } } \ No newline at end of file diff --git a/src/OneScript.DebugServices/DelayedConnectionChannel.cs b/src/OneScript.DebugServices/DelayedConnectionChannel.cs deleted file mode 100644 index 3ae9bb6b8..000000000 --- a/src/OneScript.DebugServices/DelayedConnectionChannel.cs +++ /dev/null @@ -1,65 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Net.Sockets; -using OneScript.DebugProtocol; -using OneScript.DebugProtocol.Abstractions; - -namespace OneScript.DebugServices -{ - public class DelayedConnectionChannel : ICommunicationChannel - { - private TcpListener _listener; - private BinaryChannel _connectedChannel; - - public DelayedConnectionChannel(TcpListener listener) - { - _listener = listener; - } - - public void Dispose() - { - _listener?.Stop(); - _listener = null; - _connectedChannel?.Dispose(); - } - - public void Write(object data) - { - if(_connectedChannel == null) - throw new InvalidOperationException("No client connected"); - - _connectedChannel.Write(data); - } - - private void MakeChannel() - { - if (_connectedChannel == null) - { - _listener.Start(); - var tcpClient = _listener.AcceptTcpClient(); - _connectedChannel = new BinaryChannel(tcpClient); - _listener.Stop(); - _listener = null; - } - } - - - public T Read() - { - MakeChannel(); - return _connectedChannel.Read(); - } - - public object Read() - { - MakeChannel(); - return _connectedChannel.Read(); - } - } -} \ No newline at end of file diff --git a/src/OneScript.DebugServices/IDebugServer.cs b/src/OneScript.DebugServices/IDebugServer.cs new file mode 100644 index 000000000..bc35feaec --- /dev/null +++ b/src/OneScript.DebugServices/IDebugServer.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; +using OneScript.DebugProtocol.Abstractions; + +namespace OneScript.DebugServices +{ + /// + /// Сервер отладки, слушающий соединения от IDE и уведомляющий о подключении нового клиента. + /// + public interface IDebugServer + { + void Listen(); + void Stop(); + + event EventHandler OnClientConnected; + + event EventHandler OnListenException; + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/IVariableVisualizer.cs b/src/OneScript.DebugServices/IVariableVisualizer.cs index 1cdae4358..0953bd601 100644 --- a/src/OneScript.DebugServices/IVariableVisualizer.cs +++ b/src/OneScript.DebugServices/IVariableVisualizer.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Collections.Generic; +using OneScript.Contexts; using ScriptEngine.Machine; using Variable = OneScript.DebugProtocol.Variable; diff --git a/src/OneScript.DebugServices/Internal/ConnectableSessionProxy.cs b/src/OneScript.DebugServices/Internal/ConnectableSessionProxy.cs new file mode 100644 index 000000000..6c5b6378c --- /dev/null +++ b/src/OneScript.DebugServices/Internal/ConnectableSessionProxy.cs @@ -0,0 +1,66 @@ +/*---------------------------------------------------------- +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.Diagnostics; +using System.Threading; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Debugger; + +namespace OneScript.DebugServices.Internal +{ + internal class ConnectableSessionProxy : IDebugSession + { + private IDebugSession _session = new DisabledDebugSession(); + private bool _isConnected; + private readonly ManualResetEventSlim _connectionEvent = new ManualResetEventSlim(); + + public ConnectableSessionProxy(bool attachMode) + { + _isConnected = attachMode; + } + + public void Dispose() + { + var activeSession = _session; + + _connectionEvent.Reset(); + _isConnected = false; + _session = new DisabledDebugSession(); + + activeSession.Dispose(); + } + + public void Connect(IDebugSession session) + { + _session = session; + _isConnected = true; + _connectionEvent.Set(); + } + + public IBreakpointManager BreakpointManager => _session.BreakpointManager; + + public IThreadEventsListener ThreadManager => _session.ThreadManager; + + public void WaitReadyToRun() + { + if (_isConnected) + { + _session.WaitReadyToRun(); // просто делегируем + return; + } + + _connectionEvent.Wait(); + Debug.Assert(_isConnected, "Must be connected"); + Debug.Assert(_session is DebugSession, "Session must be DebugSession"); + + // Делегируем ожидание реальной сессии + _session.WaitReadyToRun(); + } + + public bool IsActive => _session.IsActive; + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/Internal/DebugSession.cs b/src/OneScript.DebugServices/Internal/DebugSession.cs new file mode 100644 index 000000000..53e093397 --- /dev/null +++ b/src/OneScript.DebugServices/Internal/DebugSession.cs @@ -0,0 +1,121 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Threading; +using OneScript.DebugProtocol; +using OneScript.DebugProtocol.Abstractions; +using OneScript.DebugProtocol.TcpServer; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Debugger; + +namespace OneScript.DebugServices.Internal +{ + internal class DebugSession : IDebugSession + { + private bool _isStarted; + private readonly ThreadManager _threadManager; + private readonly TcpEventCallbackChannel _callbackChannel; + private readonly DispatchingService _messageServer; + + private readonly ManualResetEventSlim _startEvent = new ManualResetEventSlim(); + + public bool IsActive { get; private set; } + + public DebugSession(IDebuggerClient connectedClient, bool attachMode) + { + // Если это режим Attach, то не ждем готовности к запуску + // т.к. IDE не пришлет команду Execute и мы сразу считаем себя готовой сессией. + _isStarted = attachMode; + + var channel = new JsonDtoChannel(connectedClient); + var ipcServer = new DefaultMessageServer(channel) + { + ServerThreadName = "debug-server" + }; + + ipcServer.OnError += CommunicationError; + + BreakpointManager = new DefaultBreakpointManager(); + _threadManager = new ThreadManager(); + _callbackChannel = new TcpEventCallbackChannel(channel); + + var commandsHandler = new DebuggerServiceImpl(this, new DefaultVariableVisualizer()); + + _messageServer = new DispatchingService(ipcServer, commandsHandler); + _messageServer.Start(); + + _threadManager.ThreadStopped += ThreadManagerOnThreadStopped; + IsActive = true; + } + + private void CommunicationError(object sender, CommunicationEventArgs e) + { + // Unexpected error in message loop + Dispose(); + } + + private void ThreadManagerOnThreadStopped(object sender, ThreadStoppedEventArgs e) + { + MachineWaitToken token; + try + { + token = _threadManager.GetTokenForThread(e.ThreadId); + } + catch (ArgumentOutOfRangeException) + { + return; + } + + token.Reset(); + + _callbackChannel.ThreadStoppedEx(e.ThreadId, ConvertStopReason(e.StopReason), e.ErrorMessage); + token.Wait(); + } + + public void Dispose() + { + _threadManager.ThreadStopped -= ThreadManagerOnThreadStopped; + _threadManager.Dispose(); + _messageServer.Stop(); + IsActive = false; + + OnClose?.Invoke(this); + } + + public IBreakpointManager BreakpointManager { get; } + public IThreadEventsListener ThreadManager => _threadManager; + + public void WaitReadyToRun() + { + if (_isStarted) + return; + + _startEvent.Wait(); + _isStarted = true; + } + + internal void SetReadyToRun() + { + if (_isStarted) + return; + + _startEvent.Set(); + } + + public event Action OnClose; + + private static ThreadStopReason ConvertStopReason(MachineStopReason reason) => reason switch + { + MachineStopReason.Breakpoint => ThreadStopReason.Breakpoint, + MachineStopReason.BreakpointConditionError => ThreadStopReason.Breakpoint, + MachineStopReason.Step => ThreadStopReason.Step, + MachineStopReason.Exception => ThreadStopReason.Exception, + _ => throw new NotImplementedException(), + }; + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/Internal/DebuggerServiceImpl.cs b/src/OneScript.DebugServices/Internal/DebuggerServiceImpl.cs new file mode 100644 index 000000000..cf284162f --- /dev/null +++ b/src/OneScript.DebugServices/Internal/DebuggerServiceImpl.cs @@ -0,0 +1,260 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.DebugProtocol; +using OneScript.Language; +using ScriptEngine.Machine; +using StackFrame = OneScript.DebugProtocol.StackFrame; +using Variable = OneScript.DebugProtocol.Variable; +using MachineVariable = OneScript.Contexts.Variable; + +namespace OneScript.DebugServices.Internal +{ + internal class DebuggerServiceImpl : IDebuggerService + { + private readonly DebugSession _debugSession; + private readonly IBreakpointManager _breakpointManager; + private readonly IVariableVisualizer _visualizer; + private readonly ThreadManager _threadManager; + + public DebuggerServiceImpl(DebugSession debugSession, IVariableVisualizer visualizer) + { + _debugSession = debugSession; + _breakpointManager = _debugSession.BreakpointManager; + _threadManager = (ThreadManager)_debugSession.ThreadManager; + _visualizer = visualizer; + } + + public void Execute(int threadId) + { + _debugSession.SetReadyToRun(); + + if (threadId > 0) + { + var token = _threadManager.GetTokenForThread(threadId); + token.Set(); + } + else + { + var tokens = _threadManager.GetAllTokens(); + + foreach (var token in tokens) + token.Set(); + } + } + + public void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters) + { + _breakpointManager.SetExceptionBreakpoints(filters); + + // Уведомить все потоки о новых фильтрах + foreach (var machine in _threadManager.GetAllTokens().Select(x => x.Machine)) + machine.SetDebugMode(_threadManager, _breakpointManager); + } + + public void SetExceptionBreakpoints(ExceptionBreakpointFilter[] filters) + { + SetMachineExceptionBreakpoints(filters.Select(f => (f.Id, f.Condition)).ToArray()); + } + + public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet) + { + var confirmedBreakpoints = new List(); + + var grouped = breaksToSet.GroupBy(g => g.Source); + + foreach (var item in grouped) + { + var points = item + .Where(x => x.Line != 0) + .Select(x => (x.Line, x.Condition)) + .ToArray(); + + _breakpointManager.SetBreakpoints(item.Key, points); + foreach (var point in points) + { + confirmedBreakpoints.Add(new Breakpoint() + { + Line = point.Line, + Source = item.Key, + Condition = point.Condition + }); + } + } + + // Уведомить все потоки о новых точках остановки + foreach (var machine in _threadManager.GetAllTokens().Select(x=>x.Machine)) + { + machine.SetDebugMode(_threadManager, _breakpointManager); + } + + return confirmedBreakpoints.ToArray(); + } + + public StackFrame[] GetStackFrames(int threadId) + { + var machine = _threadManager.GetTokenForThread(threadId).Machine; + var frames = machine.GetExecutionFrames(); + var result = new StackFrame[frames.Count]; + int index = 0; + foreach (var frameInfo in frames) + { + var frame = new StackFrame + { + LineNumber = frameInfo.LineNumber, + Index = index++, + MethodName = frameInfo.MethodName, + Source = frameInfo.Source + }; + result[frame.Index] = frame; + } + return result; + } + + private MachineInstance GetMachine(int threadId) + { + return _threadManager.GetTokenForThread(threadId).Machine; + } + + public Variable[] GetVariables(int threadId, int frameIndex, int[] path) + { + var machine = GetMachine(threadId); + + var locals = machine.GetFrameLocals(frameIndex); + + foreach (var step in path) + { + var variable = locals[step]; + locals = GetChildVariables(variable); + } + + return GetDebugVariables(locals); + } + + public Variable[] GetModuleVariables(int threadId, int frameIndex, int[] path) + { + var machine = GetMachine(threadId); + + var moduleVars = machine.GetModuleVariables(frameIndex); + + foreach (var step in path) + { + var variable = moduleVars[step]; + moduleVars = GetChildVariables(variable); + } + + return GetDebugVariables(moduleVars); + } + + public Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path) + { + IValue value; + + try + { + value = GetMachine(threadId).EvaluateInFrame(expression, frameIndex); + } + catch (Exception e) + { + value = ValueFactory.Create(e.Message); + } + + var locals = GetChildVariables(MachineVariable.Create(value, "$eval")); + + foreach (var step in path) + { + var variable = locals[step]; + locals = GetChildVariables(variable); + } + + return GetDebugVariables(locals); + } + + public Variable Evaluate(int threadId, int contextFrame, string expression) + { + try + { + var value = GetMachine(threadId) + .EvaluateInFrame(expression, contextFrame); + + var variable = _visualizer.GetVariable(MachineVariable.Create(value, "$evalResult")); + return variable; + } + catch (ScriptException e) + { + return CreateDebuggerVariable("$evalFault", e.ErrorDescription, + "Ошибка"); + } + } + + public void Next(int threadId) + { + var t = _threadManager.GetTokenForThread(threadId); + t.Machine.StepOver(); + t.Set(); + } + + public void StepIn(int threadId) + { + var t = _threadManager.GetTokenForThread(threadId); + t.Machine.StepIn(); + t.Set(); + } + + public void StepOut(int threadId) + { + var t = _threadManager.GetTokenForThread(threadId); + t.Machine.StepOut(); + t.Set(); + } + + public void Disconnect(bool terminate) + { + _breakpointManager.Clear(); + _threadManager.ReleaseAllThreads(); + _debugSession.Dispose(); + } + + public int[] GetThreads() + { + return _threadManager.GetThreadIds().ToArray(); + } + + public int GetProcessId() + { + return System.Diagnostics.Process.GetCurrentProcess().Id; + } + + private Variable[] GetDebugVariables(IList machineVariables) + { + return machineVariables.Select(x => _visualizer.GetVariable(x)) + .ToArray(); + } + + public Variable CreateDebuggerVariable(string name, string presentation, string typeName) + { + if (presentation.Length > DebuggerSettings.MAX_PRESENTATION_LENGTH) + presentation = presentation.Substring(0, DebuggerSettings.MAX_PRESENTATION_LENGTH) + "..."; + + return new Variable() + { + Name = name, + Presentation = presentation, + TypeName = typeName + }; + } + + private IList GetChildVariables(IVariable src) + { + return _visualizer.GetChildVariables(src.Value).ToList(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/Internal/TcpEventCallbackChannel.cs b/src/OneScript.DebugServices/Internal/TcpEventCallbackChannel.cs new file mode 100644 index 000000000..e1aafea2e --- /dev/null +++ b/src/OneScript.DebugServices/Internal/TcpEventCallbackChannel.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +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.IO; +using OneScript.DebugProtocol; +using OneScript.DebugProtocol.Abstractions; +using OneScript.DebugProtocol.TcpServer; + +namespace OneScript.DebugServices.Internal +{ + internal class TcpEventCallbackChannel : IDebugEventListener + { + private readonly IMessageChannel _channel; + + public TcpEventCallbackChannel(IMessageChannel channel) + { + _channel = channel; + } + + public void ThreadStoppedEx(int threadId, ThreadStopReason reason, string errorMessage) + { + var dto = RpcCall.Create(nameof(IDebugEventListener), nameof(ThreadStoppedEx), threadId, reason, errorMessage); + Write(dto); + } + + public void ThreadStopped(int threadId, ThreadStopReason reason) + { + var dto = RpcCall.Create(nameof(IDebugEventListener), nameof(ThreadStopped), threadId, reason); + Write(dto); + } + + public void ProcessExited(int exitCode) + { + var dto = RpcCall.Create(nameof(IDebugEventListener), nameof(ProcessExited), exitCode); + Write(dto); + } + + private void Write(RpcCall dto) + { + if (!_channel.Connected) + return; + + try + { + _channel.Write(dto); + } + catch (IOException) + { + // Ignore + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/ListenerErrorEventArgs.cs b/src/OneScript.DebugServices/ListenerErrorEventArgs.cs new file mode 100644 index 000000000..142733c2d --- /dev/null +++ b/src/OneScript.DebugServices/ListenerErrorEventArgs.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +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.DebugServices +{ + public class ListenerErrorEventArgs : EventArgs + { + public ListenerErrorEventArgs(Exception exception) + { + this.Exception = exception; + StopServer = true; + } + + public Exception Exception { get; } + + public bool StopServer { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/MachineWaitToken.cs b/src/OneScript.DebugServices/MachineWaitToken.cs index 732fa143a..ddb22b481 100644 --- a/src/OneScript.DebugServices/MachineWaitToken.cs +++ b/src/OneScript.DebugServices/MachineWaitToken.cs @@ -5,20 +5,16 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Threading; using ScriptEngine.Machine; namespace OneScript.DebugServices { - public class MachineWaitToken + public class MachineWaitToken: IDisposable { - private ManualResetEventSlim _threadEvent; + private ManualResetEventSlim _threadEvent = new ManualResetEventSlim(); - public MachineWaitToken() - { - _threadEvent = new ManualResetEventSlim(); - } - public MachineInstance Machine { get; set; } public void Wait() => _threadEvent.Wait(); @@ -26,5 +22,12 @@ public MachineWaitToken() public void Set() => _threadEvent.Set(); public void Reset() => _threadEvent.Reset(); + + public void Dispose() + { + Machine.UnsetDebugMode(); + _threadEvent.Set(); + _threadEvent.Dispose(); + } } } \ No newline at end of file diff --git a/src/OneScript.DebugServices/OneScript.DebugServices.csproj b/src/OneScript.DebugServices/OneScript.DebugServices.csproj index 32ca77019..983fe3e14 100644 --- a/src/OneScript.DebugServices/OneScript.DebugServices.csproj +++ b/src/OneScript.DebugServices/OneScript.DebugServices.csproj @@ -2,9 +2,16 @@ - net452;netstandard2.0 + $(TargetFrameworkVersion) Debug services for apps hosting 1script engine Copyright (c) 2020 EvilBeaver + Debug;Release;LinuxDebug + AnyCPU + + + + true + false diff --git a/src/OneScript.DebugServices/TcpDebugServer.cs b/src/OneScript.DebugServices/TcpDebugServer.cs new file mode 100644 index 000000000..9bd585ab0 --- /dev/null +++ b/src/OneScript.DebugServices/TcpDebugServer.cs @@ -0,0 +1,160 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using OneScript.DebugProtocol.Abstractions; + +namespace OneScript.DebugServices +{ + public class TcpDebugServer : IDebugServer + { + private readonly int _port; + private volatile bool _isListening; + private TcpListener _listener; + + public TcpDebugServer(int port) + { + _port = port; + } + + public enum LogLevel + { + Debug, + Info, + Error + } + + public int ActualPort() => (_listener.LocalEndpoint as IPEndPoint)?.Port ?? 0; + + public delegate void LogHandler(LogLevel level, string message); + + public event LogHandler OnLogEvent; + + public void Listen() + { + if (_isListening) + return; + + _listener = TcpListener.Create(_port); + + _listener.Start(); + _isListening = true; + + StartListenerThread(); + } + + private void StartListenerThread() + { + new Thread(() => + { + OnLogEvent?.Invoke(LogLevel.Info, "Starting listener thread"); + while (_isListening) + { + try + { + var client = _listener.AcceptTcpClient(); + OnLogEvent?.Invoke(LogLevel.Debug, "Client connected"); + OnClientConnected?.Invoke(this, new TcpDebuggerClient(client)); + } + catch (ObjectDisposedException) + { + _isListening = false; + OnLogEvent?.Invoke(LogLevel.Debug, "Listener interrupted"); + break; + } + catch (SocketException e) + { + if (_isListening == false) + { + // Сервер находится в процессе остановки внешним потоком + // и ошибка сокета не является ошибкой. + break; + } + + OnLogEvent?.Invoke(LogLevel.Error, $"Socket exception: {e}"); + if (OnListenException == null) + { + Stop(); + break; + } + + var args = new ListenerErrorEventArgs(e); + try + { + OnLogEvent?.Invoke(LogLevel.Debug, $"Calling exception handler"); + OnListenException?.Invoke(this, args); + if (args.StopServer) + { + Stop(); + } + } + catch (Exception handlerException) + { + OnLogEvent?.Invoke(LogLevel.Error, $"Exception handler raised exception {handlerException}"); + Stop(); + throw; + } + } + } + OnLogEvent?.Invoke(LogLevel.Info, "Listener thread stopped"); + }) + { + IsBackground = true, + Name = "debug-server-listener", + }.Start(); + } + + private class TcpDebuggerClient : IDebuggerClient + { + private TcpClient Client { get; } + + public TcpDebuggerClient(TcpClient client) + { + Client = client; + } + + public void Dispose() + { + Client.Dispose(); + } + + public Stream GetDataStream() + { + return Client.GetStream(); + } + + public bool Connected => Client.Connected; + } + + public void Stop() + { + OnLogEvent?.Invoke(LogLevel.Debug, "Stopping listener"); + _isListening = false; + try + { + _listener?.Stop(); + } + catch (Exception e) + { + // тут нечего делать + OnLogEvent?.Invoke(LogLevel.Error, $"Exception while stopping listener: {e}"); + } + finally + { + _listener = null; + } + + } + + public event EventHandler OnClientConnected; + public event EventHandler OnListenException; + } +} \ No newline at end of file diff --git a/src/OneScript.DebugServices/TcpEventCallbackChannel.cs b/src/OneScript.DebugServices/TcpEventCallbackChannel.cs deleted file mode 100644 index 4ac85d933..000000000 --- a/src/OneScript.DebugServices/TcpEventCallbackChannel.cs +++ /dev/null @@ -1,42 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Net.Sockets; -using OneScript.DebugProtocol; -using OneScript.DebugProtocol.Abstractions; -using OneScript.DebugProtocol.TcpServer; - -namespace OneScript.DebugServices -{ - public class TcpEventCallbackChannel : IDebugEventListener, IDisposable - { - private readonly ICommunicationChannel _channel; - - public TcpEventCallbackChannel(ICommunicationChannel channel) - { - _channel = channel; - } - - public void ThreadStopped(int threadId, ThreadStopReason reason) - { - var dto = RpcCall.Create(nameof(IDebugEventListener), nameof(ThreadStopped), threadId, reason); - _channel.Write(dto); - } - - public void ProcessExited(int exitCode) - { - var dto = RpcCall.Create(nameof(IDebugEventListener), nameof(ProcessExited), exitCode); - _channel.Write(dto); - } - - public void Dispose() - { - _channel?.Dispose(); - } - } -} \ No newline at end of file diff --git a/src/OneScript.DebugServices/ThreadManager.cs b/src/OneScript.DebugServices/ThreadManager.cs index 210867a7e..f93cae0c0 100644 --- a/src/OneScript.DebugServices/ThreadManager.cs +++ b/src/OneScript.DebugServices/ThreadManager.cs @@ -6,86 +6,98 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Threading; using ScriptEngine.Machine; +using ScriptEngine.Machine.Debugger; namespace OneScript.DebugServices { - public class ThreadManager : IDisposable + public class ThreadManager : IThreadEventsListener { - private readonly Dictionary _machinesOnThreads = new Dictionary(); + private readonly IDictionary _machinesOnThreads = new ConcurrentDictionary(); public MachineWaitToken GetTokenForThread(int threadId) { - return _machinesOnThreads[threadId]; + if (_machinesOnThreads.TryGetValue(threadId, out var value)) + { + return value; + } + + throw new ArgumentOutOfRangeException($"Thread {threadId} is unregistered"); } - public MachineWaitToken GetTokenForCurrentThread() + public event EventHandler ThreadStopped; + + public MachineWaitToken[] GetAllTokens() { - return GetTokenForThread(Thread.CurrentThread.ManagedThreadId); + return _machinesOnThreads.Values.ToArray(); } - public event EventHandler ThreadStopped; - - public void AttachToCurrentThread() + private void EmitThreadStopped(int threadId, MachineStopReason reason, string errMessage) { - var machine = MachineInstance.Current; - _machinesOnThreads[Thread.CurrentThread.ManagedThreadId] = new MachineWaitToken() + var machine = GetTokenForThread(threadId).Machine; + + var args = new ThreadStoppedEventArgs { - Machine = machine + Machine = machine, + ThreadId = threadId, + StopReason = reason, + ErrorMessage = errMessage }; - - machine.MachineStopped += Machine_MachineStopped; + + ThreadStopped?.Invoke(this, args); } - - public void DetachFromCurrentThread() + + public void ReleaseAllThreads() { - var threadId = Thread.CurrentThread.ManagedThreadId; - DetachFromThread(threadId); + var tokens = GetAllTokens(); + foreach (var machineWaitToken in tokens) + { + machineWaitToken.Machine.UnsetDebugMode(); + machineWaitToken.Dispose(); + } + + _machinesOnThreads.Clear(); } - public void DetachFromThread(int threadId) + public void Detach(int threadId) { - if (_machinesOnThreads.TryGetValue(threadId, out var t)) + if (_machinesOnThreads.Remove(threadId, out var t)) { - t.Machine.MachineStopped -= Machine_MachineStopped; - _machinesOnThreads.Remove(threadId); + // Если машина была остановлена - продолжаем её уже без остановок + t.Machine.UnsetDebugMode(); + t.Set(); } } - public MachineWaitToken[] GetAllTokens() + public void Dispose() { - return _machinesOnThreads.Values.ToArray(); + ReleaseAllThreads(); } - private void Machine_MachineStopped(object sender, MachineStoppedEventArgs e) + public void ThreadStarted(int threadId, MachineInstance machine) { - var args = new ThreadStoppedEventArgs + _machinesOnThreads[threadId] = new MachineWaitToken { - Machine = (MachineInstance)sender, - ThreadId = e.ThreadId, - StopReason = e.Reason + Machine = machine }; - - ThreadStopped?.Invoke(this, args); } - - public int[] GetAllThreadIds() + + void IThreadEventsListener.ThreadStopped(int threadId, MachineStopReason reason, string errorMessage) { - return _machinesOnThreads.Keys.ToArray(); + EmitThreadStopped(threadId, reason, errorMessage); } - - public void Dispose() + + public void ThreadExited(int threadId) { - var tokens = GetAllTokens(); - foreach (var machineWaitToken in tokens) - { - machineWaitToken.Machine.MachineStopped -= Machine_MachineStopped; - } + _machinesOnThreads.Remove(threadId); + } - _machinesOnThreads.Clear(); + public IEnumerable GetThreadIds() + { + return _machinesOnThreads.Keys.ToList(); } } } \ No newline at end of file diff --git a/src/OneScript.DebugServices/ThreadStoppedEventArgs.cs b/src/OneScript.DebugServices/ThreadStoppedEventArgs.cs index ddbdd11cf..4526d97d8 100644 --- a/src/OneScript.DebugServices/ThreadStoppedEventArgs.cs +++ b/src/OneScript.DebugServices/ThreadStoppedEventArgs.cs @@ -16,5 +16,7 @@ public class ThreadStoppedEventArgs public int ThreadId { get; set; } public MachineStopReason StopReason { get; set; } + + public string ErrorMessage { get; set; } } } \ No newline at end of file diff --git a/src/OneScript.Language.Tests/OneScript.Language.Tests.csproj b/src/OneScript.Language.Tests/OneScript.Language.Tests.csproj deleted file mode 100644 index 2813473f2..000000000 --- a/src/OneScript.Language.Tests/OneScript.Language.Tests.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netcoreapp2.1 - - false - - - - - - - - - - - - - diff --git a/src/OneScript.Language.Tests/TrieTests.cs b/src/OneScript.Language.Tests/TrieTests.cs deleted file mode 100644 index af81bc852..000000000 --- a/src/OneScript.Language.Tests/TrieTests.cs +++ /dev/null @@ -1,39 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using Xunit; - -namespace OneScript.Language.Tests -{ - public class TrieTests - { - [Fact] - public void LexemTrieAdd() - { - var t = new LexemTrie(); - t.Add("Иван", 0); - t.Add("Иволга", 1); - - Assert.Equal(1, t.Get("Иволга")); - Assert.Equal(1, t.Get("ивОлга")); - Assert.Equal(0, t.Get("Иван")); - Assert.Equal(0, t.Get("иван")); - Assert.Equal(0, t.Get("ивАн")); - } - - [Fact] - public void Tokens() - { - var t = new LexemTrie(); - t.Add("иначе", 1); - t.Add("иначеесли", 2); - - Assert.Equal(2, t.Get("ИначеЕсли")); - Assert.Equal(1, t.Get("Иначе")); - } - } -} \ No newline at end of file diff --git a/src/OneScript.Language/CodeError.cs b/src/OneScript.Language/CodeError.cs new file mode 100644 index 000000000..d98c3b3db --- /dev/null +++ b/src/OneScript.Language/CodeError.cs @@ -0,0 +1,108 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Text; + +namespace OneScript.Language +{ + public struct CodeError + { + public string ErrorId { get; set; } + + public string Description { get; set; } + + public ErrorPositionInfo Position { get; set; } + + public override string ToString() + { + return + $"[{ErrorId}] {Description} ({Position.LineNumber},{Position.ColumnNumber}) / {Position.ModuleName} / {Position.Code}"; + } + + public string ToString(ErrorDetails details) + { + if (details == ErrorDetails.Full) + return ToString(); + + var sb = new StringBuilder(); + if ((details & ErrorDetails.ErrorId) == ErrorDetails.ErrorId) + { + sb.Append('[').Append(ErrorId).Append("] "); + } + + sb.Append(Description); + + if ((details & ErrorDetails.Position) == ErrorDetails.Position) + { + sb.Append(" (") + .Append(Position.LineNumber) + .Append(",") + .Append(Position.ColumnNumber) + .Append(")"); + } + + if ((details & ErrorDetails.ModuleName) == ErrorDetails.ModuleName) + { + sb.Append(" / "); + sb.Append(Position.ModuleName); + } + + if ((details & ErrorDetails.CodeFragment) == ErrorDetails.CodeFragment) + { + sb.Append(" / "); + sb.Append(Position.Code); + } + + return sb.ToString(); + } + + [Flags] + public enum ErrorDetails + { + /// + /// Только текст сообщения + /// + MessageOnly = 0, + + /// + /// Показывать идентификатор ошибки + /// + ErrorId = 1, + + /// + /// Показывать позицию (Строка,Колонка) + /// + Position = 2, + + /// + /// Показывать имя модуля с ошибкой + /// + ModuleName = 4, + + /// + /// Показывать фрагмент кода с ошибкой + /// + CodeFragment = 8, + + /// + /// Показывать позицию и имя модуля + /// + Short = Position | ModuleName, + + /// + /// Показывать позицию, имя модуля, фрагмент кода + /// + Simple = Position | ModuleName | CodeFragment, + + /// + /// Вся информация об ошибке + /// + Full = 16 + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/CodePositionInfo.cs b/src/OneScript.Language/CodePositionInfo.cs deleted file mode 100644 index 67338f56f..000000000 --- a/src/OneScript.Language/CodePositionInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace OneScript.Language -{ - public class CodePositionInfo - { - public const int OUT_OF_TEXT = -1; - - public CodePositionInfo() - { - LineNumber = OUT_OF_TEXT; - ColumnNumber = OUT_OF_TEXT; - } - - public int LineNumber { get; set; } - public int ColumnNumber { get; set; } - public string Code { get; set; } - public string ModuleName { get; set; } - - } -} diff --git a/src/OneScript.Language/ErrorPositionInfo.cs b/src/OneScript.Language/ErrorPositionInfo.cs new file mode 100644 index 000000000..fb5c31b24 --- /dev/null +++ b/src/OneScript.Language/ErrorPositionInfo.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +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.Language +{ + public class ErrorPositionInfo + { + public const int OUT_OF_TEXT = -1; + + public ErrorPositionInfo() + { + LineNumber = OUT_OF_TEXT; + ColumnNumber = OUT_OF_TEXT; + } + + public int LineNumber { get; set; } + public int ColumnNumber { get; set; } + public string Code { get; set; } + public string ModuleName { get; set; } + + public bool IsEmpty => LineNumber == OUT_OF_TEXT && ColumnNumber == OUT_OF_TEXT && ModuleName == default; + } +} diff --git a/src/OneScript.Language/Extensions/AstNodesExtensions.cs b/src/OneScript.Language/Extensions/AstNodesExtensions.cs new file mode 100644 index 000000000..a2b5df7a2 --- /dev/null +++ b/src/OneScript.Language/Extensions/AstNodesExtensions.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Runtime.CompilerServices; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.Extensions +{ + public static class AstNodesExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetIdentifier(this BslSyntaxNode node) + { + return GetIdentifier((TerminalNode) node); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetIdentifier(this TerminalNode node) + { + return node.Lexem.Content; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Lexem GetLexem(this TerminalNode node) + { + return node.Lexem; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TerminalNode AsTerminal(this BslSyntaxNode node) + { + return node as TerminalNode; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/Extensions/ExceptionHelperExtensions.cs b/src/OneScript.Language/Extensions/ExceptionHelperExtensions.cs new file mode 100644 index 000000000..57d443381 --- /dev/null +++ b/src/OneScript.Language/Extensions/ExceptionHelperExtensions.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace OneScript.Language.Extensions +{ + public static class ExceptionHelperExtensions + { + public static ScriptException AppendCodeInfo(ScriptException exc, ErrorPositionInfo errorPosInfo) + { + exc.LineNumber = errorPosInfo.LineNumber; + exc.ColumnNumber = errorPosInfo.ColumnNumber; + exc.Code = errorPosInfo.Code; + exc.ModuleName = errorPosInfo.ModuleName; + + return exc; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/IErrorSink.cs b/src/OneScript.Language/IErrorSink.cs new file mode 100644 index 000000000..226a74af2 --- /dev/null +++ b/src/OneScript.Language/IErrorSink.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/. +----------------------------------------------------------*/ + +using System.Collections.Generic; + +namespace OneScript.Language +{ + public interface IErrorSink + { + IEnumerable Errors { get; } + bool HasErrors { get; } + void AddError(CodeError err); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/IdentifiersTrie.cs b/src/OneScript.Language/IdentifiersTrie.cs index 6eb3bc0e0..5f014ba6f 100644 --- a/src/OneScript.Language/IdentifiersTrie.cs +++ b/src/OneScript.Language/IdentifiersTrie.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Collections; using System.Collections.Generic; @@ -12,15 +13,24 @@ namespace OneScript.Language { public class IdentifiersTrie : IDictionary { + private readonly TrieNode _root = new TrieNode(); + private class TrieNode { - public char charL; - public char charU; - public T value; - public TrieNode sibl; - public TrieNode next; + internal char charL; + internal char charU; + internal TrieNode sibl; + internal TrieNode next; - public TrieNode Find(char ch) + internal T value; + + internal bool hasValue; + + internal TrieNode() { } + internal TrieNode(char ch) + { charL = char.ToLower(ch); charU = char.ToUpper(ch); } + + internal TrieNode Find(char ch) { var node = sibl; while (node != null) @@ -31,42 +41,46 @@ public TrieNode Find(char ch) } return null; } - } - - private readonly TrieNode _root; - - public IdentifiersTrie() - { - _root = new TrieNode(); - } - + } + public void Add(string str, T val) { var node = _root; + TrieNode key = node; foreach (char ch in str) { - var key = node.Find(ch); - if (key == null) + if (node == null) { - key = new TrieNode + node = new TrieNode(ch); + key.next = node; + key = node; + node = null; + } + else + { + TrieNode last = node; + key = node; + while (key != null && key.charL != ch && key.charU != ch) { - charL = char.ToLower(ch), - charU = char.ToUpper(ch), - value = default(T), - sibl = node.sibl - }; - node.sibl = key; - key.next = new TrieNode(); + last = key; + key = key.sibl; + } + if (key == null) + { + key = new TrieNode(ch); + last.sibl = key; + } + node = key.next; } - node = key.next; } - node.value = val; + key.value = val; + key.hasValue = true; } - public bool ContainsKey(string key) - { - throw new System.NotImplementedException(); + public bool ContainsKey(string str) + { + return TryGetValue(str, out _); } public bool Remove(string key) @@ -75,19 +89,10 @@ public bool Remove(string key) } public T Get(string str) - { - var node = _root; - foreach (char ch in str) - { - TrieNode key = node.Find(ch); - if (key == null) - throw new KeyNotFoundException(); - - node = key.next; - } - - return node.value; - } + { + return TryGetValue(str, out var value) ? value + : throw new KeyNotFoundException(); + } public T this[string index] { @@ -95,26 +100,39 @@ public T this[string index] set => Add(index, value); } - public ICollection Keys { get; } - public ICollection Values { get; } + public ICollection Keys => throw new NotSupportedException(); + public ICollection Values => throw new NotSupportedException(); public bool TryGetValue(string str, out T value) { - var node = _root; + TrieNode key = _root; + var node = key.sibl; foreach (char ch in str) - { - var key = node.Find(ch); - if (key == null) - { - value = default(T); - return false; - } - + { + while (node != null && node.charL != ch && node.charU != ch) + { + node = node.sibl; + } + if (node == null) + { + value = default; + return false; + } + + key = node; node = key.next; } - value = node.value; - return true; + if (key.hasValue) + { + value = key.value; + return true; + } + else + { + value = default; + return false; + } } public IEnumerator> GetEnumerator() diff --git a/src/OneScript.Language/LanguageDef.cs b/src/OneScript.Language/LanguageDef.cs index 9008027be..b32456fbd 100644 --- a/src/OneScript.Language/LanguageDef.cs +++ b/src/OneScript.Language/LanguageDef.cs @@ -5,29 +5,36 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Language.LexicalAnalysis; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Text; -using OneScript.Language.LexicalAnalysis; namespace OneScript.Language { public static class LanguageDef { static readonly Dictionary _priority = new Dictionary(); + public const int MAX_OPERATION_PRIORITY = 8; - private static readonly LexemTrie _stringToToken = new LexemTrie(); - - private static readonly LexemTrie _undefined = new LexemTrie(); - private static readonly LexemTrie _booleans = new LexemTrie(); - private static readonly LexemTrie _logicalOp = new LexemTrie(); - - private static readonly LexemTrie _preprocRegion = new LexemTrie(); - private static readonly LexemTrie _preprocEndRegion = new LexemTrie(); + private static readonly Dictionary _keywords = new Dictionary(); + + private static readonly IdentifiersTrie _stringToToken = new IdentifiersTrie(); const int BUILTINS_INDEX = (int)Token.ByValParam; + public enum WordType + { + Undefined, + Boolean, + Logical, + Null, + Preproc, + None + }; + + private static readonly IdentifiersTrie _specwords = new IdentifiersTrie(); + static LanguageDef() { _priority.Add(Token.Plus, 5); @@ -51,21 +58,26 @@ static LanguageDef() #region constants - _undefined.Add("Undefined", true); - _undefined.Add("Неопределено", true); + _specwords.Add("Undefined", WordType.Undefined); + _specwords.Add("Неопределено", WordType.Undefined); + + _specwords.Add("True", WordType.Boolean); + _specwords.Add("False", WordType.Boolean); + _specwords.Add("Истина", WordType.Boolean); + _specwords.Add("Ложь", WordType.Boolean); - _booleans.Add("True", true); - _booleans.Add("False", true); - _booleans.Add("Истина", true); - _booleans.Add("Ложь", true); + _specwords.Add("And", WordType.Logical); + _specwords.Add("Or", WordType.Logical); + _specwords.Add("Not", WordType.Logical); - _logicalOp.Add("And", true); - _logicalOp.Add("Or", true); - _logicalOp.Add("Not", true); + _specwords.Add("И", WordType.Logical); + _specwords.Add("ИЛИ", WordType.Logical); + _specwords.Add("НЕ", WordType.Logical); - _logicalOp.Add("И", true); - _logicalOp.Add("ИЛИ", true); - _logicalOp.Add("НЕ", true); + _specwords.Add("NULL", WordType.Null); + + _specwords.Add("Использовать", WordType.Preproc); + _specwords.Add("Use", WordType.Preproc); #endregion @@ -73,39 +85,42 @@ static LanguageDef() #region Ключевые слова - AddToken(Token.If, "если", "if"); - AddToken(Token.Then, "тогда", "then"); - AddToken(Token.Else, "иначе", "else"); - AddToken(Token.ElseIf, "иначеесли", "elsif"); - AddToken(Token.EndIf, "конецесли", "endif"); - AddToken(Token.VarDef, "перем", "var"); - AddToken(Token.ByValParam, "знач", "val"); - AddToken(Token.Procedure, "процедура", "procedure"); - AddToken(Token.EndProcedure, "конецпроцедуры", "endprocedure"); - AddToken(Token.Function, "функция", "function"); - AddToken(Token.EndFunction, "конецфункции", "endfunction"); - AddToken(Token.For, "для", "for"); - AddToken(Token.Each, "каждого", "each"); - AddToken(Token.In, "из", "in"); - AddToken(Token.To, "по", "to"); - AddToken(Token.While, "пока", "while"); - AddToken(Token.Loop, "цикл", "do"); - AddToken(Token.EndLoop, "конеццикла", "enddo"); - AddToken(Token.Return, "возврат", "return"); - AddToken(Token.Continue, "продолжить", "continue"); - AddToken(Token.Break, "прервать", "break"); - AddToken(Token.Try, "попытка", "try"); - AddToken(Token.Exception, "исключение", "except"); - AddToken(Token.Execute, "выполнить", "execute"); - AddToken(Token.RaiseException, "вызватьисключение", "raise"); - AddToken(Token.EndTry, "конецпопытки", "endtry"); - AddToken(Token.NewObject, "новый", "new"); - AddToken(Token.Export, "экспорт", "export"); - AddToken(Token.And, "и", "and"); - AddToken(Token.Or, "или", "or"); - AddToken(Token.Not, "не", "not"); - AddToken(Token.AddHandler, "ДобавитьОбработчик", "AddHandler"); - AddToken(Token.RemoveHandler, "УдалитьОбработчик", "RemoveHandler"); + AddKeyword(Token.If, "Если", "If"); + AddKeyword(Token.Then, "Тогда", "Then"); + AddKeyword(Token.Else, "Иначе", "Else"); + AddKeyword(Token.ElseIf, "ИначеЕсли", "ElsIf"); + AddKeyword(Token.EndIf, "КонецЕсли", "EndIf"); + AddKeyword(Token.VarDef, "Перем", "Var"); + AddKeyword(Token.ByValParam, "Знач", "Val"); + AddKeyword(Token.Procedure, "Процедура", "Procedure"); + AddKeyword(Token.EndProcedure, "КонецПроцедуры", "EndProcedure"); + AddKeyword(Token.Function, "Функция", "Function"); + AddKeyword(Token.EndFunction, "КонецФункции", "EndFunction"); + AddKeyword(Token.For, "Для", "For"); + AddKeyword(Token.Each, "Каждого", "Each"); + AddKeyword(Token.In, "Из", "In"); + AddKeyword(Token.To, "По", "To"); + AddKeyword(Token.While, "Пока", "While"); + AddKeyword(Token.Loop, "Цикл", "Do"); + AddKeyword(Token.EndLoop, "КонецЦикла", "EndDo"); + AddKeyword(Token.Return, "Возврат", "Return"); + AddKeyword(Token.Continue, "Продолжить", "Continue"); + AddKeyword(Token.Break, "Прервать", "Break"); + AddKeyword(Token.Try, "Попытка", "Try"); + AddKeyword(Token.Exception, "Исключение", "Except"); + AddKeyword(Token.Execute, "Выполнить", "Execute"); + AddKeyword(Token.RaiseException, "ВызватьИсключение", "Raise"); + AddKeyword(Token.EndTry, "КонецПопытки", "EndTry"); + AddKeyword(Token.NewObject, "Новый", "New"); + AddKeyword(Token.Export, "Экспорт", "Export"); + AddKeyword(Token.And, "И", "And"); + AddKeyword(Token.Or, "Или", "Or"); + AddKeyword(Token.Not, "Не", "Not"); + AddKeyword(Token.AddHandler, "ДобавитьОбработчик", "AddHandler"); + AddKeyword(Token.RemoveHandler, "УдалитьОбработчик", "RemoveHandler"); + AddKeyword(Token.Async, "Асинх", "Async"); + AddKeyword(Token.Await, "Ждать", "Await"); + AddKeyword(Token.Goto, "Перейти", "Goto"); #endregion @@ -171,6 +186,7 @@ static LanguageDef() AddToken(Token.Hour, "час", "hour"); AddToken(Token.Minute, "минута", "minute"); AddToken(Token.Second, "секунда", "second"); + AddToken(Token.BegOfWeek, "началонедели", "begofweek"); AddToken(Token.BegOfYear, "началогода", "begofyear"); AddToken(Token.BegOfMonth, "началомесяца", "begofmonth"); AddToken(Token.BegOfDay, "началодня", "begofday"); @@ -183,6 +199,7 @@ static LanguageDef() AddToken(Token.EndOfHour, "конецчаса", "endofhour"); AddToken(Token.EndOfMinute, "конецминуты", "endofminute"); AddToken(Token.EndOfQuarter, "конецквартала", "endofquarter"); + AddToken(Token.EndOfWeek, "конецнедели", "endofweek"); AddToken(Token.WeekOfYear, "неделягода", "weekofyear"); AddToken(Token.DayOfYear, "деньгода", "dayofyear"); AddToken(Token.DayOfWeek, "деньнедели", "dayofweek"); @@ -210,10 +227,6 @@ static LanguageDef() #endregion - _preprocRegion.Add("Область",true); - _preprocRegion.Add("Region", true); - _preprocEndRegion.Add("КонецОбласти", true); - _preprocEndRegion.Add("EndRegion", true); } private static void AddToken(Token token, string name) @@ -227,10 +240,36 @@ private static void AddToken(Token token, string name, string alias) _stringToToken.Add(alias, token); } + private static void AddKeyword(Token token, string name, string alias) + { + _keywords.Add(token, (name,alias)); + _stringToToken.Add(name, token); + _stringToToken.Add(alias, token); + } + + public static string GetTokenName(Token token) + { + if (_keywords.TryGetValue(token,out var strings)) + { + return strings.Item1; + } + + return Enum.GetName(typeof(Token), token); + } + + public static string GetTokenAlias(Token token) + { + if (_keywords.TryGetValue(token,out var strings)) + { + return strings.Item2; + } + + return Enum.GetName(typeof(Token), token); + } + public static Token GetToken(string tokText) { - Token result; - if (_stringToToken.TryGetValue(tokText, out result)) + if (_stringToToken.TryGetValue(tokText, out Token result)) { return result; } @@ -243,7 +282,31 @@ public static Token GetToken(string tokText) public static int GetPriority(Token op) { return _priority[op]; - } + } + + public static int GetBinaryPriority(Token op) + { + return IsBinaryOperator(op) ? _priority[op] : -1; + } + + public static int GetUnaryPriority(Token op) + { + return op switch + { + Token.OpenPar => MAX_OPERATION_PRIORITY + 1, + + Token.Not => _priority[op], + Token.UnaryMinus => _priority[op], + Token.UnaryPlus => _priority[op], + + Token.Minus => _priority[Token.UnaryMinus], + Token.Plus => _priority[Token.UnaryPlus], + + _ => MAX_OPERATION_PRIORITY + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsBuiltInFunction(Token token) @@ -253,42 +316,83 @@ public static bool IsBuiltInFunction(Token token) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsBinaryOperator(Token token) - { - return token == Token.Plus - || token == Token.Minus - || token == Token.Multiply - || token == Token.Division - || token == Token.Modulo - || token == Token.And - || token == Token.Or - || token == Token.Not - || token == Token.LessThan - || token == Token.LessOrEqual - || token == Token.MoreThan - || token == Token.MoreOrEqual - || token == Token.Equal - || token == Token.NotEqual; - } - + { + switch (token) + { + case Token.Plus: + case Token.Minus: + case Token.Multiply: + case Token.Division: + case Token.Modulo: + case Token.Equal: + case Token.LessThan: + case Token.LessOrEqual: + case Token.MoreThan: + case Token.MoreOrEqual: + case Token.NotEqual: + case Token.And: + case Token.Or: + return true; + default: + return false; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLogicalBinaryOperator(Token token) { return token == Token.And || token == Token.Or; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsUnaryOperator(Token token) + { + return token == Token.Plus || token == Token.Minus || token == Token.Not; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLiteral(ref Lexem lex) + public static bool IsLiteral(in Lexem lex) { - return lex.Type == LexemType.StringLiteral - || lex.Type == LexemType.NumberLiteral - || lex.Type == LexemType.BooleanLiteral - || lex.Type == LexemType.DateLiteral - || lex.Type == LexemType.UndefinedLiteral - || lex.Type == LexemType.NullLiteral; + switch (lex.Type) + { + case LexemType.StringLiteral: + case LexemType.NumberLiteral: + case LexemType.BooleanLiteral: + case LexemType.DateLiteral: + case LexemType.UndefinedLiteral: + case LexemType.NullLiteral: + return true; + default: + return false; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsUserSymbol(ref Lexem lex) + public static bool IsValidPropertyName(in Lexem lex) + { + switch (lex.Type) + { + case LexemType.Identifier: + case LexemType.BooleanLiteral: + case LexemType.NullLiteral: + case LexemType.UndefinedLiteral: + return true; + + default: + switch (lex.Token) + { + case Token.And: + case Token.Or: + case Token.Not: + return true; + default: + return false; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsUserSymbol(in Lexem lex) { return lex.Type == LexemType.Identifier && lex.Token == Token.NotAToken; } @@ -346,27 +450,40 @@ public static bool IsBeginOfStatement(Token token) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEndOfBlockToken(Token token) + { + switch (token) + { + case Token.EndIf: + case Token.EndProcedure: + case Token.EndFunction: + case Token.Else: + case Token.EndLoop: + case Token.EndTry: + case Token.EndOfText: + case Token.ElseIf: + case Token.Exception: + return true; + default: + return false; + } + } + + + public static WordType GetWordType(string value) { - return token == Token.EndIf - || token == Token.EndProcedure - || token == Token.EndFunction - || token == Token.Else - || token == Token.EndLoop - || token == Token.EndTry - || token == Token.EndOfText - || token == Token.ElseIf; + return _specwords.TryGetValue(value, out var wordType)? wordType : WordType.None; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsBooleanLiteralString(string value) { - return _booleans.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(value, out var wordType) && wordType == WordType.Boolean; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsUndefinedString(string value) { - return _undefined.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(value, out var wordType) && wordType == WordType.Undefined; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -378,20 +495,13 @@ public static bool IsNullString(string value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLogicalOperatorString(string content) { - return _logicalOp.TryGetValue(content, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(content, out var wordType) && wordType == WordType.Logical; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsPreprocRegion(string value) - { - return _preprocRegion.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsPreprocEndRegion(string value) + public static bool IsImportDirective(string value) { - return _preprocEndRegion.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + return _specwords.TryGetValue(value, out var wordType) && wordType == WordType.Preproc; } - - } } diff --git a/src/OneScript.Language/LexemTrie.cs b/src/OneScript.Language/LexemTrie.cs deleted file mode 100644 index 0476fdcab..000000000 --- a/src/OneScript.Language/LexemTrie.cs +++ /dev/null @@ -1,209 +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.Collections.Generic; -using System.Diagnostics; - -namespace OneScript.Language -{ - public class LexemTrie - { - private class TrieNode - { - public char UCase; - public char LCase; - - public TrieNode next; - public TrieNode sibling; - public T value; - } - - private static int _alphabetLength; - private TrieNode[] _alphabet; - - static LexemTrie() - { - var ru = "абвгдеёжзийклмнопрстуфхцчшщьыъэюя"; - var en = "abcdefghijklmnopqrstuvwxyz"; - var symbols = @"+-*/\()[].,<>=;?%0123456789"; - - var all = symbols + - ru + - ru.ToUpper() + - en + - en.ToUpper(); - - _alphabetLength = all.Length; - } - - public LexemTrie() - { - _alphabet = new TrieNode[_alphabetLength]; - } - - private static int GetIndex(char c) - { - var code = (int) c; - - if (code >= 1040 && code <= 1103) - { - return code - 960; - } - - if (code >= 40 && code <= 57) - { - return code - 39; - } - - if (code >= 59 && code <= 63) - { - return code - 40; - } - - if (code >= 65 && code <= 93) - { - return code - 41; - } - - if (code >= 97 && code <= 122) - { - return code - 44; - } - - switch (c) - { - case '%': - return 0; - case 'Ё': - return 79; - case 'ё': - return 144; - } - - return -1; - } - - private TrieNode GetValueNode(string key) - { - var index = GetIndex(key[0]); - if (index == -1) - return null; - - var node = _alphabet[index]; - if (node == null) - { - node = new TrieNode(); - node.LCase = char.ToLower(key[0]); - node.UCase = char.ToUpper(key[0]); - _alphabet[GetIndex(node.LCase)] = node; - _alphabet[GetIndex(node.UCase)] = node; - } - - for (int i = 1; i < key.Length; i++) - { - var current = node; - node = node.next; - if (node == null) - { - var newNode = new TrieNode(); - newNode.LCase = char.ToLower(key[i]); - newNode.UCase = char.ToUpper(key[i]); - current.next = newNode; - node = newNode; - } - else if (node.LCase != key[i] && node.UCase != key[i]) - { - var insert = node.sibling; - while (insert != null) - { - if (insert.LCase == key[i] || insert.UCase == key[i]) - { - node = insert; - break; - } - - node = insert; - insert = insert.sibling; - } - - if (insert == null) - { - var newNode = new TrieNode(); - newNode.LCase = char.ToLower(key[i]); - newNode.UCase = char.ToUpper(key[i]); - node.sibling = newNode; - node = newNode; - } - } - } - - return node; - } - - public void Add(string key, T value) - { - var node = GetValueNode(key); - Debug.Assert(node != null); - - node.value = value; - } - - public T Get(string key) - { - var node = FindNode(key); - if (node == null) - { - throw new KeyNotFoundException(); - } - - return node.value; - } - - private TrieNode FindNode(string key) - { - var index = GetIndex(key[0]); - if (index == -1 || _alphabet[index] == null) - return null; - - var node = _alphabet[index]; - for (int i = 1; i < key.Length; i++) - { - node = node.next; - if (node == null) - return null; - - while(node.LCase != key[i] && node.UCase != key[i]) - { - node = node.sibling; - if(node == null) - return null; - } - } - - return node; - } - - public bool TryGetValue(string key, out T value) - { - var node = FindNode(key); - if (node == null) - { - value = default(T); - return false; - } - - value = node.value; - return true; - } - - public T this[string key] - { - get => Get(key); - set => Add(key, value); - } - } -} diff --git a/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs b/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs index 66daaa8a8..8bd933bc3 100644 --- a/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/AnnotationLexerState.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- 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 @@ -7,11 +7,22 @@ This Source Code Form is subject to the terms of the namespace OneScript.Language.LexicalAnalysis { - public class AnnotationLexerState : WordLexerState + public class AnnotationLexerState : LexerState { + const string MESSAGE_ANNOTATION_EXPECTED = "Ожидается имя аннотации"; + private readonly WordLexerState _wordExtractor = new WordLexerState(); + public override Lexem ReadNextLexem(SourceCodeIterator iterator) { - var lexem = base.ReadNextLexem(iterator); + iterator.MoveNext(); + + if (!iterator.MoveToContent()) + throw CreateExceptionOnCurrentLine(MESSAGE_ANNOTATION_EXPECTED, iterator); + + if (!char.IsLetter(iterator.CurrentSymbol)) + throw CreateExceptionOnCurrentLine(MESSAGE_ANNOTATION_EXPECTED, iterator); + + var lexem = _wordExtractor.ReadNextLexem(iterator); lexem.Type = LexemType.Annotation; return lexem; } diff --git a/src/OneScript.Language/LexicalAnalysis/CodeRange.cs b/src/OneScript.Language/LexicalAnalysis/CodeRange.cs new file mode 100644 index 000000000..666dcce14 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/CodeRange.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + public readonly struct CodeRange + { + public CodeRange(int line, int column) + { + LineNumber = line; + ColumnNumber = column; + } + + public static CodeRange EmptyRange() + { + return new CodeRange(-1,-1); + } + + public int LineNumber { get; } + + public int ColumnNumber { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/DateLexerState.cs b/src/OneScript.Language/LexicalAnalysis/DateLexerState.cs index 61e28523f..ab29c15f0 100644 --- a/src/OneScript.Language/LexicalAnalysis/DateLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/DateLexerState.cs @@ -52,7 +52,7 @@ public override Lexem ReadNextLexem(SourceCodeIterator iterator) var cs = iterator.CurrentSymbol; if (cs == SpecialChars.DateQuote) { - iterator.GetContents(1, 1); + iterator.GetContentSpan(); iterator.MoveNext(); try @@ -60,7 +60,8 @@ public override Lexem ReadNextLexem(SourceCodeIterator iterator) var lex = new Lexem() { Type = LexemType.DateLiteral, - Content = FullDateTimeString(numbers) + Content = FullDateTimeString(numbers), + Location = new CodeRange(iterator.CurrentLine, iterator.CurrentColumn) }; return lex; diff --git a/src/OneScript.Language/LexicalAnalysis/DefaultLexer.cs b/src/OneScript.Language/LexicalAnalysis/DefaultLexer.cs new file mode 100644 index 000000000..741526698 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/DefaultLexer.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + /// + /// Лексер, который выдает все, кроме комментариев. + /// + public class DefaultLexer : FullSourceLexer + { + public override Lexem NextLexem() + { + Lexem lex; + while((lex = base.NextLexem()).Type == LexemType.Comment) + ; // skip + + return lex; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/ExpressionBasedLexer.cs b/src/OneScript.Language/LexicalAnalysis/ExpressionBasedLexer.cs new file mode 100644 index 000000000..002744b9e --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/ExpressionBasedLexer.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.SyntaxAnalysis; + +namespace OneScript.Language.LexicalAnalysis +{ + public delegate LexerState LexerStateSelector(SourceCodeIterator iterator, char symbol); + + public class ExpressionBasedLexer : ILexer + { + private readonly LexerStateSelector _selector; + + internal ExpressionBasedLexer(LexerStateSelector selector) + { + _selector = selector; + } + + public Lexem NextLexem() + { + if (Iterator.MoveToContent()) + { + var state = _selector(Iterator, Iterator.CurrentSymbol); + if (state == default) + { + throw new SyntaxErrorException(Iterator.GetErrorPosition(), + LocalizedErrors.UnexpectedSymbol(Iterator.CurrentSymbol)); + } + + return state.ReadNextLexem(Iterator); + } + return Lexem.EndOfText(); + } + + public SourceCodeIterator Iterator { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/FixedLexerState.cs b/src/OneScript.Language/LexicalAnalysis/FixedLexerState.cs new file mode 100644 index 000000000..91a381a7a --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/FixedLexerState.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + public class FixedLexerState : LexerState + { + private Lexem Lex { get; set; } + + public void SetOutput(Lexem lex) + { + Lex = lex; + } + + public override Lexem ReadNextLexem(SourceCodeIterator iterator) + { + return Lex; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs b/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs index 89d519fe8..0fc6c88c2 100644 --- a/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs +++ b/src/OneScript.Language/LexicalAnalysis/FullSourceLexer.cs @@ -9,60 +9,33 @@ This Source Code Form is subject to the terms of the namespace OneScript.Language.LexicalAnalysis { - public class FullSourceLexer : ILexemGenerator + public class FullSourceLexer : ILexer { - private string _code; private SourceCodeIterator _iterator; private LexerState _state; - private LexerState _emptyState = new EmptyLexerState(); - private LexerState _wordState = new WordLexerState(); - private LexerState _numberState = new NumberLexerState(); - private LexerState _stringState = new StringLexerState(); - private LexerState _operatorState = new OperatorLexerState(); - private LexerState _dateState = new DateLexerState(); - private LexerState _commentState = new CommentLexerState(); - private FixedParserState _fixedState = new FixedParserState(); - private AnnotationLexerState _annotationState = new AnnotationLexerState(); - private PreprocessorDirectiveLexerState _directiveState = new PreprocessorDirectiveLexerState(); - - private class FixedParserState : LexerState - { - Lexem _lex; - - public void SetOutput(Lexem lex) - { - _lex = lex; - } - - public override Lexem ReadNextLexem(SourceCodeIterator iterator) - { - return _lex; - } - } + private readonly LexerState _emptyState = new EmptyLexerState(); + private readonly LexerState _wordState = new WordLexerState(); + private readonly LexerState _numberState = new NumberLexerState(); + private readonly LexerState _stringState = new StringLexerState(); + private readonly LexerState _operatorState = new OperatorLexerState(); + private readonly LexerState _dateState = new DateLexerState(); + private readonly LexerState _commentState = new CommentLexerState(); + private readonly LexerState _annotationState = new AnnotationLexerState(); + private readonly LexerState _directiveState = new PreprocessorDirectiveLexerState(); + private readonly LexerState _labelState = new LabelLexerState(); + + private readonly FixedLexerState _fixedState = new FixedLexerState(); public FullSourceLexer() { _iterator = new SourceCodeIterator(); } - - public SourceCodeIterator Iterator => _iterator; - - public int CurrentColumn => _iterator.CurrentColumn; - - public int CurrentLine => _iterator.CurrentLine; - - public virtual string Code + + public SourceCodeIterator Iterator { - get - { - return _code; - } - set - { - _code = value; - _iterator = new SourceCodeIterator(value); - } + get => _iterator; + set => _iterator = value; } public virtual Lexem NextLexem() @@ -145,13 +118,15 @@ private void SelectState() } else if (cs == SpecialChars.Annotation) { - _iterator.GetContents(); - _iterator.MoveNext(); _state = _annotationState; } + else if (cs == SpecialChars.Tilde) + { + _state = _labelState; + } else { - var cp = _iterator.GetPositionInfo(); + var cp = _iterator.GetErrorPosition(); var exc = new SyntaxErrorException(cp, string.Format("Неизвестный символ {0}", cs)); if (!HandleError(exc)) { @@ -176,18 +151,14 @@ private void SetFixedState(LexemType lexemType, Token token) _fixedState.SetOutput(new Lexem() { Type = lexemType, - Token = token + Token = token, + Location = new CodeRange(Iterator.CurrentLine, Iterator.CurrentColumn) }); _state = _fixedState; } - public CodePositionInfo GetCodePosition() - { - return _iterator.GetPositionInfo(); - } - private bool HandleError(SyntaxErrorException exc) { if (UnexpectedCharacterFound != null) @@ -210,15 +181,5 @@ private bool HandleError(SyntaxErrorException exc) } public event EventHandler UnexpectedCharacterFound; - - public const int OUT_OF_TEXT = -1; - } - - public class LexerErrorEventArgs : EventArgs - { - public bool IsHandled { get; set; } - public SourceCodeIterator Iterator { get; set; } - public LexerState CurrentState { get; set; } - public SyntaxErrorException Exception{ get; set; } } } diff --git a/src/OneScript.Language/LexicalAnalysis/ILexemGenerator.cs b/src/OneScript.Language/LexicalAnalysis/ILexemGenerator.cs deleted file mode 100644 index 62df3ed69..000000000 --- a/src/OneScript.Language/LexicalAnalysis/ILexemGenerator.cs +++ /dev/null @@ -1,19 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace OneScript.Language.LexicalAnalysis -{ - public interface ILexemGenerator - { - string Code { get; set; } - int CurrentColumn { get; } - int CurrentLine { get; } - CodePositionInfo GetCodePosition(); - Lexem NextLexem(); - SourceCodeIterator Iterator { get; } - } -} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/ILexer.cs b/src/OneScript.Language/LexicalAnalysis/ILexer.cs new file mode 100644 index 000000000..375b700f3 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/ILexer.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + public interface ILexer + { + Lexem NextLexem(); + + SourceCodeIterator Iterator { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs b/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs index b6e5b406c..84f9521c1 100644 --- a/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs +++ b/src/OneScript.Language/LexicalAnalysis/ISourceCodeIndexer.cs @@ -9,6 +9,6 @@ namespace OneScript.Language.LexicalAnalysis { public interface ISourceCodeIndexer { - string GetCodeLine(int index); + string GetCodeLine(int lineNumber); } } diff --git a/src/OneScript.Language/LexicalAnalysis/LabelLexerState.cs b/src/OneScript.Language/LexicalAnalysis/LabelLexerState.cs new file mode 100644 index 000000000..607b53946 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/LabelLexerState.cs @@ -0,0 +1,60 @@ +/*---------------------------------------------------------- +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.Diagnostics; +using OneScript.Localization; + +namespace OneScript.Language.LexicalAnalysis +{ + public class LabelLexerState : LexerState + { + private static BilingualString MESSAGE_NAME_EXPECTED = new BilingualString( + "Ожидается имя метки", + "Label name expected" + ); + + private static BilingualString INVALID_LABEL = new BilingualString( + "Неверно задана метка", + "Invalid label definition" + ); + + private readonly WordLexerState _wordExtractor = new WordLexerState(); + + public override Lexem ReadNextLexem(SourceCodeIterator iterator) + { + Debug.Assert(iterator.CurrentSymbol == SpecialChars.Tilde); + + var start = new CodeRange(iterator.CurrentLine, iterator.CurrentColumn); + iterator.MoveNext(); + if (!iterator.MoveToContent()) + throw CreateExceptionOnCurrentLine(MESSAGE_NAME_EXPECTED.ToString(), iterator); + + if (!char.IsLetter(iterator.CurrentSymbol)) + throw CreateExceptionOnCurrentLine(MESSAGE_NAME_EXPECTED.ToString(), iterator); + + var result = _wordExtractor.ReadNextLexem(iterator); + if (!LanguageDef.IsUserSymbol(result)) + { + throw CreateExceptionOnCurrentLine(INVALID_LABEL.ToString(), iterator); + } + + result.Type = LexemType.LabelRef; + if (iterator.CurrentSymbol == SpecialChars.Colon) + { + result.Type = LexemType.Label; + var tail = iterator.ReadToLineEnd(); + if (tail.Trim().Length != 0) + { + throw CreateExceptionOnCurrentLine(INVALID_LABEL.ToString(), iterator); + } + } + + result.Location = start; + return result; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/Lexem.cs b/src/OneScript.Language/LexicalAnalysis/Lexem.cs index 1cb21c5d4..974775916 100644 --- a/src/OneScript.Language/LexicalAnalysis/Lexem.cs +++ b/src/OneScript.Language/LexicalAnalysis/Lexem.cs @@ -14,7 +14,9 @@ public struct Lexem public LexemType Type; public string Content; public Token Token; - public int LineNumber; + public CodeRange Location { get; set; } + + public int LineNumber => Location.LineNumber; public static Lexem Empty() { diff --git a/src/OneScript.Language/LexicalAnalysis/LexemType.cs b/src/OneScript.Language/LexicalAnalysis/LexemType.cs index 48f8c5fc6..86723c470 100644 --- a/src/OneScript.Language/LexicalAnalysis/LexemType.cs +++ b/src/OneScript.Language/LexicalAnalysis/LexemType.cs @@ -22,6 +22,8 @@ public enum LexemType PreprocessorDirective, Annotation, Comment, + Label, + LabelRef, EndOfText } } diff --git a/src/OneScript.Language/LexicalAnalysis/Lexer.cs b/src/OneScript.Language/LexicalAnalysis/Lexer.cs deleted file mode 100644 index 336e3c717..000000000 --- a/src/OneScript.Language/LexicalAnalysis/Lexer.cs +++ /dev/null @@ -1,21 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace OneScript.Language.LexicalAnalysis -{ - public class Lexer : FullSourceLexer - { - public override Lexem NextLexem() - { - Lexem lex; - while((lex = base.NextLexem()).Type == LexemType.Comment) - ; // skip - - return lex; - } - } -} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/LexerBuilder.cs b/src/OneScript.Language/LexicalAnalysis/LexerBuilder.cs new file mode 100644 index 000000000..873f1db57 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/LexerBuilder.cs @@ -0,0 +1,95 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading; + +namespace OneScript.Language.LexicalAnalysis +{ + public class LexerBuilder + { + private List _detectors = new List(); + private static Expression nullState = Expression.Constant(default, typeof(LexerState)); + + private readonly Lazy _selectorFunction; + + public LexerBuilder() + { + _selectorFunction = + new Lazy(MakeSelectorFunction, LazyThreadSafetyMode.PublicationOnly); + } + + public LexerDetectorBuilder Detect(Expression> detectExpression) + { + var detector = new LexerDetectorBuilder(detectExpression); + _detectors.Add(detector); + return detector; + } + + public ILexer Build() + { + return new ExpressionBasedLexer(_selectorFunction.Value); + } + + private LexerStateSelector MakeSelectorFunction() + { + Expression expr; + var charParam = Expression.Parameter(typeof(char), "cs"); + var iteratorParam = Expression.Parameter(typeof(SourceCodeIterator), "i"); + + using (var iterator = _detectors.GetEnumerator()) + { + var map = new Dictionary() + { + {typeof(char), charParam}, + {typeof(SourceCodeIterator), iteratorParam} + }; + var remapper = new ParameterRemapper(map); + expr = BuildNode(iterator, remapper); + } + + + var lambda = Expression.Lambda(expr, iteratorParam, charParam); + var selectorFunction = lambda.Compile(); + return selectorFunction; + } + + private Expression BuildNode(IEnumerator iterator, ParameterRemapper parameterRemapper) + { + if (iterator.MoveNext()) + { + var test = parameterRemapper.Visit(iterator.Current.DetectExpression.Body); + var truePart = Expression.Convert(iterator.Current.HandlerExpression, typeof(LexerState)); + var falsePart = BuildNode(iterator, parameterRemapper); + return Expression.Condition(test, truePart, falsePart); + } + + return nullState; + } + + private class ParameterRemapper : ExpressionVisitor + { + private readonly Dictionary _map; + + public ParameterRemapper(Dictionary map) + { + _map = map; + } + + public override Expression Visit(Expression node) + { + if (node?.NodeType == ExpressionType.Parameter) + { + return _map[node.Type]; + } + return base.Visit(node); + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/LexerBuilderExtensions.cs b/src/OneScript.Language/LexicalAnalysis/LexerBuilderExtensions.cs new file mode 100644 index 000000000..4cd45edc3 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/LexerBuilderExtensions.cs @@ -0,0 +1,55 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + public static class LexerBuilderExtensions + { + public static LexerBuilder DetectWords(this LexerBuilder builder) + { + builder.Detect((cs, i) => char.IsLetter(cs) || cs == SpecialChars.Underscore) + .HandleWith(new WordLexerState()); + return builder; + } + + public static LexerBuilder DetectStrings(this LexerBuilder builder) + { + builder.Detect((cs, i) => cs == SpecialChars.StringQuote) + .HandleWith(new StringLexerState()); + return builder; + } + + public static LexerBuilder DetectOperators(this LexerBuilder builder) + { + builder.Detect((cs, i) => SpecialChars.IsOperatorChar(cs) && + !(cs == '/' && i.PeekNext() == '/')) + .HandleWith(new OperatorLexerState()); + return builder; + } + + public static LexerBuilder DetectComments(this LexerBuilder builder) + { + builder.Detect((cs, i) => cs == '/' && i.PeekNext() == '/') + .HandleWith(new CommentLexerState()); + return builder; + } + + public static LexerBuilder DetectNumbers(this LexerBuilder builder) + { + builder.Detect((cs, i) => char.IsDigit(cs)) + .HandleWith(new NumberLexerState()); + return builder; + } + + public static LexerBuilder DetectPreprocessorDirectives(this LexerBuilder builder) + { + builder.Detect((cs, i) => cs == SpecialChars.Preprocessor) + .HandleWith(new PreprocessorDirectiveLexerState()); + return builder; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/LexerDetectorBuilder.cs b/src/OneScript.Language/LexicalAnalysis/LexerDetectorBuilder.cs new file mode 100644 index 000000000..9febe206e --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/LexerDetectorBuilder.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq.Expressions; + +namespace OneScript.Language.LexicalAnalysis +{ + public class LexerDetectorBuilder + { + public Expression> DetectExpression { get; } + public Expression HandlerExpression { get; private set; } + + public LexerDetectorBuilder(Expression> detectExpression) + { + DetectExpression = detectExpression; + } + + public void HandleWith(LexerState state) + { + HandlerExpression = Expression.Constant(state); + } + + public void HandleWith(Expression> selector) + { + HandlerExpression = Expression.Invoke(selector); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/LexerErrorEventArgs.cs b/src/OneScript.Language/LexicalAnalysis/LexerErrorEventArgs.cs new file mode 100644 index 000000000..b4bb19225 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/LexerErrorEventArgs.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + public class LexerErrorEventArgs : EventArgs + { + public bool IsHandled { get; set; } + public SourceCodeIterator Iterator { get; set; } + public LexerState CurrentState { get; set; } + public SyntaxErrorException Exception{ get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/LexerExtensions.cs b/src/OneScript.Language/LexicalAnalysis/LexerExtensions.cs new file mode 100644 index 000000000..2f533f6aa --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/LexerExtensions.cs @@ -0,0 +1,48 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + public static class LexerExtensions + { + public static int ReadToLineEnd(this ILexer lexer) + { + var data = lexer.Iterator.ReadToLineEnd(); + return data.Length; + } + + public static Lexem NextLexemOnSameLine(this ILexer lexer) + { + if (lexer.Iterator.CurrentSymbol == '\n') + { + return Lexem.EndOfText(); + } + + try + { + lexer.Iterator.StayOnSameLine = true; + return lexer.Iterator.MoveToContent() ? lexer.NextLexem() : Lexem.EndOfText(); + } + finally + { + lexer.Iterator.StayOnSameLine = false; + } + } + + public static void SkipTillLineEnd(this ILexer lexer) + { + lexer.ReadToLineEnd(); + } + + public static Lexem SkipTillNextStatement(this ILexer lexer) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/LexerState.cs b/src/OneScript.Language/LexicalAnalysis/LexerState.cs index 4238a697c..a0947acde 100644 --- a/src/OneScript.Language/LexicalAnalysis/LexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/LexerState.cs @@ -13,7 +13,7 @@ public abstract class LexerState public SyntaxErrorException CreateExceptionOnCurrentLine(string message, SourceCodeIterator iterator) { - var cp = iterator.GetPositionInfo(); + var cp = iterator.GetErrorPosition(); return new SyntaxErrorException(cp, message); } } diff --git a/src/OneScript.Language/LexicalAnalysis/NonWhitespaceLexerState.cs b/src/OneScript.Language/LexicalAnalysis/NonWhitespaceLexerState.cs new file mode 100644 index 000000000..998b14f63 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/NonWhitespaceLexerState.cs @@ -0,0 +1,79 @@ +/*---------------------------------------------------------- +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.Language.LexicalAnalysis +{ + public class NonWhitespaceLexerState : LexerState + { + public bool DiscardComments { get; set; } = true; + + public override Lexem ReadNextLexem(SourceCodeIterator iterator) + { + var currentLine = iterator.CurrentLine; + var currentColumn = iterator.CurrentColumn; + string content; + if (iterator.CurrentSymbol == '"') + content = QuotedValue(iterator); + else + content = UnquotedValue(iterator); + + return new Lexem + { + Content = content, + Location = new CodeRange(currentLine, currentColumn), + Type = LexemType.NotALexem, + Token = Token.NotAToken + }; + } + + private string QuotedValue(SourceCodeIterator iterator) + { + while (iterator.MoveNext() && iterator.CurrentSymbol != '"') + { + if(iterator.CurrentSymbol == '\n') + throw CreateExceptionOnCurrentLine("Незавершенный строковый литерал", iterator); + } + + if(iterator.CurrentSymbol != '"') + throw CreateExceptionOnCurrentLine("Незавершенный строковый литерал", iterator); + + iterator.MoveNext(); + return iterator.GetContents(); + } + + private string UnquotedValue(SourceCodeIterator iterator) + { + string content = default; + while (!char.IsWhiteSpace(iterator.CurrentSymbol)) + { + if (DiscardComments && iterator.CurrentSymbol == '/' && iterator.PeekNext() == '/') + { + content = iterator.GetContents(); + SkipToLineEnd(iterator); + break; + } + + if (!iterator.MoveNext()) + { + content = iterator.GetContents(); + break; + } + } + + return content ?? iterator.GetContents(); + } + + private void SkipToLineEnd(SourceCodeIterator iterator) + { + while (iterator.MoveNext() && iterator.CurrentSymbol != '\n') + { + } + + iterator.GetContentSpan(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/OperatorLexerState.cs b/src/OneScript.Language/LexicalAnalysis/OperatorLexerState.cs index 3f679820b..0ec883673 100644 --- a/src/OneScript.Language/LexicalAnalysis/OperatorLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/OperatorLexerState.cs @@ -48,11 +48,14 @@ private static Lexem ExtractOperatorContent(SourceCodeIterator iterator) { Lexem lex; var content = iterator.GetContents(); - lex = new Lexem() + lex = new Lexem { Type = LexemType.Operator, Content = content, - Token = LanguageDef.GetToken(content) + Token = LanguageDef.GetToken(content), + Location = new CodeRange( + iterator.CurrentLine, + iterator.CurrentColumn) }; return lex; } diff --git a/src/OneScript.Language/LexicalAnalysis/PreprocessingLexer.cs b/src/OneScript.Language/LexicalAnalysis/PreprocessingLexer.cs deleted file mode 100644 index 8082af46b..000000000 --- a/src/OneScript.Language/LexicalAnalysis/PreprocessingLexer.cs +++ /dev/null @@ -1,386 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; - -namespace OneScript.Language.LexicalAnalysis -{ - public class PreprocessingLexer : ILexemGenerator - { - HashSet _definitions = new HashSet(StringComparer.InvariantCultureIgnoreCase); - FullSourceLexer _lexer; - string _code; - - Lexem _lastExtractedLexem; - - Stack _blocks = new Stack(); - private int _regionsNesting = 0; - - private class PreprocessorBlock - { - public bool IsSolved; - } - - public PreprocessingLexer() - { - _lexer = new FullSourceLexer(); - } - - public event EventHandler UnknownDirective; - - public void Define(string param) - { - _definitions.Add(param); - } - - public bool IsDefined(string param) - { - return _definitions.Contains(param); - } - - public void Undef(string param) - { - _definitions.Remove(param); - } - - private void PushBlock() - { - var block = new PreprocessorBlock() - { - IsSolved = false - }; - _blocks.Push(block); - } - - private bool BlockIsSolved() - { - if (_blocks.Count == 0) - throw PreprocessorError("Ожидается директива #Если"); - - return _blocks.Peek().IsSolved; - } - - private void MarkAsSolved() - { - _blocks.Peek().IsSolved = true; - } - - private void PopBlock() - { - if (_blocks.Count > 0) - _blocks.Pop(); - else - throw PreprocessorError("Пропущена директива #Если"); - } - - private int BlockLevel() - { - return _blocks.Count; - } - - private void HandleUnknownDirective(Lexem lex) - { - if (UnknownDirective != null) - { - var args = new PreprocessorUnknownTokenEventArgs() - { - Lexer = _lexer, - Lexem = lex - }; - - UnknownDirective(this, args); - if (args.IsHandled) - { - _lastExtractedLexem = args.Lexem; - return; - } - - } - - throw PreprocessorError("Неизвестная директива: " + lex.Content); - - } - - private SyntaxErrorException PreprocessorError(string message) - { - return new SyntaxErrorException(_lexer.GetCodePosition(), message); - } - - private bool SolveExpression() - { - MoveNextSameLine(); - - return SolveOrExpression(); - } - - private bool SolveOrExpression() - { - var argument = SolveAndExpression(); - if (_lastExtractedLexem.Token == Token.Then) - { - return argument; - } - - if (_lastExtractedLexem.Token == Token.Or) - { - MoveNextSameLine(); - var secondArgument = SolveOrExpression(); - return argument || secondArgument; // здесь нужны НЕ-сокращенные вычисления - } - - return argument; - } - - private bool SolveAndExpression() - { - var argument = SolveNotExpression(); - - if (_lastExtractedLexem.Token == Token.And) - { - MoveNextSameLine(); - var secondArgument = SolveAndExpression(); - return argument && secondArgument; // здесь нужны НЕ-сокращенные вычисления - } - - return argument; - } - - private bool SolveNotExpression() - { - if (_lastExtractedLexem.Token == Token.Not) - { - MoveNextSameLine(); - return !GetArgument(); - } - - return GetArgument(); - } - - private bool GetArgument() - { - if (_lastExtractedLexem.Token == Token.OpenPar) - { - MoveNextSameLine(); - var result = SolveOrExpression(); - if (_lastExtractedLexem.Token == Token.ClosePar) - { - MoveNextSameLine(); - return result; - } - throw PreprocessorError("Ожидается закрывающая скобка"); - } - - if (!LanguageDef.IsUserSymbol(ref _lastExtractedLexem)) - throw PreprocessorError("Ожидается объявление препроцессора"); - - var expression = IsDefined(_lastExtractedLexem.Content); - MoveNextSameLine(); - return expression; - } - - public string Code - { - get - { - return _code; - } - set - { - _code = value; - _lexer.Code = _code; - } - } - - public int CurrentColumn => _lexer.CurrentColumn; - public int CurrentLine => _lexer.CurrentLine; - public CodePositionInfo GetCodePosition() => _lexer.GetCodePosition(); - public SourceCodeIterator Iterator => _lexer.Iterator; - - public Lexem NextLexem() - { - do - { - MoveNext(); - if (_lastExtractedLexem.Type == LexemType.PreprocessorDirective) - _lastExtractedLexem = Preprocess(_lastExtractedLexem); - } - while (_lastExtractedLexem.Type == LexemType.Comment); - - if (_lastExtractedLexem.Type == LexemType.EndOfText) - { - if (BlockLevel() != 0) - throw PreprocessorError("Ожидается завершение директивы препроцессора #Если"); - if (_regionsNesting != 0) - throw PreprocessorError("Ожидается завершение директивы препроцессора #Область"); - } - - return _lastExtractedLexem; - } - - private void MoveNext() - { - _lastExtractedLexem = _lexer.NextLexem(); - } - - private void MoveNextSameLine() - { - _lastExtractedLexem = _lexer.NextLexem(); - if (_lexer.Iterator.OnNewLine) - throw PreprocessorError("Неожиданное завершение директивы"); - } - - private Lexem Preprocess(Lexem directive) - { - if(directive.Token == Token.If) - { - PushBlock(); - return ResolveCondition(); - } - else if(directive.Token == Token.ElseIf) - { - if (BlockIsSolved()) - { - SolveExpression(); // проверить корректность условия - SkipTillNextDirective(); - return Preprocess(_lastExtractedLexem); - } - - return ResolveCondition(); - } - else if(directive.Token == Token.Else) - { - if (BlockIsSolved()) - { - SkipTillNextDirective(); - if (_lastExtractedLexem.Token != Token.EndIf) - throw PreprocessorError("Ожидается директива препроцессора #КонецЕсли"); - - return Preprocess(_lastExtractedLexem); - } - - return LexemFromNewLine(); - } - else if(directive.Token == Token.EndIf) - { - PopBlock(); - return LexemFromNewLine(); - } - else - { - if (LanguageDef.IsPreprocRegion(directive.Content)) - { - MoveNext(); - if (_lexer.Iterator.OnNewLine) - throw PreprocessorError("Ожидается имя области"); - - if (!LanguageDef.IsIdentifier(ref _lastExtractedLexem)) - throw PreprocessorError($"Недопустимое имя Области: {_lastExtractedLexem.Content}"); - - _regionsNesting++; - - return LexemFromNewLine(); - } - else if (LanguageDef.IsPreprocEndRegion(directive.Content)) - { - if (_regionsNesting == 0) - throw PreprocessorError("Пропущена директива препроцессора #Область"); - - _regionsNesting--; - - return LexemFromNewLine(); - } - } - - HandleUnknownDirective(_lastExtractedLexem); - - return _lastExtractedLexem; - } - - private Lexem ResolveCondition() - { - var enterBlock = SolveExpression(); - if (_lastExtractedLexem.Token != Token.Then) - throw PreprocessorError("Ошибка в директиве препроцессора: ожидается ключевое слово Тогда"); - - if (enterBlock) - { - MarkAsSolved(); - return LexemFromNewLine(); - } - else - { - SkipTillNextDirective(); - return Preprocess(_lastExtractedLexem); - } - } - - private Lexem LexemFromNewLine() - { - var lex = NextLexem(); - CheckNewLine(); - - return lex; - } - - private void CheckNewLine() - { - if (!_lexer.Iterator.OnNewLine) - throw PreprocessorError("Недопустимые символы в директиве"); - } - - private void SkipComment() - { - _lexer.Iterator.MoveToContent(); - if (!_lexer.Iterator.OnNewLine) - { - MoveNext(); - if (_lastExtractedLexem.Type != LexemType.Comment) - throw PreprocessorError("Недопустимые символы в директиве"); - } - } - - private void SkipErrors(object sender, LexerErrorEventArgs args) - { - args.Iterator.MoveNext(); - args.IsHandled = true; - } - - private void SkipTillNextDirective() - { - SkipComment(); - - _lexer.UnexpectedCharacterFound += SkipErrors; - - int currentLevel = BlockLevel(); - while (true) - { - MoveNext(); - - while (_lastExtractedLexem.Type != LexemType.PreprocessorDirective) - { - if (_lastExtractedLexem.Token == Token.EndOfText) - throw PreprocessorError("Ожидается директива препроцессора #КонецЕсли"); - - MoveNext(); - } - - if (_lastExtractedLexem.Token == Token.If) - PushBlock(); - else if (_lastExtractedLexem.Token == Token.EndIf && BlockLevel() > currentLevel) - PopBlock(); - else if (BlockLevel() == currentLevel && - (_lastExtractedLexem.Token == Token.EndIf || - _lastExtractedLexem.Token == Token.ElseIf || - _lastExtractedLexem.Token == Token.Else) ) - break; - } - - _lexer.UnexpectedCharacterFound -= SkipErrors; - } - } -} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs b/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs index 23f5c27c3..95a676ffb 100644 --- a/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/PreprocessorDirectiveLexerState.cs @@ -11,7 +11,7 @@ namespace OneScript.Language.LexicalAnalysis { public class PreprocessorDirectiveLexerState : LexerState { - WordLexerState _wordExtractor = new WordLexerState(); + private readonly WordLexerState _wordExtractor = new WordLexerState(); const string MESSAGE_DIRECTIVE_EXPECTED = "Ожидается директива препроцессора"; public override Lexem ReadNextLexem(SourceCodeIterator iterator) @@ -22,7 +22,7 @@ public override Lexem ReadNextLexem(SourceCodeIterator iterator) throw CreateExceptionOnCurrentLine("Недопустимое начало директивы препроцессора", iterator); iterator.MoveNext(); - var position = iterator.GetPositionInfo(); + var position = iterator.GetErrorPosition(); if (!iterator.MoveToContent()) throw CreateExceptionOnCurrentLine(MESSAGE_DIRECTIVE_EXPECTED, iterator); if (position.LineNumber != iterator.CurrentLine) diff --git a/src/OneScript.Language/LexicalAnalysis/PreprocessorUnknownTokenEventArgs.cs b/src/OneScript.Language/LexicalAnalysis/PreprocessorUnknownTokenEventArgs.cs index 918c64743..1256d250c 100644 --- a/src/OneScript.Language/LexicalAnalysis/PreprocessorUnknownTokenEventArgs.cs +++ b/src/OneScript.Language/LexicalAnalysis/PreprocessorUnknownTokenEventArgs.cs @@ -12,7 +12,7 @@ namespace OneScript.Language.LexicalAnalysis public class PreprocessorUnknownTokenEventArgs : EventArgs { public bool IsHandled { get; set; } - public ILexemGenerator Lexer { get; set; } + public ILexer Lexer { get; set; } public Lexem Lexem { get; set; } } } \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/SourceCodeExtensions.cs b/src/OneScript.Language/LexicalAnalysis/SourceCodeExtensions.cs new file mode 100644 index 000000000..a8e498d62 --- /dev/null +++ b/src/OneScript.Language/LexicalAnalysis/SourceCodeExtensions.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace OneScript.Language.LexicalAnalysis +{ + public static class SourceCodeExtensions + { + public static ErrorPositionInfo GetErrorPosition(this SourceCodeIterator iterator) + { + return new ErrorPositionInfo + { + LineNumber = iterator.CurrentLine, + ColumnNumber = iterator.CurrentColumn, + Code = iterator.GetCodeLine(iterator.CurrentLine), + ModuleName = iterator.Source.Name + }; + } + + public static ErrorPositionInfo GetErrorPosition(this ILexer lexer) + { + return lexer.Iterator.GetErrorPosition(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs index f0a11e306..720ebf271 100644 --- a/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs +++ b/src/OneScript.Language/LexicalAnalysis/SourceCodeIterator.cs @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; +using OneScript.Sources; namespace OneScript.Language.LexicalAnalysis { @@ -17,24 +18,34 @@ public class SourceCodeIterator : ISourceCodeIndexer private int _lineCounter; private int _index; private int _startPosition; + private int _codeLength; private List _lineBounds; private bool _onNewLine; public bool OnNewLine { get; private set; } + + public bool StayOnSameLine { get; set; } private const int OUT_OF_TEXT = -1; - public SourceCodeIterator() : this(string.Empty) + public SourceCodeIterator(SourceCode code) { + Source = code; + InitOnString(code.GetSourceCode()); } - public SourceCodeIterator(string code) + internal SourceCodeIterator() { - if (code == null) - throw new ArgumentNullException(nameof(code)); + InitOnString(String.Empty); + } + + public SourceCode Source { get; } + private void InitOnString(string code) + { _code = code; + _codeLength = code.Length; int cap = code.Length < 512 ? 32 : 512; _lineBounds = new List(cap); _index = OUT_OF_TEXT; @@ -59,18 +70,8 @@ public SourceCodeIterator(string code) public int CurrentLine => _lineCounter; public int CurrentColumn - { - get - { - if (_startPosition == OUT_OF_TEXT) - { - return OUT_OF_TEXT; - } - - int start = GetLineBound(CurrentLine); - return _index - start + 1; - } - } + => _startPosition == OUT_OF_TEXT ? OUT_OF_TEXT : _index - _lineBounds[^1] + 1; + // CurrentLine's start is last in _lineBounds public char CurrentSymbol => _currentSymbol; @@ -78,14 +79,13 @@ public int CurrentColumn public bool MoveNext() { _index++; - if (_index < _code.Length) + if (_index < _codeLength) { _currentSymbol = _code[_index]; if (_currentSymbol == '\n') { _lineCounter++; - if (_index < _code.Length) - _lineBounds.Add(_index + 1); + _lineBounds.Add(_index + 1); } return true; @@ -100,7 +100,7 @@ public bool MoveNext() public char PeekNext() { char result = '\0'; - if (_index + 1 < _code.Length) + if (_index + 1 < _codeLength) { result = _code[_index + 1]; } @@ -134,6 +134,9 @@ public bool SkipSpaces() { if (_currentSymbol == '\n') { + if (StayOnSameLine) + return false; + _onNewLine = true; } @@ -143,20 +146,36 @@ public bool SkipSpaces() } } - if (_index >= _code.Length) + if (_index >= _codeLength) { return false; } return true; + } + + public char ReadNextChar() + { + while (Char.IsWhiteSpace(_currentSymbol)) + { + if (_currentSymbol == '\n') + { + _onNewLine = true; + } + if (!MoveNext()) + { + break; + } + } + + return _currentSymbol; } + public string ReadToLineEnd() { - while (MoveNext()) + while (_currentSymbol != '\n' && MoveNext()) { - if (_currentSymbol == '\n') - break; } var res = GetContents(); @@ -170,6 +189,9 @@ public string ReadToLineEnd() public string GetCodeLine(int lineNumber) { int start = GetLineBound(lineNumber); + if (start >= _code.Length) + return String.Empty; + int end = _code.IndexOf('\n', start); if (end >= 0) { @@ -185,17 +207,12 @@ private int GetLineBound(int lineNumber) { return _lineBounds[lineNumber - 1]; } - - public string GetContents() - { - return GetContents(0, 0); - } - - public string GetContents(int padLeft, int padRight) + + public ReadOnlyMemory GetContentSpan() { int len; - if (_startPosition == _index && _startPosition < _code.Length) + if (_startPosition == _index && _startPosition < _codeLength) { len = 1; } @@ -205,29 +222,22 @@ public string GetContents(int padLeft, int padRight) } else { - return ""; + return ReadOnlyMemory.Empty; } - var contents = _code.Substring(_startPosition + padLeft, len - padRight); + var contents = _code.AsMemory(_startPosition, len); _startPosition = _index + 1; - + OnNewLine = _onNewLine; _onNewLine = false; return contents; } - - public CodePositionInfo GetPositionInfo() + + public string GetContents() { - var posInfo = new CodePositionInfo() - { - LineNumber = CurrentLine, - ColumnNumber = CurrentColumn, - Code = GetCodeLine(CurrentLine) - }; - - return posInfo; + return GetContentSpan().ToString(); } } } diff --git a/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs b/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs index 3afe4c4df..b9163315d 100644 --- a/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/StringLexerState.cs @@ -1,97 +1,96 @@ -/*---------------------------------------------------------- -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.Text; - -namespace OneScript.Language.LexicalAnalysis -{ - public class StringLexerState : LexerState - { - private void SkipSpacesAndComments(SourceCodeIterator iterator) - { - while (true) - { /* Пропускаем все пробелы и комментарии */ - iterator.SkipSpaces(); - - if (iterator.CurrentSymbol == '/') - { - if (!iterator.MoveNext()) - throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); - - if (iterator.CurrentSymbol != '/') - throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); - - do - { - if (!iterator.MoveNext()) - break; - - } while (iterator.CurrentSymbol != '\n'); - - } - else - break; - } - } - - public override Lexem ReadNextLexem(SourceCodeIterator iterator) - { +/*---------------------------------------------------------- +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.Text; + +namespace OneScript.Language.LexicalAnalysis +{ + public class StringLexerState : LexerState + { + private void SkipSpacesAndComments(SourceCodeIterator iterator) + { + while (true) + { /* Пропускаем все пробелы и комментарии */ + if (iterator.ReadNextChar() == '/') + { + if (!iterator.MoveNext()) + throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); + + if (iterator.CurrentSymbol != '/') + throw CreateExceptionOnCurrentLine("Некорректный символ", iterator); + + do + { + if (!iterator.MoveNext()) + break; + + } while (iterator.CurrentSymbol != '\n'); + + } + else + break; + } + } + + public override Lexem ReadNextLexem(SourceCodeIterator iterator) + { StringBuilder contentBuilder = new StringBuilder(); - - while (iterator.MoveNext()) - { - var cs = iterator.CurrentSymbol; - - if (cs == SpecialChars.StringQuote) - { - if (iterator.MoveNext()) - { - if (iterator.CurrentSymbol == SpecialChars.StringQuote) - { - /* Двойная кавычка */ - contentBuilder.Append("\""); - continue; - } - - /* Завершение строки */ - SkipSpacesAndComments(iterator); - - if (iterator.CurrentSymbol == SpecialChars.StringQuote) - { - /* Сразу же началась новая строка */ - contentBuilder.Append('\n'); - continue; - } - } - - var lex = new Lexem - { - Type = LexemType.StringLiteral, - Content = contentBuilder.ToString() - }; - return lex; - } - - if (cs == '\n') - { - iterator.MoveNext(); - SkipSpacesAndComments(iterator); - - if (iterator.CurrentSymbol != '|') - throw CreateExceptionOnCurrentLine("Некорректный строковый литерал!", iterator); - - contentBuilder.Append('\n'); - } - else if(cs != '\r') - contentBuilder.Append(cs); - - } - - throw CreateExceptionOnCurrentLine("Незавершённый строковой интервал!", iterator); - } - } -} + + while (iterator.MoveNext()) + { + var cs = iterator.CurrentSymbol; + + if (cs == SpecialChars.StringQuote) + { + if (iterator.MoveNext()) + { + if (iterator.CurrentSymbol == SpecialChars.StringQuote) + { + /* Двойная кавычка */ + contentBuilder.Append('"'); + + continue; + } + + /* Завершение строки */ + SkipSpacesAndComments(iterator); + + if (iterator.CurrentSymbol == SpecialChars.StringQuote) + { + /* Сразу же началась новая строка */ + contentBuilder.Append('\n'); + + continue; + } + } + + return new Lexem + { + Type = LexemType.StringLiteral, + Content = contentBuilder.ToString() + }; + } + + if (cs == '\n') + { + iterator.MoveNext(); + SkipSpacesAndComments(iterator); + + if (iterator.CurrentSymbol != '|') + throw CreateExceptionOnCurrentLine("Некорректный строковый литерал", iterator); + + contentBuilder.Append('\n'); + } + else if (cs != '\r') + contentBuilder.Append(cs); + + } + + throw CreateExceptionOnCurrentLine("Незавершённый строковый литерал", iterator); + } + } +} diff --git a/src/OneScript.Language/LexicalAnalysis/Token.cs b/src/OneScript.Language/LexicalAnalysis/Token.cs index b120b026c..681fdf324 100644 --- a/src/OneScript.Language/LexicalAnalysis/Token.cs +++ b/src/OneScript.Language/LexicalAnalysis/Token.cs @@ -41,12 +41,17 @@ public enum Token Execute, AddHandler, RemoveHandler, - - // operators + Async, + Await, + Goto, + + // operators + UnaryPlus, + UnaryMinus, + // binary begin + // recommend to be in continuous block Plus, Minus, - UnaryPlus, - UnaryMinus, Multiply, Division, Modulo, @@ -58,6 +63,7 @@ public enum Token NotEqual, And, Or, + // binary end Not, Dot, OpenPar, @@ -109,12 +115,14 @@ public enum Token Hour, Minute, Second, + BegOfWeek, BegOfYear, BegOfMonth, BegOfDay, BegOfHour, BegOfMinute, BegOfQuarter, + EndOfWeek, EndOfYear, EndOfMonth, EndOfDay, diff --git a/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs b/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs index 30dcd98f2..3fa944d63 100644 --- a/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs +++ b/src/OneScript.Language/LexicalAnalysis/WordLexerState.cs @@ -11,88 +11,78 @@ public class WordLexerState : LexerState { public override Lexem ReadNextLexem(SourceCodeIterator iterator) { - bool isEndOfText = false; - char cs = '\0'; - int currentLine = iterator.CurrentLine; - while (true) - { - if (!isEndOfText) - { - cs = iterator.CurrentSymbol; - } - if (SpecialChars.IsDelimiter(cs) || isEndOfText) - { - var content = iterator.GetContents(); + var location = new CodeRange(iterator.CurrentLine, iterator.CurrentColumn); - Lexem lex; - - if (LanguageDef.IsLogicalOperatorString(content)) - { - lex = new Lexem() - { - Type = LexemType.Operator, - Token = LanguageDef.GetToken(content), - Content = content, - LineNumber = currentLine - }; - } - else if (LanguageDef.IsBooleanLiteralString(content)) - { - lex = new Lexem() - { - Type = LexemType.BooleanLiteral, - Content = content, - LineNumber = currentLine - }; - } - else if (LanguageDef.IsUndefinedString(content)) - { - lex = new Lexem() - { - Type = LexemType.UndefinedLiteral, - Content = content, - LineNumber = currentLine - }; - - } - else if (LanguageDef.IsNullString(content)) - { - lex = new Lexem() - { - Type = LexemType.NullLiteral, - Content = content, - LineNumber = currentLine - }; - - } - else - { - lex = new Lexem() - { - Type = LexemType.Identifier, - Content = content, - Token = LanguageDef.GetToken(content), - LineNumber = currentLine - }; - - if (LanguageDef.IsBuiltInFunction(lex.Token)) - { - iterator.SkipSpaces(); - if (iterator.CurrentSymbol != '(') - { - lex.Token = Token.NotAToken; - } - } - } - - return lex; - } - - if (!iterator.MoveNext()) - { - isEndOfText = true; - } + do + { + if (SpecialChars.IsDelimiter(iterator.CurrentSymbol)) + break; } + while (iterator.MoveNext()); + + var content = iterator.GetContents(); + Lexem lex; + + switch (LanguageDef.GetWordType(content)) + { + case LanguageDef.WordType.Logical: + lex = new Lexem() + { + Type = LexemType.Operator, + Token = LanguageDef.GetToken(content), + Content = content, + Location = location + }; + break; + + case LanguageDef.WordType.Boolean: + lex = new Lexem() + { + Type = LexemType.BooleanLiteral, + Content = content, + Location = location + }; + break; + + case LanguageDef.WordType.Undefined: + lex = new Lexem() + { + Type = LexemType.UndefinedLiteral, + Content = content, + Location = location + }; + break; + + case LanguageDef.WordType.Null: + lex = new Lexem() + { + Type = LexemType.NullLiteral, + Content = content, + Location = location + }; + break; + + default: + var tok = LanguageDef.GetToken(content); + if (LanguageDef.IsBuiltInFunction(tok)) + { + if (iterator.ReadNextChar() != '(') + { + tok = Token.NotAToken; + } + } + + lex = new Lexem() + { + Type = LexemType.Identifier, + Content = content, + Token = tok, + Location = location + }; + break; + } + + return lex; } } } diff --git a/src/OneScript.Language/ListErrorSink.cs b/src/OneScript.Language/ListErrorSink.cs new file mode 100644 index 000000000..926f29d72 --- /dev/null +++ b/src/OneScript.Language/ListErrorSink.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; + +namespace OneScript.Language +{ + public class ListErrorSink : IErrorSink + { + private List _errors; + + public IEnumerable Errors => _errors ?? new List(); + + public bool HasErrors => _errors != default && _errors.Count > 0; + + public void AddError(CodeError err) + { + if (_errors == default) + _errors = new List(); + + _errors.Add(err); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/Localization/BilingualString.cs b/src/OneScript.Language/Localization/BilingualString.cs new file mode 100644 index 000000000..62da2b570 --- /dev/null +++ b/src/OneScript.Language/Localization/BilingualString.cs @@ -0,0 +1,87 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Globalization; + +namespace OneScript.Localization +{ + public class BilingualString + { + private static readonly CultureInfo RussianCulture; + + static BilingualString() + { + try + { + RussianCulture = CultureInfo.GetCultureInfoByIetfLanguageTag("ru"); + } + catch (CultureNotFoundException) + { + } + } + + public static implicit operator BilingualString(string source) + { + return new BilingualString(source); + } + + public static implicit operator string(BilingualString str) + { + return str.ToString(); + } + + public BilingualString(string ru, string en) + { + Russian = ru; + English = en; + } + + public BilingualString(string single) + { + Russian = single; + English = string.Empty; + } + + public string Russian { get; } + + public string English { get; } + + public override string ToString() + { + return Localize(Russian, English); + } + + public bool HasName(string name, StringComparison comparison = StringComparison.CurrentCultureIgnoreCase) + { + return string.Equals(Russian, name, comparison) || string.Equals(English, name, comparison); + } + + public static bool UseRussianLocale => CultureInfo.CurrentCulture.Parent.Equals(RussianCulture); + + public static string Localize(string russian, string english) + { + var currentCulture = CultureInfo.CurrentCulture; + if (!Equals(currentCulture.Parent, RussianCulture)) + { + return string.IsNullOrEmpty(english) ? russian : english; + } + + return russian; + } + } + + public static class BilingualStringExtension + { + public static bool BilingualEquals(this string str, string lang1, string lang2, + StringComparison comparison = StringComparison.CurrentCultureIgnoreCase) + { + return string.Equals(str, lang1, comparison) || string.Equals(str, lang2, comparison); + } + } + +} \ No newline at end of file diff --git a/src/ScriptEngine/Environment/ModuleInformation.cs b/src/OneScript.Language/ModuleInformation.cs similarity index 95% rename from src/ScriptEngine/Environment/ModuleInformation.cs rename to src/OneScript.Language/ModuleInformation.cs index 69660547a..76c586640 100644 --- a/src/ScriptEngine/Environment/ModuleInformation.cs +++ b/src/OneScript.Language/ModuleInformation.cs @@ -8,8 +8,9 @@ This Source Code Form is subject to the terms of the using System; using OneScript.Language.LexicalAnalysis; -namespace ScriptEngine.Environment +namespace OneScript.Language { + [Obsolete("Use SourceCode")] [Serializable] public class ModuleInformation { diff --git a/src/OneScript.Language/OneScript.Language.csproj b/src/OneScript.Language/OneScript.Language.csproj index 8f1c6e2df..256cf7c09 100644 --- a/src/OneScript.Language/OneScript.Language.csproj +++ b/src/OneScript.Language/OneScript.Language.csproj @@ -2,7 +2,9 @@ - net452;netstandard2.0;netstandard2.1; + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug + AnyCPU @@ -16,10 +18,11 @@ OneScript OpenSource Community 2019 BSL 1C 1Script OneScript - - - - - + + + true + false + DEBUG;TRACE + diff --git a/src/OneScript.Language/ScriptException.cs b/src/OneScript.Language/ScriptException.cs index 7674dafbe..329285557 100644 --- a/src/OneScript.Language/ScriptException.cs +++ b/src/OneScript.Language/ScriptException.cs @@ -6,102 +6,89 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Collections.Generic; using System.Text; +using OneScript.Localization; namespace OneScript.Language { public class ScriptException : ApplicationException { - private readonly CodePositionInfo _codePosition; - - public ScriptException() + private readonly ErrorPositionInfo _codePosition; + + public ScriptException(string message, Exception innerException = null) + : this(new ErrorPositionInfo(), message, innerException) { - _codePosition = new CodePositionInfo(); - _codePosition.LineNumber = -1; } - public ScriptException(string message) - : this(new CodePositionInfo(), message, null) + protected ScriptException(ErrorPositionInfo errorInfo, string message, Exception innerException = null) + : base(message, innerException) { - + _codePosition = errorInfo ?? throw new ArgumentNullException(nameof(errorInfo)); } - public ScriptException(CodePositionInfo codeInfo, string message) - : this(codeInfo, message, null) + public ScriptException(ErrorPositionInfo errorInfo, Exception innerException) + : base(innerException.Message, innerException) { - + _codePosition = errorInfo ?? throw new ArgumentNullException(nameof(errorInfo)); } - - public ScriptException(CodePositionInfo codeInfo, string message, Exception innerException) - : base(message, innerException) + + public ScriptException(Exception innerException) + : base(innerException.Message, innerException) { - _codePosition = codeInfo; + _codePosition = new ErrorPositionInfo(); } - + public int LineNumber { - get - { - return _codePosition.LineNumber; - } - set - { - _codePosition.LineNumber = value; - } + get => _codePosition.LineNumber; + set => _codePosition.LineNumber = value; } public int ColumnNumber { - get - { - return _codePosition.ColumnNumber; - } - set - { - _codePosition.ColumnNumber = value; - } + get => _codePosition.ColumnNumber; + set => _codePosition.ColumnNumber = value; } public string Code { - get - { - return _codePosition.Code; - } - set - { - _codePosition.Code = value; - } + get => _codePosition.Code; + set => _codePosition.Code = value; } public string ModuleName { - get - { - return _codePosition.ModuleName; - } - set - { - _codePosition.ModuleName = value; - } + get => _codePosition.ModuleName; + set => _codePosition.ModuleName = value; } - public string ErrorDescription + public ErrorPositionInfo GetPosition() { - get - { - return base.Message; - } + return _codePosition; } + public string ErrorDescription => base.Message; + public string MessageWithoutCodeFragment { get { - if (ColumnNumber != CodePositionInfo.OUT_OF_TEXT) - return $"{{Модуль {ModuleName} / Ошибка в строке: {LineNumber},{ColumnNumber} / {base.Message}}}"; - - return $"{{Модуль {ModuleName} / Ошибка в строке: {LineNumber} / {base.Message}}}"; + var parts = new List(); + + if (!string.IsNullOrEmpty(ModuleName)) + parts.Add($"Модуль {ModuleName}"); + + if (LineNumber != ErrorPositionInfo.OUT_OF_TEXT) + { + parts.Add(ColumnNumber != ErrorPositionInfo.OUT_OF_TEXT + ? BilingualString.Localize($"Ошибка в строке: {LineNumber},{ColumnNumber}", $"Error in line {LineNumber},{ColumnNumber}") + : BilingualString.Localize($"Ошибка в строке: {LineNumber}", $"Error in line: {LineNumber}")); + } + parts.Add(base.Message); + + var unquotedResult = string.Join(" / ", parts); + return $"{{{unquotedResult}}}"; } } @@ -110,11 +97,39 @@ public override string Message get { var sb = new StringBuilder(MessageWithoutCodeFragment); - sb.AppendLine(" "); - sb.Append(Code); + sb.AppendLine(); + var codeLine = Code?.Replace('\t', ' ')?.TrimEnd() ?? String.Empty; + if (ColumnNumber > codeLine.Length) + { + ColumnNumber = codeLine.Length; + } + + if (ColumnNumber != ErrorPositionInfo.OUT_OF_TEXT) + { + sb.Append(codeLine[..ColumnNumber]); + sb.Append("<>"); + sb.AppendLine(codeLine[ColumnNumber..]); + } + else + { + sb.AppendLine(codeLine); + } return sb.ToString(); } } + + public object RuntimeSpecificInfo { get; set; } + + public void SetPositionIfEmpty(ErrorPositionInfo newPosition) + { + if (!_codePosition.IsEmpty) + return; + + _codePosition.LineNumber = newPosition.LineNumber; + _codePosition.ColumnNumber = newPosition.ColumnNumber; + _codePosition.Code = newPosition.Code; + _codePosition.ModuleName = newPosition.ModuleName; + } } } diff --git a/src/OneScript.Language/Sources/ICodeSource.cs b/src/OneScript.Language/Sources/ICodeSource.cs new file mode 100644 index 000000000..27db60fad --- /dev/null +++ b/src/OneScript.Language/Sources/ICodeSource.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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.Language.Sources +{ + public interface ICodeSource + { + string Location { get; } + + string GetSourceCode(); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/Sources/SourceCode.cs b/src/OneScript.Language/Sources/SourceCode.cs new file mode 100644 index 000000000..48c10007f --- /dev/null +++ b/src/OneScript.Language/Sources/SourceCode.cs @@ -0,0 +1,59 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.Sources; + +// ReSharper disable once CheckNamespace +namespace OneScript.Sources +{ + public class SourceCode : ISourceCodeIndexer + { + private readonly ICodeSource _source; + private ISourceCodeIndexer _indexer; + + private string _code = null; + + internal SourceCode(string sourceName, ICodeSource source, string ownerPackageId = null) + { + _source = source; + Name = sourceName; + OwnerPackageId = ownerPackageId; + } + + public SourceCodeIterator CreateIterator() + { + var newIterator = new SourceCodeIterator(this); + _indexer = newIterator; + + return newIterator; + } + + public string Location => _source.Location; + + public string Name { get; } + + /// + /// Идентификатор пакета-владельца. null если модуль не принадлежит библиотеке. + /// + public string OwnerPackageId { get; } + + public string GetSourceCode() + { + // Однократное считывание того, что отдано на компиляцию + // При изменении источника (напр. файла) в любом случае потребуется перекомпиляция и смена номеров строк. + return _code ??= _source.GetSourceCode(); + } + + public string GetCodeLine(int lineNumber) + { + return _indexer?.GetCodeLine(lineNumber); + } + + public override string ToString() => Name ?? Location; + } +} diff --git a/src/OneScript.Language/Sources/SourceCodeBuilder.cs b/src/OneScript.Language/Sources/SourceCodeBuilder.cs new file mode 100644 index 000000000..fa307c2d1 --- /dev/null +++ b/src/OneScript.Language/Sources/SourceCodeBuilder.cs @@ -0,0 +1,58 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Language.Sources; + +// ReSharper disable once CheckNamespace +namespace OneScript.Sources +{ + public class SourceCodeBuilder + { + private ICodeSource _source; + private string _moduleName; + private string _ownerPackageId; + + private SourceCodeBuilder() + { + } + + public SourceCodeBuilder FromSource(ICodeSource source) + { + _source = source; + return this; + } + + public SourceCodeBuilder WithName(string name) + { + _moduleName = name; + return this; + } + + /// + /// Устанавливает идентификатор пакета-владельца для исходного кода. + /// + public SourceCodeBuilder WithOwnerPackage(string packageId) + { + _ownerPackageId = packageId; + return this; + } + + public SourceCode Build() + { + if (_source == default) + throw new InvalidOperationException("Source is not set"); + + if (_moduleName == default) + _moduleName = _source.Location; + + return new SourceCode(_moduleName, _source, _ownerPackageId); + } + + public static SourceCodeBuilder Create() => new SourceCodeBuilder(); + } +} diff --git a/src/OneScript.Language/SpecialChars.cs b/src/OneScript.Language/SpecialChars.cs index fb46c4f42..cfc96e761 100644 --- a/src/OneScript.Language/SpecialChars.cs +++ b/src/OneScript.Language/SpecialChars.cs @@ -18,6 +18,8 @@ public static class SpecialChars public const char QuestionMark = '?'; public const char Preprocessor = '#'; public const char Annotation = '&'; + public const char Tilde = '~'; + public const char Colon = ':'; public static bool IsOperatorChar(char symbol) { diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotatableNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotatableNode.cs new file mode 100644 index 000000000..ecbd34d01 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotatableNode.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +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; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class AnnotatableNode : NonTerminalNode + { + protected AnnotatableNode() + { + } + + public AnnotatableNode(NodeKind kind) : base(kind) + { + } + + private List AnnotationsList { get; } = new List(); + + public IEnumerable Annotations => AnnotationsList; + + protected override void OnChildAdded(BslSyntaxNode child) + { + if(child is AnnotationNode anno) + AnnotationsList.Add(anno); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationNode.cs new file mode 100644 index 000000000..37095f144 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationNode.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; +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class AnnotationNode : NonTerminalNode + { + public AnnotationNode(NodeKind kind, Lexem startLexem) : base(kind, startLexem) + { + if(Kind != NodeKind.Annotation && Kind != NodeKind.Import) + throw new ArgumentException(nameof(kind)); + + Name = startLexem.Content; + } + + public string Name { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationParameterNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationParameterNode.cs new file mode 100644 index 000000000..4c1669784 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/AnnotationParameterNode.cs @@ -0,0 +1,45 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class AnnotationParameterNode : NonTerminalNode + { + public AnnotationParameterNode() : base(NodeKind.AnnotationParameter) + { + } + + protected override void OnChildAdded(BslSyntaxNode child) + { + if (child.Kind == NodeKind.Annotation) + { + AnnotationNode = (AnnotationNode)child; + } + else + { + var node = (TerminalNode)child; + if (child.Kind == NodeKind.AnnotationParameterName) + { + Name = node.Lexem.Content; + } + if (child.Kind == NodeKind.AnnotationParameterValue) + { + Value = node.Lexem; + } + } + } + + public string Name { get; private set; } + + public Lexem Value { get; private set; } + + public AnnotationNode AnnotationNode { get; private set; } + + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/BinaryOperationNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BinaryOperationNode.cs new file mode 100644 index 000000000..2582f31f0 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BinaryOperationNode.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class BinaryOperationNode : NonTerminalNode + { + public Token Operation { get; } + + public BinaryOperationNode(Lexem operation) : base(NodeKind.BinaryOperation, operation) + { + Operation = operation.Token; + } + + public BinaryOperationNode(BslSyntaxNode firstArg, BslSyntaxNode secondArg, Lexem operation) + : base(NodeKind.BinaryOperation, operation) + { + Operation = operation.Token; + AddChild(firstArg); + AddChild(secondArg); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/BranchingStatementNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BranchingStatementNode.cs new file mode 100644 index 000000000..7442bca5a --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BranchingStatementNode.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public abstract class BranchingStatementNode : NonTerminalNode + { + protected BranchingStatementNode(NodeKind kind) : base(kind) + { + } + + protected BranchingStatementNode(NodeKind kind, Lexem startLexem) : base(kind, startLexem) + { + } + + + public CodeRange EndLocation { get; private set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + EndLocation = child.Location; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/BslSyntaxNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BslSyntaxNode.cs new file mode 100644 index 000000000..4ea94496e --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/BslSyntaxNode.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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 OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public abstract class BslSyntaxNode + { + public NodeKind Kind { get; protected set; } + + public BslSyntaxNode Parent { get; internal set; } + + public CodeRange Location { get; protected set; } + + public abstract IReadOnlyList Children { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/CallNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/CallNode.cs new file mode 100644 index 000000000..da563b86d --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/CallNode.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class CallNode : NonTerminalNode + { + public CallNode(NodeKind kind, Lexem startLexem) : base (kind, startLexem) + { + } + + public TerminalNode Identifier { get; private set; } + + public BslSyntaxNode ArgumentList { get; private set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + if (child.Kind == NodeKind.Identifier) + { + Identifier = (TerminalNode) child; + } + else + { + ArgumentList = (NonTerminalNode)child; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/CodeBatchNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/CodeBatchNode.cs new file mode 100644 index 000000000..8cc0d2bb8 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/CodeBatchNode.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/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class CodeBatchNode : NonTerminalNode + { + public CodeBatchNode(Lexem startLexem) : base(NodeKind.CodeBatch, startLexem) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ConditionNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ConditionNode.cs new file mode 100644 index 000000000..86a41d118 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ConditionNode.cs @@ -0,0 +1,62 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using System.Linq; +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class ConditionNode : BranchingStatementNode + { + private int _alternativesStart = 0; + + public ConditionNode(Lexem startLexem) : base(NodeKind.Condition, startLexem) + { + } + + public BslSyntaxNode Expression { get; private set; } + + public CodeBatchNode TruePart { get; private set; } + + public IEnumerable GetAlternatives() + { + if(_alternativesStart == 0) + return System.Array.Empty(); + + return Children + .Skip(_alternativesStart) + .TakeWhile(x=>x.Kind != NodeKind.BlockEnd); + } + + protected override void OnChildAdded(BslSyntaxNode child) + { + switch (child.Kind) + { + case NodeKind.CodeBatch when TruePart == default: + TruePart = (CodeBatchNode) child; + break; + case NodeKind.CodeBatch: + case NodeKind.Condition: + UpdateAlternatives(); + break; + case NodeKind.BlockEnd: + base.OnChildAdded(child); + break; + default: + Expression = child; + break; + } + } + + private void UpdateAlternatives() + { + if (_alternativesStart == 0) + _alternativesStart = Children.Count - 1; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ErrorTerminalNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ErrorTerminalNode.cs new file mode 100644 index 000000000..a4a3c76f9 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ErrorTerminalNode.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 OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + /// + /// Нода ошибочного синтаксиса + /// + public class ErrorTerminalNode : TerminalNode + { + public ErrorTerminalNode() : base(NodeKind.Unknown) + { + } + + public ErrorTerminalNode(Lexem lexem) : base(NodeKind.Unknown, lexem) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ForEachLoopNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ForEachLoopNode.cs new file mode 100644 index 000000000..ac10525ac --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ForEachLoopNode.cs @@ -0,0 +1,55 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class ForEachLoopNode : BranchingStatementNode + { + public ForEachLoopNode(Lexem startLexem) : base(NodeKind.ForEachLoop, startLexem) + { + } + + public TerminalNode IteratorVariable { get; set; } + + public BslSyntaxNode CollectionExpression { get; set; } + + public CodeBatchNode LoopBody { get; set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + /* В случае ошибки в заголовке цикла, если строится полное дерево без выброса исключений, + парсер "перемотается" на первую понятную ему конструкцию If/Loop/Try/etc.. + В этом случае переменной или коллекции может не быть, т.к. они "перемотаются" и + парситься не будут. Нужно отловить этот случай, когда Batch добавляется, а элементы из заголовка цикла не + добавлялись. + */ + switch (child.Kind) + { + case NodeKind.ForEachVariable: + IteratorVariable = (TerminalNode) child; + break; + case NodeKind.ForEachCollection: + CollectionExpression = child.Children[0]; + break; + case NodeKind.CodeBatch: + if (CollectionExpression == default) + { + IteratorVariable = new TerminalNode(NodeKind.Unknown); + CollectionExpression = new NonTerminalNode(NodeKind.Unknown); + } + + LoopBody = (CodeBatchNode) child; + break; + case NodeKind.BlockEnd: + base.OnChildAdded(child); + break; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ForLoopNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ForLoopNode.cs new file mode 100644 index 000000000..395815314 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ForLoopNode.cs @@ -0,0 +1,49 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class ForLoopNode : BranchingStatementNode + { + public ForLoopNode(Lexem startLexem) : base(NodeKind.ForLoop, startLexem) + { + } + + public BslSyntaxNode InitializationClause { get; set; } + + public BslSyntaxNode UpperLimitExpression { get; set; } + + public CodeBatchNode LoopBody { get; set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + switch (child.Kind) + { + case NodeKind.ForInitializer: + InitializationClause = child; + break; + case NodeKind.ForLimit: + UpperLimitExpression = child.Children[0]; + break; + case NodeKind.CodeBatch: + if (UpperLimitExpression == default) + { + InitializationClause = new NonTerminalNode(NodeKind.Unknown); + UpperLimitExpression = new NonTerminalNode(NodeKind.Unknown); + } + + LoopBody = (CodeBatchNode)child; + break; + case NodeKind.BlockEnd: + base.OnChildAdded(child); + break; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/LabelNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LabelNode.cs new file mode 100644 index 000000000..9721e90ce --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LabelNode.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class LabelNode : LineMarkerNode + { + public LabelNode(Lexem labelLexem) : base(labelLexem.Location, NodeKind.Label) + { + LabelName = labelLexem.Content; + } + + public string LabelName { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/LineMarkerNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LineMarkerNode.cs new file mode 100644 index 000000000..3399bc17f --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/LineMarkerNode.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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 OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class LineMarkerNode : BslSyntaxNode + { + public LineMarkerNode(CodeRange location, NodeKind kind) + { + Location = location; + Kind = kind; + } + + public override IReadOnlyList Children => System.Array.Empty(); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs new file mode 100644 index 000000000..7cea61576 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodNode.cs @@ -0,0 +1,63 @@ +/*---------------------------------------------------------- +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 OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class MethodNode : AnnotatableNode + { + public MethodNode() : base(NodeKind.Method) + { + } + + public bool IsAsync { get; set; } + + public MethodSignatureNode Signature { get; private set; } + + public BslSyntaxNode MethodBody { get; private set; } + + public BslSyntaxNode VariableSection { get; private set; } + + public CodeRange EndLocation { get; private set; } + + public IReadOnlyList VariableDefinitions() + { + if (VariableSection == default) + return System.Array.Empty(); + + return VariableSection.Children + .Cast() + .ToList() + .AsReadOnly(); + } + + protected override void OnChildAdded(BslSyntaxNode child) + { + switch (child.Kind) + { + case NodeKind.MethodSignature: + Signature = (MethodSignatureNode) child; + break; + case NodeKind.CodeBatch: + MethodBody = child; + break; + case NodeKind.VariablesSection: + VariableSection = child; + break; + case NodeKind.BlockEnd: + EndLocation = child.Location; + break; + default: + base.OnChildAdded(child); + break; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodParameterNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodParameterNode.cs new file mode 100644 index 000000000..c903ce25e --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodParameterNode.cs @@ -0,0 +1,44 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class MethodParameterNode : AnnotatableNode + { + public MethodParameterNode() : base(NodeKind.MethodParameter) + { + } + + public bool IsByValue { get; private set; } + + public string Name { get; private set; } + + public bool HasDefaultValue { get; private set; } + + public Lexem DefaultValue { get; private set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + base.OnChildAdded(child); + switch (child.Kind) + { + case NodeKind.ByValModifier: + IsByValue = true; + break; + case NodeKind.Identifier: + Name = ((TerminalNode) child).Lexem.Content; + break; + case NodeKind.ParameterDefaultValue: + DefaultValue = ((TerminalNode) child).Lexem; + HasDefaultValue = true; + break; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodSignatureNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodSignatureNode.cs new file mode 100644 index 000000000..cc245493d --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/MethodSignatureNode.cs @@ -0,0 +1,56 @@ +/*---------------------------------------------------------- +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 OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class MethodSignatureNode : NonTerminalNode + { + public MethodSignatureNode(Lexem startLexem) + : base (NodeKind.MethodSignature, startLexem) + { + } + + public bool IsFunction { get; set; } + + public string MethodName { get; set; } + + public bool IsExported { get; set; } + + public IEnumerable GetParameters() + { + var paramList = Children.FirstOrDefault(x => x.Kind == NodeKind.MethodParameters); + if (paramList == default) + return System.Array.Empty(); + + return ((NonTerminalNode) paramList).Children.Cast(); + } + + protected override void OnChildAdded(BslSyntaxNode child) + { + base.OnChildAdded(child); + switch (child.Kind) + { + case NodeKind.Procedure: + IsFunction = false; + break; + case NodeKind.Function: + IsFunction = true; + break; + case NodeKind.Identifier: + MethodName = ((TerminalNode) child).Lexem.Content; + break; + case NodeKind.ExportFlag: + IsExported = true; + break; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/ModuleNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ModuleNode.cs new file mode 100644 index 000000000..a268f44db --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/ModuleNode.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; +using OneScript.Sources; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class ModuleNode : AnnotatableNode + { + public ModuleNode(SourceCode source, Lexem startLexem) : base(NodeKind.Module) + { + Source = source; + Location = startLexem.Location; + } + + public SourceCode Source { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/NewObjectNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/NewObjectNode.cs new file mode 100644 index 000000000..47a7afad8 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/NewObjectNode.cs @@ -0,0 +1,45 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class NewObjectNode : NonTerminalNode + { + public NewObjectNode(Lexem startLexem) : base(NodeKind.NewObject, startLexem) + { + } + + public bool IsDynamic { get; private set; } + + public BslSyntaxNode TypeNameNode { get; private set; } + + public BslSyntaxNode ConstructorArguments { get; private set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + if (Children.Count == 1) + { + if (child.Kind == NodeKind.CallArgument) + { + IsDynamic = true; + TypeNameNode = child.Children[0]; + } + else + { + IsDynamic = false; + TypeNameNode = child; + } + } + else + { + ConstructorArguments = child; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/NonTerminalNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/NonTerminalNode.cs new file mode 100644 index 000000000..3f4e83fc9 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/NonTerminalNode.cs @@ -0,0 +1,45 @@ +/*---------------------------------------------------------- +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 OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class NonTerminalNode : BslSyntaxNode + { + private List _children = new List(); + + public NonTerminalNode(NodeKind kind, Lexem startLexem) + :this(kind) + { + Location = startLexem.Location; + } + + protected NonTerminalNode() + { + } + + public NonTerminalNode(NodeKind kind) + { + Kind = kind; + } + + public override IReadOnlyList Children => _children; + + public void AddChild(BslSyntaxNode child) + { + child.Parent = this; + _children.Add(child); + OnChildAdded(child); + } + + protected virtual void OnChildAdded(BslSyntaxNode child) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/PreprocessorDirectiveNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/PreprocessorDirectiveNode.cs new file mode 100644 index 000000000..d8defeef2 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/PreprocessorDirectiveNode.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class PreprocessorDirectiveNode : NonTerminalNode + { + public PreprocessorDirectiveNode(Lexem startLexem) + : base (NodeKind.Preprocessor, startLexem) + { + DirectiveName = startLexem.Content; + } + + public string DirectiveName { get; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/TerminalNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/TerminalNode.cs new file mode 100644 index 000000000..09fe2996f --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/TerminalNode.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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 OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class TerminalNode : BslSyntaxNode + { + public Lexem Lexem { get; } + + public TerminalNode(NodeKind kind) + { + Kind = kind; + } + + public TerminalNode(NodeKind kind, Lexem lexem) + { + Kind = kind; + Lexem = lexem; + Location = lexem.Location; + } + + public override IReadOnlyList Children => System.Array.Empty(); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/TryExceptNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/TryExceptNode.cs new file mode 100644 index 000000000..2beeb321d --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/TryExceptNode.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class TryExceptNode : BranchingStatementNode + { + public TryExceptNode(Lexem startLexem) : base(NodeKind.TryExcept, startLexem) + { + } + + public CodeBatchNode TryBlock { get; private set; } + + public CodeBatchNode ExceptBlock { get; private set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + switch (Children.Count) + { + case 1: + TryBlock = (CodeBatchNode) child; + break; + case 2: + ExceptBlock = (CodeBatchNode) child; + break; + default: + base.OnChildAdded(child); + break; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs new file mode 100644 index 000000000..d55774cf1 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/UnaryOperationNode.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class UnaryOperationNode : NonTerminalNode + { + public Token Operation { get; } + + public UnaryOperationNode(Lexem operation) : base(NodeKind.UnaryOperation, operation) + { + Operation = operation.Token; + } + + public UnaryOperationNode(BslSyntaxNode arg, Lexem operation) : base(NodeKind.UnaryOperation, operation) + { + Operation = operation.Token; + AddChild(arg); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/VariableDefinitionNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/VariableDefinitionNode.cs new file mode 100644 index 000000000..d1f2a1071 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/VariableDefinitionNode.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class VariableDefinitionNode : AnnotatableNode + { + public VariableDefinitionNode(Lexem startLexem) + : base(NodeKind.VariableDefinition) + { + Location = startLexem.Location; + } + + public string Name { get; set; } + + public bool IsExported { get; private set; } + + protected override void OnChildAdded(BslSyntaxNode child) + { + if (child.Kind == NodeKind.ExportFlag) + IsExported = true; + else if (child.Kind == NodeKind.Identifier && child is TerminalNode term) + Name = term.Lexem.Content; + else + base.OnChildAdded(child); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/AstNodes/WhileLoopNode.cs b/src/OneScript.Language/SyntaxAnalysis/AstNodes/WhileLoopNode.cs new file mode 100644 index 000000000..e5342af83 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/AstNodes/WhileLoopNode.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis.AstNodes +{ + public class WhileLoopNode : BranchingStatementNode + { + public WhileLoopNode(Lexem startLexem) : base(NodeKind.WhileLoop, startLexem) + { + } + + protected override void OnChildAdded(BslSyntaxNode child) + { + if(child is LineMarkerNode) + base.OnChildAdded(child); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs b/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs new file mode 100644 index 000000000..1a0c6f8cf --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/BslSyntaxWalker.cs @@ -0,0 +1,512 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.SyntaxAnalysis +{ + public abstract class BslSyntaxWalker + { + private Action[] _nodeVisitors; + + public BslSyntaxWalker() + { + CreateVisitors(); + } + + private void CreateVisitors() + { + _nodeVisitors = new Action[ + typeof(NodeKind).GetFields(BindingFlags.Static|BindingFlags.Public).Length + ]; + + _nodeVisitors[(int)NodeKind.Module] = x => VisitModule((ModuleNode)x); + _nodeVisitors[(int)NodeKind.Assignment] = VisitAssignment; + _nodeVisitors[(int)NodeKind.DereferenceOperation] = VisitDereferenceOperation; + _nodeVisitors[(int)NodeKind.IndexAccess] = VisitIndexAccess; + _nodeVisitors[(int)NodeKind.GlobalCall] = (x) => VisitGlobalFunctionCall((CallNode)x); + _nodeVisitors[(int)NodeKind.BinaryOperation] = (x) => VisitBinaryOperation((BinaryOperationNode)x); + _nodeVisitors[(int)NodeKind.UnaryOperation] = (x) => VisitUnaryOperation((UnaryOperationNode)x); + _nodeVisitors[(int)NodeKind.TernaryOperator] = VisitTernaryOperation; + _nodeVisitors[(int)NodeKind.WhileLoop] = (x) => VisitWhileNode((WhileLoopNode)x); + _nodeVisitors[(int)NodeKind.Condition] = (x) => VisitIfNode((ConditionNode)x); + _nodeVisitors[(int)NodeKind.ForEachLoop] = (x) => VisitForEachLoopNode((ForEachLoopNode)x); + _nodeVisitors[(int)NodeKind.ForLoop] = (x) => VisitForLoopNode((ForLoopNode)x); + _nodeVisitors[(int)NodeKind.BreakStatement] = (x) => VisitBreakNode((LineMarkerNode)x); + _nodeVisitors[(int)NodeKind.ContinueStatement] = (x) => VisitContinueNode((LineMarkerNode)x); + _nodeVisitors[(int)NodeKind.ReturnStatement] = VisitReturnNode; + _nodeVisitors[(int)NodeKind.RaiseException] = VisitRaiseNode; + _nodeVisitors[(int)NodeKind.TryExcept] = (x) => VisitTryExceptNode((TryExceptNode)x); + _nodeVisitors[(int)NodeKind.ExecuteStatement] = VisitExecuteStatement; + _nodeVisitors[(int)NodeKind.AddHandler] = VisitHandlerOperation; + _nodeVisitors[(int)NodeKind.RemoveHandler] = VisitHandlerOperation; + _nodeVisitors[(int)NodeKind.NewObject] = (x) => VisitNewObjectCreation((NewObjectNode)x); + _nodeVisitors[(int)NodeKind.Preprocessor] = (x) => VisitPreprocessorDirective((PreprocessorDirectiveNode)x); + _nodeVisitors[(int)NodeKind.Goto] = (x) => VisitGotoNode((NonTerminalNode)x); + _nodeVisitors[(int)NodeKind.Label] = (x) => VisitLabelNode((LabelNode)x); + + } + + protected virtual void VisitGotoNode(NonTerminalNode node) + { + } + + protected virtual void VisitLabelNode(LabelNode node) + { + } + + protected void SetDefaultVisitorFor(NodeKind kind, Action action) + { + _nodeVisitors[(int)kind] = action; + } + + protected void ChangeVisitorsDispatch(IEnumerable> newVisitors) + { + _nodeVisitors = newVisitors.ToArray(); + } + + protected Action[] GetVisitorsDispatch() => _nodeVisitors; + + protected virtual void VisitModule(ModuleNode node) + { + foreach (var child in node.Children) + { + switch (child.Kind) + { + case NodeKind.Annotation: + case NodeKind.Import: + VisitModuleAnnotation((AnnotationNode) child); + break; + case NodeKind.VariablesSection: + foreach (var varNode in child.Children) + { + VisitModuleVariable((VariableDefinitionNode) varNode); + } + break; + case NodeKind.MethodsSection: + foreach (var methodNode in child.Children) + { + VisitMethod((MethodNode) methodNode); + } + break; + case NodeKind.ModuleBody: + VisitModuleBody(child); + break; + case NodeKind.TopLevelExpression: + var expression = child.Children.FirstOrDefault(); + if(expression != default) + VisitExpression(expression); + + break; + default: + Visit(child); + break; + } + } + + } + + protected virtual void VisitModuleAnnotation(AnnotationNode node) + { + } + + protected virtual void VisitModuleVariable(VariableDefinitionNode varNode) + { + } + + protected virtual void VisitMethod(MethodNode methodNode) + { + VisitMethodSignature(methodNode.Signature); + VisitMethodBody(methodNode); + VisitBlockEnd(methodNode.EndLocation); + } + + protected virtual void VisitBlockEnd(in CodeRange endLocation) + { + } + + protected virtual void VisitMethodSignature(MethodSignatureNode node) + { + } + + protected virtual void VisitMethodBody(MethodNode methodNode) + { + foreach (var variableDefinition in methodNode.VariableDefinitions()) + { + VisitMethodVariable(methodNode, variableDefinition); + } + + VisitCodeBlock(methodNode.MethodBody); + VisitBlockEnd(methodNode.EndLocation); + } + + protected virtual void VisitCodeBlock(CodeBatchNode statements) + { + foreach (var statement in statements.Children) + { + VisitStatement(statement); + } + } + + protected void VisitCodeBlock(BslSyntaxNode node) + { + VisitCodeBlock((CodeBatchNode)node); + } + + protected virtual void VisitStatement(BslSyntaxNode statement) + { + if(statement.Kind == NodeKind.GlobalCall) + VisitGlobalProcedureCall(statement as CallNode); + else if (statement.Kind == NodeKind.DereferenceOperation) + VisitProcedureDereference(statement); + else if (statement.Kind == NodeKind.UnaryOperation && statement is UnaryOperationNode + { + Operation: Token.Await + } unaryOp) + VisitGlobalAwaitCall(unaryOp); + else + DefaultVisit(statement); + } + + private void VisitGlobalAwaitCall(UnaryOperationNode awaitStatement) + { + VisitStatement(awaitStatement.Children[0]); + } + + protected virtual void VisitAssignment(BslSyntaxNode assignment) + { + var left = assignment.Children[0]; + var right = assignment.Children[1]; + VisitAssignmentLeftPart(left); + VisitAssignmentRightPart(right); + } + + protected virtual void VisitAssignmentLeftPart(BslSyntaxNode node) + { + } + + protected virtual void VisitAssignmentRightPart(BslSyntaxNode node) + { + VisitExpression(node); + } + + protected virtual void VisitMethodVariable(MethodNode method, VariableDefinitionNode variableDefinition) + { + } + + protected virtual void VisitReferenceRead(BslSyntaxNode node) + { + DefaultVisit(node); + } + + protected virtual void VisitReferenceWrite(BslSyntaxNode node) + { + } + + protected virtual void VisitDereferenceOperation(BslSyntaxNode node) + { + var target = node.Children[0]; + var operand = node.Children[1]; + VisitAccessTarget(target); + VisitDereferenceOperand(operand); + } + + protected virtual void VisitIndexAccess(BslSyntaxNode node) + { + var target = node.Children[0]; + var operand = node.Children[1]; + VisitAccessTarget(target); + VisitIndexExpression(operand); + } + + protected virtual void VisitIndexExpression(BslSyntaxNode operand) + { + VisitExpression(operand); + } + + protected virtual void VisitExpression(BslSyntaxNode expression) + { + if (expression is TerminalNode term) + { + if (term.Kind == NodeKind.Identifier) + VisitVariableRead(term); + else + VisitConstant(term); + } + else + { + DefaultVisit(expression); + } + } + + protected virtual void VisitConstant(TerminalNode node) + { + } + + protected virtual void VisitGlobalFunctionCall(CallNode node) + { + } + + protected virtual void VisitGlobalProcedureCall(CallNode node) + { + } + + protected virtual void VisitProcedureDereference(BslSyntaxNode statement) + { + VisitAccessTarget(statement.Children[0]); + VisitObjectProcedureCall(statement.Children[1]); + } + + protected virtual void VisitObjectProcedureCall(BslSyntaxNode node) + { + } + + protected virtual void VisitDereferenceOperand(BslSyntaxNode operand) + { + if (operand.Kind == NodeKind.Identifier) + { + VisitResolveProperty((TerminalNode) operand); + } + else + { + VisitObjectFunctionCall(operand); + } + } + + protected virtual void VisitObjectFunctionCall(BslSyntaxNode node) + { + } + + protected virtual void VisitResolveProperty(TerminalNode operand) + { + } + + protected virtual void VisitAccessTarget(BslSyntaxNode node) + { + if (node.Kind == NodeKind.Identifier) + { + VisitVariableRead((TerminalNode) node); + } + else + { + VisitReferenceRead(node); + } + } + + protected virtual void VisitVariableRead(TerminalNode node) + { + } + + protected virtual void VisitVariableWrite(TerminalNode node) + { + } + + protected virtual void VisitBinaryOperation(BinaryOperationNode node) + { + } + + protected virtual void VisitUnaryOperation(UnaryOperationNode node) + { + } + + protected virtual void VisitTernaryOperation(BslSyntaxNode node) + { + } + + protected virtual void VisitModuleBody(BslSyntaxNode codeBlock) + { + if(codeBlock.Children.Count != 0) + VisitCodeBlock(codeBlock.Children[0]); + } + + protected virtual void VisitWhileNode(WhileLoopNode node) + { + VisitWhileCondition(node.Children[0]); + VisitWhileBody(node.Children[1]); + VisitBlockEnd(node.EndLocation); + } + + protected virtual void VisitWhileBody(BslSyntaxNode node) + { + VisitCodeBlock(node); + } + + protected virtual void VisitWhileCondition(BslSyntaxNode node) + { + VisitExpression(node); + } + + protected virtual void VisitIfNode(ConditionNode node) + { + VisitIfExpression(node.Expression); + VisitIfTruePart(node.TruePart); + foreach (var alternative in node.GetAlternatives()) + { + VisitIfAlternative(alternative); + } + VisitBlockEnd(node.EndLocation); + } + + protected virtual void VisitIfExpression(BslSyntaxNode node) + { + VisitExpression(node); + } + + protected virtual void VisitIfTruePart(CodeBatchNode node) + { + VisitCodeBlock(node); + } + + protected virtual void VisitIfAlternative(BslSyntaxNode node) + { + if (node.Kind == NodeKind.Condition) + { + VisitElseIfNode((ConditionNode)node); + } + else + { + VisitElseNode((CodeBatchNode)node); + } + } + + protected virtual void VisitElseIfNode(ConditionNode node) + { + VisitIfExpression(node.Expression); + VisitIfTruePart(node.TruePart); + } + + protected virtual void VisitElseNode(CodeBatchNode node) + { + VisitCodeBlock(node); + } + + protected virtual void VisitForEachLoopNode(ForEachLoopNode node) + { + VisitIteratorLoopVariable(node.IteratorVariable); + VisitIteratorExpression(node.CollectionExpression); + VisitIteratorLoopBody(node.LoopBody); + VisitBlockEnd(node.EndLocation); + } + + protected virtual void VisitIteratorLoopVariable(TerminalNode node) + { + VisitVariableWrite(node); + } + + protected virtual void VisitIteratorExpression(BslSyntaxNode node) + { + VisitExpression(node); + } + + protected virtual void VisitIteratorLoopBody(BslSyntaxNode node) + { + VisitCodeBlock(node); + } + + protected virtual void VisitForLoopNode(ForLoopNode node) + { + VisitForInitializer(node.InitializationClause); + VisitForUpperLimit(node.UpperLimitExpression); + VisitForLoopBody(node.LoopBody); + VisitBlockEnd(node.EndLocation); + } + + protected virtual void VisitForInitializer(BslSyntaxNode node) + { + var forLoopIterator = node.Children[0]; + var forLoopInitialValue = node.Children[1]; + VisitForLoopIterator(forLoopIterator); + VisitForLoopInitialValue(forLoopInitialValue); + } + + protected virtual void VisitForLoopIterator(BslSyntaxNode node) + { + VisitAssignmentLeftPart(node); + } + + protected virtual void VisitForLoopInitialValue(BslSyntaxNode node) + { + VisitExpression(node); + } + + protected virtual void VisitForUpperLimit(BslSyntaxNode node) + { + VisitExpression(node); + } + + protected virtual void VisitForLoopBody(CodeBatchNode node) + { + VisitCodeBlock(node); + } + + protected virtual void VisitBreakNode(LineMarkerNode node) + { + } + + protected virtual void VisitContinueNode(LineMarkerNode node) + { + } + + protected virtual void VisitReturnNode(BslSyntaxNode node) + { + } + + protected virtual void VisitRaiseNode(BslSyntaxNode node) + { + } + + protected virtual void VisitTryExceptNode(TryExceptNode node) + { + VisitTryBlock(node.TryBlock); + VisitExceptBlock(node.ExceptBlock); + VisitBlockEnd(node.EndLocation); + } + + protected virtual void VisitTryBlock(CodeBatchNode node) + { + VisitCodeBlock(node); + } + + protected virtual void VisitExceptBlock(CodeBatchNode node) + { + VisitCodeBlock(node); + } + + protected virtual void VisitExecuteStatement(BslSyntaxNode node) + { + VisitExpression(node.Children[0]); + } + + protected virtual void VisitHandlerOperation(BslSyntaxNode node) + { + } + + protected virtual void VisitNewObjectCreation(NewObjectNode node) + { + } + + protected virtual void VisitPreprocessorDirective(PreprocessorDirectiveNode node) + { + } + + public void Visit(BslSyntaxNode node) + { + DefaultVisit(node); + } + + protected virtual void DefaultVisit(BslSyntaxNode node) + { + var action = _nodeVisitors[(int)node.Kind]; + action?.Invoke(node); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/ConditionalDirectiveHandler.cs b/src/OneScript.Language/SyntaxAnalysis/ConditionalDirectiveHandler.cs new file mode 100644 index 000000000..5a5572dde --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/ConditionalDirectiveHandler.cs @@ -0,0 +1,398 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class ConditionalDirectiveHandler : DirectiveHandlerBase, IDirectiveHandler + { + private readonly HashSet _definitions = new HashSet(StringComparer.InvariantCultureIgnoreCase); + private readonly Stack _blocks = new Stack(); + + private ILexer _lexer; + Lexem _lastExtractedLexem; + + public ConditionalDirectiveHandler(IErrorSink errorSink) : base(errorSink) + { + _lexer = new FullSourceLexer(); + } + + private class PreprocessorBlock + { + public bool IsSolved; + } + + public void Define(string param) + { + _definitions.Add(param); + } + + public bool IsDefined(string param) + { + return _definitions.Contains(param); + } + + public void Undef(string param) + { + _definitions.Remove(param); + } + + public override void OnModuleEnter() + { + base.OnModuleEnter(); + _blocks.Clear(); + } + + public override void OnModuleLeave() + { + base.OnModuleLeave(); + if (BlockLevel != 0) + AddError(LocalizedErrors.EndOfDirectiveExpected("Если")); // FIXME: назвать на том языке, на котором началась директива + } + + public override bool HandleDirective(ref Lexem lexem, ILexer lexer) + { + if (!IsConditional(lexem)) + return default; + + try + { + _lexer.Iterator = lexer.Iterator; + _lastExtractedLexem = lexem; + + lexem = Preprocess(_lastExtractedLexem); + } + catch (SyntaxErrorException) + { + _blocks.Clear(); + throw; + } + return true; + } + + private static bool IsConditional(in Lexem lastExtractedLexem) + { + bool isConditional; + switch (lastExtractedLexem.Token) + { + case Token.If: + case Token.ElseIf: + case Token.Else: + case Token.EndIf: + isConditional = true; + break; + default: + isConditional = false; + break; + } + + return isConditional; + } + + private Lexem Preprocess(Lexem directive) + { + if(directive.Token == Token.If) + { + PushBlock(); + return ResolveCondition(); + } + else if(directive.Token == Token.ElseIf) + { + if (BlockIsSolved()) + { + SolveExpression(); // проверить корректность условия + SkipTillNextDirective(); + return Preprocess(_lastExtractedLexem); + } + + return ResolveCondition(); + } + else if(directive.Token == Token.Else) + { + if (BlockIsSolved()) + { + SkipTillNextDirective(); + if (_lastExtractedLexem.Token != Token.EndIf) + { + return LexemFromNewLine(); + } + + return Preprocess(_lastExtractedLexem); + } + + return LexemFromNewLine(); + } + else if(directive.Token == Token.EndIf) + { + PopBlock(); + return LexemFromNewLine(); + } + + return _lastExtractedLexem; + } + + private Lexem ResolveCondition() + { + var enterBlock = SolveExpression(); + if (_lastExtractedLexem.Token != Token.Then) + { + AddError(LocalizedErrors.TokenExpected(Token.Then)); + return LexemFromNewLine(); + } + + if (enterBlock) + { + MarkAsSolved(); + return LexemFromNewLine(); + } + else + { + SkipTillNextDirective(); + return Preprocess(_lastExtractedLexem); + } + } + + private bool SolveExpression() + { + MoveNextSameLine(); + + return SolveOrExpression(); + } + + private bool SolveOrExpression() + { + var argument = SolveAndExpression(); + if (_lastExtractedLexem.Token == Token.Then) + { + return argument; + } + + if (_lastExtractedLexem.Token == Token.Or) + { + MoveNextSameLine(); + var secondArgument = SolveOrExpression(); + return argument || secondArgument; // здесь нужны НЕ-сокращенные вычисления + } + + return argument; + } + + private bool SolveAndExpression() + { + var argument = SolveNotExpression(); + + if (_lastExtractedLexem.Token == Token.And) + { + MoveNextSameLine(); + var secondArgument = SolveAndExpression(); + return argument && secondArgument; // здесь нужны НЕ-сокращенные вычисления + } + + return argument; + } + + private bool SolveNotExpression() + { + if (_lastExtractedLexem.Token == Token.Not) + { + MoveNextSameLine(); + return !GetArgument(); + } + + return GetArgument(); + } + + private bool GetArgument() + { + if (_lastExtractedLexem.Token == Token.OpenPar) + { + MoveNextSameLine(); + var result = SolveOrExpression(); + if (_lastExtractedLexem.Token == Token.ClosePar) + { + MoveNextSameLine(); + return result; + } + + AddError(LocalizedErrors.TokenExpected(Token.OpenPar)); + return true; // если ошибка и не было исключения от ErrorSink то войдем в блок + } + + if (!LanguageDef.IsUserSymbol(_lastExtractedLexem)) + { + AddError(LocalizedErrors.PreprocessorDefinitionExpected()); + return true; + } + + var expression = IsDefined(_lastExtractedLexem.Content); + MoveNextSameLine(); + return expression; + } + + private void NextLexem() + { + do + { + MoveNext(); + if (_lastExtractedLexem.Type == LexemType.PreprocessorDirective) + _lastExtractedLexem = Preprocess(_lastExtractedLexem); + } + while (_lastExtractedLexem.Type == LexemType.Comment); + + switch (_lastExtractedLexem.Type) + { + case LexemType.PreprocessorDirective: + Preprocess(_lastExtractedLexem); + break; + case LexemType.EndOfText when BlockLevel != 0: + AddError(LocalizedErrors.EndOfDirectiveExpected("Если")); + break; + } + } + + private void MoveNextSameLine() + { + _lastExtractedLexem = _lexer.NextLexem(); + if (_lexer.Iterator.OnNewLine) + { + AddError("Неожиданное завершение директивы"); + var recovery = new NextLineRecoveryStrategy(); + recovery.Recover(_lexer); + } + } + + private int BlockLevel => _blocks.Count; + + private void PushBlock() + { + var block = new PreprocessorBlock() + { + IsSolved = false + }; + _blocks.Push(block); + } + + private bool BlockIsSolved() + { + if (_blocks.Count == 0) + { + AddError(LocalizedErrors.DirectiveExpected("Если")); + return true; // зайдем внутрь для синтаксического контроля + } + + return _blocks.Peek().IsSolved; + } + + private void MarkAsSolved() + { + _blocks.Peek().IsSolved = true; + } + + private void PopBlock() + { + if (_blocks.Count != 0) + _blocks.Pop(); + else + AddError(LocalizedErrors.DirectiveIsMissing("Если")); + } + + private void AddError(string message) + { + var err = new CodeError + { + Description = message, + Position = _lexer.GetErrorPosition(), + ErrorId = "PreprocessorError" + }; + + ErrorSink.AddError(err); + } + + private void AddError(CodeError err) + { + err.Position = _lexer.GetErrorPosition(); + ErrorSink.AddError(err); + } + + private void MoveNext() + { + _lastExtractedLexem = _lexer.NextLexem(); + } + + private void CheckNewLine() + { + if (!_lexer.Iterator.OnNewLine) + { + AddError("Недопустимые символы в директиве"); + _lexer.ReadToLineEnd(); + } + } + + private void SkipTillNextDirective() + { + int currentLevel = BlockLevel; + var lineTail = _lexer.Iterator.ReadToLineEnd(); + if(lineTail.Length > 0 && !lineTail.StartsWith("//")) + { + AddError("Недопустимые символы в директиве"); + _lexer.ReadToLineEnd(); + } + + while (true) + { + if (!FindHashSign()) + { + AddError(LocalizedErrors.DirectiveExpected("КонецЕсли")); + return; + } + MoveNext(); + + if (_lastExtractedLexem.Token == Token.If) + PushBlock(); + else if (_lastExtractedLexem.Token == Token.EndIf && BlockLevel > currentLevel) + PopBlock(); + else if (BlockLevel == currentLevel && + (_lastExtractedLexem.Token == Token.EndIf || + _lastExtractedLexem.Token == Token.ElseIf || + _lastExtractedLexem.Token == Token.Else) ) + break; + } + } + + private bool FindHashSign() + { + var iterator = _lexer.Iterator; + + while (true) + { + if (iterator.CurrentSymbol == SpecialChars.Preprocessor) + { + if(iterator.OnNewLine) + return true; + } + + if (!iterator.MoveNext()) + break; + + iterator.SkipSpaces(); + } + + return false; + } + + private Lexem LexemFromNewLine() + { + NextLexem(); + CheckNewLine(); + + return _lastExtractedLexem; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs new file mode 100644 index 000000000..ad0839376 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/DefaultBslParser.cs @@ -0,0 +1,1618 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class DefaultBslParser + { + private readonly ParserContext _nodeContext; + private readonly ILexer _lexer; + private readonly PreprocessorHandlers _preprocessorHandlers; + + private Lexem _lastExtractedLexem; + + private bool _inMethodScope; + private bool _isMethodsDefined; + private bool _isStatementsDefined; + private bool _isInFunctionScope; + private bool _isInAsyncMethod; + private bool _lastDereferenceIsWritable; + + private readonly Stack _tokenStack = new Stack(); + private bool _isInLoopScope; + private bool _enableException; + + private readonly List _annotations = new List(); + + public DefaultBslParser( + ILexer lexer, + IErrorSink errorSink, + PreprocessorHandlers preprocessorHandlers) + { + _lexer = lexer; + _preprocessorHandlers = preprocessorHandlers; + ErrorSink = errorSink; + _nodeContext = new ParserContext(); + } + + private IErrorSink ErrorSink { get; } + + public IEnumerable Errors => ErrorSink.Errors ?? Array.Empty(); + + public BslSyntaxNode ParseStatefulModule() + { + ModuleNode node; + + _preprocessorHandlers.OnModuleEnter(); + NextLexem(); + + node = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); + PushContext(node); + try + { + ParseModuleSections(); + } + finally + { + PopContext(); + } + + _preprocessorHandlers.OnModuleLeave(); + + return node; + } + + public BslSyntaxNode ParseCodeBatch(bool allowReturns = false) + { + NextLexem(); + var node = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); + PushContext(node); + try + { + _inMethodScope = allowReturns; + _isInFunctionScope = allowReturns; + + BuildModuleBody(); + } + finally + { + PopContext(); + _inMethodScope = false; + _isInFunctionScope = false; + } + + return node; + } + + public BslSyntaxNode ParseExpression() + { + NextLexem(); + var module = new ModuleNode(_lexer.Iterator.Source, _lastExtractedLexem); + var parent = module.AddNode(new NonTerminalNode(NodeKind.TopLevelExpression, _lastExtractedLexem)); + BuildExpression(parent, Token.EndOfText); + return module; + } + + private void PushContext(NonTerminalNode node) => _nodeContext.PushContext(node); + + private NonTerminalNode PopContext() => _nodeContext.PopContext(); + + private NonTerminalNode CurrentParent => _nodeContext.CurrentParent; + + private void ParseModuleAnnotation() + { + if (_lastExtractedLexem.Type != LexemType.PreprocessorDirective) + return; + + var annotationParser = _preprocessorHandlers + .Slice(x => x is ModuleAnnotationDirectiveHandler) + .Cast() + .ToList(); + + if (annotationParser.Count == 0) + return; + + while (_lastExtractedLexem.Type == LexemType.PreprocessorDirective) + { + bool handled = false; + var directive = _lastExtractedLexem.Content; + foreach (var handler in annotationParser) + { + handled = handler.ParseAnnotation(ref _lastExtractedLexem, _lexer, _nodeContext); + if (handled) + break; + } + + if (!handled) + { + AddError(LocalizedErrors.DirectiveNotSupported(directive)); + } + } + + foreach (var handler in annotationParser) + { + handler.OnModuleLeave(); + } + } + + private void ParseModuleSections() + { + ParseModuleAnnotation(); + BuildVariablesSection(); + BuildMethodsSection(); + if (_annotations.Count != 0) + { + AddError(LocalizedErrors.AnnotationNotAllowed()); + } + BuildModuleBody(); + } + + #region Variables + + private void BuildVariablesSection() + { + if (_lastExtractedLexem.Token != Token.VarDef && _lastExtractedLexem.Type != LexemType.Annotation) + { + return; + } + + var parent = CurrentParent; + var allVarsSection = new NonTerminalNode(NodeKind.VariablesSection, _lastExtractedLexem); + PushContext(allVarsSection); + bool hasVars = false; + try + { + while (true) + { + 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; + } + + 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 + _annotations.Clear(); + } + + 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; + } + CreateChild(variable, NodeKind.ExportFlag, _lastExtractedLexem); + NextLexem(); + } + } + + private void ApplyAnnotations(AnnotatableNode annotatable) + { + foreach (var astNode in _annotations) + { + annotatable.AddChild(astNode); + } + _annotations.Clear(); + } + + #endregion + + #region Methods + + private void BuildMethodsSection() + { + if (_lastExtractedLexem.Type != LexemType.Annotation + && !IsStartOfMethod(_lastExtractedLexem)) + { + return; + } + + var parent = CurrentParent; + var allMethodsSection = new NonTerminalNode(NodeKind.MethodsSection, _lastExtractedLexem); + var sectionExist = false; + PushContext(allMethodsSection); + + try + { + while (true) + { + BuildAnnotations(); + if (!IsStartOfMethod(_lastExtractedLexem)) + break; + + if (!sectionExist) + { + sectionExist = true; + _isMethodsDefined = true; + parent.AddChild(allMethodsSection); + } + + BuildMethod(); + } + } + finally + { + PopContext(); + } + } + + 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()); + + ApplyAnnotations(method); + PushContext(method); + if (_lastExtractedLexem.Token == Token.Async) + { + method.IsAsync = true; + _isInAsyncMethod = true; + NextLexem(); + } + + try + { + BuildMethodSignature(); + _inMethodScope = true; + BuildMethodVariablesSection(); + _isStatementsDefined = true; + BuildMethodBody(); + } + finally + { + _isInFunctionScope = false; + _inMethodScope = false; + _isStatementsDefined = false; + _isInAsyncMethod = false; + PopContext(); + } + } + + private void BuildMethodVariablesSection() + { + try + { + // для корректной перемотки вперед в случае ошибок в секции переменных + PushStructureToken(_isInFunctionScope ? Token.EndFunction : Token.EndProcedure); + BuildVariablesSection(); + } + finally + { + PopStructureToken(); + } + } + + private void BuildMethodBody() + { + var body = _nodeContext.AddChild(new CodeBatchNode(_lastExtractedLexem)); + PushContext(body); + try + { + BuildCodeBatch(_isInFunctionScope ? Token.EndFunction : Token.EndProcedure); + } + finally + { + PopContext(); + } + + CreateChild(CurrentParent, NodeKind.BlockEnd, _lastExtractedLexem); + NextLexem(); + } + + private void BuildMethodSignature() + { + var signature = _nodeContext.AddChild(new MethodSignatureNode(_lastExtractedLexem)); + var isFunction = _lastExtractedLexem.Token == Token.Function; + CreateChild(signature, isFunction ? NodeKind.Function : NodeKind.Procedure, _lastExtractedLexem); + _isInFunctionScope = isFunction; + NextLexem(); + if (!IsUserSymbol(_lastExtractedLexem)) + { + AddError(LocalizedErrors.IdentifierExpected()); + return; + } + + CreateChild(signature, NodeKind.Identifier, _lastExtractedLexem); + BuildMethodParameters(signature); + if (_lastExtractedLexem.Token == Token.Export) + { + CreateChild(signature, NodeKind.ExportFlag, _lastExtractedLexem); + NextLexem(); + } + } + + private void BuildMethodParameters(MethodSignatureNode signature) + { + if (!NextExpected(Token.OpenPar)) + { + AddError(LocalizedErrors.TokenExpected(Token.OpenPar)); + return; + } + + var paramList = new NonTerminalNode(NodeKind.MethodParameters, _lastExtractedLexem); + 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; + } + } + + 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 bool BuildDefaultParameterValue(NonTerminalNode param, NodeKind nodeKind) + { + bool hasSign = false; + bool signIsMinus = _lastExtractedLexem.Token == Token.Minus; + if (signIsMinus || _lastExtractedLexem.Token == Token.Plus) + { + hasSign = true; + NextLexem(); + } + + if (LanguageDef.IsLiteral(_lastExtractedLexem)) + { + if (hasSign) + { + if (_lastExtractedLexem.Type == LexemType.NumberLiteral && signIsMinus) + { + _lastExtractedLexem.Content = '-' + _lastExtractedLexem.Content; + } + else if (_lastExtractedLexem.Type == LexemType.StringLiteral + || _lastExtractedLexem.Type == LexemType.DateLiteral) + { + AddError(LocalizedErrors.NumberExpected()); + return false; + } + } + + CreateChild(param, nodeKind, _lastExtractedLexem); + NextLexem(); + } + else + { + AddError(LocalizedErrors.LiteralExpected()); + return false; + } + + return true; + } + + #endregion + + private void BuildModuleBody() + { + if (!_lexer.Iterator.MoveToContent()) + return; + + var moduleBody = new NonTerminalNode(NodeKind.ModuleBody, _lastExtractedLexem); + var node = moduleBody.AddNode(new CodeBatchNode(_lastExtractedLexem)); + PushContext(node); + try + { + BuildCodeBatch(Token.EndOfText); + } + finally + { + PopContext(); + } + CurrentParent.AddChild(moduleBody); + } + + #region Annotations + private void BuildAnnotations() + { + while (_lastExtractedLexem.Type == LexemType.Annotation) + { + if (_inMethodScope) + { + AddError(LocalizedErrors.AnnotationNotAllowed()); + return; + } + + var node = BuildAnnotationDefinition(); + _annotations.Add(node); + } + } + + private AnnotationNode BuildAnnotationDefinition() { + var node = new AnnotationNode(NodeKind.Annotation, _lastExtractedLexem); + NextLexem(); + BuildAnnotationParameters(node); + return node; + } + + private void BuildAnnotationParameters(AnnotationNode annotation) + { + if (_lastExtractedLexem.Token != Token.OpenPar) + return; + + NextLexem(); + + if (_lastExtractedLexem.Token != Token.ClosePar) + while (true) + { + 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; + var node = new AnnotationParameterNode(); + // id | id = value | value + if (_lastExtractedLexem.Type == LexemType.Identifier) + { + CreateChild(node, NodeKind.AnnotationParameterName, _lastExtractedLexem); + NextLexem(); + if (_lastExtractedLexem.Token == Token.Equal) + { + NextLexem(); + success = BuildAnnotationParamValue(node); + } + } + else + { + success = BuildAnnotationParamValue(node); + } + + if (success) + { + annotation.AddChild(node); + } + } + + private bool BuildAnnotationParamValue(AnnotationParameterNode annotationParam) + { + if (_lastExtractedLexem.Type == LexemType.Annotation) { + var annotation = BuildAnnotationDefinition(); + annotationParam.AddChild(annotation); + return true; + } + return BuildDefaultParameterValue(annotationParam, NodeKind.AnnotationParameterValue); + } + + #endregion + + private void BuildCodeBatch(params Token[] endTokens) + { + PushStructureToken(endTokens); + + while (true) + { + if (endTokens.Contains(_lastExtractedLexem.Token)) + { + break; + } + + if (_lastExtractedLexem.Token == Token.Semicolon) + { + NextLexem(); + continue; + } + + if (_lastExtractedLexem.Type == LexemType.Label) + { + DefineLabel(_lastExtractedLexem); + continue; + } + + if (_lastExtractedLexem.Type != LexemType.Identifier && _lastExtractedLexem.Token != Token.EndOfText) + { + if (_lastExtractedLexem.Type == LexemType.Annotation) + AddError(LocalizedErrors.AnnotationNotAllowed()); + else + AddError(LocalizedErrors.UnexpectedOperation()); + continue; + } + + BuildStatement(); + + if (_lastExtractedLexem.Token != Token.Semicolon) + { + if (!endTokens.Contains(_lastExtractedLexem.Token)) + { + AddError(LocalizedErrors.SemicolonExpected()); + } + break; + } + NextLexem(); + } + PopStructureToken(); + } + + private void DefineLabel(Lexem label) + { + var node = new LabelNode(label); + CurrentParent.AddChild(node); + NextLexem(); + } + + #region Statements + + private void BuildStatement() + { + if (!_isInAsyncMethod && (_lastExtractedLexem.Token == Token.Async || _lastExtractedLexem.Token == Token.Await)) + { + _lastExtractedLexem.Token = Token.NotAToken; + } + + if (_lastExtractedLexem.Token == Token.NotAToken) + { + BuildSimpleStatement(); + } + else + { + BuildComplexStructureStatement(); + } + } + + private void BuildComplexStructureStatement() + { + switch (_lastExtractedLexem.Token) + { + case Token.If: + BuildIfStatement(); + break; + case Token.For: + BuildForStatement(); + break; + case Token.While: + BuildWhileStatement(); + break; + case Token.Break: + BuildBreakStatement(); + break; + case Token.Continue: + BuildContinueStatement(); + break; + case Token.Return: + BuildReturnStatement(); + break; + case Token.Try: + BuildTryExceptStatement(); + break; + case Token.RaiseException: + BuildRaiseExceptionStatement(); + break; + case Token.Execute: + BuildExecuteStatement(); + break; + case Token.AddHandler: + case Token.RemoveHandler: + BuildEventHandlerOperation(_lastExtractedLexem.Token); + break; + case Token.Await: + BuildGlobalCallAwaitOperator(); + break; + case Token.Goto: + BuildGotoOperator(); + break; + default: + if (LanguageDef.IsBuiltInFunction(_lastExtractedLexem.Token)) + { + AddError(LocalizedErrors.UseBuiltInFunctionAsProcedure()); + } + else + { + AddError(LocalizedErrors.TokenExpected(_tokenStack.Peek())); + } + break; + } + } + + private void BuildGlobalCallAwaitOperator() + { + Debug.Assert(_lastExtractedLexem.Token == Token.Await); + + CurrentParent.AddChild(TerminalNode()); + } + + + private BslSyntaxNode BuildExpressionAwaitOperator(Lexem lexem) + { + Debug.Assert(_lastExtractedLexem.Token == Token.Await); + + NextLexem(); + + var argument = SelectTerminalNode(_lastExtractedLexem, false); + if (argument != default) + { + CheckAsyncMethod(); + return new UnaryOperationNode(argument, lexem); + } + else if (!_isInAsyncMethod) + { + // это просто переменная Ждать или метод Ждать + return CallOrVariable(lexem); + } + else + { + return CreateError(LocalizedErrors.ExpressionSyntax()); + } + } + + private void BuildGotoOperator() + { + var gotoNode = new NonTerminalNode(NodeKind.Goto, _lastExtractedLexem); + NextLexem(); + + if (_lastExtractedLexem.Type != LexemType.LabelRef) + { + AddError(LocalizedErrors.LabelNameExpected()); + return; + } + + gotoNode.AddChild(new LabelNode(_lastExtractedLexem)); + NextLexem(); + + CurrentParent.AddChild(gotoNode); + } + + private void CheckAsyncMethod() + { + if (!_isInAsyncMethod) + { + AddError(LocalizedErrors.AwaitMustBeInAsyncMethod(), false); + } + } + + private void BuildIfStatement() + { + var condition = _nodeContext.AddChild(new ConditionNode(_lastExtractedLexem)); + + NextLexem(); + BuildExpressionUpTo(condition, Token.Then); + BuildBatchWithContext(condition, Token.Else, Token.ElseIf, Token.EndIf); + + while (_lastExtractedLexem.Token == Token.ElseIf) + { + var elif = new ConditionNode(_lastExtractedLexem); + condition.AddChild(elif); + NextLexem(); + BuildExpressionUpTo(elif, Token.Then); + BuildBatchWithContext(elif, Token.Else, Token.ElseIf, Token.EndIf); + } + + if (_lastExtractedLexem.Token == Token.Else) + { + NextLexem(); + BuildBatchWithContext(condition, Token.EndIf); + } + + CreateChild(condition, NodeKind.BlockEnd, _lastExtractedLexem); + + NextLexem(); + } + + private void BuildBatchWithContext(NonTerminalNode context, params Token[] stopTokens) + { + var batch = new CodeBatchNode(_lastExtractedLexem); + context.AddChild(batch); + PushContext(batch); + try + { + BuildCodeBatch(stopTokens); + } + finally + { + PopContext(); + } + } + + private void BuildWhileStatement() + { + var loopNode = _nodeContext.AddChild(new WhileLoopNode(_lastExtractedLexem)); + NextLexem(); + BuildExpressionUpTo(loopNode, Token.Loop); + var body = loopNode.AddNode(new CodeBatchNode(_lastExtractedLexem)); + + PushContext(body); + var loopState = _isInLoopScope; + try + { + _isInLoopScope = true; + BuildCodeBatch(Token.EndLoop); + NextLexem(); + CreateChild(loopNode, NodeKind.BlockEnd, _lastExtractedLexem); + } + finally + { + _isInLoopScope = loopState; + PopContext(); + } + } + + private void BuildForStatement() + { + NextLexem(); + + NodeKind loopKind; + NonTerminalNode loopNode; + if (_lastExtractedLexem.Token == Token.Each) + { + loopKind = NodeKind.ForEachLoop; + loopNode = _nodeContext.AddChild(new ForEachLoopNode(_lastExtractedLexem)); + } + else + { + loopKind = NodeKind.ForLoop; + loopNode = _nodeContext.AddChild(new ForLoopNode(_lastExtractedLexem)); + } + + PushContext(loopNode); + var loopState = _isInLoopScope; + try + { + _isInLoopScope = true; + if (loopKind == NodeKind.ForEachLoop) + BuildForEachStatement(loopNode); + else + BuildCountableForStatement(loopNode); + } + finally + { + _isInLoopScope = loopState; + PopContext(); + } + } + + private void BuildCountableForStatement(NonTerminalNode loopNode) + { + if (!IsUserSymbol(_lastExtractedLexem)) + { + AddError(LocalizedErrors.IdentifierExpected()); + BuildBatchWithContext(loopNode, Token.EndLoop); + return; + } + + var counter = _lastExtractedLexem; + if (!NextExpected(Token.Equal)) + { + AddError(LocalizedErrors.TokenExpected(Token.Equal)); + BuildBatchWithContext(loopNode, Token.EndLoop); + return; + } + + var assignment = new NonTerminalNode(NodeKind.ForInitializer, _lastExtractedLexem); + + NextLexem(); + + CreateChild(assignment, NodeKind.Identifier, counter); + BuildExpressionUpTo(assignment, Token.To); + loopNode.AddChild(assignment); + + var limit = new NonTerminalNode(NodeKind.ForLimit, _lastExtractedLexem); + BuildExpressionUpTo(limit, Token.Loop); + loopNode.AddChild(limit); + + BuildBatchWithContext(loopNode, Token.EndLoop); + + CreateChild(loopNode, NodeKind.BlockEnd, _lastExtractedLexem); + + NextLexem(); + } + + private void BuildForEachStatement(NonTerminalNode loopNode) + { + NextLexem(); + if (!IsUserSymbol(_lastExtractedLexem)) + { + AddError(LocalizedErrors.IdentifierExpected()); + BuildBatchWithContext(loopNode, Token.EndLoop); + return; + } + + CreateChild(loopNode, NodeKind.ForEachVariable, _lastExtractedLexem); + if (!NextExpected(Token.In)) + { + AddError(LocalizedErrors.TokenExpected(Token.In)); + BuildBatchWithContext(loopNode, Token.EndLoop); + return; + } + + NextLexem(); + TryParseNode(() => + { + var collection = new NonTerminalNode(NodeKind.ForEachCollection, _lastExtractedLexem); + BuildExpressionUpTo(collection, Token.Loop); + loopNode.AddChild(collection); + }); + + BuildBatchWithContext(loopNode, Token.EndLoop); + CreateChild(loopNode, NodeKind.BlockEnd, _lastExtractedLexem); + + NextLexem(); + } + + private void BuildBreakStatement() + { + if (!_isInLoopScope) + { + AddError(LocalizedErrors.BreakOutsideOfLoop()); + } + + CreateChild(CurrentParent, NodeKind.BreakStatement, _lastExtractedLexem); + NextLexem(); + } + + private void BuildContinueStatement() + { + if (!_isInLoopScope) + { + AddError(LocalizedErrors.ContinueOutsideLoop()); + } + + CreateChild(CurrentParent, NodeKind.ContinueStatement, _lastExtractedLexem); + NextLexem(); + } + + private void BuildReturnStatement() + { + var returnNode = new NonTerminalNode(NodeKind.ReturnStatement, _lastExtractedLexem); + if (_isInFunctionScope) + { + NextLexem(); + if (_lastExtractedLexem.Token == Token.Semicolon || + LanguageDef.IsEndOfBlockToken(_lastExtractedLexem.Token)) + { + AddError(LocalizedErrors.FuncEmptyReturnValue()); + } + else + { + BuildExpression(returnNode, Token.Semicolon); + } + } + else if (_inMethodScope) + { + NextLexem(); + if (_lastExtractedLexem.Token != Token.Semicolon + && !LanguageDef.IsEndOfBlockToken(_lastExtractedLexem.Token)) + { + AddError(LocalizedErrors.ProcReturnsAValue()); + } + } + else + { + AddError(LocalizedErrors.ReturnOutsideOfMethod()); + } + + CurrentParent.AddChild(returnNode); + } + + private void BuildTryExceptStatement() + { + var node = new TryExceptNode(_lastExtractedLexem); + NextLexem(); + BuildBatchWithContext(node, Token.Exception); + + Debug.Assert(_lastExtractedLexem.Token == Token.Exception); + + NextLexem(); + BuildBatchWithContext(node, Token.EndTry); + CreateChild(node, NodeKind.BlockEnd, _lastExtractedLexem); + NextLexem(); + CurrentParent.AddChild(node); + } + + private void BuildRaiseExceptionStatement() + { + var node = new NonTerminalNode(NodeKind.RaiseException, _lastExtractedLexem); + NextLexem(); + if (_lastExtractedLexem.Token == Token.Semicolon || LanguageDef.IsEndOfBlockToken(_lastExtractedLexem.Token)) + { + if (!_tokenStack.Any(x => x.Contains(Token.EndTry))) + { + AddError(LocalizedErrors.MismatchedRaiseException()); + return; + } + } + else + { + BuildExpression(node, Token.Semicolon); + } + + CurrentParent.AddChild(node); + } + + private void BuildExecuteStatement() + { + var node = new NonTerminalNode(NodeKind.ExecuteStatement, _lastExtractedLexem); + NextLexem(); + BuildExpression(node, Token.Semicolon); + CurrentParent.AddChild(node); + } + + private void BuildEventHandlerOperation(Token token) + { + var node = new NonTerminalNode( + token == Token.AddHandler ? NodeKind.AddHandler : NodeKind.RemoveHandler, + _lastExtractedLexem); + + NextLexem(); + + var source = BuildExpressionUpTo(node, Token.Comma); + + if ((source.Kind != NodeKind.DereferenceOperation || !_lastDereferenceIsWritable) + && source.Kind != NodeKind.IndexAccess) + { + AddError(LocalizedErrors.WrongEventName()); + return; + } + + var expr = BuildExpression(node, Token.Semicolon); + + if (expr.Kind != NodeKind.Identifier && + (expr.Kind != NodeKind.DereferenceOperation || !_lastDereferenceIsWritable) && + expr.Kind != NodeKind.IndexAccess) + { + AddError(LocalizedErrors.WrongHandlerName()); + return; + } + + CurrentParent.AddChild(node); + } + + private void BuildSimpleStatement() + { + _isStatementsDefined = true; + TryParseNode(() => BuildAssignment(CurrentParent)); + } + + private void BuildAssignment(NonTerminalNode batch) + { + var call = BuildGlobalCall(_lastExtractedLexem); + + if (_lastExtractedLexem.Token == Token.Equal) + { + if (_lastDereferenceIsWritable) + { + var node = batch.AddNode(new NonTerminalNode(NodeKind.Assignment, _lastExtractedLexem)); + node.AddChild(call); + NextLexem(); + BuildExpression(node, Token.Semicolon); + } + else + { + AddError(LocalizedErrors.ExpressionSyntax()); + } + } + else + { + if (_lastDereferenceIsWritable) + { + AddError(LocalizedErrors.ExpressionSyntax()); + } + else + { + batch.AddChild(call); + } + } + } + + private BslSyntaxNode BuildGlobalCall(Lexem identifier) + { + NextLexem(); + + return CallOrVariable(identifier); + } + + private BslSyntaxNode CallOrVariable(Lexem identifier) + { + BslSyntaxNode target = new TerminalNode(NodeKind.Identifier, identifier); + if (_lastExtractedLexem.Token != Token.OpenPar) + { + _lastDereferenceIsWritable = true; // одиночный идентификатор + } + else + { + target = BuildCall(target, NodeKind.GlobalCall); + } + + return BuildDereference(target); + } + + private CallNode BuildCall(BslSyntaxNode target, NodeKind callKind) + { + var callNode = new CallNode(callKind, _lastExtractedLexem); + callNode.AddChild(target); + BuildCallParameters(callNode); + _lastDereferenceIsWritable = false; + return callNode; + } + + private void BuildCallParameters(NonTerminalNode callNode) + { + var node = callNode.AddNode(new NonTerminalNode(NodeKind.CallArgumentList, _lastExtractedLexem)); + PushStructureToken(Token.ClosePar); + try + { + NextLexem(); // съели открывающую скобку + BuildCallArguments(node); + NextLexem(); // съели закрывающую скобку + } + finally + { + 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; + } + } + } + + private void BuildOptionalCallArgument(NonTerminalNode argsList) + { + var arg = argsList.AddNode(new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem)); + if (_lastExtractedLexem.Token == Token.Comma + || _lastExtractedLexem.Token == Token.ClosePar) + { + return; + } + + arg.AddNode( BuildExpression(0) ); + } + + #endregion + + #region Expression + + private BslSyntaxNode BuildExpression(NonTerminalNode parent, Token stopToken) + { + if (_lastExtractedLexem.Token == stopToken) + { + return CreateError(LocalizedErrors.ExpressionExpected()); + } + + var op = BuildExpression(0); + parent.AddChild(op); + return op; + } + + private BslSyntaxNode BuildExpression(int prio) + { + var firstArg = BuildPrimaryExpression(); + while (LanguageDef.GetBinaryPriority(_lastExtractedLexem.Token) > prio) + { + var operationLexem = _lastExtractedLexem; + NextLexem(); + var secondArg = BuildExpression(LanguageDef.GetBinaryPriority(operationLexem.Token)); + + firstArg = new BinaryOperationNode(firstArg, secondArg, operationLexem); + } + + return firstArg; + } + + private BslSyntaxNode BuildPrimaryExpression() + { + if (_lastExtractedLexem.Token == Token.OpenPar) + { + return BuildParenthesis(); + } + + var operation = _lastExtractedLexem; + var prio = LanguageDef.GetUnaryPriority(operation.Token); + + if (prio == LanguageDef.MAX_OPERATION_PRIORITY) + { + 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); + } + + + private BslSyntaxNode BuildExpressionUpTo(NonTerminalNode parent, Token stopToken) + { + var node = BuildExpression(parent, stopToken); + if (_lastExtractedLexem.Token == stopToken) + { + NextLexem(); + } + else + { + if (_lastExtractedLexem.Token == Token.EndOfText) + { + return CreateError(LocalizedErrors.UnexpectedEof()); + } + else + { + return CreateError(LocalizedErrors.TokenExpected(stopToken), false); + } + } + + return node; + } + + private void BuildOptionalExpression(NonTerminalNode parent, Token stopToken) + { + if (_lastExtractedLexem.Token == stopToken) + { + return; + } + + var op = BuildExpression(0); + parent.AddChild(op); + } + + #region Operators + + private BslSyntaxNode BuildParenthesis() + { + NextLexem(); + var expr = BuildExpression(0); + if (_lastExtractedLexem.Token != Token.ClosePar) + { + return CreateError(LocalizedErrors.TokenExpected(Token.ClosePar)); + } + NextLexem(); + + return BuildDereference(expr); + } + + #endregion + + private BslSyntaxNode TerminalNode() + { + BslSyntaxNode node = SelectTerminalNode(_lastExtractedLexem, true); + if (node == default) + { + return CreateError(LocalizedErrors.ExpressionSyntax()); + } + + return node; + } + + private BslSyntaxNode SelectTerminalNode(in Lexem currentLexem, bool supportAwait) + { + BslSyntaxNode node = default; + if (LanguageDef.IsLiteral(currentLexem)) + { + node = new TerminalNode(NodeKind.Constant, currentLexem); + NextLexem(); + } + else if (LanguageDef.IsUserSymbol(currentLexem)) + { + node = BuildGlobalCall(currentLexem); + } + else if (currentLexem.Token == Token.NewObject) + { + node = BuildNewObjectCreation(); + } + else if (LanguageDef.IsBuiltInFunction(currentLexem.Token)) + { + node = BuildGlobalCall(currentLexem); + } + else if (currentLexem.Token == Token.Question) + { + node = BuildQuestionOperator(); + } + else if (supportAwait && currentLexem.Token == Token.Await) + { + node = BuildExpressionAwaitOperator(currentLexem); + } + + return node; + } + + private BslSyntaxNode BuildQuestionOperator() + { + var node = new NonTerminalNode(NodeKind.TernaryOperator, _lastExtractedLexem); + if (!NextExpected(Token.OpenPar)) + return CreateError(LocalizedErrors.TokenExpected(Token.OpenPar)); + + NextLexem(); + + if (!TryParseNode(() => + { + BuildExpressionUpTo(node, Token.Comma); + BuildExpressionUpTo(node, Token.Comma); + BuildExpressionUpTo(node, Token.ClosePar); + })) + { + return CreateError(LocalizedErrors.ExpressionSyntax()); + } + + return BuildDereference(node); + } + + private BslSyntaxNode BuildDereference(BslSyntaxNode target) + { + var activeTarget = BuildIndexerAccess(target); + if (_lastExtractedLexem.Token == Token.Dot) + { + var dotNode = new NonTerminalNode(NodeKind.DereferenceOperation, _lastExtractedLexem); + dotNode.AddChild(activeTarget); + NextLexem(); + if (!LanguageDef.IsValidPropertyName(_lastExtractedLexem)) + { + return CreateError(LocalizedErrors.IdentifierExpected()); + } + + var identifier = _lastExtractedLexem; + NextLexem(); + if (_lastExtractedLexem.Token == Token.OpenPar) + { + var ident = new TerminalNode(NodeKind.Identifier, identifier); + var call = BuildCall(ident, NodeKind.MethodCall); + dotNode.AddChild(call); + } + else + { + _lastDereferenceIsWritable = true; + CreateChild(dotNode, NodeKind.Identifier, identifier); + } + + return BuildDereference(dotNode); + } + + return activeTarget; + } + + private BslSyntaxNode BuildIndexerAccess(BslSyntaxNode target) + { + if (_lastExtractedLexem.Token == Token.OpenBracket) + { + var node = new NonTerminalNode(NodeKind.IndexAccess, _lastExtractedLexem); + node.AddChild(target); + NextLexem(); + var expression = BuildExpression(node, Token.CloseBracket); + if (expression.Kind == NodeKind.Unknown) + { + return CreateError(LocalizedErrors.ExpressionSyntax()); + } + NextLexem(); + _lastDereferenceIsWritable = true; + return BuildDereference(node); + } + + return target; + } + + private BslSyntaxNode BuildNewObjectCreation() + { + var node = new NewObjectNode(_lastExtractedLexem); + NextLexem(); + if (_lastExtractedLexem.Token == Token.OpenPar) + { + // создание по строковому имени класса + NewObjectDynamicConstructor(node); + } + else if (IsUserSymbol(_lastExtractedLexem) || _lastExtractedLexem.Token == Token.ExceptionInfo) + { + NewObjectStaticConstructor(node); + } + else + { + return CreateError(LocalizedErrors.IdentifierExpected()); + } + + return BuildDereference(node); + } + + private void NewObjectDynamicConstructor(NonTerminalNode node) + { + NextLexem(); + if (_lastExtractedLexem.Token == Token.ClosePar) + { + AddError(LocalizedErrors.ExpressionExpected()); + return; + } + + var nameArg = new NonTerminalNode(NodeKind.CallArgument, _lastExtractedLexem); + PushStructureToken(Token.ClosePar); + try + { + BuildExpression(nameArg, Token.Comma); + node.AddChild(nameArg); + var callArgs = new NonTerminalNode(NodeKind.CallArgumentList, _lastExtractedLexem); + if (_lastExtractedLexem.Token == Token.Comma) + { + // есть аргументы после имени + NextLexem(); + } + BuildCallArguments(callArgs); + node.AddChild(callArgs); + NextLexem(); + } + finally + { + PopStructureToken(); + } + } + + private void NewObjectStaticConstructor(NonTerminalNode node) + { + CreateChild(node, NodeKind.Identifier, _lastExtractedLexem); + + NextLexem(); + if (_lastExtractedLexem.Token == Token.OpenPar) + { + BuildCallParameters(node); + } + } + + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void NextLexem() + { + _lastExtractedLexem = _lexer.NextLexem(); + } + + private bool NextExpected(Token expected) + { + NextLexem(); + + return expected == _lastExtractedLexem.Token; + } + + private void SkipToNextStatement(Token[] additionalStops = null) + { + var recovery = new NextStatementRecoveryStrategy + { + AdditionalStops = additionalStops + }; + + _lastExtractedLexem = recovery.Recover(_lexer); + } + + private void AddError(CodeError err, bool doFastForward = true) + { + err.Position = _lexer.GetErrorPosition(); + err.Position.ColumnNumber -= _lastExtractedLexem.Content?.Length ?? 1; + ErrorSink.AddError(err); + + if (doFastForward) + { + if (_tokenStack.Count != 0) + SkipToNextStatement(_tokenStack.Peek()); + else + SkipToNextStatement(); + } + + if(_enableException) + throw new InternalParseException(err); + } + + private ErrorTerminalNode CreateError(CodeError error, bool doFastForward = true) + { + var lexem = _lastExtractedLexem; + AddError(error, doFastForward); + return new ErrorTerminalNode(lexem); + } + + private bool IsUserSymbol(in Lexem lex) + { + return LanguageDef.IsUserSymbol(in lex) || (!_isInAsyncMethod && lex.Token == Token.Await); + } + + private void PushStructureToken(params Token[] tok) + { + _tokenStack.Push(tok); + } + + private Token[] PopStructureToken() + { + var tok = _tokenStack.Pop(); + return tok; + } + + private static void CreateChild(NonTerminalNode parent, NodeKind kind, in Lexem lex) + { + var child = NodeBuilder.CreateNode(kind, lex); + parent.AddChild(child); + } + + private bool TryParseNode(Action action) + { + var exc = _enableException; + try + { + _enableException = true; + action(); + return true; + } + catch (InternalParseException) + { + return false; + } + finally + { + _enableException = exc; + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/DirectiveHandlerBase.cs b/src/OneScript.Language/SyntaxAnalysis/DirectiveHandlerBase.cs new file mode 100644 index 000000000..ac769aea4 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/DirectiveHandlerBase.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public abstract class DirectiveHandlerBase : IDirectiveHandler + { + public DirectiveHandlerBase(IErrorSink errorSink) + { + ErrorSink = errorSink; + } + + public IErrorSink ErrorSink { get; } + + public virtual void OnModuleEnter() + { + } + + public virtual void OnModuleLeave() + { + } + + public abstract bool HandleDirective(ref Lexem lastExtractedLexem, ILexer lexer); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/IDirectiveHandler.cs b/src/OneScript.Language/SyntaxAnalysis/IDirectiveHandler.cs new file mode 100644 index 000000000..f607e88de --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/IDirectiveHandler.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/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public interface IDirectiveHandler + { + void OnModuleEnter(); + void OnModuleLeave(); + bool HandleDirective(ref Lexem lastExtractedLexem, ILexer lexer); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/IErrorRecoveryStrategy.cs b/src/OneScript.Language/SyntaxAnalysis/IErrorRecoveryStrategy.cs new file mode 100644 index 000000000..d725af040 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/IErrorRecoveryStrategy.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public interface IErrorRecoveryStrategy + { + Lexem Recover(ILexer lexer); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/ImportDirectivesHandler.cs b/src/OneScript.Language/SyntaxAnalysis/ImportDirectivesHandler.cs new file mode 100644 index 000000000..5d0a5058f --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/ImportDirectivesHandler.cs @@ -0,0 +1,72 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class ImportDirectivesHandler : ModuleAnnotationDirectiveHandler + { + private readonly ILexer _importClauseLexer; + + private static readonly LexerBuilder InstanceBuilder = SetupLexerBuilder(); + + public ImportDirectivesHandler(IErrorSink errorSink) : base(errorSink) + { + _importClauseLexer = InstanceBuilder.Build(); + } + + private static LexerBuilder SetupLexerBuilder() + { + var builder = new LexerBuilder(); + builder + .DetectComments() + .Detect((cs, i) => !char.IsWhiteSpace(cs)) + .HandleWith(new NonWhitespaceLexerState()); + + return builder; + } + + protected override void ParseAnnotationInternal( + ref Lexem lastExtractedLexem, + ILexer lexer, + ParserContext parserContext) + { + var node = new AnnotationNode(NodeKind.Import, lastExtractedLexem); + _importClauseLexer.Iterator = lexer.Iterator; + + var lex = _importClauseLexer.NextLexem(); + if (lex.Type == LexemType.EndOfText) + { + ErrorSink.AddError(LocalizedErrors.LibraryNameExpected()); + return; + } + + var argumentNode = new AnnotationParameterNode(); + var value = new TerminalNode(NodeKind.AnnotationParameterValue, lex); + argumentNode.AddChild(value); + + node.AddChild(argumentNode); + parserContext.AddChild(node); + + lex = _importClauseLexer.NextLexemOnSameLine(); + if (lex.Type != LexemType.EndOfText && lex.Type != LexemType.Comment) + { + ErrorSink.AddError(LocalizedErrors.UnexpectedOperation()); + return; + } + + lastExtractedLexem = lexer.NextLexem(); + } + + protected override bool DirectiveSupported(string directive) + { + return LanguageDef.IsImportDirective(directive); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/InternalParseException.cs b/src/OneScript.Language/SyntaxAnalysis/InternalParseException.cs new file mode 100644 index 000000000..41e675058 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/InternalParseException.cs @@ -0,0 +1,26 @@ +/*---------------------------------------------------------- +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.Language.SyntaxAnalysis +{ + internal class InternalParseException : Exception + { + public CodeError Error { get; } + + public InternalParseException(CodeError error) + { + Error = error; + } + + public override string ToString() + { + return Error.Description + base.ToString(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs new file mode 100644 index 000000000..a6e23d267 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/LocalizedErrors.cs @@ -0,0 +1,160 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using OneScript.Language.LexicalAnalysis; +using OneScript.Localization; + +namespace OneScript.Language.SyntaxAnalysis +{ + public static class LocalizedErrors + { + public static CodeError UnexpectedOperation() + { + return Create("Неизвестная операция", "Unknown operation"); + } + + public static CodeError LateVarDefinition() + { + return Create("Объявления переменных должны быть расположены в начале модуля, процедуры или функции", + "Variable declarations must be placed at beginning of module, procedure, or function"); + } + + public static CodeError SemicolonExpected() + => Create("Ожидается символ ; (точка с запятой)", "Expecting \";\""); + + public static CodeError ExpressionExpected() => + Create("Ожидается выражение", "Expression expected"); + + private static CodeError Create(string ru, string en, [CallerMemberName] string errorId = default) + { + return new CodeError + { + ErrorId = errorId, + Description = BilingualString.Localize(ru, en) + }; + } + + public static CodeError IdentifierExpected() + => Create("Ожидается идентификатор", "Identifier expecting"); + + public static CodeError LabelNameExpected() + => Create("Ожидается имя метки", "Label name expected"); + + public static CodeError ExpressionSyntax() + => Create("Ошибка в выражении", "Expression syntax error"); + + public static CodeError TokenExpected(params Token[] expected) + { + if (expected.Length == 1) + { + return Create($"Ожидается символ: {LanguageDef.GetTokenName(expected[0])}", + $"Expecting symbol: {LanguageDef.GetTokenAlias(expected[0])}"); + } + + var names = String.Join("/", expected.Select(x => LanguageDef.GetTokenName(x))); + var aliases = String.Join("/", expected.Select(x => LanguageDef.GetTokenAlias(x))); + + return Create($"Ожидается один из символов: {names}", $"Expecting one of symbols: {aliases}"); + + } + + public static CodeError ExportedLocalVar(string varName) + { + return Create($"Локальная переменная не может быть экспортирована ({varName})", + $"Local variable can't be exported ({varName})"); + } + + public static CodeError AwaitMustBeInAsyncMethod() => Create( + "Оператор Ждать (Await) может употребляться только в асинхронных процедурах или функциях", + "Operator Await can be used only in async procedures or functions" + ); + + public static CodeError LiteralExpected() => Create("Ожидается константа", "Constant expected"); + + public static CodeError NumberExpected() => Create("Ожидается числовая константа", "Numeric constant expected"); + + public static CodeError UnexpectedEof() => + Create("Неожиданный конец модуля", "Unexpected end of text"); + + public static CodeError BreakOutsideOfLoop() => + Create("Оператор \"Прервать\" может использоваться только внутри цикла", "Break operator may be used only within loop"); + + public static CodeError ContinueOutsideLoop() => + Create("Оператор \"Продолжить\" может использоваться только внутри цикла", "Continue operator may be used only within loop"); + + public static CodeError FuncEmptyReturnValue() => + Create("Функция должна возвращать значение", "Function should return a value"); + + public static CodeError ProcReturnsAValue() => + Create("Процедуры не могут возвращать значение", "Procedures cannot return value"); + + public static CodeError ReturnOutsideOfMethod() => Create("Оператор \"Возврат\" может использоваться только внутри метода", + "Return operator may not be used outside procedure or function"); + + public static CodeError MismatchedRaiseException() => + Create("Оператор \"ВызватьИсключение\" без параметров может использоваться только в блоке \"Исключение\"", + "Raise operator may be used without arguments only when handling exception"); + + public static CodeError WrongEventName() => + Create("Ожидается имя события", "Event name expected"); + + public static CodeError WrongHandlerName() => + Create("Ожидается имя обработчика события", "Event handler name expected"); + + public static CodeError UnexpectedSymbol(char c) => + Create($"Неизвестный символ {c}", $"Unexpected character {c}"); + + public static CodeError AnnotationNotAllowed() => + Create("Аннотация неприменима в данном месте", "Annotation is not allowed here"); + + public static CodeError DirectiveNotSupported(string directive) => + Create($"Директива {directive} не разрешена в данном месте", $"Directive {directive} is not supported here"); + + public static CodeError EndOfDirectiveExpected(string directive) => + Create($"Ожидается завершение директивы препроцессора #{directive}", + $"End of directive #{directive} expected"); + + public static CodeError DirectiveExpected(string directive) => + Create($"Ожидается директива препроцессора #{directive}", $"Preprocessor directive #{directive} expected"); + + public static CodeError RegionNameExpected() => + Create("Ожидается имя области", "Region name expected"); + + public static CodeError InvalidRegionName(string name) => + Create($"Недопустимое имя Области: {name}", $"Invalid Region name {name}"); + + public static CodeError DirectiveIsMissing(string directive) => + Create($"Пропущена директива #{directive}", $"Directive #{directive} is missing"); + + public static CodeError LibraryNameExpected() => + Create("Ожидается имя библиотеки", "Library name expected"); + + public static CodeError PreprocessorDefinitionExpected() => + Create("Ожидается объявление препроцессора", "Preprocessor definition expected"); + + public static CodeError UseBuiltInFunctionAsProcedure() => + Create("Использование встроенной функции, как процедуры", "Using build-in function as procedure"); + + public static CodeError UseProcAsFunction() => + Create("Использование процедуры, как функции", "Procedure called as function"); + + public static CodeError DuplicateVarDefinition(string varName) => + Create($"Переменная {varName} уже определена", $"Variable {varName} already defined"); + + public static CodeError DuplicateMethodDefinition(string methodName) => + Create($"Метод {methodName} уже определен", $"Method {methodName} already defined"); + + public static CodeError SymbolNotFound(string symbol) => + Create($"Неизвестный символ: {symbol}", $"Symbol not found {symbol}"); + + public static CodeError AsyncMethodsNotSupported() => + Create("Асинхронные методы не поддерживаются", "Async methods aren't supported"); + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/ModuleAnnotationDirectiveHandler.cs b/src/OneScript.Language/SyntaxAnalysis/ModuleAnnotationDirectiveHandler.cs new file mode 100644 index 000000000..22e358a25 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/ModuleAnnotationDirectiveHandler.cs @@ -0,0 +1,76 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + /// + /// Используется в os.web для создания аннотаций на уровне класса + /// + public abstract class ModuleAnnotationDirectiveHandler : DirectiveHandlerBase + { + private bool _enabled; + + protected ModuleAnnotationDirectiveHandler(IErrorSink errorSink) : base(errorSink) + { + } + + public override void OnModuleEnter() + { + _enabled = true; + base.OnModuleEnter(); + } + + public override void OnModuleLeave() + { + _enabled = false; + base.OnModuleLeave(); + } + + public sealed override bool HandleDirective(ref Lexem lastExtractedLexem, ILexer lexer) + { + if (!DirectiveSupported(lastExtractedLexem.Content)) + { + return default; + } + + if (!_enabled) + { + var err = LocalizedErrors.DirectiveNotSupported(lastExtractedLexem.Content); + err.Position = lexer.GetErrorPosition(); + ErrorSink.AddError(err); + return true; + } + + return HandleDirectiveInternal(ref lastExtractedLexem, lexer); + } + + protected virtual bool HandleDirectiveInternal(ref Lexem lastExtractedLexem, ILexer lexer) + { + return true; // не сдвигаем лексер, выдаем на уровень парсера + } + + protected abstract bool DirectiveSupported(string directive); + + protected abstract void ParseAnnotationInternal( + ref Lexem lastExtractedLexem, + ILexer lexer, + ParserContext parserContext); + + public bool ParseAnnotation(ref Lexem lastExtractedLexem, ILexer lexer, ParserContext context) + { + if (!DirectiveSupported(lastExtractedLexem.Content)) + { + return false; + } + + ParseAnnotationInternal(ref lastExtractedLexem, lexer, context); + return true; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/NextLineRecoveryStrategy.cs b/src/OneScript.Language/SyntaxAnalysis/NextLineRecoveryStrategy.cs new file mode 100644 index 000000000..8c5029994 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/NextLineRecoveryStrategy.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class NextLineRecoveryStrategy : IErrorRecoveryStrategy + { + public Lexem Recover(ILexer lexer) + { + lexer.SkipTillLineEnd(); + return lexer.NextLexem(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/NextStatementRecoveryStrategy.cs b/src/OneScript.Language/SyntaxAnalysis/NextStatementRecoveryStrategy.cs new file mode 100644 index 000000000..58b7acc4c --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/NextStatementRecoveryStrategy.cs @@ -0,0 +1,44 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; +using System.Linq; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class NextStatementRecoveryStrategy : IErrorRecoveryStrategy + { + public Lexem Recover(ILexer lexer) + { + if(!lexer.Iterator.MoveToContent()) + return Lexem.EndOfText(); + + Lexem lastLexem = default; + do + { + try + { + lastLexem = lexer.NextLexem(); + if(AdditionalStops != null && AdditionalStops.Contains(lastLexem.Token) ) + { + break; + } + } + catch (SyntaxErrorException) + { + lexer.Iterator.MoveNext(); + } + + } while (!(lastLexem.Token == Token.EndOfText + || LanguageDef.IsBeginOfStatement(lastLexem.Token))); + + return lastLexem; + } + + public Token[] AdditionalStops { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/NodeBuilder.cs b/src/OneScript.Language/SyntaxAnalysis/NodeBuilder.cs new file mode 100644 index 000000000..913a98bb9 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/NodeBuilder.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; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.SyntaxAnalysis +{ + /// + /// helper for creating ast nodes of different types + /// + internal static class NodeBuilder + { + public static BslSyntaxNode CreateNode(NodeKind kind, in Lexem startLexem) + { + switch (kind) + { + case NodeKind.Identifier: + case NodeKind.Constant: + case NodeKind.ExportFlag: + case NodeKind.ByValModifier: + case NodeKind.AnnotationParameterName: + case NodeKind.AnnotationParameterValue: + case NodeKind.ParameterDefaultValue: + case NodeKind.ForEachVariable: + case NodeKind.Unknown: + return new TerminalNode(kind, startLexem); + case NodeKind.BlockEnd: + case NodeKind.ContinueStatement: + case NodeKind.BreakStatement: + return new LineMarkerNode(startLexem.Location, kind); + default: + var node = MakeNonTerminal(kind, startLexem); + return node; + } + } + + private static NonTerminalNode MakeNonTerminal(NodeKind kind, in Lexem startLexem) + { + switch (kind) + { + case NodeKind.Annotation: + case NodeKind.Import: + return new AnnotationNode(kind, startLexem); + case NodeKind.AnnotationParameter: + return new AnnotationParameterNode(); + case NodeKind.VariableDefinition: + return new VariableDefinitionNode(startLexem); + case NodeKind.Method: + return new MethodNode(); + case NodeKind.MethodSignature: + return new MethodSignatureNode(startLexem); + case NodeKind.MethodParameter: + return new MethodParameterNode(); + case NodeKind.BinaryOperation: + return new BinaryOperationNode(startLexem); + case NodeKind.UnaryOperation: + return new UnaryOperationNode(startLexem); + case NodeKind.WhileLoop: + return new WhileLoopNode(startLexem); + case NodeKind.Condition: + return new ConditionNode(startLexem); + case NodeKind.CodeBatch: + return new CodeBatchNode(startLexem); + case NodeKind.ForEachLoop: + return new ForEachLoopNode(startLexem); + case NodeKind.ForLoop: + return new ForLoopNode(startLexem); + case NodeKind.TryExcept: + return new TryExceptNode(startLexem); + case NodeKind.NewObject: + return new NewObjectNode(startLexem); + case NodeKind.Preprocessor: + return new PreprocessorDirectiveNode(startLexem); + case NodeKind.GlobalCall: + case NodeKind.MethodCall: + return new CallNode(kind, startLexem); + case NodeKind.Module: + throw new InvalidOperationException(); + default: + return new NonTerminalNode(kind, startLexem); + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/NodeKind.cs b/src/OneScript.Language/SyntaxAnalysis/NodeKind.cs new file mode 100644 index 000000000..975941278 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/NodeKind.cs @@ -0,0 +1,68 @@ +/*---------------------------------------------------------- +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.Language.SyntaxAnalysis +{ + public enum NodeKind + { + Unknown, + Module, + VariablesSection, + MethodsSection, + ModuleBody, + Annotation, + AnnotationParameter, + AnnotationParameterName, + AnnotationParameterValue, + VariableDefinition, + ByValModifier, + Identifier, + ExportFlag, + Procedure, + Function, + Method, + MethodSignature, + MethodParameters, + MethodParameter, + ParameterDefaultValue, + BlockEnd, + CodeBatch, + GlobalCall, + MethodCall, + CallArgumentList, + CallArgument, + DereferenceOperation, + Constant, + IndexAccess, + BinaryOperation, + UnaryOperation, + Assignment, + TernaryOperator, + NewObject, + WhileLoop, + ForLoop, + Condition, + ForEachLoop, + ForEachVariable, + ForEachCollection, + ForInitializer, + ForLimit, + BreakStatement, + ContinueStatement, + ReturnStatement, + RaiseException, + TryExcept, + ExecuteStatement, + AddHandler, + RemoveHandler, + Preprocessor, + Import, + TopLevelExpression, + Label, + Goto + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/NonTerminalExtensions.cs b/src/OneScript.Language/SyntaxAnalysis/NonTerminalExtensions.cs new file mode 100644 index 000000000..24086bb85 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/NonTerminalExtensions.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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.Runtime.CompilerServices; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.SyntaxAnalysis +{ + internal static class NonTerminalExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T AddNode(this NonTerminalNode parent, T child) + where T : BslSyntaxNode + { + parent.AddChild(child); + return child; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/ParserContext.cs b/src/OneScript.Language/SyntaxAnalysis/ParserContext.cs new file mode 100644 index 000000000..87e19409a --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/ParserContext.cs @@ -0,0 +1,35 @@ +/*---------------------------------------------------------- +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 OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class ParserContext + { + private readonly Stack _nodeContext = new Stack(); + + public void PushContext(NonTerminalNode node) + { + _nodeContext.Push(node); + } + + public NonTerminalNode PopContext() + { + return _nodeContext.Pop(); + } + + public NonTerminalNode CurrentParent => _nodeContext.Peek(); + + public T AddChild(T child) where T : BslSyntaxNode + { + CurrentParent.AddChild(child); + return child; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/ParserExtensions.cs b/src/OneScript.Language/SyntaxAnalysis/ParserExtensions.cs new file mode 100644 index 000000000..d1d444265 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/ParserExtensions.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +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.Text; +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public static class ParserExtensions + { + public static string Describe(this IEnumerable errors) + { + var builder = new StringBuilder(128); + foreach (var parseError in errors) + { + builder.AppendLine($"{parseError.ErrorId} / {parseError.Position.LineNumber}"); + } + + return builder.ToString(); + } + + public static bool IsEmpty(this CodeRange range) + { + return range.LineNumber == -1; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/PreprocessingLexer.cs b/src/OneScript.Language/SyntaxAnalysis/PreprocessingLexer.cs new file mode 100644 index 000000000..06a9c23c8 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/PreprocessingLexer.cs @@ -0,0 +1,51 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class PreprocessingLexer : ILexer + { + private readonly ILexer _baseLexer; + + public PreprocessingLexer(ILexer baseLexer) + { + _baseLexer = baseLexer; + } + + public PreprocessorHandlers Handlers { get; set; } + + public IErrorSink ErrorSink { get; set; } + + public Lexem NextLexem() + { + Lexem lex; + while ((lex = _baseLexer.NextLexem()).Type == LexemType.PreprocessorDirective) + { + var handled = Handlers.HandleDirective(ref lex, this); + if (handled) + { + return lex; + } + + var err = LocalizedErrors.DirectiveNotSupported(lex.Content); + err.Position = this.GetErrorPosition(); + + ErrorSink?.AddError(err); + } + + return lex; + } + + public SourceCodeIterator Iterator + { + get => _baseLexer.Iterator; + set => _baseLexer.Iterator = value; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/PreprocessorHandlers.cs b/src/OneScript.Language/SyntaxAnalysis/PreprocessorHandlers.cs new file mode 100644 index 000000000..0649be80a --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/PreprocessorHandlers.cs @@ -0,0 +1,96 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class PreprocessorHandlers : IDirectiveHandler, IEnumerable + { + private readonly IList _handlers; + + public PreprocessorHandlers() + { + _handlers = new List(); + } + + public PreprocessorHandlers(IEnumerable handlers) + { + _handlers = new List(handlers); + } + + public void Add(IDirectiveHandler handler) + { + _handlers.Add(handler); + } + + public void Remove(IDirectiveHandler handler) + { + _handlers.Remove(handler); + } + + public IDirectiveHandler Get(Type type) + { + return _handlers.FirstOrDefault(type.IsInstanceOfType); + } + + public T Get() where T : IDirectiveHandler + { + return (T)Get(typeof(T)); + } + + public PreprocessorHandlers Slice(Func predicate) + { + var slice = _handlers.Where(predicate).ToArray(); + if (slice.Length == 0) + return new PreprocessorHandlers(); + + return new PreprocessorHandlers(slice); + } + + public void OnModuleEnter() + { + foreach (var handler in _handlers) + { + handler.OnModuleEnter(); + } + } + + public void OnModuleLeave() + { + foreach (var handler in _handlers) + { + handler.OnModuleLeave(); + } + } + + public bool HandleDirective(ref Lexem lexem, ILexer lexer) + { + foreach (var handler in _handlers) + { + if (handler.HandleDirective(ref lexem, lexer)) + return true; + } + + return false; + } + + public IEnumerator GetEnumerator() + { + return _handlers.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) _handlers).GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/RegionDirectiveHandler.cs b/src/OneScript.Language/SyntaxAnalysis/RegionDirectiveHandler.cs new file mode 100644 index 000000000..d659ce4da --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/RegionDirectiveHandler.cs @@ -0,0 +1,103 @@ +/*---------------------------------------------------------- +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.Runtime.CompilerServices; +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.SyntaxAnalysis +{ + public class RegionDirectiveHandler : DirectiveHandlerBase + { + private readonly IdentifiersTrie _preprocRegion = new IdentifiersTrie(); + private readonly IdentifiersTrie _preprocEndRegion = new IdentifiersTrie(); + + private int _regionsNesting = 0; + + public RegionDirectiveHandler(IErrorSink errorSink) : base(errorSink) + { + _preprocRegion.Add("Область",true); + _preprocRegion.Add("Region", true); + _preprocEndRegion.Add("КонецОбласти", true); + _preprocEndRegion.Add("EndRegion", true); + } + + public override void OnModuleLeave() + { + if (_regionsNesting != 0) + { + AddError(LocalizedErrors.EndOfDirectiveExpected("Область"), new ErrorPositionInfo()); + } + } + + public override bool HandleDirective(ref Lexem lastExtractedLexem, ILexer lexer) + { + var result = false; + if (IsPreprocRegion(lastExtractedLexem.Content)) + { + var regionName = lexer.NextLexemOnSameLine(); + if (regionName.Type == LexemType.EndOfText) + { + AddError(LocalizedErrors.RegionNameExpected(), lexer.GetErrorPosition()); + return true; + } + + if (!LanguageDef.IsValidIdentifier(regionName.Content)) + { + AddError(LocalizedErrors.InvalidRegionName(regionName.Content), lexer.GetErrorPosition()); + return true; + } + + _regionsNesting++; + + lastExtractedLexem = LexemFromNewLine(lexer); + result = true; + } + else if (IsPreprocEndRegion(lastExtractedLexem.Content)) + { + if (_regionsNesting == 0) + { + AddError(LocalizedErrors.DirectiveIsMissing("Область"), lexer.GetErrorPosition()); + return true; + } + + _regionsNesting--; + + lastExtractedLexem = LexemFromNewLine(lexer); + result = true; + } + + return result; + } + + private void AddError(CodeError err, ErrorPositionInfo position) + { + err.Position ??= position; + ErrorSink.AddError(err); + } + + private Lexem LexemFromNewLine(ILexer lexer) + { + var lex = lexer.NextLexem(); + + if (!lexer.Iterator.OnNewLine) + throw new SyntaxErrorException(lexer.GetErrorPosition(), "Недопустимые символы в директиве"); + + return lex; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsPreprocRegion(string value) + { + return _preprocRegion.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsPreprocEndRegion(string value) + { + return _preprocEndRegion.TryGetValue(value, out var nodeIsFilled) && nodeIsFilled; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxAnalysis/SingleWordModuleAnnotationHandler.cs b/src/OneScript.Language/SyntaxAnalysis/SingleWordModuleAnnotationHandler.cs new file mode 100644 index 000000000..5abba04a8 --- /dev/null +++ b/src/OneScript.Language/SyntaxAnalysis/SingleWordModuleAnnotationHandler.cs @@ -0,0 +1,83 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Localization; + +namespace OneScript.Language.SyntaxAnalysis +{ + /// + /// Обработчик аннотаций модуля, состоящих из одного слова + /// + public class SingleWordModuleAnnotationHandler : ModuleAnnotationDirectiveHandler + { + private readonly ILexer _allLineContentLexer; + private readonly ISet _knownNames = new HashSet(StringComparer.CurrentCultureIgnoreCase); + + private static readonly LexerBuilder InstanceBuilder = SetupLexerBuilder(); + + public SingleWordModuleAnnotationHandler(ISet knownNames, IErrorSink errorSink) : base(errorSink) + { + _allLineContentLexer = InstanceBuilder.Build(); + _knownNames = knownNames; + } + + public SingleWordModuleAnnotationHandler(ISet knownNames, IErrorSink errorSink) : base(errorSink) + { + _allLineContentLexer = InstanceBuilder.Build(); + + foreach (var twoNames in knownNames) + { + _knownNames.Add(twoNames.Russian); + _knownNames.Add(twoNames.English); + } + } + + private static LexerBuilder SetupLexerBuilder() + { + var builder = new LexerBuilder(); + builder + .DetectComments() + .Detect((cs, i) => !char.IsWhiteSpace(cs)) + .HandleWith(new WordLexerState()); + + return builder; + } + + protected override bool DirectiveSupported(string directive) + { + return _knownNames.Contains(directive); + } + + protected override void ParseAnnotationInternal(ref Lexem lastExtractedLexem, ILexer lexer, ParserContext parserContext) + { + var node = new AnnotationNode(NodeKind.Annotation, lastExtractedLexem); + _allLineContentLexer.Iterator = lexer.Iterator; + + parserContext.AddChild(node); + + // после ничего не должно находиться + var nextLexem = _allLineContentLexer.NextLexemOnSameLine(); + lastExtractedLexem = lexer.NextLexem(); // сдвиг основного лексера + if (nextLexem.Type != LexemType.EndOfText && nextLexem.Type != LexemType.Comment) + { + var err = LocalizedErrors.ExpressionSyntax(); + err.Position = new ErrorPositionInfo + { + LineNumber = node.Location.LineNumber, + ColumnNumber = node.Location.ColumnNumber, + Code = lexer.Iterator.GetCodeLine(node.Location.LineNumber), + ModuleName = lexer.Iterator.Source.Name + }; + ErrorSink.AddError(err); + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language/SyntaxErrorException.cs b/src/OneScript.Language/SyntaxErrorException.cs index 258b27072..e6cb3e8b1 100644 --- a/src/OneScript.Language/SyntaxErrorException.cs +++ b/src/OneScript.Language/SyntaxErrorException.cs @@ -9,9 +9,19 @@ namespace OneScript.Language { public class SyntaxErrorException : ScriptException { - internal SyntaxErrorException(CodePositionInfo codeInfo, string message):base(codeInfo, message) + public SyntaxErrorException(ErrorPositionInfo codeInfo, string message):base(codeInfo, message) { } + + internal SyntaxErrorException(CodeError error) : base(error.Position ?? new ErrorPositionInfo(), error.Description) + { + + } + + internal SyntaxErrorException(ErrorPositionInfo codeInfo, CodeError error) : base(codeInfo, error.Description) + { + + } } } \ No newline at end of file diff --git a/src/OneScript.Language/ThrowingErrorSink.cs b/src/OneScript.Language/ThrowingErrorSink.cs new file mode 100644 index 000000000..48218a91e --- /dev/null +++ b/src/OneScript.Language/ThrowingErrorSink.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace OneScript.Language +{ + public class ThrowingErrorSink : IErrorSink + { + public ThrowingErrorSink() + { + ExceptionFactory = err => new SyntaxErrorException(err); + } + + public ThrowingErrorSink(Func exceptionFactory) + { + ExceptionFactory = exceptionFactory; + } + + public IEnumerable Errors => Array.Empty(); + + public bool HasErrors => false; + + private Func ExceptionFactory { get; set; } + + public void AddError(CodeError err) + { + throw ExceptionFactory(err); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/BinaryOperationCompiler.cs b/src/OneScript.Native/Compiler/BinaryOperationCompiler.cs new file mode 100644 index 000000000..76f47f7a8 --- /dev/null +++ b/src/OneScript.Native/Compiler/BinaryOperationCompiler.cs @@ -0,0 +1,221 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Native.Compiler +{ + internal class BinaryOperationCompiler + { + private ExpressionType _opCode; + + public Expression Compile(BinaryOperationNode node, Expression left, Expression right) + { + _opCode = ExpressionHelpers.TokenToOperationCode(node.Operation); + + if (left.Type.IsValue() || right.Type.IsValue()) + { + return CompileDynamicOperation(left, right); + } + else + { + return CompileStaticOperation(left, right); + } + } + + private Expression CompileStaticOperation(Expression left, Expression right) + { + if (left.Type.IsNumeric()) + { + return MakeNumericOperation(left, right); + } + + if (left.Type == typeof(DateTime)) + { + return DateOperation(left, right); + } + + if (left.Type == typeof(string)) + { + return MakeStringOperation(left, right); + } + + if (left.Type == typeof(bool)) + { + return MakeLogicalOperation(left, right); + } + + throw NativeCompilerException.OperationNotDefined(_opCode, left.Type, right.Type); + } + + private Expression MakeStringOperation(Expression left, Expression right) + { + if (_opCode == ExpressionType.Add) + { + return StringAddition(left, right); + } + + // Для строк допустимо сравнение со строками на < > + if (IsComparisonOperation(_opCode)) + { + // для простоты сделаем через BslValue.CompareTo + return MakeDynamicComparison(left, right); + } + + if (IsEqualityOperation(_opCode)) + { + return right.Type == typeof(string) ? + Expression.MakeBinary(_opCode, left, right) + : MakeDynamicEquality(left, right); + } + + throw NativeCompilerException.OperationNotDefined(_opCode, left.Type, right.Type); + } + + private Expression MakeNumericOperation(Expression left, Expression right) + { + Debug.Assert(left.Type.IsNumeric()); + + if (right.Type.IsNumeric()) + { + return AdjustArithmeticOperandTypesAndMakeBinary(left, right); + } + + throw NativeCompilerException.OperationNotDefined(_opCode, left.Type, right.Type); + } + + private Expression AdjustArithmeticOperandTypesAndMakeBinary(Expression left, Expression right) + { + if (left.Type == right.Type) + return Expression.MakeBinary(_opCode, left, right); + + // try find direct operator + var method = GetUserDefinedBinaryOperator("op_" + _opCode.ToString(), left.Type, right.Type); + if (method == null) + { + // try convert + if (left.Type.IsInteger() && !right.Type.IsInteger()) + { + // нужна нецелочисленная операция + left = Expression.Convert(left, typeof(decimal)); + return AdjustArithmeticOperandTypesAndMakeBinary(left, right); + } + + right = Expression.Convert(right, left.Type); + } + + return Expression.MakeBinary(_opCode, left, right, false, method); + } + + private static MethodInfo GetUserDefinedBinaryOperator(string name, Type leftType, Type rightType) { + + Type[] types = { leftType, rightType }; + + BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + MethodInfo method = leftType.GetMethod(name, flags, null, types, null); + if (method == null) + { + method = rightType.GetMethod(name, flags, null, types, null); + } + + return method; + } + + private Expression DateOperation(Expression left, Expression right) + { + if (right.Type.IsNumeric()) + { + return DateOperations.DateOffsetOperation(left, right, _opCode); + } + + if (right.Type == typeof(DateTime)) + { + return DateOperations.DatesBinaryOperation(left, right, _opCode); + } + + throw NativeCompilerException.OperationNotDefined(_opCode, left.Type, right.Type); + } + + private static bool IsComparisonOperation(ExpressionType opCode) + { + return opCode == ExpressionType.LessThan || + opCode == ExpressionType.LessThanOrEqual || + opCode == ExpressionType.GreaterThan || + opCode == ExpressionType.GreaterThanOrEqual; + } + + private static bool IsEqualityOperation(ExpressionType opCode) + { + return opCode == ExpressionType.Equal || opCode == ExpressionType.NotEqual; + } + + private Expression MakeLogicalOperation(Expression left, Expression right) + { + return Expression.MakeBinary(_opCode, ExpressionHelpers.ToBoolean(left), ExpressionHelpers.ToBoolean(right)); + } + + private Expression StringAddition(Expression left, Expression right) + { + var concatMethod = typeof(string).GetMethod( + nameof(string.Concat), + new[] { typeof(string), typeof(string) }); + + Debug.Assert(concatMethod != null); + + return Expression.Call(null, concatMethod, left, ExpressionHelpers.ToString(right)); + } + + private Expression CompileDynamicOperation(Expression left, Expression right) + { + switch (_opCode) + { + case ExpressionType.Add: + return ExpressionHelpers.DynamicAdd(left, right); + case ExpressionType.Subtract: + return ExpressionHelpers.DynamicSubtract(left, right); + case ExpressionType.LessThan: + case ExpressionType.LessThanOrEqual: + case ExpressionType.GreaterThan: + case ExpressionType.GreaterThanOrEqual: + return MakeDynamicComparison(left, right); + case ExpressionType.Equal: + case ExpressionType.NotEqual: + return MakeDynamicEquality(left, right); + case ExpressionType.Multiply: + case ExpressionType.Divide: + case ExpressionType.Modulo: + return MakeNumericOperation( + ExpressionHelpers.ToNumber(left), + ExpressionHelpers.ToNumber(right)); + case ExpressionType.AndAlso: + case ExpressionType.OrElse: + return MakeLogicalOperation(left, right); + default: + throw NativeCompilerException.OperationNotDefined(_opCode, left.Type, right.Type); + } + } + + private Expression MakeDynamicEquality(Expression left, Expression right) + { + var result = ExpressionHelpers.CallEquals(ExpressionHelpers.ConvertToBslValue(left), ExpressionHelpers.ConvertToBslValue(right)); + if (_opCode == ExpressionType.NotEqual) + result = Expression.Not(result); + + return result; + } + + private Expression MakeDynamicComparison(Expression left, Expression right) + { + var compareCall = ExpressionHelpers.CallCompareTo(ExpressionHelpers.ConvertToBslValue(left), ExpressionHelpers.ConvertToBslValue(right)); + return Expression.MakeBinary(_opCode, compareCall, Expression.Constant(0)); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/BslWalkerContext.cs b/src/OneScript.Native/Compiler/BslWalkerContext.cs new file mode 100644 index 000000000..41cffc379 --- /dev/null +++ b/src/OneScript.Native/Compiler/BslWalkerContext.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 OneScript.Compilation.Binding; +using OneScript.DependencyInjection; +using OneScript.Language; +using OneScript.Sources; + +namespace OneScript.Native.Compiler +{ + public class BslWalkerContext + { + public SymbolTable Symbols { get; set; } + + public IErrorSink Errors { get; set; } + + public SourceCode Source { get; set; } + + public IServiceContainer Services { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/BuiltInFunctionsCache.cs b/src/OneScript.Native/Compiler/BuiltInFunctionsCache.cs new file mode 100644 index 000000000..e6cc1fa09 --- /dev/null +++ b/src/OneScript.Native/Compiler/BuiltInFunctionsCache.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +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.Reflection; +using OneScript.Contexts; +using OneScript.Language; +using OneScript.Native.Runtime; + +namespace OneScript.Native.Compiler +{ + public class BuiltInFunctionsCache + { + private readonly IdentifiersTrie _cache = new IdentifiersTrie(); + + public BuiltInFunctionsCache() + { + var publicMethods = typeof(BuiltInFunctions).GetMethods(); + foreach (var methodInfo in publicMethods) + { + var markup = methodInfo.GetCustomAttribute(); + if (markup == null) + continue; + + _cache.Add(markup.Name, methodInfo); + if(markup.Alias != null) + _cache.Add(markup.Alias, methodInfo); + } + } + + public MethodInfo GetMethod(string name) => _cache[name]; + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/CompilerHelpers.cs b/src/OneScript.Native/Compiler/CompilerHelpers.cs new file mode 100644 index 000000000..e8cc930a1 --- /dev/null +++ b/src/OneScript.Native/Compiler/CompilerHelpers.cs @@ -0,0 +1,128 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using OneScript.Contexts; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Types; +using OneScript.Values; + +namespace OneScript.Native.Compiler +{ + public static class CompilerHelpers + { + public static BslPrimitiveValue ValueFromLiteral(in Lexem lex) + { + return lex.Type switch + { + LexemType.NumberLiteral => BslNumericValue.Parse(lex.Content), + LexemType.BooleanLiteral => BslBooleanValue.Parse(lex.Content), + LexemType.StringLiteral => BslStringValue.Create(lex.Content), + LexemType.DateLiteral => BslDateValue.Parse(lex.Content), + LexemType.UndefinedLiteral => BslUndefinedValue.Instance, + LexemType.NullLiteral => BslNullValue.Instance, + _ => throw new NotImplementedException() + }; + } + + public static object ClrValueFromLiteral(in Lexem lex) + { + return lex.Type switch + { + LexemType.NumberLiteral => (decimal)BslNumericValue.Parse(lex.Content), + LexemType.BooleanLiteral => (bool)BslBooleanValue.Parse(lex.Content), + LexemType.StringLiteral => (string)BslStringValue.Create(lex.Content), + LexemType.DateLiteral => (DateTime)BslDateValue.Parse(lex.Content), + LexemType.UndefinedLiteral => BslUndefinedValue.Instance, + LexemType.NullLiteral => BslNullValue.Instance, + _ => throw new NotImplementedException() + }; + } + + public static IEnumerable GetAnnotations(IEnumerable annotations) + { + // Возможно будут какие-то маппинги на системные атрибуты, не только на BslAnnotation + // поэтому возвращаем Attribute[] а не BslAnnotation[] + + return annotations.Select(GetBslAnnotation).ToList(); + } + + public static BslAnnotationAttribute GetBslAnnotation(AnnotationNode node) + { + var parameters = GetAnnotationParameters(node); + return new BslAnnotationAttribute(node.Name, parameters); + } + + public static Type GetClrType(TypeDescriptor type) + { + Type clrType; + if (type == BasicTypes.String) + clrType = typeof(string); + else if (type == BasicTypes.Date) + clrType = typeof(DateTime); + else if (type == BasicTypes.Boolean) + clrType = typeof(bool); + else if (type == BasicTypes.Number) + clrType = typeof(decimal); + else if (type == BasicTypes.Type) + clrType = typeof(BslTypeValue); + else + clrType = type.ImplementingClass; + + return clrType; + } + + private static IEnumerable GetAnnotationParameters(AnnotationNode node) + { + return node.Children.Cast() + .Select(MakeAnnotationParameter) + .ToList(); + } + + private static BslAnnotationParameter MakeAnnotationParameter(AnnotationParameterNode param) + { + BslAnnotationParameter result; + if (param.AnnotationNode != null) + { + var runtimeValue = new BslAnnotationValue(param.AnnotationNode.Name); + foreach (var child in param.AnnotationNode.Children) + { + runtimeValue.Parameters.Add(MakeAnnotationParameter((AnnotationParameterNode)child)); + } + result = new BslAnnotationParameter(param.Name, runtimeValue); + } + else + if (param.Value.Type != LexemType.NotALexem) + { + var runtimeValue = ValueFromLiteral(param.Value); + result = new BslAnnotationParameter(param.Name, runtimeValue); + } + else + { + result = new BslAnnotationParameter(param.Name, null); + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetIdentifier(this BslSyntaxNode node) + { + return GetIdentifier((TerminalNode) node); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetIdentifier(this TerminalNode node) + { + return node.Lexem.Content; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ContextMethodsCache.cs b/src/OneScript.Native/Compiler/ContextMethodsCache.cs new file mode 100644 index 000000000..aae308711 --- /dev/null +++ b/src/OneScript.Native/Compiler/ContextMethodsCache.cs @@ -0,0 +1,70 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Contexts; + +namespace OneScript.Native.Compiler +{ + /// + /// L2 Cache for context methods + /// + public class ContextMethodsCache : ReflectedMethodsCache + { + private struct CacheRecord + { + public string Name; + public string Alias; + public MethodInfo Method; + } + + private const int _l2Size = 128; + private Dictionary _methodCache = new Dictionary(); + + public ContextMethodsCache() : base(32) + { + } + + public override MethodInfo GetOrAdd(Type type, string name) + { + return GetOrAdd(type, name, BindingFlags.Instance | BindingFlags.Public); + } + + protected override MethodInfo SearchImpl(Type type, string name, BindingFlags flags) + { + if(!_methodCache.TryGetValue(type, out var cacheRecords)) + { + cacheRecords = type.GetMethods() + .Select(x => new + { + Method = x, + Markup = x.GetCustomAttribute() + }) + .Where(x => x.Markup != null) + .Select(x => new CacheRecord + { + Name = x.Markup.Name, + Alias = x.Markup.Alias, + Method = x.Method + }).ToArray(); + + if(_methodCache.Count > _l2Size) + _methodCache.Clear(); + + _methodCache.Add(type, cacheRecords); + } + + return cacheRecords.FirstOrDefault(x => + name.Equals(x.Name, StringComparison.OrdinalIgnoreCase) + || (x.Alias != default && name.Equals(x.Alias, StringComparison.OrdinalIgnoreCase))) + .Method; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ContextPropertiesCache.cs b/src/OneScript.Native/Compiler/ContextPropertiesCache.cs new file mode 100644 index 000000000..9fe2d0939 --- /dev/null +++ b/src/OneScript.Native/Compiler/ContextPropertiesCache.cs @@ -0,0 +1,53 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Reflection; +using OneScript.Contexts; + +namespace OneScript.Native.Compiler +{ + public class ContextPropertiesCache : ReflectedMembersCache + { + public ContextPropertiesCache() + { + } + + public ContextPropertiesCache(int size): base(size) + { + } + + public override PropertyInfo GetOrAdd(Type type, string name) + { + return GetOrAdd(type, name, BindingFlags.Public | BindingFlags.Instance); + } + + protected override PropertyInfo SearchImpl(Type type, string name, BindingFlags flags) + { + var props = type.FindMembers( + MemberTypes.Field | MemberTypes.Property, + BindingFlags.Public | BindingFlags.Instance, + (info, criteria) => + { + var a = info.CustomAttributes.FirstOrDefault(data => + data.AttributeType == typeof(ContextPropertyAttribute)); + if (a == null) return false; + return a.ConstructorArguments.Any(alias => + StringComparer.CurrentCultureIgnoreCase.Equals(alias.Value.ToString(), name)); + }, + null); + + if (props.Length != 1) + { + return null; + } + + return props[0] as PropertyInfo; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/DateOperations.cs b/src/OneScript.Native/Compiler/DateOperations.cs new file mode 100644 index 000000000..4627a2d2f --- /dev/null +++ b/src/OneScript.Native/Compiler/DateOperations.cs @@ -0,0 +1,98 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; + +namespace OneScript.Native.Compiler +{ + internal static class DateOperations + { + /// + /// Операция смещения дат (прибавление или удаление секунд + /// + /// Выражение с типом DateTime + /// Выражение с типом Число + /// Вид операции Add или Substract + /// Выражение содержащее операцию + /// opCode указан неверно + public static Expression DateOffsetOperation(Expression left, Expression right, ExpressionType opCode) + { + Debug.Assert(left.Type == typeof(DateTime)); + Debug.Assert(right.Type == typeof(decimal)); + + var adder = typeof(DateTime).GetMethod(nameof(DateTime.AddSeconds)); + Debug.Assert(adder != null); + + var toDouble = Expression.Convert(right, typeof(double)); + Expression arg = opCode switch + { + ExpressionType.Add => toDouble, + ExpressionType.Subtract => Expression.Negate(toDouble), + _ => throw NativeCompilerException.OperationNotDefined(opCode, left.Type, right.Type) + }; + + return Expression.Call(left, adder, arg); + } + + /// + /// Операция разности дат (вычитания дат) + /// + /// Выражение с типом DateTime + /// Выражение с типом DateTime + /// Выражение, содержащее операцию + public static Expression DateDiffExpression(Expression left, Expression right) + { + Debug.Assert(left.Type == typeof(DateTime)); + Debug.Assert(right.Type == typeof(DateTime)); + + var spanExpr = Expression.Subtract(left, right); + var totalSeconds = Expression.Property(spanExpr, nameof(TimeSpan.TotalSeconds)); + var decimalSeconds = Expression.Convert(totalSeconds, typeof(decimal)); + + return decimalSeconds; + } + + /// + /// Операция сравнения двух дат + /// + /// Выражение с типом DateTime + /// Выражение с типом DateTime + /// Двоичный оператор + /// Выражение, содержащее операцию + /// opCode указан неверно + public static Expression DatesBinaryOperation(Expression left, Expression right, ExpressionType opCode) + { + Debug.Assert(left.Type == typeof(DateTime)); + Debug.Assert(right.Type == typeof(DateTime)); + + if (IsComparisonOperation(opCode) || IsEqualityOperation(opCode)) + { + return Expression.MakeBinary(opCode, left, right); + } + + if (opCode != ExpressionType.Subtract) + throw NativeCompilerException.OperationNotDefined(opCode, left.Type, right.Type); + + return DateDiffExpression(left, right); + } + + private static bool IsComparisonOperation(ExpressionType opCode) + { + return opCode == ExpressionType.LessThan || + opCode == ExpressionType.LessThanOrEqual || + opCode == ExpressionType.GreaterThan || + opCode == ExpressionType.GreaterThanOrEqual; + } + + private static bool IsEqualityOperation(ExpressionType opCode) + { + return opCode == ExpressionType.Equal || opCode == ExpressionType.NotEqual; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/DynamicModule.cs b/src/OneScript.Native/Compiler/DynamicModule.cs new file mode 100644 index 000000000..682612a98 --- /dev/null +++ b/src/OneScript.Native/Compiler/DynamicModule.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Sources; + +namespace OneScript.Native.Compiler +{ + public class DynamicModule : IExecutableModule + { + public IList ModuleAttributes { get; } = new List(); + + public IList Fields { get; } = new List(); + + public IList Properties { get; } = new List(); + + public IList Methods { get; } = new List(); + + public BslScriptMethodInfo ModuleBody => Methods.FirstOrDefault(x => x.Name == IExecutableModule.BODY_METHOD_NAME); + + public SourceCode Source { get; set; } + + public IDictionary Interfaces { get; } = new Dictionary(); + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ExpressionHelpers.cs b/src/OneScript.Native/Compiler/ExpressionHelpers.cs new file mode 100644 index 000000000..8ed0f479e --- /dev/null +++ b/src/OneScript.Native/Compiler/ExpressionHelpers.cs @@ -0,0 +1,776 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CSharp.RuntimeBinder; +using OneScript.Contexts; +using OneScript.Language.LexicalAnalysis; +using OneScript.Localization; +using OneScript.Native.Runtime; +using OneScript.Sources; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Native.Compiler +{ + public static class ExpressionHelpers + { + public static Expression DowncastDecimal(Expression decimalValue, Type targetType) + { + return Expression.Convert(decimalValue, targetType); + } + + public static Expression CastToDecimal(Expression value) + { + if (value.Type == typeof(decimal)) + return value; + + return Expression.Convert(value, typeof(decimal)); + } + + public static ExpressionType TokenToOperationCode(Token stackOp) + { + ExpressionType opCode; + switch (stackOp) + { + case Token.Equal: + opCode = ExpressionType.Equal; + break; + case Token.NotEqual: + opCode = ExpressionType.NotEqual; + break; + case Token.And: + opCode = ExpressionType.AndAlso; + break; + case Token.Or: + opCode = ExpressionType.OrElse; + break; + case Token.Plus: + opCode = ExpressionType.Add; + break; + case Token.Minus: + opCode = ExpressionType.Subtract; + break; + case Token.Multiply: + opCode = ExpressionType.Multiply; + break; + case Token.Division: + opCode = ExpressionType.Divide; + break; + case Token.Modulo: + opCode = ExpressionType.Modulo; + break; + case Token.UnaryPlus: + opCode = ExpressionType.UnaryPlus; + break; + case Token.UnaryMinus: + opCode = ExpressionType.Negate; + break; + case Token.Not: + opCode = ExpressionType.Not; + break; + case Token.LessThan: + opCode = ExpressionType.LessThan; + break; + case Token.LessOrEqual: + opCode = ExpressionType.LessThanOrEqual; + break; + case Token.MoreThan: + opCode = ExpressionType.GreaterThan; + break; + case Token.MoreOrEqual: + opCode = ExpressionType.GreaterThanOrEqual; + break; + default: + throw new NotSupportedException(); + } + return opCode; + } + + private static readonly ReflectedMethodsCache OperationsCache = new ReflectedMethodsCache(); + private static readonly ReflectedPropertiesCache PropertiesCache = new ReflectedPropertiesCache(); + + public static Expression ToBoolean(Expression right) + { + if (right.Type == typeof(bool)) + return right; + + if (right.Type.IsValue()) + return ConvertBslValueToPrimitiveType(right, typeof(bool)); + + if (right.Type.IsNumeric()) + { + return Expression.NotEqual(right, Expression.Default(right.Type)); + } + + return Expression.Convert(right, typeof(bool)); + } + + public static Expression ToNumber(Expression right) + { + if (right.Type == typeof(decimal)) + return right; + + if (right.Type.IsValue()) + return ConvertBslValueToPrimitiveType(right, typeof(decimal)); + + return Expression.Convert(right, typeof(decimal)); + } + + public static Expression ToDate(Expression right) + { + if (right.Type == typeof(DateTime)) + return right; + + if (right.Type.IsValue()) + return ConvertBslValueToPrimitiveType(right, typeof(DateTime)); + + return Expression.Convert(right, typeof(DateTime)); + } + + public static Expression ToString(Expression right) + { + if (right.Type == typeof(string)) + return right; + + if (right.Type.IsValue()) + return ConvertBslValueToPrimitiveType(right, typeof(string)); + + var method = OperationsCache.GetOrAdd( + typeof(object), + nameof(object.ToString), + BindingFlags.Public | BindingFlags.Instance); + + return Expression.Call(right, method); + } + + private static Expression TryConvertBslValueToPrimitiveType(Expression right, Type type) + { + Debug.Assert(right.Type.IsValue()); + + // DLR не находит операторы конверсии, объявленные в BslValue, надо ей помочь. + // FIXME: история с кастомными операторами конверсии, а значит и всё введение иерархии BslValue + // себя не оправдало и надо это удалить, т.к. сейчас только производит путаницу. + string operatorName; + if (type == typeof(bool)) + operatorName = nameof(DynamicOperations.ToBoolean); + else if (type.IsNumeric()) + operatorName = nameof(DynamicOperations.ToNumber); + else if (type == typeof(DateTime)) + operatorName = nameof(DynamicOperations.ToDate); + else if (type == typeof(string)) + operatorName = nameof(DynamicOperations.ToString); + else + return null; + + var method = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + operatorName + ); + + return Expression.Convert(right, type, method); + } + + private static Expression ConvertBslValueToPrimitiveType(Expression right, Type type) + { + return TryConvertBslValueToPrimitiveType(right, type) ?? + throw new NativeCompilerException( + BilingualString.Localize( + $"Преобразование {right.Type} в тип {type} недоступно", + $"Conversion from {right.Type} to {type} is unavailable") + ); + } + + public static Expression DynamicAdd(Expression left, Expression right) + { + var operation = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.Add)); + + return Expression.Call(operation, ConvertToBslValue(left), ConvertToBslValue(right)); + } + + public static Expression DynamicSubtract(Expression left, Expression right) + { + var operation = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.Subtract)); + + return Expression.Call(operation, ConvertToBslValue(left), ConvertToBslValue(right)); + } + + public static Expression ConvertToType(Expression value, Type targetType) + { + if (!TryStaticConversion(value, targetType, out var result)) + { + return ConvertThroughBslValue(value, targetType); + } + + return result; + } + + private static Expression ConvertThroughBslValue(Expression value, Type targetType) + { + var bslVal = ConvertToBslValue(value); + return ConvertToType(bslVal, targetType); + } + + public static bool TryStaticConversion(Expression value, Type targetType, out Expression result) + { + if (targetType == typeof(BslValue) || targetType == typeof(IValue)) + { + result = ConvertToBslValue(value); + return true; + } + else if (typeof(BslObjectValue).IsAssignableFrom(targetType) && value.Type == typeof(BslUndefinedValue)) + { + // это присваивание Неопределено + // в переменную строго типизированного объекта + // просто обнуляем переменную, не меняя тип на Неопределено + result = Expression.Default(targetType); + return true; + } + else + { + var conversion = TryFindConversionOp(value, targetType); + if (conversion != null) + { + result = conversion; + return true; + } + } + + result = null; + return false; + } + + private static Expression TryFindConversionOp(Expression value, Type targetType) + { + if (value.Type.IsValue()) + { + if (targetType.IsNumeric()) + { + var decimalNum = ToNumber(value); + if (targetType.IsAssignableFrom(typeof(decimal))) + return decimalNum; + + return DowncastDecimal(decimalNum, targetType); + } + else if (targetType.IsEnum) + { + Type generic = typeof(ClrEnumValueWrapper<>); + var wrapperType = generic.MakeGenericType(new[]{targetType}); + try + { + var wrapper = Expression.Convert(value, wrapperType); + return Expression.Property(wrapper,"UnderlyingValue"); + } + catch (InvalidOperationException) + { + throw new NativeCompilerException( + BilingualString.Localize( + $"Преобразование {value.Type} в тип {targetType} недоступно", + $"Conversion from {value.Type} to {targetType} is unavailable") + ); + } + } + else + { + var conversion = TryConvertBslValueToPrimitiveType(value, targetType); + if (conversion != null) + return conversion; + } + } + + // пока в тупую делаем каст, а вдруг повезет + // если будет ненадежно - поиграем с поиском статических конверсий + try + { + return Expression.Convert(value, targetType); + } + catch (InvalidOperationException) + { + return null; + } + } + + private static Expression DynamicallyCastToClrType(Expression value, Type targetType) + { + var binder = Microsoft.CSharp.RuntimeBinder.Binder.Convert( + CSharpBinderFlags.ConvertExplicit, + targetType, + value.Type); + + return Expression.Dynamic(binder, targetType, value); + } + + public static Expression DynamicGetIndex(Expression target, Expression index) + { + var binder = Microsoft.CSharp.RuntimeBinder.Binder.GetIndex( + CSharpBinderFlags.ResultIndexed, + target.Type, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + }); + + return Expression.Dynamic(binder, typeof(object), target, index); + } + + public static Expression DynamicSetIndex(Expression target, Expression index, Expression value) + { + var binder = Microsoft.CSharp.RuntimeBinder.Binder.SetIndex( + CSharpBinderFlags.ResultIndexed, + target.Type, + new[] + { + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) + }); + + return Expression.Dynamic(binder, typeof(object), target, index, value); + } + + /// + /// Пытается найти статический конвертер из типа в тип при передаче параметра + /// + /// параметр + /// тип аргумента + /// null если статический каст не удался, выражение конверсии, если удался + public static Expression TryConvertParameter(Expression parameter, Type targetType) + { + if (targetType.IsAssignableFrom(parameter.Type) && !IsNullable(targetType)) + { + return parameter; + } + + if (targetType.IsNumeric() && parameter.Type.IsNumeric()) + { + return DowncastDecimal(parameter, targetType); + } + + if (targetType == typeof(string)) + { + return ToString(parameter); + } + + return ConvertToType(parameter, targetType); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsNullable(Type targetType) + { + return targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + public static Expression CallCompareTo(Expression target, Expression argument) + { + var compareToMethod = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.Comparison) + ); + + Debug.Assert(argument.Type.IsValue()); + + return Expression.Call(compareToMethod, target, argument); + } + + public static Expression CallEquals(Expression target, Expression argument) + { + var equalsMethod = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.Equality) + ); + + Debug.Assert(argument.Type.IsValue()); + + return Expression.Call(equalsMethod, target, argument); + } + + public static Expression ConvertToBslValue(Expression value) + { + if (value.Type.IsValue()) + return value; + + var factoryClass = GetValueFactoryType(value.Type); + if (factoryClass == null) + { + if (value.Type==typeof(IValue) || value.Type.IsSubclassOf(typeof(IValue)) + || value.Type == typeof(object)) + { + // это результат динамической операции + // просто верим, что он BslValue + var meth = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.WrapClrObjectToValue)); + return Expression.Call(meth, value); + } + throw new NativeCompilerException(BilingualString.Localize( + $"Преобразование из типа {value.Type} в тип BslValue не поддерживается", + $"Conversion from type {value.Type} into BslValue is not supported")); + } + + if (value.Type.IsNumeric()) + { + value = CastToDecimal(value); + } + + var factory = OperationsCache.GetOrAdd(factoryClass, "Create"); + return Expression.Call(factory, value); + } + + public static (Expression, Expression) ConvertToCompatibleBslValues(Expression value1, Expression value2) + { + value1 = ConvertToBslValue(value1); + value2 = ConvertToBslValue(value2); + + if (value1.Type != value2.Type) + { + if (value1.Type != typeof(BslValue)) + value1 = Expression.Convert(value1, typeof(BslValue)); + + if (value2.Type != typeof(BslValue)) + value2 = Expression.Convert(value2, typeof(BslValue)); + } + + return (value1, value2); + } + + private static Type GetValueFactoryType(Type clrType) + { + Type factoryClass = default; + if (clrType == typeof(string)) + { + factoryClass = typeof(BslStringValue); + } + else if (clrType == typeof(bool)) + { + factoryClass = typeof(BslBooleanValue); + } + else if (clrType.IsNumeric()) + { + factoryClass = typeof(BslNumericValue); + } + else if (clrType == typeof(DateTime)) + { + factoryClass = typeof(BslDateValue); + } + else if (clrType == typeof(TypeDescriptor)) + { + factoryClass = typeof(BslTypeValue); + } + + return factoryClass; + } + + public static bool IsInteger(this Type type) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.Int16: + return true; + default: + return false; + } + } + + public static Expression CreateAssignmentSource(Expression source, Type targetType) + { + if (targetType == typeof(BslValue) && source.Type.IsValue()) + { + // это универсальный вариант - не нужны конверсии + return source; + } + + // возможно прямое clr-присваивание + if (targetType.IsAssignableFrom(source.Type) && !IsNullable(targetType)) + return source; + + var canBeCasted = TryStaticConversion(source, targetType, out var conversion); + if (canBeCasted) + return conversion; + + throw new NativeCompilerException(BilingualString.Localize( + $"Преобразование из типа {source.Type} в тип {targetType} не поддерживается", + $"Conversion from type {source.Type} into {targetType} is not supported")); + } + + public static Expression ConstructorCall(ITypeManager typeManager, Expression services, Expression type, + Expression process, + Expression[] argsArray) + { + var method = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.ConstructorCall)); + + var arrayOfArgs = Expression.NewArrayInit(typeof(BslValue), argsArray.Select(ConvertToBslValue)); + + return Expression.Call(method, + Expression.Constant(typeManager), + services, + type, + process, + arrayOfArgs); + } + + public static Expression ConstructorCall(ITypeManager typeManager, Expression services, TypeDescriptor knownType, + Expression process, + Expression[] argsArray) + { + MethodInfo method; + + if (knownType.ImplementingClass.IsValue()) + { + var genericMethod = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.StrictConstructorCall)); + + method = genericMethod.MakeGenericMethod(knownType.ImplementingClass); + } + else // подключенный сценарий + { + method = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.ConstructorCall)); + } + + var arrayOfArgs = Expression.NewArrayInit(typeof(BslValue), argsArray.Select(ConvertToBslValue)); + + return Expression.Call(method, + Expression.Constant(typeManager), + services, + Expression.Constant(knownType.Name), + process, + arrayOfArgs); + } + + public static Expression TypeByNameCall(ITypeManager manager, Expression argument) + { + var method = OperationsCache.GetOrAdd(typeof(DynamicOperations), nameof(DynamicOperations.GetTypeByName), + BindingFlags.Static | BindingFlags.Public); + + Debug.Assert(method != null); + + return Expression.Call(method, Expression.Constant(manager), argument); + } + + public static Expression GetExceptionInfo(Expression factory, ParameterExpression excVariable) + { + var method = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.GetExceptionInfo)); + + return Expression.Call(method, factory, excVariable); + } + + public static Expression CallOfInstanceMethod(Expression instance, string name, params Expression[] arguments) + { + var method = OperationsCache.GetOrAdd( + instance.Type, + name, + BindingFlags.Public | BindingFlags.Instance); + + return Expression.Call(instance, method, arguments); + } + + public static Expression AccessModuleVariable(ParameterExpression thisArg, int variableIndex) + { + var contextProperty = PropertiesCache.GetOrAdd( + typeof(NativeClassInstanceWrapper), + nameof(NativeClassInstanceWrapper.Context), + BindingFlags.Instance | BindingFlags.Public); + + var contextAccess = Expression.Property(thisArg, contextProperty); + var getVariableMethod = OperationsCache.GetOrAdd( + typeof(IAttachableContext), + nameof(IAttachableContext.GetVariable), + BindingFlags.Instance | BindingFlags.Public); + + var iVariable = Expression.Call(contextAccess, getVariableMethod, Expression.Constant(variableIndex)); + var valueProperty = PropertiesCache.GetOrAdd( + typeof(IValueReference), + nameof(IValueReference.BslValue), + BindingFlags.Instance | BindingFlags.Public); + + return Expression.Property(iVariable, valueProperty); + } + + public static Expression GetContextPropertyValue(IRuntimeContextInstance target, int propertyNumber) + { + var getter = OperationsCache.GetOrAdd( + typeof(IRuntimeContextInstance), + nameof(IRuntimeContextInstance.GetPropValue), + BindingFlags.Instance | BindingFlags.Public); + + return Expression.Convert(Expression.Call( + Expression.Constant(target), + getter, + Expression.Constant(propertyNumber) + ), typeof(BslValue)); + } + + public static Expression GetContextPropertyValue(Expression target, string propertyName) + { + var getter = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.GetPropertyValue)); + + return Expression.Call( + null, + getter, + target, + Expression.Constant(propertyName) + ); + } + + public static Expression GetIndexedValue(Expression target, Expression index) + { + var method = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.GetIndexedValue)); + + return Expression.Call(null, method, target, ConvertToBslValue(index)); + } + + public static Expression SetIndexedValue(Expression target, Expression index, Expression value) + { + var method = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + nameof(DynamicOperations.SetIndexedValue)); + + return Expression.Call(null, method, target, ConvertToBslValue(index), ConvertToBslValue(value)); + } + + public static Expression InvokeBslNativeMethod(BslNativeMethodInfo nativeMethod, ParameterExpression process, object target, List args) + { + var helperMethod = OperationsCache.GetOrAdd( + typeof(CallableMethod), + nameof(CallableMethod.Invoke), + BindingFlags.Instance | BindingFlags.Public + ); + + return Expression.Call( + Expression.Constant(nativeMethod.GetCallable()), + helperMethod, + process, + nativeMethod.IsInstance ? + InvocationTargetExpression(target) : + Expression.Constant(null, typeof(object)), + PackArgsToArgsArray(args)); + } + + private static Expression PackArgsToArgsArray(IEnumerable args) + { + return Expression.NewArrayInit(typeof(BslValue), args.Select(ConvertToBslValue)); + } + + private static Expression[] PrepareInstanceCallArguments(object target, List args) + { + var actualArgs = new Expression[args.Count + 1]; + actualArgs[0] = InvocationTargetExpression(target); + for (int i = 0; i < args.Count; i++) + { + actualArgs[i + 1] = args[i]; + } + + return actualArgs; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Expression InvocationTargetExpression(object target) => + target as Expression ?? Expression.Constant(target, typeof(object)); + + public static Expression Increment(Expression counterVar) + { + if (!(counterVar is ParameterExpression || IsModuleVariable(counterVar))) + throw new ArgumentException("Must be an assignable expression"); + + if (counterVar.Type.IsNumeric()) + { + return Expression.PreIncrementAssign(counterVar); + } + + var plusOne = DynamicAdd(counterVar, Expression.Constant(1)); + return Expression.Assign(counterVar, plusOne); + } + + public static Expression CallScriptInfo(IScriptInformationFactory factory, SourceCode source) + { + var methodInfo = OperationsCache.GetOrAdd( + typeof(IScriptInformationFactory), + nameof(IScriptInformationFactory.GetInfo), + BindingFlags.Public | BindingFlags.Instance); + + return Expression.Call(Expression.Constant(factory), methodInfo, Expression.Constant(source)); + } + + private static bool IsModuleVariable(Expression counterVar) + { + if (!(counterVar is MemberExpression memberExpr)) + return false; + + return memberExpr.Expression.Type == typeof(IVariable) + && memberExpr.Member.Name == nameof(IVariable.BslValue); + } + + public static Expression CallContextMethod(Expression target, string name, ParameterExpression processParameter, + IEnumerable arguments) + { + return CallContextMethodInternal( + nameof(DynamicOperations.CallContextMethod), target, name, processParameter, arguments); + } + + public static Expression TryCallContextMethod( + Expression target, + string name, + ParameterExpression processParameter, + IEnumerable arguments) + { + return CallContextMethodInternal( + nameof(DynamicOperations.TryCallContextMethod), target, name, processParameter, arguments); + } + + private static Expression CallContextMethodInternal( + string implementationName, + Expression target, + string name, + ParameterExpression processParameter, + IEnumerable arguments) + { + var methodInfo = OperationsCache.GetOrAdd( + typeof(DynamicOperations), + implementationName + ); + + var argExpressions = new List + { + target, + Expression.Constant(name), + processParameter, + PackArgsToArgsArray(arguments) + }; + + return Expression.Call(methodInfo, argExpressions); + } + } +} diff --git a/src/OneScript.Native/Compiler/ExpressionTreeGeneratorBase.cs b/src/OneScript.Native/Compiler/ExpressionTreeGeneratorBase.cs new file mode 100644 index 000000000..e33a6968f --- /dev/null +++ b/src/OneScript.Native/Compiler/ExpressionTreeGeneratorBase.cs @@ -0,0 +1,95 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Compilation.Binding; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Localization; +using OneScript.Sources; + +namespace OneScript.Native.Compiler +{ + public abstract class ExpressionTreeGeneratorBase : BslSyntaxWalker + { + private IErrorSink _errors; + private SourceCode _sourceCode; + private SymbolTable _ctx; + + protected ExpressionTreeGeneratorBase() + { + } + + protected ExpressionTreeGeneratorBase(IErrorSink errors) + { + _errors = errors; + } + + protected ExpressionTreeGeneratorBase(BslWalkerContext context) + { + InitContext(context); + } + + protected void InitContext(BslWalkerContext context) + { + InitContext(context.Errors, context.Source, context.Symbols); + } + + protected void InitContext(IErrorSink errors, SourceCode lineInfo, SymbolTable symbols) + { + _errors = errors; + _sourceCode = lineInfo; + _ctx = symbols; + } + + protected IErrorSink Errors => _errors; + + protected SymbolTable Symbols => _ctx; + + protected virtual BslWalkerContext MakeContext() + { + return new BslWalkerContext + { + Symbols = _ctx, + Errors = _errors, + Source = _sourceCode + }; + } + + protected virtual void AddError(BilingualString errorText, CodeRange location) + { + Errors.AddError(new CodeError + { + Description = errorText.ToString(), + Position = ToCodePosition(location) + }); + } + + protected void AddError(CodeError err) + { + Errors.AddError(err); + } + + protected void AddError(CodeError err, CodeRange location) + { + err.Position = ToCodePosition(location); + Errors.AddError(err); + } + + protected ErrorPositionInfo ToCodePosition(CodeRange range) + { + var codeLine = range.LineNumber > 0 ? _sourceCode.GetCodeLine(range.LineNumber)?.Trim() : "no code line"; + return new ErrorPositionInfo + { + Code = codeLine, + LineNumber = range.LineNumber, + ColumnNumber = range.ColumnNumber, + ModuleName = _sourceCode.Name + }; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/JumpInformationRecord.cs b/src/OneScript.Native/Compiler/JumpInformationRecord.cs new file mode 100644 index 000000000..37655b7f0 --- /dev/null +++ b/src/OneScript.Native/Compiler/JumpInformationRecord.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Linq.Expressions; + +namespace OneScript.Native.Compiler +{ + public struct JumpInformationRecord + { + public LabelTarget MethodReturn { get; set; } + + public LabelTarget LoopContinue { get; set; } + + public LabelTarget LoopBreak { get; set; } + + public ParameterExpression ExceptionInfo { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/MethodCompiler.cs b/src/OneScript.Native/Compiler/MethodCompiler.cs new file mode 100644 index 000000000..190643ebf --- /dev/null +++ b/src/OneScript.Native/Compiler/MethodCompiler.cs @@ -0,0 +1,1574 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.CSharp.RuntimeBinder; +using OneScript.Commons; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Localization; +using OneScript.Native.Runtime; +using OneScript.Sources; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Native.Compiler +{ + public class MethodCompiler : ExpressionTreeGeneratorBase + { + private readonly BslNativeMethodInfo _method; + private readonly List _localVariables = new List(); + private readonly StatementBlocksWriter _blocks = new StatementBlocksWriter(); + private readonly Stack _statementBuildParts = new Stack(); + private BslParameterInfo[] _declaredParameters; + private ParameterExpression _thisParameter; + private readonly ParameterExpression _processParameter = Expression.Parameter(typeof(IBslProcess), "bslProcess"); + + private readonly BinaryOperationCompiler _binaryOperationCompiler = new BinaryOperationCompiler(); + private ITypeManager _typeManager; + private IExceptionInfoFactory _exceptionInfoFactory; + private readonly IServiceContainer _services; + private readonly BuiltInFunctionsCache _builtInFunctions = new BuiltInFunctionsCache(); + + private readonly ContextMethodsCache _methodsCache = new ContextMethodsCache(); + private readonly ContextPropertiesCache _propertiesCache = new ContextPropertiesCache(); + private readonly SourceCode _source; + + public MethodCompiler(BslWalkerContext walkContext, BslNativeMethodInfo method) : base(walkContext) + { + _method = method; + _services = walkContext.Services; + _source = walkContext.Source; + } + + private ITypeManager CurrentTypeManager => GetService(_services, ref _typeManager); + + private IExceptionInfoFactory ExceptionInfoFactory => GetService(_services, ref _exceptionInfoFactory); + + private static T GetService(IServiceContainer services, ref T value) where T : class + { + if (value != default) + return value; + + value = services.TryResolve(); + if (value == default) + throw new NotSupportedException($"{typeof(T)} is not registered in services."); + + return value; + } + + public void CompileMethod(MethodNode methodNode) + { + CompileFragment(methodNode, x=>VisitMethodBody((MethodNode)x)); + } + + public void CompileModuleBody(BslSyntaxNode moduleBodyNode) + { + if(moduleBodyNode != default) + CompileFragment(moduleBodyNode, VisitModuleBody); + else + CompileFragment(null, n => {}); + } + + private class InternalFlowInterruptException : Exception + { + } + + private void CompileFragment(BslSyntaxNode node, Action visitor) + { + _blocks.EnterBlock(new JumpInformationRecord + { + MethodReturn = Expression.Label(typeof(BslValue)) + }); + Symbols.PushScope(new SymbolScope(), ScopeBindingDescriptor.ThisScope()); + FillParameterVariables(); + + try + { + visitor(node); + } + catch + { + _blocks.LeaveBlock(); + throw; + } + finally + { + Symbols.PopScope(); + } + + var block = _blocks.LeaveBlock(); + block.Add(Expression.Label( + block.MethodReturn, + Expression.Constant(BslUndefinedValue.Instance))); + + var parameters = new List(); + parameters.Add(_processParameter); + + if (_method.IsInstance) + parameters.Add(_thisParameter); + + parameters.AddRange(_localVariables.Take(_declaredParameters.Length)); + var blockVariables = _localVariables.Skip(_declaredParameters.Length); + + var body = Expression.Block( + blockVariables.ToArray(), + block.GetStatements()); + + var impl = Expression.Lambda(body, parameters); + + _method.SetImplementation(impl); + } + + private void FillParameterVariables() + { + _declaredParameters = _method.GetBslParameters(); + _thisParameter = _method.IsInstance ? Expression.Parameter(typeof(NativeClassInstanceWrapper), "$this") : null; + + var localScope = Symbols.GetScope(Symbols.ScopeCount-1); + foreach (var parameter in _declaredParameters) + { + var paramSymbol = new LocalVariableSymbol(parameter.Name, parameter.ParameterType); + localScope.DefineVariable(paramSymbol); + _localVariables.Add(Expression.Parameter(parameter.ParameterType, parameter.Name)); + } + } + + private Expression DefineLocalVariable(string identifier, Type type) + { + var varSymbol = new LocalVariableSymbol(identifier, type); + var scope = Symbols.GetScope(Symbols.ScopeCount - 1); + scope.DefineVariable(varSymbol); + var variable = Expression.Variable(type, identifier); + _localVariables.Add(variable); + return variable; + } + + protected override void VisitMethodVariable(MethodNode method, VariableDefinitionNode variableDefinition) + { + var identifier = variableDefinition.Name; + if (Symbols.FindVariable(identifier, out var binding) && binding.ScopeNumber == Symbols.ScopeCount - 1) + { + AddError(LocalizedErrors.DuplicateVarDefinition(identifier)); + return; + } + + var variable = DefineLocalVariable(identifier, typeof(BslValue)); + _blocks.Add(Expression.Assign(variable, Expression.Constant(BslUndefinedValue.Instance))); + } + + protected override void VisitStatement(BslSyntaxNode statement) + { + _statementBuildParts.Clear(); + var nestingLevel = _blocks.Count; + try + { + base.VisitStatement(statement); + } + catch (InternalFlowInterruptException) + { + // нижележащий код заполнил коллекцию errors + // а мы просто переходим к следующей строке кода + RestoreNestingLevel(nestingLevel); + } + catch (NativeCompilerException e) + { + RestoreNestingLevel(nestingLevel); + e.SetPositionIfEmpty(ToCodePosition(statement.Location)); + AddError(new CodeError + { + Description = e.Message, + Position = e.GetPosition(), + ErrorId = nameof(NativeCompilerException) + }); + } + catch (Exception e) + { + + RestoreNestingLevel(nestingLevel); + if (e is NativeCompilerException ce) + { + ce.SetPositionIfEmpty(ToCodePosition(statement.Location)); + throw; + } + + var msg = new BilingualString( + "Ошибка компиляции статического модуля\n" + e, + "Error compiling static module\n" + e); + base.AddError(msg, statement.Location); + } + } + + private void RestoreNestingLevel(int desiredLevel) + { + while (_blocks.Count > desiredLevel) + { + _blocks.LeaveBlock(); + } + } + + #region Expressions + + protected override void VisitVariableRead(TerminalNode node) + { + if (!Symbols.FindVariable(node.GetIdentifier(), out var binding)) + { + AddError(LocalizedErrors.SymbolNotFound(node.GetIdentifier()), node.Location); + return; + } + + var symbol = Symbols.GetScope(binding.ScopeNumber).Variables[binding.MemberNumber]; + if (IsLocalScope(binding.ScopeNumber)) + { + // local read + var expr = GetLocalVariable(binding.MemberNumber); + _statementBuildParts.Push(expr); + } + else if (symbol is IPropertySymbol prop) + { + var descriptor = Symbols.GetBinding(binding.ScopeNumber); + var expression = ReadGlobalProperty((IRuntimeContextInstance)descriptor.Target, prop); + _statementBuildParts.Push(expression); + } + else if (symbol is IFieldSymbol && _method.IsInstance) + { + var iVariableVal = ExpressionHelpers.AccessModuleVariable(_thisParameter, binding.MemberNumber); + _statementBuildParts.Push(Expression.Convert(iVariableVal, typeof(BslValue))); + } + else + { + AddError(new BilingualString($"Unsupported symbol in non-local context: {symbol.Name} {symbol.GetType()}"), + node.Location); + } + } + + private Expression ReadGlobalProperty(IRuntimeContextInstance target, IPropertySymbol prop) + { + var propInfo = prop.Property; + + if (propInfo is ContextPropertyInfo contextProp) + { + return Expression.MakeMemberAccess( + Expression.Constant(target), + contextProp.GetUnderlying()); + } + else + { + var propIdx = target.GetPropertyNumber(prop.Name); + return ExpressionHelpers.GetContextPropertyValue(target, propIdx); + } + } + + private object GetPropertyBinding(SymbolBinding binding, IVariableSymbol symbol) + { + return Symbols.GetBinding(binding.ScopeNumber).Target ?? + throw new InvalidOperationException($"Property {symbol.Name} is not bound to a value"); + } + + private object GetMethodBinding(SymbolBinding binding, IMethodSymbol symbol) + { + return Symbols.GetBinding(binding.ScopeNumber).Target ?? + throw new InvalidOperationException($"Method {symbol.Name} is not bound to a value"); + } + + private void MakeReadPropertyAccess(TerminalNode operand) + { + var memberName = operand.Lexem.Content; + var instance = _statementBuildParts.Pop(); + var instanceType = instance.Type; + var prop = TryFindPropertyOfType(operand, instanceType, memberName); + + if (prop != null) + { + var expression = Expression.MakeMemberAccess(instance, prop); + + if (typeof(IValue) == expression.Type) + { + _statementBuildParts.Push(Expression.TypeAs(expression, typeof(BslValue))); + return; + } + + _statementBuildParts.Push(expression); + return; + } + + var expr = ExpressionHelpers.GetContextPropertyValue(instance, memberName); + _statementBuildParts.Push(expr); + } + + private void MakeWritePropertyAccess(TerminalNode operand) + { + var memberName = operand.Lexem.Content; + var instance = _statementBuildParts.Pop(); + var instanceType = instance.Type; + var prop = TryFindPropertyOfType(operand, instanceType, memberName); + + if (prop != null) + { + _statementBuildParts.Push(Expression.MakeMemberAccess(instance, prop)); + return; + } + + var valueToSet = _statementBuildParts.Pop(); + + var args = new List(); + args.Add(instance); + args.Add(valueToSet); + + var csharpArgs = new List(); + csharpArgs.Add(CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default)); + csharpArgs.AddRange(args.Select(x => + CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default))); + + var binder = Microsoft.CSharp.RuntimeBinder.Binder.SetMember( + CSharpBinderFlags.InvokeSimpleName, + memberName, + typeof(BslObjectValue), csharpArgs); + var expr = Expression.Dynamic(binder, typeof(object), args); + _statementBuildParts.Push(expr); + } + + protected override void VisitResolveProperty(TerminalNode operand) + { + MakeReadPropertyAccess(operand); + } + + private ParameterExpression GetLocalVariable(int index) => _localVariables[index]; + + private bool IsLocalScope(int scopeNumber) => scopeNumber == Symbols.ScopeCount - 1; + + private bool IsModuleScope(int scopeNumber) => scopeNumber == Symbols.ScopeCount - 2; + + protected override void VisitVariableWrite(TerminalNode node) + { + var identifier = node.GetIdentifier(); + var hasVar = Symbols.FindVariable(identifier, out var varBinding); + if (hasVar) + { + var symbol = Symbols.GetScope(varBinding.ScopeNumber).Variables[varBinding.MemberNumber]; + if (IsLocalScope(varBinding.ScopeNumber)) + { + var local = GetLocalVariable(varBinding.MemberNumber); + _statementBuildParts.Push(local); + } + else if (symbol is IPropertySymbol propSymbol) + { + var target = GetPropertyBinding(varBinding, propSymbol); + var convert = Expression.Convert(Expression.Constant(target), + target.GetType()); + + var accessExpression = Expression.Property(convert, propSymbol.Property.SetMethod); + _statementBuildParts.Push(accessExpression); + } + else if (symbol is IFieldSymbol && _method.IsInstance) + { + var iVariableVal = ExpressionHelpers.AccessModuleVariable(_thisParameter, varBinding.MemberNumber); + _statementBuildParts.Push(iVariableVal); + } + else + { + AddError(new BilingualString($"Unsupported symbol in non-local context: {symbol.Name} {symbol.GetType()}"), + node.Location); + } + } + else + { + // can create variable + var typeOnStack = _statementBuildParts.Peek().Type; + + if (typeOnStack == typeof(BslUndefinedValue)) + typeOnStack = typeof(BslValue); + if (typeOnStack.IsNumeric()) + typeOnStack = typeof(decimal); + + _statementBuildParts.Push( DefineLocalVariable(identifier, typeOnStack) ); + } + } + + protected override void VisitConstant(TerminalNode node) + { + object constant = CompilerHelpers.ClrValueFromLiteral(node.Lexem); + _statementBuildParts.Push(Expression.Constant(constant)); + } + + protected override void VisitAssignment(BslSyntaxNode assignment) + { + var astLeft = assignment.Children[0]; + var astRight = assignment.Children[1]; + + VisitAssignmentRightPart(astRight); + VisitAssignmentLeftPart(astLeft); + + var left = _statementBuildParts.Pop(); + + if (left.NodeType == ExpressionType.Dynamic && left is DynamicExpression dyn) + { + if (dyn.Binder is SetIndexBinder || dyn.Binder is SetMemberBinder) + { + _blocks.Add(left); + return; + } + + throw new NotSupportedException($"Dynamic operation {dyn.Binder} is not supported"); + } + else if (left.NodeType == ExpressionType.Call) + { + _blocks.Add(left); + return; + } + + var right = ExpressionHelpers.CreateAssignmentSource(_statementBuildParts.Pop(), left.Type); + + _blocks.Add(Expression.Assign(left, right)); + } + + protected override void VisitAssignmentLeftPart(BslSyntaxNode node) + { + if (node is TerminalNode t) + { + VisitVariableWrite(t); + } + else if (node.Kind == NodeKind.IndexAccess) + { + VisitIndexAccessWrite(node); + } + else + { + VisitReferenceWrite(node); + } + } + + protected override void VisitReferenceWrite(BslSyntaxNode node) + { + var instanceNode = node.Children[0]; + var memberNode = node.Children[1] as TerminalNode; + + if (instanceNode is TerminalNode terminalNode) + { + VisitVariableRead(terminalNode); + } + else + { + DefaultVisit(instanceNode); + } + + MakeWritePropertyAccess(memberNode); + } + + protected override void VisitBinaryOperation(BinaryOperationNode binaryOperationNode) + { + VisitExpression(binaryOperationNode.Children[0]); + VisitExpression(binaryOperationNode.Children[1]); + + var right = _statementBuildParts.Pop(); + var left = _statementBuildParts.Pop(); + + var binaryOp = CompileBinaryOp(left, right, binaryOperationNode); + + if (LanguageDef.IsLogicalBinaryOperator(binaryOperationNode.Operation)) + { + var toBool = Expression.Convert(binaryOp, typeof(bool)); + _statementBuildParts.Push(toBool); + } + else + { + _statementBuildParts.Push(binaryOp); + } + } + + protected override void VisitUnaryOperation(UnaryOperationNode unaryOperationNode) + { + var child = unaryOperationNode.Children[0]; + VisitExpression(child); + var opCode = ExpressionHelpers.TokenToOperationCode(unaryOperationNode.Operation); + + Type resultType = null; + Expression argument = _statementBuildParts.Pop(); + switch (opCode) + { + case ExpressionType.UnaryPlus: + case ExpressionType.Negate: + resultType = typeof(decimal); + argument = ExpressionHelpers.ToNumber(argument); + break; + case ExpressionType.Not: + argument = ExpressionHelpers.ToBoolean(argument); + resultType = typeof(bool); + break; + } + + var operation = Expression.MakeUnary(opCode, argument, resultType); + _statementBuildParts.Push(operation); + } + + protected override void VisitTernaryOperation(BslSyntaxNode node) + { + Debug.Assert(node.Children.Count == 3); + + var test = ConvertToExpressionTree(node.Children[0]); + var ifTrue = ConvertToExpressionTree(node.Children[1]); + var ifFalse = ConvertToExpressionTree(node.Children[2]); + + if (ifTrue.Type != ifFalse.Type) + { + (ifTrue, ifFalse) = ExpressionHelpers.ConvertToCompatibleBslValues(ifTrue, ifFalse); + } + + _statementBuildParts.Push(Expression.Condition(ExpressionHelpers.ToBoolean(test), ifTrue, ifFalse)); + } + + #region Dereferencing + + protected override void VisitIndexAccess(BslSyntaxNode node) + { + base.VisitIndexAccess(node); + + var index = _statementBuildParts.Pop(); + var target = _statementBuildParts.Pop(); + + var indexExpression = TryCreateIndexExpression(node, target, index); + + if (indexExpression == null) + { + indexExpression = TryFindMagicGetterMethod(target, index) + ?? ExpressionHelpers.GetIndexedValue(target, index); + } + + _statementBuildParts.Push(indexExpression); + } + + private void VisitIndexAccessWrite(BslSyntaxNode node) + { + base.VisitIndexAccess(node); + + var index = _statementBuildParts.Pop(); + var target = _statementBuildParts.Pop(); + + var indexExpression = TryCreateIndexExpression(node, target, index); + + if (indexExpression == null) + { + var value = _statementBuildParts.Pop(); + indexExpression = TryFindMagicSetterMethod(target, index, value) + ?? ExpressionHelpers.SetIndexedValue(target, index, value); + } + + _statementBuildParts.Push(indexExpression); + } + + private Expression TryFindMagicGetterMethod(Expression target, Expression index) + { + var targetType = target.Type; + var method = targetType.GetMethod("BslIndexGetter"); + if (method == null) + return null; + + var parameters = method.GetParameters(); + if (parameters.Length != 1) + return null; + + var indexParamType = parameters[0].ParameterType; + + return Expression.Call(target, method,ExpressionHelpers.ConvertToType(index, indexParamType)); + } + + private Expression TryFindMagicSetterMethod(Expression target, Expression index, Expression value) + { + var targetType = target.Type; + var method = targetType.GetMethod("BslIndexSetter"); + if (method == null) + return null; + + var parameters = method.GetParameters(); + if (parameters.Length != 2) + return null; + + var indexParamType = parameters[0].ParameterType; + var valueType = parameters[1].ParameterType; + + return Expression.Call(target, method, + ExpressionHelpers.ConvertToType(index, indexParamType), + ExpressionHelpers.ConvertToType(value, valueType)); + + } + + private Expression TryCreateIndexExpression(BslSyntaxNode node, Expression target, Expression index) + { + if (!typeof(BslObjectValue).IsAssignableFrom(target.Type)) + { + // Мы не знаем тип выражения и будем выяснять это в рантайме + return null; + } + + var indexerProps = target.Type + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.GetIndexParameters().Length != 0) + .ToList(); + + Expression indexExpression = null; + + if(indexerProps.Count > 1) + { + foreach (var propertyInfo in indexerProps) + { + var parameterType = propertyInfo.GetIndexParameters()[0].ParameterType; + var passExpression = ExpressionHelpers.TryConvertParameter(index, parameterType); + + if (passExpression == null) + continue; + + indexExpression = Expression.MakeIndex(target, propertyInfo, new[] {passExpression}); + break; + } + } + else if(indexerProps.Count == 1) + { + var propInfo = indexerProps[0]; + var parameterType = propInfo.GetIndexParameters()[0].ParameterType; + var passExpression = ExpressionHelpers.TryConvertParameter(index, parameterType); + if(passExpression != null) + indexExpression = Expression.MakeIndex(target, propInfo, new[]{passExpression}); + } + + return indexExpression; + } + + #endregion + + private Expression CompileBinaryOp(Expression left, Expression right, BinaryOperationNode binaryOperationNode) + { + try + { + return _binaryOperationCompiler.Compile(binaryOperationNode, left, right); + } + catch (NativeCompilerException e) + { + AddError(e.Message, binaryOperationNode.Location); + return null; + } + } + + protected override void VisitReturnNode(BslSyntaxNode node) + { + if (_method.IsFunction() && node.Children.Count == 0) + { + AddError(LocalizedErrors.FuncEmptyReturnValue(), node.Location); + return; + } + + var label = _blocks.GetCurrentBlock().MethodReturn; + + if (!_method.IsFunction() && node.Children.Count == 0) + { + var undefinedExpr = Expression.Constant(BslUndefinedValue.Instance); + _blocks.Add(Expression.Return(label, undefinedExpr)); + return; + } + + VisitExpression(node.Children[0]); + + var resultExpr = _statementBuildParts.Pop(); + + if (!resultExpr.Type.IsValue()) + resultExpr = ExpressionHelpers.ConvertToType(resultExpr, typeof(BslValue)); + + var statement = Expression.Return(label, resultExpr); + _blocks.Add(statement); + } + + #endregion + + #region If Block + + protected override void VisitIfNode(ConditionNode node) + { + _blocks.EnterBlock(); + + VisitIfExpression(node.Expression); + VisitIfTruePart(node.TruePart); + + var stack = new Stack(); + foreach (var alternative in node.GetAlternatives()) + { + if (alternative is ConditionNode elif) + { + stack.Push(elif); + } + else if (stack.Count != 0) + { + var cond = stack.Pop(); + + VisitElseNode((CodeBatchNode) alternative); + VisitElseIfNode(cond); + } + else + { + VisitElseNode((CodeBatchNode) alternative); + } + } + + while (stack.Count != 0) + { + var elseIfNode = stack.Pop(); + VisitElseIfNode(elseIfNode); + } + + + var expression = CreateIfExpression(_blocks.LeaveBlock()); + _blocks.Add(expression); + } + + private Expression CreateIfExpression(StatementsBlockRecord block) + { + if (block.BuildStack.Count == 3) + { + var falsePart = block.BuildStack.Pop(); + var truePart = block.BuildStack.Pop(); + var condition = block.BuildStack.Pop(); + + return Expression.IfThenElse(ExpressionHelpers.ToBoolean(condition), truePart, falsePart); + } + else + { + Debug.Assert(block.BuildStack.Count == 2); + var truePart = block.BuildStack.Pop(); + var condition = block.BuildStack.Pop(); + if (condition.Type != typeof(bool)) + condition = Expression.Convert(condition, typeof(bool)); + + return Expression.IfThen(ExpressionHelpers.ToBoolean(condition), truePart); + } + } + + protected override void VisitIfExpression(BslSyntaxNode node) + { + var condition = ConvertToExpressionTree(node); + _blocks.GetCurrentBlock().BuildStack.Push(condition); + } + + protected override void VisitIfTruePart(CodeBatchNode node) + { + _blocks.EnterBlock(); + base.VisitIfTruePart(node); + + var body = _blocks.LeaveBlock().GetStatements(); + _blocks.GetCurrentBlock().BuildStack.Push(Expression.Block(body)); + } + + protected override void VisitElseIfNode(ConditionNode node) + { + var hasCompiledElse = _blocks.GetCurrentBlock().BuildStack.Count == 3; + Expression elseNode = null; + if (hasCompiledElse) + elseNode = _blocks.GetCurrentBlock().BuildStack.Pop(); + + _blocks.EnterBlock(); + VisitIfExpression(node.Expression); + VisitIfTruePart(node.TruePart); + + if(hasCompiledElse) + _blocks.GetCurrentBlock().BuildStack.Push(elseNode); + + var expr = CreateIfExpression(_blocks.LeaveBlock()); + _blocks.GetCurrentBlock().BuildStack.Push(expr); + } + + protected override void VisitElseNode(CodeBatchNode node) + { + _blocks.EnterBlock(); + try + { + base.VisitElseNode(node); + var block = _blocks.LeaveBlock(); + var body = Expression.Block(block.GetStatements()); + _blocks.GetCurrentBlock().BuildStack.Push(body); + } + catch + { + _blocks.LeaveBlock(); + throw; + } + } + + #endregion + + #region While Loop + + protected override void VisitWhileNode(WhileLoopNode node) + { + _blocks.EnterBlock(new JumpInformationRecord() + { + LoopBreak = Expression.Label(), + LoopContinue = Expression.Label() + }); + base.VisitWhileNode(node); + + var block = _blocks.LeaveBlock(); + + var result = new List(); + result.Add(Expression.IfThen( + Expression.Not(block.BuildStack.Pop()), + Expression.Break(block.LoopBreak))); + + result.AddRange(block.GetStatements()); + + var loop = Expression.Loop(Expression.Block(result), block.LoopBreak, block.LoopContinue); + _blocks.Add(loop); + } + + protected override void VisitWhileCondition(BslSyntaxNode node) + { + var expr = ExpressionHelpers.ToBoolean(ConvertToExpressionTree(node)); + _blocks.GetCurrentBlock().BuildStack.Push(expr); + } + + protected override void VisitContinueNode(LineMarkerNode node) + { + var label = _blocks.GetCurrentBlock().LoopContinue; + _blocks.Add(Expression.Continue(label)); + } + + protected override void VisitBreakNode(LineMarkerNode node) + { + var label = _blocks.GetCurrentBlock().LoopBreak; + _blocks.Add(Expression.Break(label)); + } + + #endregion + + #region For With Counter Loop + + protected override void VisitForLoopNode(ForLoopNode node) + { + _blocks.EnterBlock(new JumpInformationRecord + { + LoopBreak = Expression.Label(), + LoopContinue = Expression.Label() + }); + base.VisitForLoopNode(node); + var block = _blocks.LeaveBlock(); + + var upperLimit = block.BuildStack.Pop(); + var initialValue = block.BuildStack.Pop(); + var counterVar = block.BuildStack.Pop(); + + var result = new List(); + result.Add(Expression.Assign(counterVar, ExpressionHelpers.CreateAssignmentSource(initialValue, counterVar.Type))); + var finalVar = Expression.Variable(typeof(decimal)); // TODO: BslNumericValue ? + result.Add(Expression.Assign(finalVar, upperLimit)); + + var loop = new List(); + loop.Add(Expression.IfThen( + Expression.GreaterThan(ExpressionHelpers.ToNumber(counterVar), finalVar), + Expression.Break(block.LoopBreak))); + + loop.AddRange(block.GetStatements()); + + loop.Add(Expression.Label(block.LoopContinue)); + loop.Add(ExpressionHelpers.Increment(counterVar)); + + result.Add(Expression.Loop(Expression.Block(loop), block.LoopBreak)); + + _blocks.Add(Expression.Block(new[] {finalVar}, result)); + } + + protected override void VisitForInitializer(BslSyntaxNode node) + { + var forLoopIterator = node.Children[0]; + var forLoopInitialValue = node.Children[1]; + VisitForLoopInitialValue(forLoopInitialValue); + VisitForLoopIterator(forLoopIterator); + + // counter variable + _blocks.GetCurrentBlock().BuildStack.Push(_statementBuildParts.Pop()); + // initial value + _blocks.GetCurrentBlock().BuildStack.Push(_statementBuildParts.Pop()); + } + + protected override void VisitForLoopInitialValue(BslSyntaxNode node) + { + base.VisitForLoopInitialValue(node); + var expr = ExpressionHelpers.ToNumber(_statementBuildParts.Pop()); + _statementBuildParts.Push(expr); + } + + protected override void VisitForUpperLimit(BslSyntaxNode node) + { + base.VisitForUpperLimit(node); + var limit = Expression.Convert( + ExpressionHelpers.ToNumber(_statementBuildParts.Pop()), + typeof(decimal)); + + _blocks.GetCurrentBlock().BuildStack.Push(limit); + } + + #endregion + + #region ForEach Loop + + protected override void VisitForEachLoopNode(ForEachLoopNode node) + { + _blocks.EnterBlock(new JumpInformationRecord + { + LoopBreak = Expression.Label(), + LoopContinue = Expression.Label() + }); + base.VisitForEachLoopNode(node); + + var block = _blocks.LeaveBlock(); + var enumerableCollection = block.BuildStack.Pop(); + var itemVariable = block.BuildStack.Pop(); + + var collectionType = typeof(IEnumerable); + var getEnumeratorMethod = collectionType.GetMethod("GetEnumerator"); + var moveNextMethod = typeof(IEnumerator).GetMethod("MoveNext"); + var collectionCast = Expression.Convert(enumerableCollection, collectionType); + + Debug.Assert(moveNextMethod != null); + Debug.Assert(getEnumeratorMethod != null); + + // loop init section + var getEnumeratorInvoke = Expression.Call(collectionCast, getEnumeratorMethod); + var enumeratorVar = Expression.Variable(typeof(IEnumerator)); + + var result = new List(); + result.Add(Expression.Assign(enumeratorVar, getEnumeratorInvoke)); + + + var loop = new List(); + + var assignCurrent = Expression.Assign( + itemVariable, + Expression.Convert( + Expression.Property(enumeratorVar, "Current"), + typeof(BslValue)) + ); + + loop.Add(assignCurrent); + loop.AddRange(block.GetStatements()); + + var finalLoop = Expression.Loop( + Expression.IfThenElse( + Expression.Equal(Expression.Call(enumeratorVar, moveNextMethod), Expression.Constant(true)), + Expression.Block(loop), + Expression.Break(block.LoopBreak)), + block.LoopBreak, block.LoopContinue); + + result.Add(finalLoop); + + _blocks.Add(Expression.Block(new[] {enumeratorVar}, result)); + } + + protected override void VisitIteratorLoopVariable(TerminalNode node) + { + // temp var for VisitVariableWrite() + _statementBuildParts.Push(Expression.Variable(typeof(BslValue))); + base.VisitIteratorLoopVariable(node); + // push variable + _blocks.GetCurrentBlock().BuildStack.Push(_statementBuildParts.Pop()); + // clear temp var + _statementBuildParts.Pop(); + } + + protected override void VisitIteratorExpression(BslSyntaxNode node) + { + base.VisitIteratorExpression(node); + _blocks.GetCurrentBlock().BuildStack.Push(_statementBuildParts.Pop()); + } + #endregion + + #region TryExcept Block + + protected override void VisitTryExceptNode(TryExceptNode node) + { + _blocks.EnterBlock(new JumpInformationRecord + { + ExceptionInfo = Expression.Parameter(typeof(Exception)) + }); + base.VisitTryExceptNode(node); + + // TODO доделать все переобертки RuntimeException для стековой машины и для нативной + + var block = _blocks.LeaveBlock(); + var except = block.BuildStack.Pop(); + var tryBlock = block.BuildStack.Pop(); + + _blocks.Add(Expression.TryCatch(tryBlock, + Expression.Catch(block.CurrentException, except)) + ); + } + + protected override void VisitTryBlock(CodeBatchNode node) + { + _blocks.EnterBlock(); + base.VisitTryBlock(node); + var block = _blocks.LeaveBlock(); + + _blocks.GetCurrentBlock().BuildStack.Push(Expression.Block(typeof(void),block.GetStatements())); + } + + protected override void VisitExceptBlock(CodeBatchNode node) + { + _blocks.EnterBlock(); + base.VisitExceptBlock(node); + var block = _blocks.LeaveBlock(); + + _blocks.GetCurrentBlock().BuildStack.Push(Expression.Block(typeof(void),block.GetStatements())); + } + + protected override void VisitRaiseNode(BslSyntaxNode node) + { + if (node.Children.Count == 0) + { + _blocks.Add(Expression.Rethrow()); + } + else + { + VisitExpression(node.Children[0]); + var raiseArgExpression = ExpressionHelpers.ToString(_statementBuildParts.Pop()); + + var exceptionExpression = ExpressionHelpers.CallOfInstanceMethod( + Expression.Constant(ExceptionInfoFactory), + nameof(IExceptionInfoFactory.Raise), + raiseArgExpression); + + _blocks.Add(Expression.Throw(exceptionExpression)); + } + base.VisitRaiseNode(node); + } + + #endregion + + #region Method Calls + + protected override void VisitGlobalProcedureCall(CallNode node) + { + if (LanguageDef.IsBuiltInFunction(node.Identifier.Lexem.Token)) + { + AddError(LocalizedErrors.UseBuiltInFunctionAsProcedure()); + return; + } + + var expression = CreateMethodCall(node); + _blocks.Add(expression); + } + + protected override void VisitGlobalFunctionCall(CallNode node) + { + if (LanguageDef.IsBuiltInFunction(node.Identifier.Lexem.Token)) + { + _statementBuildParts.Push(CreateBuiltInFunctionCall(node)); + return; + } + + var methodExists = Symbols.TryFindMethod( + node.Identifier.GetIdentifier(), out var methodSymbol); + + if (methodExists && !methodSymbol.Method.IsFunction()) + { + AddError(LocalizedErrors.UseProcAsFunction(), node.Location); + } + + var expression = CreateMethodCall(node); + _statementBuildParts.Push(expression); + } + + protected override void VisitObjectProcedureCall(BslSyntaxNode node) + { + var target = _statementBuildParts.Pop(); + var call = (CallNode) node; + + var targetType = target.Type; + var name = call.Identifier.GetIdentifier(); + if (targetType.IsObjectValue()) + { + var methodInfo = FindMethodOfType(node, targetType, name); + var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), methodInfo is ContextMethodInfo { InjectsProcess: true }); + + _blocks.Add(Expression.Call(target, methodInfo, args)); + } + else if (targetType.IsContext()) + { + var contextCall = ExpressionHelpers.CallContextMethod(target, name, _processParameter, + PrepareDynamicCallArguments(call.ArgumentList)); + _blocks.Add(contextCall); + } + else if (targetType.IsValue()) + { + var contextCall = ExpressionHelpers.TryCallContextMethod(target, name, _processParameter, + PrepareDynamicCallArguments(call.ArgumentList)); + _blocks.Add(contextCall); + } + else if (target is DynamicExpression) + { + var args = new List(); + args.Add(target); + args.AddRange(PrepareDynamicCallArguments(call.ArgumentList)); + + var csharpArgs = new List(); + csharpArgs.Add(CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default)); + csharpArgs.AddRange(args.Select(x => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default))); + + var binder = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember( + CSharpBinderFlags.InvokeSimpleName, + name, + null, + typeof(BslObjectValue), + csharpArgs); + + var objectExpr = Expression.Dynamic(binder, typeof(object), args); + _blocks.Add(ExpressionHelpers.ConvertToType(objectExpr, typeof(BslValue))); + } + else + { + AddError(NativeCompilerErrors.TypeIsNotAnObjectType(targetType), node.Location); + } + } + + private IEnumerable PrepareDynamicCallArguments(BslSyntaxNode argList) + { + return argList.Children.Select(passedArg => + passedArg.Children.Count != 0 + ? ConvertToExpressionTree(passedArg.Children[0]) + : Expression.Constant(BslSkippedParameterValue.Instance)); + } + + protected override void VisitObjectFunctionCall(BslSyntaxNode node) + { + var target = _statementBuildParts.Pop(); + var call = (CallNode) node; + + var targetType = target.Type; + var name = call.Identifier.GetIdentifier(); + if (targetType.IsObjectValue()) + { + var methodInfo = FindMethodOfType(node, targetType, name); + if (methodInfo.ReturnType == typeof(void)) + { + throw new NativeCompilerException(BilingualString.Localize( + $"Метод {targetType}.{name} не является функцией", + $"Method {targetType}.{name} is not a function"), ToCodePosition(node.Location)); + } + + var args = PrepareCallArguments(call.ArgumentList, methodInfo.GetParameters(), methodInfo is ContextMethodInfo { InjectsProcess: true }); + _statementBuildParts.Push(Expression.Call(target, methodInfo, args)); + } + else if (targetType.IsContext()) + { + _statementBuildParts.Push(ExpressionHelpers.CallContextMethod(target, name, _processParameter, PrepareDynamicCallArguments(call.ArgumentList))); + } + else if (targetType.IsValue()) + { + var contextCall = ExpressionHelpers.TryCallContextMethod(target, name, _processParameter, + PrepareDynamicCallArguments(call.ArgumentList)); + _statementBuildParts.Push(contextCall); + } + else if (target is DynamicExpression) + { + var args = new List(); + args.Add(target); + args.AddRange(PrepareDynamicCallArguments(call.ArgumentList)); + + var csharpArgs = new List(); + csharpArgs.Add(CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default)); + csharpArgs.AddRange(args.Select(x => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, default))); + + var binder = Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember( + CSharpBinderFlags.InvokeSimpleName, + name, + null, + typeof(BslObjectValue), + csharpArgs); + + var objectExpr = Expression.Dynamic(binder, typeof(object), args); + _statementBuildParts.Push(ExpressionHelpers.ConvertToType(objectExpr, typeof(BslValue))); + } + else + { + AddError(NativeCompilerErrors.TypeIsNotAnObjectType(targetType), node.Location); + } + } + + private MethodInfo FindMethodOfType(BslSyntaxNode node, Type targetType, string name) + { + MethodInfo methodInfo; + try + { + methodInfo = _methodsCache.GetOrAdd(targetType, name); + } + catch (InvalidOperationException) + { + throw new NativeCompilerException(BilingualString.Localize( + $"Метод {name} не определен для типа {targetType}", + $"Method {name} is not defined for type {targetType}"), ToCodePosition(node.Location)); + } + + return methodInfo; + } + + private PropertyInfo TryFindPropertyOfType(BslSyntaxNode node, Type targetType, string name) + { + PropertyInfo propertyInfo; + try + { + propertyInfo = _propertiesCache.GetOrAdd(targetType, name); + } + catch (InvalidOperationException) + { + propertyInfo = null; + } + + return propertyInfo; + } + + private PropertyInfo FindPropertyOfType(BslSyntaxNode node, Type targetType, string name) + { + PropertyInfo propertyInfo; + try + { + propertyInfo = _propertiesCache.GetOrAdd(targetType, name); + } + catch (InvalidOperationException) + { + throw new NativeCompilerException(BilingualString.Localize( + $"Свойство {name} не определено для типа {targetType}", + $"Property {name} is not defined for type {targetType}")); + } + + return propertyInfo; + } + + private Expression CreateMethodCall(CallNode node) + { + if (!Symbols.TryFindMethodBinding(node.Identifier.GetIdentifier(), out var binding)) + { + AddError($"Unknown method {node.Identifier.GetIdentifier()}", node.Location); + return null; + } + + var symbol = Symbols.GetScope(binding.ScopeNumber).Methods[binding.MemberNumber]; + var args = PrepareCallArguments(node.ArgumentList, symbol.Method.GetParameters(), symbol.Method is ContextMethodInfo { InjectsProcess: true }); + + var methodInfo = symbol.Method; + if (methodInfo is ContextMethodInfo contextMethod) + { + return DirectClrCall( + GetMethodBinding(binding, symbol), + contextMethod.GetWrappedMethod(), + args); + } + + if (methodInfo is BslNativeMethodInfo nativeMethod) + { + return ExpressionHelpers.InvokeBslNativeMethod( + nativeMethod, + _processParameter, + IsModuleScope(binding.ScopeNumber) ? _thisParameter : GetMethodBinding(binding, symbol), + args); + } + + throw new InvalidOperationException($"Unknown method type {symbol.Method.GetType()}"); + + } + + private Expression DirectClrCall(object target, MethodInfo clrMethod, IEnumerable args) + { + return Expression.Call(Expression.Constant(target), clrMethod, args); + } + + private Expression CreateBuiltInFunctionCall(CallNode node) + { + Expression result; + switch (node.Identifier.Lexem.Token) + { + case Token.Bool: + result = DirectConversionCall(node, typeof(bool)); + break; + case Token.Number: + result = DirectConversionCall(node, typeof(decimal)); + break; + case Token.Str: + result = DirectConversionCall(node, typeof(string)); + break; + case Token.Date: + result = DirectConversionCall(node, typeof(string)); + break; + case Token.Type: + CheckArgumentsCount(node.ArgumentList, 1); + result = ExpressionHelpers.TypeByNameCall(CurrentTypeManager, + ConvertToExpressionTree(node.ArgumentList.Children[0].Children[0])); + break; + case Token.ExceptionInfo: + CheckArgumentsCount(node.ArgumentList, 0); + result = GetRuntimeExceptionObject(); + + break; + case Token.ExceptionDescr: + CheckArgumentsCount(node.ArgumentList, 0); + result = GetRuntimeExceptionDescription(); + break; + case Token.ModuleInfo: + var factory = _services.Resolve(); + result = ExpressionHelpers.CallScriptInfo(factory, _source); + break; + default: + var methodName = node.Identifier.GetIdentifier(); + var method = _builtInFunctions.GetMethod(methodName); + var declaredParameters = method.GetParameters(); + + var args = PrepareCallArguments(node.ArgumentList, declaredParameters, false); + + result = Expression.Call(method, args); + break; + } + + return result; + } + + private Expression GetRuntimeExceptionDescription() + { + var excVariable = _blocks.GetCurrentBlock().CurrentException; + Expression factoryArgument; + // нас вызвали вне попытки-исключения + factoryArgument = excVariable == null ? Expression.Constant(null, typeof(IRuntimeContextInstance)) : GetRuntimeExceptionObject(); + + var factory = Expression.Constant(ExceptionInfoFactory); + return ExpressionHelpers.CallOfInstanceMethod( + factory, + nameof(IExceptionInfoFactory.GetExceptionDescription), + factoryArgument); + } + + private Expression GetRuntimeExceptionObject() + { + var excVariable = _blocks.GetCurrentBlock().CurrentException; + Expression factoryArgument; + if (excVariable == null) + { + // нас вызвали вне попытки-исключения + factoryArgument = Expression.Constant(null, typeof(Exception)); + } + else + { + factoryArgument = excVariable; + } + + var factory = Expression.Constant(ExceptionInfoFactory); + return ExpressionHelpers.CallOfInstanceMethod( + factory, + nameof(IExceptionInfoFactory.GetExceptionInfo), + factoryArgument); + } + + private void CheckArgumentsCount(BslSyntaxNode argList, int needed) + { + if (argList.Children.Count < needed) + { + var errText = new BilingualString("Недостаточно фактических параметров", + "Not enough actual parameters"); + AddError(errText, argList.Location); + } + + if (argList.Children.Count > needed || argList.Children.Any(x => x.Children.Count == 0)) + { + var errText = new BilingualString("Слишком много фактических параметров", + "Too many actual parameters"); + AddError(errText, argList.Location); + } + } + + private Expression DirectConversionCall(CallNode node, Type type) + { + CheckArgumentsCount(node.ArgumentList, 1); + return ExpressionHelpers.ConvertToType( + ConvertToExpressionTree(node.ArgumentList.Children[0].Children[0]), + type); + } + + private List PrepareCallArguments(BslSyntaxNode argList, ParameterInfo[] declaredParameters, bool injectsProcess) + { + var factArguments = new List(); + + if (injectsProcess) + { + factArguments.Add(_processParameter); + } + + var parameters = argList.Children.Select(passedArg => + passedArg.Children.Count != 0 + ? ConvertToExpressionTree(passedArg.Children[0]) + : null).ToArray(); + + var parametersToProcess = declaredParameters.Length; + var declStart = injectsProcess ? 1 : 0; + for (int i = 0, decl = declStart; i < parameters.Length; i++, decl++) + { + if (parametersToProcess == 0) + { + var errText = new BilingualString("Слишком много фактических параметров", + "Too many actual parameters"); + AddError(errText, argList.Location); + } + + var passedArg = parameters[i]; + var declaredParam = declaredParameters[decl]; + + if (declaredParam.GetCustomAttribute() != null) + { + // это спецпараметр + Debug.Assert(declaredParam.ParameterType.IsArray); + var arrayItemType = declaredParam.ParameterType.GetElementType(); + Debug.Assert(arrayItemType != null); + var remainParameters = parameters + .Skip(i) + .Select(x => PassSingleParameter(x, arrayItemType, argList.Location)); + + var paramArray = Expression.NewArrayInit(arrayItemType, remainParameters); + factArguments.Add(paramArray); + + parametersToProcess = 0; + break; + } + else + { + if (passedArg != null) + { + var convertedOrDirect = PassSingleParameter(passedArg, declaredParam.ParameterType, argList.Location); + factArguments.Add(convertedOrDirect); + } + else if (declaredParam.HasDefaultValue) + { + factArguments.Add(Expression.Constant(declaredParam.DefaultValue, declaredParam.ParameterType)); + } + else + { + var errText = new BilingualString( + $"Пропущен обязательный параметр {declaredParam.Position+1} '{declaredParam.Name}'", + $"Missing mandatory parameter {declaredParam.Position+1} '{declaredParam.Name}'"); + AddError(errText, argList.Location); + } + } + + --parametersToProcess; + } + + if (parametersToProcess > 0) + { + foreach (var declaredParam in declaredParameters.Skip(parameters.Length + declStart)) + { + if (declaredParam.HasDefaultValue) + { + factArguments.Add(Expression.Constant(declaredParam.DefaultValue, declaredParam.ParameterType)); + } + else + { + var errText = new BilingualString("Недостаточно фактических параметров", + "Not enough actual parameters"); + AddError(errText, argList.Location); + } + } + } + + return factArguments; + } + + private Expression PassSingleParameter(Expression passedArg, Type targetType, CodeRange location) + { + if (passedArg == null) + { + return Expression.Constant(BslSkippedParameterValue.Instance); + } + + var convertedOrDirect = ExpressionHelpers.TryConvertParameter(passedArg, targetType); + if (convertedOrDirect == null) + { + AddError( + new BilingualString( + $"Не удается выполнить преобразование параметра из типа {passedArg.Type} в тип {targetType}", + $"Cannot convert parameter from type {passedArg.Type} to type {targetType}"), + location); + } + + return convertedOrDirect; + } + + #endregion + + #region Constructors + + protected override void VisitNewObjectCreation(NewObjectNode node) + { + var services = Expression.Constant(_services); + + Expression[] parameters; + if (node.ConstructorArguments != default) + { + parameters = node.ConstructorArguments.Children.Select(passedArg => + passedArg.Children.Count != 0 ? + ConvertToExpressionTree(passedArg.Children[0]) : + Expression.Default(typeof(BslValue))).ToArray(); + } + else + { + parameters = new Expression[0]; + } + + if (node.IsDynamic) + { + var typeName = ConvertToExpressionTree(node.TypeNameNode); + var call = ExpressionHelpers.ConstructorCall(CurrentTypeManager, services, typeName, _processParameter, parameters); + _statementBuildParts.Push(call); + } + else + { + var typeNameString = node.TypeNameNode.GetIdentifier(); + var isKnownType = CurrentTypeManager.TryGetType(typeNameString, out var typeDef); + if (isKnownType) + { + var call = ExpressionHelpers.ConstructorCall(CurrentTypeManager, services, typeDef, _processParameter, parameters); + _statementBuildParts.Push(call); + } + else // это может быть тип, подключенный через ПодключитьСценарий + { + var typeName = Expression.Constant(typeNameString); + var call = ExpressionHelpers.ConstructorCall(CurrentTypeManager, services, typeName, _processParameter, parameters); + _statementBuildParts.Push(call); + } + } + + } + + #endregion + + private Expression ConvertToExpressionTree(BslSyntaxNode arg) + { + VisitExpression(arg); + return _statementBuildParts.Pop(); + } + + protected override void AddError(BilingualString errorText, CodeRange location) + { + base.AddError(errorText, location); + throw new InternalFlowInterruptException(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ModuleCompiler.cs b/src/OneScript.Native/Compiler/ModuleCompiler.cs new file mode 100644 index 000000000..499d197bb --- /dev/null +++ b/src/OneScript.Native/Compiler/ModuleCompiler.cs @@ -0,0 +1,258 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Native.Runtime; +using OneScript.Sources; +using OneScript.Values; + +namespace OneScript.Native.Compiler +{ + public class ModuleCompiler : ExpressionTreeGeneratorBase + { + private readonly IServiceContainer _runtimeServices; + private readonly ICompileTimeDependencyResolver _dependencyResolver; + private DynamicModule _module; + private readonly BslMethodInfoFactory _methodsFactory; + private IBslProcess _compilerProcess; + + public ModuleCompiler(IErrorSink errors, IServiceContainer runtimeServices, ICompileTimeDependencyResolver dependencyResolver) : base(errors) + { + _runtimeServices = runtimeServices; + _dependencyResolver = dependencyResolver; + _methodsFactory = new BslMethodInfoFactory(() => new BslNativeMethodInfo()); + + } + + public DynamicModule Compile(SourceCode moduleInfo, + BslSyntaxNode moduleNode, + SymbolTable symbols, IBslProcess process) + { + InitContext(Errors, moduleInfo, symbols); + _compilerProcess = process; + + _module = new DynamicModule + { + Source = moduleInfo + }; + + // TODO MakeConstructor - метод инициализации и установки значения в переменную this + Visit(moduleNode); + + return _module; + } + + protected override BslWalkerContext MakeContext() + { + var c = base.MakeContext(); + c.Services = _runtimeServices; + return c; + } + + /// + /// Заранее заготавливаем символы методов, т.к. выражения могут ссылатся на методы, + /// объявленные позже в теле модуля. + /// + /// + private void RegisterLocalMethods(BslSyntaxNode module) + { + var methodsSection = module.Children.FirstOrDefault(x => x.Kind == NodeKind.MethodsSection); + if(methodsSection == default) + return; + + var ownMethodsCount = Symbols.GetScope(Symbols.ScopeCount - 1).Methods.Count; + + foreach (var methodNode in methodsSection.Children.Cast()) + { + var signature = methodNode.Signature; + if (Symbols.TryFindMethodBinding(signature.MethodName, out _)) + { + AddError(LocalizedErrors.DuplicateMethodDefinition(signature.MethodName), signature.Location); + continue; + } + + var builder = _methodsFactory.NewMethod(); + builder.SetAnnotations(CompilerHelpers.GetAnnotations(methodNode.Annotations)); + builder.SetDispatchingIndex(ownMethodsCount++); + + VisitMethodSignature(builder, methodNode.Signature); + + var methodInfo = builder.Build(); + methodInfo.IsInstance = true; + var symbol = methodInfo.ToSymbol(); + + Symbols.DefineMethod(symbol); + } + } + + private void VisitMethodSignature(BslMethodBuilder builder, MethodSignatureNode node) + { + builder + .Name(node.MethodName) + .ReturnType(node.IsFunction ? typeof(BslValue): typeof(void)) + .IsExported(node.IsExported); + + foreach (var parameterNode in node.GetParameters()) + { + CreateParameterInfo(builder.NewParameter(), parameterNode); + } + } + + private void CreateParameterInfo(BslParameterBuilder param, MethodParameterNode paramNode) + { + // TODO: Возможно, в native у нас будут все параметры как byval, даже без Знач + param.Name(paramNode.Name); + //if(paramNode.IsByValue) + param.ByValue(true); + + if(paramNode.HasDefaultValue) + param.DefaultValue(CompilerHelpers.ValueFromLiteral(paramNode.DefaultValue)); + + var attributes = CompilerHelpers.GetAnnotations(paramNode.Annotations); + param.SetAnnotations(attributes); + } + + protected override void VisitModule(ModuleNode node) + { + RegisterLocalMethods(node); + base.VisitModule(node); + + Symbols.PopScope(); + } + + protected override void VisitModuleVariable(VariableDefinitionNode varNode) + { + if (Symbols.FindVariable(varNode.Name, out _)) + { + AddError(LocalizedErrors.DuplicateVarDefinition(varNode.Name)); + return; + } + + var annotations = CompilerHelpers.GetAnnotations(varNode.Annotations).ToArray(); + var field = BslFieldBuilder.Create() + .Name(varNode.Name) + .IsExported(varNode.IsExported) + .SetAnnotations(annotations) + .ValueType(typeof(BslValue)) + .Build(); + + var varSymbol = field.ToSymbol(); + + var id = Symbols.GetScope(Symbols.ScopeCount - 1).DefineVariable(varSymbol); + _module.Fields.Add(field); + + if (varNode.IsExported) + { + var propertyView = BslPropertyBuilder.Create() + .Name(varNode.Name) + .IsExported(true) + .DeclaringType(varSymbol.Type) + .SetAnnotations(annotations) + .SetDispatchingIndex(id); + + _module.Properties.Add(propertyView.Build()); + } + } + + protected override void VisitGotoNode(NonTerminalNode node) + { + throw new NotSupportedException(); + } + + protected override void VisitLabelNode(LabelNode node) + { + throw new NotSupportedException(); + } + + protected override void VisitMethod(MethodNode methodNode) + { + if (methodNode.IsAsync) + { + AddError(LocalizedErrors.AsyncMethodsNotSupported(), methodNode.Location); + } + var methodSymbol = Symbols.GetScope(Symbols.ScopeCount - 1).Methods[methodNode.Signature.MethodName]; + var methodInfo = (BslNativeMethodInfo)methodSymbol.Method; + + var context = MakeContext(); + var methCompiler = new MethodCompiler(context, methodInfo); + methCompiler.CompileMethod(methodNode); + _module.Methods.Add(methodInfo); + } + + protected override void VisitModuleBody(BslSyntaxNode moduleBody) + { + var factory = new BslMethodInfoFactory(() => new BslNativeMethodInfo()); + var builder = factory.NewMethod() + .Name("$entry"); + + var method = builder.Build(); + method.IsInstance = true; + _module.Methods.Add(method); + + var context = MakeContext(); + var methCompiler = new MethodCompiler(context, method); + methCompiler.CompileModuleBody(moduleBody); + } + + protected override void VisitModuleAnnotation(AnnotationNode node) + { + if (node.Kind == NodeKind.Import) + { + HandleImportClause(node); + } + + var annotation = CompilerHelpers.GetBslAnnotation(node); + _module.ModuleAttributes.Add(annotation); + } + + private void HandleImportClause(AnnotationNode node) + { + var libName = node.Children + .Cast() + .First() + .Value + .Content; + + try + { + _dependencyResolver.Resolve(_module.Source, libName, _compilerProcess); + } + catch (DependencyResolveException e) + { + var error = new CodeError + { + Description = e.Message, + Position = MakeCodePosition(node.Location), + ErrorId = nameof(CompilerException) + }; + AddError(error); + } + } + + private ErrorPositionInfo MakeCodePosition(CodeRange range) + { + return new ErrorPositionInfo + { + Code = _module.Source.GetCodeLine(range.LineNumber), + LineNumber = range.LineNumber, + ColumnNumber = range.ColumnNumber, + ModuleName = _module.Source.Name + }; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/NativeCompilerErrors.cs b/src/OneScript.Native/Compiler/NativeCompilerErrors.cs new file mode 100644 index 000000000..5f484a2ca --- /dev/null +++ b/src/OneScript.Native/Compiler/NativeCompilerErrors.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.CompilerServices; +using OneScript.Language; +using OneScript.Localization; + +namespace OneScript.Native.Compiler +{ + public class NativeCompilerErrors + { + public static CodeError TypeIsNotAnObjectType(Type targetType) => + Create($"Тип {targetType} не является объектным типом.",$"Type {targetType} is not an object type."); + + private static CodeError Create(string ru, string en, [CallerMemberName] string errorId = default) + { + return new CodeError + { + ErrorId = errorId, + Description = BilingualString.Localize(ru, en) + }; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/NativeCompilerException.cs b/src/OneScript.Native/Compiler/NativeCompilerException.cs new file mode 100644 index 000000000..a5c28e103 --- /dev/null +++ b/src/OneScript.Native/Compiler/NativeCompilerException.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq.Expressions; +using OneScript.Compilation; +using OneScript.Language; +using OneScript.Localization; + +namespace OneScript.Native.Compiler +{ + public class NativeCompilerException : CompilerException + { + public NativeCompilerException(string message) : base(message) + { + } + + public NativeCompilerException(string message, ErrorPositionInfo position) : base(message, position) + { + } + + public static NativeCompilerException OperationNotDefined(ExpressionType opCode, Type left, Type right) => + new NativeCompilerException( + BilingualString.Localize( + $"Операция {opCode} не определена для типов {left} и {right}", + $"Operation {opCode} is not defined for {left} and {right}") + ); + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/NativeRuntimeAnnotationHandler.cs b/src/OneScript.Native/Compiler/NativeRuntimeAnnotationHandler.cs new file mode 100644 index 000000000..763c36147 --- /dev/null +++ b/src/OneScript.Native/Compiler/NativeRuntimeAnnotationHandler.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +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 OneScript.Language; +using OneScript.Language.SyntaxAnalysis; + +namespace OneScript.Native.Compiler +{ + public class NativeRuntimeAnnotationHandler : SingleWordModuleAnnotationHandler + { + public static string NativeDirectiveName => "native"; + public static string StackRuntimeDirectiveName => "stack"; + + private static readonly HashSet Directives = new HashSet + { + NativeDirectiveName, + StackRuntimeDirectiveName + }; + + public NativeRuntimeAnnotationHandler(IErrorSink errorSink) : base(Directives, errorSink) + { + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ReflectedMembersCache.cs b/src/OneScript.Native/Compiler/ReflectedMembersCache.cs new file mode 100644 index 000000000..f53afa2b5 --- /dev/null +++ b/src/OneScript.Native/Compiler/ReflectedMembersCache.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; +using OneScript.Commons; + +namespace OneScript.Native.Compiler +{ + public abstract class ReflectedMembersCache where T : MemberInfo + { + private readonly LruCache _cache; + + public ReflectedMembersCache() : this(128) + { + } + + public ReflectedMembersCache(int size) + { + _cache = new LruCache(size); + } + + public virtual T GetOrAdd(Type type, string name) + { + return GetOrAdd(type, name, BindingFlags.Public | BindingFlags.Static); + } + + public T GetOrAdd(Type type, string name, BindingFlags flags) + { + var key = $"{type.Name}.{name}"; + return _cache.GetOrAdd(key, x => SearchImpl(type, name, flags) + ?? throw new InvalidOperationException($"No member found {key}")); + } + + protected abstract T SearchImpl(Type type, string name, BindingFlags flags); + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ReflectedMethodsCache.cs b/src/OneScript.Native/Compiler/ReflectedMethodsCache.cs new file mode 100644 index 000000000..e111d35c7 --- /dev/null +++ b/src/OneScript.Native/Compiler/ReflectedMethodsCache.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; + +namespace OneScript.Native.Compiler +{ + public class ReflectedMethodsCache : ReflectedMembersCache + { + public ReflectedMethodsCache() + { + } + + public ReflectedMethodsCache(int size) : base(size) + { + } + + protected override MethodInfo SearchImpl(Type type, string name, BindingFlags flags) + { + return type.GetMethod(name, flags); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/ReflectedPropertiesCache.cs b/src/OneScript.Native/Compiler/ReflectedPropertiesCache.cs new file mode 100644 index 000000000..343d65f41 --- /dev/null +++ b/src/OneScript.Native/Compiler/ReflectedPropertiesCache.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; + +namespace OneScript.Native.Compiler +{ + public class ReflectedPropertiesCache : ReflectedMembersCache + { + public ReflectedPropertiesCache() + { + } + + public ReflectedPropertiesCache(int size) : base(size) + { + } + + protected override PropertyInfo SearchImpl(Type type, string name, BindingFlags flags) + { + return type.GetProperty(name, flags); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/StatementBlocksWriter.cs b/src/OneScript.Native/Compiler/StatementBlocksWriter.cs new file mode 100644 index 000000000..6bf68f434 --- /dev/null +++ b/src/OneScript.Native/Compiler/StatementBlocksWriter.cs @@ -0,0 +1,45 @@ +/*---------------------------------------------------------- +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.Expressions; + +namespace OneScript.Native.Compiler +{ + public class StatementBlocksWriter + { + private readonly Stack _blocks = new Stack(); + + public int Count => _blocks.Count; + + public void EnterBlock(JumpInformationRecord newJumpStates) + { + var current = _blocks.Count != 0 ? GetCurrentBlock() : null; + if (current != null) + { + newJumpStates.MethodReturn ??= current.MethodReturn; + newJumpStates.LoopBreak ??= current.LoopBreak; + newJumpStates.LoopContinue ??= current.LoopContinue; + newJumpStates.ExceptionInfo ??= current.CurrentException; + } + + var block = new StatementsBlockRecord(newJumpStates); + _blocks.Push(block); + } + + public void EnterBlock() + { + EnterBlock(new JumpInformationRecord()); + } + + public StatementsBlockRecord LeaveBlock() => _blocks.Pop(); + + public StatementsBlockRecord GetCurrentBlock() => _blocks.Peek(); + + public void Add(Expression statement) => GetCurrentBlock().Add(statement); + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Compiler/StatementsBlockRecord.cs b/src/OneScript.Native/Compiler/StatementsBlockRecord.cs new file mode 100644 index 000000000..a41299587 --- /dev/null +++ b/src/OneScript.Native/Compiler/StatementsBlockRecord.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using System.Linq.Expressions; + +namespace OneScript.Native.Compiler +{ + public class StatementsBlockRecord + { + private readonly List _statements = new List(); + private readonly JumpInformationRecord _jumpContext; + private readonly Stack _buildParts = new Stack(); + + public StatementsBlockRecord() + { + _jumpContext = new JumpInformationRecord(); + } + + public StatementsBlockRecord(JumpInformationRecord jumpTargets) + { + _jumpContext = jumpTargets; + } + + public LabelTarget MethodReturn => _jumpContext.MethodReturn; + public LabelTarget LoopContinue => _jumpContext.LoopContinue; + public LabelTarget LoopBreak => _jumpContext.LoopBreak; + public ParameterExpression CurrentException => _jumpContext.ExceptionInfo; + + public void Add(Expression statement) => _statements.Add(statement); + + public IList GetStatements() => _statements; + + public Stack BuildStack => _buildParts; + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Extensions/ServiceRegistrationExtensions.cs b/src/OneScript.Native/Extensions/ServiceRegistrationExtensions.cs new file mode 100644 index 000000000..e74af7327 --- /dev/null +++ b/src/OneScript.Native/Extensions/ServiceRegistrationExtensions.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 OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Native.Compiler; +using OneScript.Native.Runtime; + +namespace OneScript.Native.Extensions +{ + public static class ServiceRegistrationExtensions + { + public static IServiceDefinitions UseNativeRuntime(this IServiceDefinitions services) + { + services.RegisterEnumerable(); + services.RegisterEnumerable(); + return services; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/OneScript.Native.csproj b/src/OneScript.Native/OneScript.Native.csproj new file mode 100644 index 000000000..668899136 --- /dev/null +++ b/src/OneScript.Native/OneScript.Native.csproj @@ -0,0 +1,26 @@ + + + + + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug + AnyCPU + + + + true + false + true + TRACE; DEBUG; + + + + + + + + + + + + diff --git a/src/OneScript.Native/Runtime/BslNativeMethodInfo.cs b/src/OneScript.Native/Runtime/BslNativeMethodInfo.cs new file mode 100644 index 000000000..73e452cab --- /dev/null +++ b/src/OneScript.Native/Runtime/BslNativeMethodInfo.cs @@ -0,0 +1,68 @@ +/*---------------------------------------------------------- +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 System.Linq.Expressions; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Values; + +namespace OneScript.Native.Runtime +{ + public class BslNativeMethodInfo : BslScriptMethodInfo + { + private CallableMethod _callable; + + public BslNativeMethodInfo() + { + _callable = new CallableMethod(this); + } + + public void SetImplementation(LambdaExpression lambda) + { + Implementation = lambda; + _callable.Compile(); + } + + internal CallableMethod GetCallable() => _callable; + + public LambdaExpression Implementation { get; private set; } + + public bool IsInstance { get; internal set; } + + public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + // FIXME: Из стековой машины дефолтные значения могут прийти, как null или Skipped + // здесь мы принудительно проставляем пропущенные параметры + var bslArguments = new BslValue[parameters.Length - 1]; + for (int i = 0; i < bslArguments.Length; i++) + { + var param = parameters[i + 1]; + if (param == null || param == BslSkippedParameterValue.Instance) + { + if (_parameters[i].HasDefaultValue) + bslArguments[i] = (BslValue)_parameters[i].DefaultValue; + else + throw RuntimeException.MissedArgument(); + } + else if(param is BslValue bslVal) + { + bslArguments[i] = bslVal; + } + else if (param is IVariable variable) + { + bslArguments[i] = variable.BslValue; + } + } + + return _callable.Invoke((IBslProcess)parameters[0], obj, bslArguments); + } + + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Runtime/BuiltInFunctions.cs b/src/OneScript.Native/Runtime/BuiltInFunctions.cs new file mode 100644 index 000000000..1bb5c2e3f --- /dev/null +++ b/src/OneScript.Native/Runtime/BuiltInFunctions.cs @@ -0,0 +1,563 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.CompilerServices; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; + +namespace OneScript.Native.Runtime +{ + public static class BuiltInFunctions + { + [ContextMethod("Вычислить", "Eval")] + public static BslValue Eval(string arg) + { + throw new NotSupportedException(); + } + + #region String Functions + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("СтрДлина", "StrLen")] + public static int StrLen(string arg) => arg.Length; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("СокрЛ", "TrimL")] + public static string TrimL(string arg) => arg.TrimStart(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("СокрП", "TrimR")] + public static string TrimR(string arg) => arg.TrimEnd(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("СокрЛП", "TrimAll")] + public static string TrimLR(string arg) => arg.Trim(); + + [ContextMethod("Лев", "Left")] + public static string Left(string str, int len) + { + if (len > str.Length) + len = str.Length; + else if (len < 0) + { + return string.Empty; + } + + return str.Substring(0, len); + } + + [ContextMethod("Прав", "Right")] + public static string Right(string str, int len) + { + if (len > str.Length) + len = str.Length; + else if (len < 0) + { + return string.Empty; + } + + int startIdx = str.Length - len; + return str.Substring(startIdx, len); + } + + [ContextMethod("Сред", "Mid")] + public static string Mid(string str, int start, int len = -1) + { + if (start < 1) + start = 1; + + if (start+len > str.Length || len < 0) + len = str.Length-start+1; + + string result; + + if (start > str.Length || len == 0) + { + result = ""; + } + else + { + result = str.Substring(start - 1, len); + } + + return result; + } + + [ContextMethod("Найти", "Find")] + public static int StrPos(string haystack, string needle) + { + var result = haystack.IndexOf(needle, StringComparison.Ordinal) + 1; + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("ВРег", "Upper")] + public static string UCase(string str) => str.ToUpper(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("НРег", "Lower")] + public static string LCase(string str) => str.ToLower(); + + [ContextMethod("ТРег", "Title")] + public static string TCase(string str) + { + char[] array = str.ToCharArray(); + // Handle the first letter in the string. + bool inWord = false; + if (array.Length >= 1) + { + if (char.IsLetter(array[0])) + inWord = true; + + if(char.IsLower(array[0])) + { + array[0] = char.ToUpper(array[0]); + } + } + // Scan through the letters, checking for spaces. + // ... Uppercase the lowercase letters following spaces. + for (int i = 1; i < array.Length; i++) + { + if (inWord && Char.IsLetter(array[i])) + array[i] = Char.ToLower(array[i]); + else if (Char.IsSeparator(array[i]) || Char.IsPunctuation(array[i])) + inWord = false; + else if(!inWord && Char.IsLetter(array[i])) + { + inWord = true; + if (char.IsLower(array[i])) + { + array[i] = char.ToUpper(array[i]); + } + } + } + + return new string(array); + } + + [ContextMethod("Символ", "Char")] + public static string Chr(int code) + { + return new string(new char[1] { (char)code }); + } + + [ContextMethod("КодСимвола", "CharCode")] + public static int ChrCode(string strChar, int? position = null) + { + if(position != null) + { + position -= 1; + } + else + { + position = 0; + } + + int result; + if (strChar.Length == 0) + result = 0; + else if (position >= 0 && position < strChar.Length) + result = (int)strChar[(int)position]; + else + throw RuntimeException.InvalidArgumentValue(); + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("ПустаяСтрока", "IsBlankString")] + public static bool EmptyStr(string arg) => string.IsNullOrWhiteSpace(arg); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("СтрЗаменить", "StrReplace")] + public static string StrReplace(string sourceString, string searchVal, string newVal) => + !string.IsNullOrEmpty(searchVal) ? sourceString.Replace(searchVal, newVal):sourceString; + + [ContextMethod("СтрПолучитьСтроку", "StrGetLine")] + public static string StrGetLine(string strArg, int lineNumber) + { + string result = ""; + if (lineNumber >= 1) + { + string[] subStrVals = strArg.Split(new Char[] { '\n' }, lineNumber + 1); + result = subStrVals[lineNumber - 1]; + } + + return result; + } + + [ContextMethod("СтрЧислоСтрок", "StrLineCount")] + public static int StrLineCount(string strArg) + { + int pos = 0; + int lineCount = 1; + while (pos >= 0 && pos < strArg.Length) + { + pos = strArg.IndexOf('\n', pos); + if (pos >= 0) + { + lineCount++; + pos++; + } + } + + return lineCount; + } + + [ContextMethod("СтрЧислоВхождений", "StrOccurrenceCount")] + public static int StrEntryCount(string where, string what) + { + var pos = where.IndexOf(what, StringComparison.CurrentCulture); + var entryCount = 0; + while(pos >= 0) + { + entryCount++; + var nextIndex = pos + what.Length; + if (nextIndex >= where.Length) + break; + + pos = where.IndexOf(what, nextIndex, StringComparison.CurrentCulture); + } + + return entryCount; + } + + #endregion + + #region Date Functions + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("Год", "Year")] + public static int Year(DateTime date) => date.Year; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("Месяц", "Month")] + public static int Month(DateTime date) => date.Month; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("День", "Day")] + public static int Day(DateTime date) => date.Day; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("Час", "Hour")] + public static int Hour(DateTime date) => date.Hour; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("Минута", "Minute")] + public static int Minute(DateTime date) => date.Minute; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("Секунда", "Second")] + public static int Second(DateTime date) => date.Second; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("НачалоГода", "BegOfYear")] + public static DateTime BegOfYear(DateTime date) => new DateTime(date.Year, 1, 1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("НачалоМесяца", "BegOfMonth")] + public static DateTime BegOfMonth(DateTime date) => new DateTime(date.Year, date.Month, 1); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("НачалоДня", "BegOfDay")] + public static DateTime BegOfDay(DateTime date) => new DateTime(date.Year, date.Month, date.Day); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("НачалоЧаса", "BegOfHour")] + public static DateTime BegOfHour(DateTime date) => + new DateTime(date.Year, date.Month, date.Day, date.Hour, 0, 0); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("НачалоМинуты", "BegOfMinute")] + public static DateTime BegOfMinute(DateTime date) => + new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0); + + [ContextMethod("НачалоКвартала", "BegOfQuarter")] + public static DateTime BegOfQuarter(DateTime date) + { + //1,4,7,10 + int quarterMonth; + if (date.Month >= 1 && date.Month <= 3) + { + quarterMonth = 1; + } + else if (date.Month >= 4 && date.Month <= 6) + { + quarterMonth = 4; + } + else if (date.Month >= 7 && date.Month <= 9) + { + quarterMonth = 7; + } + else + { + quarterMonth = 10; + } + var result = new DateTime(date.Year, quarterMonth, 1); + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("КонецГода", "EndOfYear")] + public static DateTime EndOfYear(DateTime date) + { + var year = date.Year; + return new DateTime(year, 12, DateTime.DaysInMonth(year, 12), 23, 59, 59); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("КонецМесяца", "EndOfMonth")] + public static DateTime EndOfMonth(DateTime date) + { + var year = date.Year; + var month = date.Month; + return new DateTime(year, month, DateTime.DaysInMonth(year, month), 23, 59, 59); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("КонецДня", "EndOfDay")] + public static DateTime EndOfDay(DateTime date) => new DateTime(date.Year, date.Month, date.Day, 23, 59, 59); + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("КонецЧаса", "EndOfHour")] + public static DateTime EndOfHour(DateTime date) => + new DateTime(date.Year, date.Month, date.Day, date.Hour, 59, 59); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("КонецМинуты", "EndOfMinute")] + public static DateTime EndOfMinute(DateTime date) => + new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 59); + + [ContextMethod("КонецКвартала", "EndOfQuarter")] + public static DateTime EndOfQuarter(DateTime date) + { + //1,4,7,10 + int quarterMonth; + if (date.Month >= 1 && date.Month <= 3) + { + quarterMonth = 3; + } + else if (date.Month >= 4 && date.Month <= 6) + { + quarterMonth = 6; + } + else if (date.Month >= 7 && date.Month <= 9) + { + quarterMonth = 9; + } + else + { + quarterMonth = 12; + } + var result = new DateTime(date.Year, quarterMonth, DateTime.DaysInMonth(date.Year, quarterMonth), 23, 59, 59); + return result; + } + + [ContextMethod("НеделяГода", "WeekOfYear")] + public static int WeekOfYear(DateTime date) + { + var cal = new System.Globalization.GregorianCalendar(); + + return cal.GetWeekOfYear(date, System.Globalization.CalendarWeekRule.FirstDay, System.DayOfWeek.Monday); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("ДеньГода", "DayOfYear")] + public static int DayOfYear(DateTime date) => date.DayOfYear; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("ДеньНедели", "DayOfWeek")] + public static int DayOfWeek(DateTime date) + { + var day = (int)date.DayOfWeek; + if (day == 0) + { + day = 7; + } + + return day; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("ДобавитьМесяц", "AddMonth")] + public static object AddMonth(DateTime date, int numToAdd) => date.AddMonths(numToAdd); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("ТекущаяДата", "CurrentDate")] + public static DateTime CurrentDate() + { + var date = DateTime.Now; + return date.AddTicks(-(date.Ticks % TimeSpan.TicksPerSecond)); + } + + #endregion + + #region Math Functions + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("Цел", "Int")] + public static decimal Integer(decimal arg) => Math.Truncate(arg); + + [ContextMethod("Окр", "Round")] + public static decimal Round(decimal num, int? digits = null, int? mode = null) + { + var digitsMath = digits??0; + var modeMath = mode??1;// по умолчанию Окр15как20 + if (modeMath != 0) + modeMath = 1; + + decimal result; + if (digits >= 0) + { + result = Math.Round(num, digitsMath, MidpointRounding.AwayFromZero); + if (modeMath == 0) + { + int scale = (int)Math.Pow(10, digitsMath); + // для.Net Core 3+, 5+ можно использовать MidpointRounding.ToZero + var diff = (result - num) * scale; + if (diff == 0.5m) + result -= 1m / scale; + else if (diff == -0.5m) + result += 1m / scale; + } + } + else + { + int scale = (int)Math.Pow(10, -digitsMath); + num /= scale; + result = Math.Round(num, MidpointRounding.AwayFromZero); + if (mode == 0) + { + var diff = result - num; + if (diff == 0.5m) + result -= 1m; + else if (diff == -0.5m) + result += 1m; + } + result *= scale; + } + + return result; + } + + [ContextMethod("Log")] + public static decimal Log(decimal num) => (decimal)Math.Log((double) num); + + [ContextMethod("Log10")] + public static decimal Log10(decimal num) => (decimal)Math.Log10((double) num); + + [ContextMethod("Sin")] + public static decimal Sin(decimal num) => (decimal) Math.Sin((double) num); + + [ContextMethod("Cos")] + public static decimal Cos(decimal num) => (decimal) Math.Cos((double) num); + + [ContextMethod("Tan")] + public static decimal Tan(decimal num) => (decimal) Math.Tan((double) num); + + [ContextMethod("ASin")] + public static decimal ASin(decimal num) => (decimal) Math.Asin((double) num); + + [ContextMethod("ACos")] + public static decimal ACos(decimal num) => (decimal) Math.Acos((double) num); + + [ContextMethod("ATan")] + public static decimal ATan(decimal num) => (decimal) Math.Atan((double) num); + + [ContextMethod("Exp")] + public static decimal Exp(decimal num) => (decimal) Math.Exp((double) num); + + [ContextMethod("Pow")] + public static decimal Pow(decimal powBase, decimal powPower) + { + int exp = (int)powPower; + decimal result; + if (exp >= 0 && exp == powPower) + result = PowInt(powBase, (uint)exp); + else + result = (decimal)Math.Pow((double)powBase, (double)powPower); + + return result; + } + + [ContextMethod("Sqrt")] + public static decimal Sqrt(decimal num) => (decimal) Math.Sqrt((double) num); + + [ContextMethod("Мин", "Min")] + public static decimal Min(decimal arg1, params decimal[] args) + { + if (args.Length == 0) + return arg1; + + var min = arg1; + for (int i = 0; i < args.Length; i++) + { + var current = args[i]; + if (current.CompareTo(min) < 0) + min = current; + } + + return min; + } + + [ContextMethod("Макс", "Max")] + public static decimal Max(decimal arg1, params decimal[] args) + { + if (args.Length == 0) + return arg1; + + var max = arg1; + for (int i = 0; i < args.Length; i++) + { + var current = args[i]; + if (current.CompareTo(max) > 0) + max = current; + } + + return max; + } + + #endregion + + #region Other + + // [ContextMethod("текущийсценарий", "currentscript")] public static object ModuleInfo(object arg){} + + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContextMethod("ТипЗнч", "TypeOf")] + public static BslValue ValType(BslValue value) => new BslTypeValue(value.SystemType); + + + [ContextMethod("Формат", "Format")] + public static string Format(BslValue value, string format) + { + return ValueFormatter.Format(value, format); + } + + private static decimal PowInt(decimal bas, uint exp) + { + decimal pow = 1; + + while (true) + { + if ((exp & 1) == 1) pow *= bas; + exp >>= 1; + if (exp == 0) break; + bas *= bas; + } + + return pow; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Runtime/CallableMethod.cs b/src/OneScript.Native/Runtime/CallableMethod.cs new file mode 100644 index 000000000..f61237aad --- /dev/null +++ b/src/OneScript.Native/Runtime/CallableMethod.cs @@ -0,0 +1,120 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Native.Compiler; +using OneScript.Values; + +namespace OneScript.Native.Runtime +{ + /// + /// Класс-обертка для вызываемой лямбды. Нужен для обхода проблемы + /// MethodInfo must be a runtime MethodInfo + /// Получаемой выражением Expression.Constant(bslNativeMethodInfo) + /// + /// Иными словами, не удается составить выражение, вызывающее кастомный метод в кастомном наследнике MethodInfo + /// Это похоже на баг в .NET, а может это byDesign, так или иначе, метод вызова отделен от BslNativeMethodInfo + /// для использования в выражениях Call () + /// + internal class CallableMethod + { + private readonly BslNativeMethodInfo _method; + + private delegate BslValue NativeCallable(NativeClassInstanceWrapper target, BslValue[] args, IBslProcess process); + + private NativeCallable _delegate; + + public CallableMethod(BslNativeMethodInfo method) + { + _method = method; + } + + public BslValue Invoke(IBslProcess process, object target, BslValue[] args) + { + if (_delegate == default) + { + throw new InvalidOperationException($"Method {_method} was not compiled"); + } + + var callableWrapper = GetCallableWrapper(target); + return _delegate.Invoke(callableWrapper, args, process); + } + + private NativeClassInstanceWrapper GetCallableWrapper(object obj) + { + NativeClassInstanceWrapper callableWrapper; + if (_method.IsInstance) + { + if (obj == null) + throw new InvalidOperationException($"Method {_method.Name} is not static and requires target"); + if (obj is NativeClassInstanceWrapper w) + { + callableWrapper = w; + } + else if (obj is IAttachableContext context) + { + callableWrapper = new NativeClassInstanceWrapper + { + Context = context + }; + } + else + { + throw new ArgumentException($"Invalid argument type {obj.GetType()}", nameof(obj)); + } + } + else + { + if (obj != null) + throw new InvalidOperationException($"Method {_method.Name} is static"); + + callableWrapper = null; + } + + return callableWrapper; + } + + private static NativeCallable CreateDelegate(BslNativeMethodInfo method) + { + if (method.Implementation == default) + throw new InvalidOperationException("Method has no implementation"); + + var targetParam = Expression.Parameter(typeof(NativeClassInstanceWrapper)); + var arrayOfValuesParam = Expression.Parameter(typeof(BslValue[])); + var processParam = Expression.Parameter(typeof(IBslProcess)); + var convertedAccessList = new List(); + + convertedAccessList.Add(processParam); + + if (method.IsInstance) + convertedAccessList.Add(targetParam); + + int index = 0; + foreach (var parameter in method.GetBslParameters()) + { + var targetType = parameter.ParameterType; + var arrayAccess = Expression.ArrayIndex(arrayOfValuesParam, Expression.Constant(index)); + var convertedParam = ExpressionHelpers.ConvertToType(arrayAccess, targetType); + convertedAccessList.Add(convertedParam); + ++index; + } + + var lambdaInvocation = Expression.Invoke(method.Implementation, convertedAccessList); + var func = Expression.Lambda(lambdaInvocation, targetParam, arrayOfValuesParam, processParam); + + return func.Compile(); + } + + public void Compile() + { + _delegate = CreateDelegate(_method); + } + } +} diff --git a/src/OneScript.Native/Runtime/DynamicOperations.cs b/src/OneScript.Native/Runtime/DynamicOperations.cs new file mode 100644 index 000000000..4cfc523a0 --- /dev/null +++ b/src/OneScript.Native/Runtime/DynamicOperations.cs @@ -0,0 +1,237 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; + +[assembly: InternalsVisibleTo("OneScript.Dynamic.Tests")] + +namespace OneScript.Native.Runtime +{ + internal static class DynamicOperations + { + 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); + } + + var dLeft = (decimal)left; + var dRight = (decimal)right; + return BslNumericValue.Create(dLeft + dRight); + } + + public static BslValue Subtract(BslValue left, BslValue right) + { + if (left is BslNumericValue num) + { + 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); + } + + 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(); + } + + // FIXME: тут не должно быть Null, но из-за несовершенства мира они тут бывают. Когда задолбает - надо починить и убрать отсюда проверки на null + public static bool Equality(BslValue left, BslValue right) + { + if (ReferenceEquals(left, right)) + return true; + + if (left == null || right == null) + return false; + + return left.Equals(right); + } + + // FIXME: тут не должно быть Null, но из-за несовершенства мира они тут бывают. Когда задолбает - надо починить и убрать отсюда проверки на null + public static int Comparison(BslValue left, BslValue right) + { + if (left == null && right == null) + return 0; + + if (left != null) + return left.CompareTo(right); + + return BslUndefinedValue.Instance.CompareTo(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)}")) + }; + } + + public static BslValue ConstructorCall(ITypeManager typeManager, IServiceContainer services, string typeName, IBslProcess process, BslValue[] args) + { + var type = typeManager.GetTypeByName(typeName); + var factory = typeManager.GetFactoryFor(type); + var context = new TypeActivationContext + { + TypeManager = typeManager, + Services = services, + TypeName = type.Name, + CurrentProcess = process + }; + + 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) + where T : BslValue + { + return (T) ConstructorCall(typeManager, services, typeName, process, args); + } + + public static BslObjectValue GetExceptionInfo(IExceptionInfoFactory factory, Exception e) + { + return factory.GetExceptionInfo(e); + } + + public static BslTypeValue GetTypeByName(ITypeManager manager, string name) + { + var foundType = manager.GetTypeByName(name); + return new BslTypeValue(foundType); + } + + public static BslValue GetIndexedValue(object target, BslValue index) + { + if (!(target is IRuntimeContextInstance context) || !context.IsIndexed) + { + throw RuntimeException.IndexedAccessIsNotSupportedException(); + } + + return (BslValue)context.GetIndexedValue((IValue)index); + } + + public static void SetIndexedValue(object target, BslValue index, BslValue value) + { + if (!(target is IRuntimeContextInstance context) || !context.IsIndexed) + { + throw RuntimeException.IndexedAccessIsNotSupportedException(); + } + + context.SetIndexedValue(index, value); + } + + public static BslValue GetPropertyValue(object target, string propertyName) + { + if (!(target is IRuntimeContextInstance context)) + throw BslExceptions.ValueIsNotObjectException(); + + var propIndex = context.GetPropertyNumber(propertyName); + return (BslValue)context.GetPropValue(propIndex); + } + + public static BslValue TryCallContextMethod(BslValue instance, string methodName, IBslProcess process, BslValue[] arguments) + { + if (!(instance is IRuntimeContextInstance context)) + throw BslExceptions.ValueIsNotObjectException(); + + return CallContextMethod(context, methodName, process, arguments); + } + + public static BslValue CallContextMethod(IRuntimeContextInstance instance, string methodName, IBslProcess process, BslValue[] arguments) + { + var idx = instance.GetMethodNumber(methodName); + + var parameters = instance.GetMethodInfo(idx).GetParameters(); + + if (arguments.Length > parameters.Length) + throw RuntimeException.TooManyArgumentsPassed(); + + var valueArgs = new IValue[parameters.Length]; + for (int i = 0; i < valueArgs.Length; i++) + { + if (i < arguments.Length) + valueArgs[i] = arguments[i]; + else + valueArgs[i] = BslSkippedParameterValue.Instance; + } + + instance.CallAsFunction(idx, valueArgs, out var result, process); + return (BslValue)result; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Native/Runtime/NativeClassInstanceWrapper.cs b/src/OneScript.Native/Runtime/NativeClassInstanceWrapper.cs new file mode 100644 index 000000000..4a69fef6e --- /dev/null +++ b/src/OneScript.Native/Runtime/NativeClassInstanceWrapper.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Contexts; + +namespace OneScript.Native.Runtime +{ + /// + /// Этот класс можно удалить, заменив все обращения к нему на IAttachableContext + /// + [Obsolete("This class can be removed, replace all usages with IAttachableContext")] + internal class NativeClassInstanceWrapper + { + public IAttachableContext Context { get; set; } + } +} diff --git a/src/OneScript.Native/Runtime/NativeExecutorProvider.cs b/src/OneScript.Native/Runtime/NativeExecutorProvider.cs new file mode 100644 index 000000000..b648ab82a --- /dev/null +++ b/src/OneScript.Native/Runtime/NativeExecutorProvider.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Globalization; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Native.Compiler; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Native.Runtime +{ + public class NativeExecutorProvider : IExecutorProvider + { + public Type SupportedModuleType => typeof(DynamicModule); + + public Invoker GetInvokeDelegate() + { + return Executor; + } + + private static BslValue Executor( + IBslProcess process, + BslObjectValue target, + IExecutableModule module, + BslScriptMethodInfo method, + IValue[] arguments) + { + if (!(module is DynamicModule)) + { + throw new InvalidOperationException(); + } + + if (!(target is IAttachableContext context)) + { + throw new InvalidOperationException(); + } + + if (!(method is BslNativeMethodInfo nativeMethod)) + { + throw new InvalidOperationException(); + } + + var methodArguments = new object[arguments.Length + 1]; + methodArguments[0] = process; + Array.Copy(arguments, 0, methodArguments, 1, arguments.Length); + + return (BslValue) nativeMethod.Invoke(context, BindingFlags.Default, null, methodArguments, CultureInfo.CurrentCulture); + + } + } +} diff --git a/src/OneScript.StandardLibrary/Binary/BackingTemporaryFile.cs b/src/OneScript.StandardLibrary/Binary/BackingTemporaryFile.cs new file mode 100644 index 000000000..0097d627e --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/BackingTemporaryFile.cs @@ -0,0 +1,75 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using Microsoft.Win32.SafeHandles; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// Обертка над временным файлом, хранящим двоичные данные + /// + internal sealed class BackingTemporaryFile : IDisposable + { + // Открытый хэндл, удерживающий файл от удаления. + private SafeFileHandle _holder; + + private long _length; + + // Стримы чтения не используют хэндл, а открываются по пути, + // т.к. при закрытии стрим закроет хэндл, кроме того, у хэндла есть позиция + // и стрим будет начат с этой позиции. + private string _backingFilePath; + + public BackingTemporaryFile(string path) + { + using var source = new FileStream(path, FileMode.Open, FileAccess.Read); + + Init(source); + } + + public BackingTemporaryFile(Stream source) + { + if (!source.CanSeek) + throw new ArgumentException("Stream must support seek"); + + var pos = source.Position; + Init(source); + source.Position = pos; + } + + public void Dispose() + { + // Благодаря DeleteOnClose, если других дескрипторов не осталось, + // файл будет удален силами ОС. + _holder?.Dispose(); + _holder = null; + _backingFilePath = null; + } + + public long Size() => _length; + + public Stream OpenReadStream() + { + return new FileStream(_backingFilePath, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete); + } + + private void Init(Stream source) + { + _length = source.Length; + _backingFilePath = Path.GetTempFileName(); + + using (var fs = new FileStream(_backingFilePath, FileMode.Create)) + { + source.CopyTo(fs); + } + + _holder = File.OpenHandle(_backingFilePath, options: FileOptions.DeleteOnClose | FileOptions.SequentialScan); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataBuffer.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataBuffer.cs similarity index 81% rename from src/ScriptEngine.HostedScript/Library/Binary/BinaryDataBuffer.cs rename to src/OneScript.StandardLibrary/Binary/BinaryDataBuffer.cs index f11bf92f3..1509059fc 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataBuffer.cs +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataBuffer.cs @@ -8,11 +8,16 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; using System.Linq; - +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; +using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Binary +namespace OneScript.StandardLibrary.Binary { /// /// @@ -24,23 +29,17 @@ namespace ScriptEngine.HostedScript.Library.Binary /// Порядок байтов, заданный для объекта ЧтениеДанных, будет использован во всех объектах, полученных на его основании. /// [ContextClass("БуферДвоичныхДанных", "BinaryDataBuffer")] - public class BinaryDataBuffer : AutoContext, ICollectionContext + public class BinaryDataBuffer : AutoCollectionContext { - private bool _readOnly; - private readonly byte[] _buffer; - public BinaryDataBuffer(byte[] buffer, ByteOrderEnum byteOrder = ByteOrderEnum.LittleEndian) { - _buffer = buffer; + Bytes = buffer; ByteOrder = byteOrder; } // для операций с содержимым буфера внутри 1Script // - public byte[] Bytes - { - get { return _buffer; } - } + public byte[] Bytes { get; } /// /// Размер буфера в байтах. @@ -49,12 +48,12 @@ public byte[] Bytes /// Значение по умолчанию: LittleEndian. /// [ScriptConstructor] - public static BinaryDataBuffer Constructor(IValue size, IValue byteOrder = null) + public static BinaryDataBuffer Constructor(int size, ByteOrderEnum? byteOrder = null) { - var orderValue = byteOrder == null ? ByteOrderEnum.LittleEndian : ContextValuesMarshaller.ConvertParam(byteOrder); + var orderValue = byteOrder ?? ByteOrderEnum.LittleEndian; return new BinaryDataBuffer( - new byte[ContextValuesMarshaller.ConvertParam(size)], + new byte[size], orderValue); } @@ -72,7 +71,7 @@ public override void SetIndexedValue(IValue index, IValue val) throw RuntimeException.InvalidArgumentValue(); var idx = (int)index.AsNumber(); - _buffer[idx] = (byte) value; + Bytes[idx] = (byte) value; } /// @@ -89,7 +88,7 @@ public override void SetIndexedValue(IValue index, IValue val) /// /// Число (Number) [ContextProperty("Размер", "Size")] - public long Size => _buffer.LongLength; + public long Size => Bytes.LongLength; /// /// @@ -97,11 +96,7 @@ public override void SetIndexedValue(IValue index, IValue val) /// /// Булево (Boolean) [ContextProperty("ТолькоЧтение", "ReadOnly")] - public bool ReadOnly - { - get { return _readOnly; } - - } + public bool ReadOnly { get; private set; } /// /// @@ -121,19 +116,19 @@ public void Write(int position, BinaryDataBuffer bytes, int number = 0) ThrowIfReadonly(); if (number == 0) - Array.Copy(bytes._buffer, 0, _buffer, position, bytes._buffer.Length); + Array.Copy(bytes.Bytes, 0, Bytes, position, bytes.Bytes.Length); else - Array.Copy(bytes._buffer, 0, _buffer, position, number); + Array.Copy(bytes.Bytes, 0, Bytes, position, number); } - private byte[] GetBytes(T value, Converter leConverter, Converter beConverter, IValue byteOrder = null) + private byte[] GetBytes(T value, Converter leConverter, Converter beConverter, BslValue byteOrder = null) { ByteOrderEnum workByteOrder; if (byteOrder == null) workByteOrder = ByteOrder; else { - var enumVal = byteOrder.GetRawValue() as IObjectWrapper; + var enumVal = byteOrder as IObjectWrapper; if (enumVal == null) throw RuntimeException.InvalidArgumentType(nameof(byteOrder)); @@ -155,13 +150,13 @@ private void CopyBytes(int position, byte[] bytes) { for (int i = 0; i < bytes.Length; i++) { - _buffer[position + i] = bytes[i]; + Bytes[position + i] = bytes[i]; } } private static ulong AsUnsignedLong( IValue value, ulong maxValue=ulong.MaxValue ) { - if (value.DataType != DataType.Number) + if (value.SystemType != BasicTypes.Number) throw RuntimeException.InvalidArgumentType(2,nameof(value)); var number = value.AsNumber(); @@ -188,7 +183,7 @@ private static ulong AsUnsignedLong( IValue value, ulong maxValue=ulong.MaxValue /// /// [ContextMethod("ЗаписатьЦелое16", "WriteInt16")] - public void WriteInt16(int position, IValue value, IValue byteOrder = null) + public void WriteInt16(int position, BslValue value, BslValue byteOrder = null) { ThrowIfReadonly(); @@ -211,7 +206,7 @@ public void WriteInt16(int position, IValue value, IValue byteOrder = null) /// Порядок байтов, который будет использован для кодировки числа при записи в буфер. Если не установлен, то будет использован порядок байтов, заданный для текущего экземпляра БуферДвоичныхДанных. /// Значение по умолчанию: Неопределено. [ContextMethod("ЗаписатьЦелое32", "WriteInt32")] - public void WriteInt32(int position, IValue value, IValue byteOrder = null) + public void WriteInt32(int position, BslValue value, BslValue byteOrder = null) { ThrowIfReadonly(); @@ -239,7 +234,7 @@ public void WriteInt32(int position, IValue value, IValue byteOrder = null) /// [ContextMethod("ЗаписатьЦелое64", "WriteInt64")] - public void WriteInt64(int position, IValue value, IValue byteOrder = null) + public void WriteInt64(int position, BslValue value, BslValue byteOrder = null) { ThrowIfReadonly(); @@ -251,13 +246,13 @@ public void WriteInt64(int position, IValue value, IValue byteOrder = null) private void WriteBitwiseOp(int position, BinaryDataBuffer buffer, int number, Func op) { if(position < 0) - throw new IndexOutOfRangeException("Значение индекса выходит за границы диапазона"); + throw RuntimeException.IndexOutOfRange(); try { - var bytesToCopy = (number == 0 ? buffer._buffer.Length : number); + var bytesToCopy = (number == 0 ? buffer.Bytes.Length : number); for (int i = 0; i < bytesToCopy; i++) - _buffer[i + position] = op(_buffer[i + position], buffer._buffer[i]); + Bytes[i + position] = op(Bytes[i + position], buffer.Bytes[i]); } catch (IndexOutOfRangeException) { @@ -360,7 +355,7 @@ public void WriteBitwiseXor(int position, BinaryDataBuffer bytes, int number = 0 [ContextMethod("Перевернуть", "Reverse")] public BinaryDataBuffer Reverse() { - var bytes = _buffer.Reverse().ToArray(); + var bytes = Bytes.Reverse().ToArray(); return new BinaryDataBuffer(bytes, ByteOrder); } @@ -381,7 +376,7 @@ public BinaryDataBuffer Reverse() [ContextMethod("Получить", "Get")] public int Get(int position) { - return _buffer[position]; + return Bytes[position]; } @@ -424,18 +419,18 @@ public BinaryDataBuffer GetSlice(int position, IValue number = null) public BinaryDataBuffer Read(int position, int number) { var data = new byte[number]; - Array.Copy(_buffer, position, data, 0, number); + Array.Copy(Bytes, position, data, 0, number); return new BinaryDataBuffer(data, ByteOrder); } - private T FromBytes(int position, Func leConverter, Func beConverter, IValue byteOrder = null) + private T FromBytes(int position, Func leConverter, Func beConverter, BslValue byteOrder = null) { ByteOrderEnum workByteOrder; if (byteOrder == null) workByteOrder = ByteOrder; else { - var enumVal = byteOrder.GetRawValue() as IObjectWrapper; + var enumVal = byteOrder as IObjectWrapper; if (enumVal == null) throw RuntimeException.InvalidArgumentType(nameof(byteOrder)); @@ -450,7 +445,7 @@ private T FromBytes(int position, Func leConverter, Func @@ -468,7 +463,7 @@ private T FromBytes(int position, Func leConverter, Func /// [ContextMethod("ПрочитатьЦелое16", "ReadInt16")] - public int ReadInt16(int position, IValue byteOrder = null) + public int ReadInt16(int position, BslValue byteOrder = null) { return FromBytes(position, BitConversionFacility.LittleEndian.ToInt16, BitConversionFacility.BigEndian.ToInt16, byteOrder); } @@ -491,7 +486,7 @@ public int ReadInt16(int position, IValue byteOrder = null) /// [ContextMethod("ПрочитатьЦелое32", "ReadInt32")] - public uint ReadInt32(int position, IValue byteOrder = null) + public uint ReadInt32(int position, BslValue byteOrder = null) { return FromBytes(position, BitConversionFacility.LittleEndian.ToUInt32, BitConversionFacility.BigEndian.ToUInt32, byteOrder); } @@ -514,7 +509,7 @@ public uint ReadInt32(int position, IValue byteOrder = null) /// [ContextMethod("ПрочитатьЦелое64", "ReadInt64")] - public ulong ReadInt64(int position, IValue byteOrder = null) + public ulong ReadInt64(int position, BslValue byteOrder = null) { return FromBytes(position, BitConversionFacility.LittleEndian.ToUInt64, BitConversionFacility.BigEndian.ToUInt64, byteOrder); } @@ -537,11 +532,112 @@ public ulong ReadInt64(int position, IValue byteOrder = null) /// /// [ContextMethod("Разделить", "Split")] - public IValue Split(IValue separator) + public ArrayImpl Split(BslValue separator) { - throw new NotImplementedException(); + var buffers = ParseParam(separator); + + // Функция поиска требует, чтобы буферы были в порядке убывания размера + buffers.Sort((a, b) => b.Bytes.LongLength.CompareTo(a.Bytes.LongLength)); + return SplitBuffer(buffers.ToArray()); + } + + private static List ParseParam(BslValue separator) + { + switch (separator) + { + case BinaryDataBuffer buffer: + return new List { CheckedBuffer(buffer) }; + + case ArrayImpl array: + { + var buffers = new List(); + + foreach (var element in array) + { + buffers.AddRange(ParseParam((BslValue)element)); + } + + return buffers; + } + + default: + throw RuntimeException.InvalidArgumentType(); + } + } + + private static BinaryDataBuffer CheckedBuffer(BinaryDataBuffer buffer) + { + if (buffer.Size == 0) + { + throw RuntimeException.InvalidArgumentValue(); + } + + return buffer; + } + + private ArrayImpl SplitBuffer(BinaryDataBuffer[] splitter) + { + var result = new List(); + long start = 0; + var foundPosition = FindFirst(splitter, start); + while (foundPosition.pos != -1) + { + var length = foundPosition.pos - start; + result.Add(new BinaryDataBuffer(Copy(start, length), ByteOrder)); + start = foundPosition.pos + foundPosition.buffer.Size; + foundPosition = FindFirst(splitter, start); + } + + // хвостовой элемент + result.Add(new BinaryDataBuffer(Copy(start, Bytes.LongLength - start))); + return new ArrayImpl(result); } + /// + /// Ищет ближайшее вхождение любого из буферов. Если на одной позиции находятся два и более буфера, берется бОльший. + /// + /// Массив искомых буферов + /// Начальная позиция поиска + /// Буфер и позиция или null, если нет вхождений + private (BinaryDataBuffer buffer, long pos) FindFirst(BinaryDataBuffer[] buffers, long start) + { + var maxI = Size - buffers[buffers.Length - 1].Size; + for (var i = start; i < maxI; i++) + { + foreach (var expectedBuffer in buffers) + { + if (SubsequenceEquals(Bytes, i, expectedBuffer.Bytes)) + { + return (expectedBuffer, i); + } + } + } + + return (null, -1); + } + + private byte[] Copy(long start, long length) + { + if (length == 0) return Array.Empty(); + var partition = new byte[length]; + Array.Copy(Bytes, start, partition, 0, length); + return partition; + } + + private static bool SubsequenceEquals(byte[] sequence, long start, byte[] subsequence) + { + for (long j = 0; j < subsequence.LongLength; j++) + { + if (subsequence[j] != sequence[start + j]) + { + return false; + } + } + + return true; + } + + /// /// /// Создает копию массива. @@ -553,8 +649,8 @@ public IValue Split(IValue separator) [ContextMethod("Скопировать", "Copy")] public BinaryDataBuffer Copy() { - byte[] copy = new byte[_buffer.Length]; - Array.Copy(_buffer, copy, _buffer.Length); + byte[] copy = new byte[Bytes.Length]; + Array.Copy(Bytes, copy, Bytes.Length); return new BinaryDataBuffer(copy, ByteOrder); } @@ -572,11 +668,11 @@ public BinaryDataBuffer Copy() [ContextMethod("Соединить", "Concat")] public BinaryDataBuffer Concat(BinaryDataBuffer buffer) { - var source = buffer._buffer; - var totalLength = _buffer.Length + source.Length; + var source = buffer.Bytes; + var totalLength = Bytes.Length + source.Length; var joinedArray = new byte[totalLength]; - Array.Copy(_buffer, joinedArray, _buffer.Length); - Array.Copy(source, 0, joinedArray, _buffer.Length, source.Length); + Array.Copy(Bytes, joinedArray, Bytes.Length); + Array.Copy(source, 0, joinedArray, Bytes.Length, source.Length); return new BinaryDataBuffer(joinedArray, ByteOrder); } @@ -597,7 +693,7 @@ public void Set(int position, IValue value) { ThrowIfReadonly(); - _buffer[position] = (byte)AsUnsignedLong(value, byte.MaxValue); + Bytes[position] = (byte)AsUnsignedLong(value, byte.MaxValue); } @@ -614,30 +710,25 @@ public void Set(int position, IValue value) [ContextMethod("УстановитьТолькоЧтение", "SetReadOnly")] public void SetReadOnly() { - _readOnly = true; - } - - public int Count() - { - return _buffer.Length; + ReadOnly = true; } - public CollectionEnumerator GetManagedIterator() + public override int Count() { - return new CollectionEnumerator(GetEnumerator()); + return Bytes.Length; } - private IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { - for (long i = 0; i < _buffer.LongLength; i++) + for (long i = 0; i < Bytes.LongLength; i++) { - yield return ValueFactory.Create(_buffer[i]); + yield return ValueFactory.Create(Bytes[i]); } } public void ThrowIfReadonly() { - if (_readOnly) + if (ReadOnly) throw new RuntimeException("Буфер находится в режиме \"Только чтение\""); } } diff --git a/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs new file mode 100644 index 000000000..e56d15ed3 --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs @@ -0,0 +1,263 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Binary +{ + [ContextClass("ДвоичныеДанные", "BinaryData")] + public sealed class BinaryDataContext : AutoContext, IDisposable + { + private byte[] _buffer; + private BackingTemporaryFile _backingFile; + + public BinaryDataContext(string filename) + { + using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read); + ReadFromStream(fs); + } + + public BinaryDataContext(byte[] buffer) + { + _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } + + public BinaryDataContext(Stream stream) + { + long pos = 0; + ReadFromStream(stream); + stream.Position = pos; + } + + /// + /// Признак хранения данных в памяти + /// + public bool InMemory => _backingFile == null; + + private void ReadFromStream(Stream stream) + { + if (stream.Length < FileBackingConstants.DEFAULT_MEMORY_LIMIT) + { + LoadToBuffer(stream); + } + else + { + _buffer = null; + _backingFile = new BackingTemporaryFile(stream); + } + } + + public void Dispose() + { + _backingFile?.Dispose(); + } + + private void LoadToBuffer(Stream fs) + { + _buffer = new byte[fs.Length]; + // ReSharper disable once MustUseReturnValue + fs.Read(_buffer, 0, _buffer.Length); + } + + /// + /// Размер двоичных данных в байтах. + /// + [ContextMethod("Размер", "Size")] + public long Size() + { + return _backingFile?.Size() ?? _buffer.Length; + } + + /// + /// Сохранить содержимое двоичных данных в файл или другой поток + /// + /// путь к файлу или Поток + [ContextMethod("Записать", "Write")] + public void Write(IValue filenameOrStream) + { + if(filenameOrStream.SystemType == BasicTypes.String) + { + var filename = filenameOrStream.ToString()!; + + using(var fs = new FileStream(filename, FileMode.Create, FileAccess.Write)) + { + CopyTo(fs); + } + } + else if (filenameOrStream.AsObject() is IStreamWrapper stream) + { + CopyTo(stream.GetUnderlyingStream()); + } + else + { + throw RuntimeException.InvalidArgumentType("filenameOrStream"); + } + } + + public void CopyTo(Stream stream) + { + if (InMemory) + { + stream.Write(_buffer, 0, _buffer.Length); + } + else + { + using var source = _backingFile.OpenReadStream(); + source.CopyTo(stream); + } + } + + public Stream GetStream() + { + return InMemory ? new MemoryStream(_buffer, 0, _buffer.Length, false, true) : _backingFile.OpenReadStream(); + } + + public byte[] Buffer + { + get + { + if (!InMemory) + { + using var readStream = _backingFile.OpenReadStream(); + LoadToBuffer(readStream); + _backingFile.Dispose(); + _backingFile = null; + } + + return _buffer; + } + } + + public override string ToString() + { + const int LIMIT = 64; + int length; + byte[] buffer; + + if (InMemory) + { + length = Math.Min(_buffer.Length, LIMIT); + if (length == 0) + return ""; + + buffer = _buffer; + } + else + { + length = (int)Math.Min(_backingFile.Size(), LIMIT); + if (length == 0) + return ""; + + buffer = new byte[length]; + using var readStream = _backingFile.OpenReadStream(); + // ReSharper disable once MustUseReturnValue + readStream.Read(buffer, 0, length); + } + + StringBuilder hex = new StringBuilder(length*3); + hex.AppendFormat("{0:X2}", buffer[0]); + for (int i = 1; i < length; ++i) + { + hex.AppendFormat(" {0:X2}", buffer[i]); + } + + if (Size() > LIMIT) + hex.Append('…'); + + return hex.ToString(); + } + + /// + /// + /// Открывает поток для чтения двоичных данных. + /// + /// + /// + /// + /// Представляет собой поток данных, который можно последовательно читать и/или в который можно последовательно писать. + /// Экземпляры объектов данного типа можно получить с помощью различных методов других объектов. + /// + [ContextMethod("ОткрытьПотокДляЧтения", "OpenStreamForRead")] + public GenericStream OpenStreamForRead() + { + return new GenericStream(GetStream(), true); + } + + [ScriptConstructor(Name = "На основании файла")] + public static BinaryDataContext Constructor(string filename) + { + return new BinaryDataContext(filename); + } + + public override bool Equals(BslValue other) + { + if (other == null) + return false; + + if (other.SystemType == SystemType) + { + var binData = other as BinaryDataContext; + Debug.Assert(binData != null); + + if (InMemory && binData.InMemory) + { + return ArraysAreEqual(_buffer, binData._buffer); + } + else + { + using var s1 = GetStream(); + using var s2 = binData.GetStream(); + + return StreamsAreEqual(s1, s2); + } + } + + return false; + } + + private static bool ArraysAreEqual(byte[] a1, byte[] a2) + { + if (a1.LongLength == a2.LongLength) + { + for (long i = 0; i < a1.LongLength; i++) + { + if (a1[i] != a2[i]) + { + return false; + } + } + return true; + } + return false; + } + + private static bool StreamsAreEqual(Stream s1, Stream s2) + { + if (s1.Length == s2.Length) + { + for (long i = 0; i < s1.Length; i++) + { + if (s1.ReadByte() != s2.ReadByte()) + { + return false; + } + } + return true; + } + return false; + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/BitConversionFacility.cs b/src/OneScript.StandardLibrary/Binary/BitConversionFacility.cs similarity index 93% rename from src/ScriptEngine.HostedScript/Library/Binary/BitConversionFacility.cs rename to src/OneScript.StandardLibrary/Binary/BitConversionFacility.cs index a33f31121..0d2e76816 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/BitConversionFacility.cs +++ b/src/OneScript.StandardLibrary/Binary/BitConversionFacility.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.Binary +namespace OneScript.StandardLibrary.Binary { public static class BitConversionFacility { diff --git a/src/ScriptEngine.HostedScript/Library/Binary/DataReader.cs b/src/OneScript.StandardLibrary/Binary/DataReader.cs similarity index 93% rename from src/ScriptEngine.HostedScript/Library/Binary/DataReader.cs rename to src/OneScript.StandardLibrary/Binary/DataReader.cs index 83cdf857b..d51b7c4f5 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/DataReader.cs +++ b/src/OneScript.StandardLibrary/Binary/DataReader.cs @@ -8,11 +8,16 @@ This Source Code Form is subject to the terms of the using System; using System.IO; using System.Text; - +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Text; +using OneScript.Types; +using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Binary +namespace OneScript.StandardLibrary.Binary { /// /// @@ -66,24 +71,23 @@ private DataReader(Stream stream, IValue textEncoding, ByteOrderEnum? byteOrder, [ScriptConstructor(Name = "На основании двоичных данных или имени файла")] public static DataReader Constructor(IValue dataSource, IValue textEncoding = null, ByteOrderEnum? byteOrder = null, string lineSplitter = "\n", string convertibleSplitterOfLines = null) { - if (dataSource.DataType == DataType.String) + Stream stream; + if (dataSource.SystemType == BasicTypes.String) { - var stream = new FileStream(dataSource.AsString(), FileMode.Open, FileAccess.Read, FileShare.Read); - return new DataReader(stream, textEncoding, byteOrder, lineSplitter, convertibleSplitterOfLines); + stream = new FileStream(dataSource.ToString()!, FileMode.Open, FileAccess.Read, FileShare.Read); } else { - var obj = dataSource.AsObject(); - Stream stream; - if (obj is BinaryDataContext) - stream = new MemoryStream(((BinaryDataContext)obj).Buffer); - else if (obj is IStreamWrapper) - stream = ((IStreamWrapper) obj).GetUnderlyingStream(); - else - throw RuntimeException.InvalidArgumentType("dataSource"); + stream = dataSource.AsObject() switch + { + BinaryDataContext binaryData => binaryData.GetStream(), + IStreamWrapper wrapper => wrapper.GetUnderlyingStream(), - return new DataReader(stream, textEncoding, byteOrder, lineSplitter, convertibleSplitterOfLines); + _ => throw RuntimeException.InvalidArgumentType("dataSource") + }; } + + return new DataReader(stream, textEncoding, byteOrder, lineSplitter, convertibleSplitterOfLines); } /// @@ -185,8 +189,16 @@ public IValue SourceStream() /// /// [ContextMethod("Пропустить", "Skip")] - public long Skip(long number) + public long Skip(IValue value) { + if (value.SystemType != BasicTypes.Number) + throw RuntimeException.InvalidArgumentType(); + + long number = (long)value.AsNumber(); + + if (number < 0 || number != value.AsNumber()) + throw RuntimeException.InvalidArgumentValue(); + var stream = _reader.BaseStream; if (stream.CanSeek) { @@ -298,9 +310,12 @@ private MemoryStream ReadAllData() /// /// [ContextMethod("ПрочитатьБайт", "ReadByte")] - public byte ReadByte() + public BslValue ReadByte() { - return _reader.ReadByte(); + if (_reader.BaseStream.Position == _reader.BaseStream.Length) + return BslUndefinedValue.Instance; + + return BslNumericValue.Create(_reader.ReadByte()); } /// @@ -331,7 +346,7 @@ public IValue ReadIntoBinaryDataBuffer(IValue buffer=null, int positionInBuffer { return new BinaryDataBuffer(ReadSomeBytes(0).ToArray()); } - else if (buffer.DataType == DataType.Number) + else if (buffer.SystemType == BasicTypes.Number) { var stream = ReadSomeBytes((int)buffer.AsNumber()); return new BinaryDataBuffer(stream.ToArray()); @@ -426,15 +441,14 @@ public string ReadLine(IValue encoding = null, string lineSplitter = "\n") return textRdr.ReadLine(lineSplitter ?? ConvertibleSplitterOfLines); } - private T FromBytes(byte[] bytes, Func leConverter, Func beConverter, IValue byteOrder = null) + private T FromBytes(byte[] bytes, Func leConverter, Func beConverter, BslValue byteOrder = null) { ByteOrderEnum workByteOrder; if (byteOrder == null) workByteOrder = ByteOrder; else { - var enumVal = byteOrder.GetRawValue() as IObjectWrapper; - if (enumVal == null) + if (!(byteOrder is IObjectWrapper enumVal)) throw RuntimeException.InvalidArgumentType(nameof(byteOrder)); try @@ -464,10 +478,10 @@ private T FromBytes(byte[] bytes, Func leConverter, Func /// [ContextMethod("ПрочитатьЦелое16", "ReadInt16")] - public int ReadInt16(IValue byteOrder = null) + public uint ReadInt16(BslValue byteOrder = null) { - var bytes = _reader.ReadBytes(sizeof(short)); - return FromBytes(bytes, BitConversionFacility.LittleEndian.ToInt16, BitConversionFacility.BigEndian.ToInt16, byteOrder); + var bytes = _reader.ReadBytes(sizeof(ushort)); + return FromBytes(bytes, BitConversionFacility.LittleEndian.ToUInt16, BitConversionFacility.BigEndian.ToUInt16, byteOrder); } @@ -485,7 +499,7 @@ public int ReadInt16(IValue byteOrder = null) /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. /// [ContextMethod("ПрочитатьЦелое32", "ReadInt32")] - public uint ReadInt32(IValue byteOrder = null) + public uint ReadInt32(BslValue byteOrder = null) { var bytes = _reader.ReadBytes(sizeof(uint)); return FromBytes(bytes, BitConversionFacility.LittleEndian.ToUInt32, BitConversionFacility.BigEndian.ToUInt32, byteOrder); @@ -505,7 +519,7 @@ public uint ReadInt32(IValue byteOrder = null) /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. /// [ContextMethod("ПрочитатьЦелое64", "ReadInt64")] - public ulong ReadInt64(IValue byteOrder = null) + public ulong ReadInt64(BslValue byteOrder = null) { var bytes = _reader.ReadBytes(sizeof(ulong)); return FromBytes(bytes, BitConversionFacility.LittleEndian.ToUInt64, BitConversionFacility.BigEndian.ToUInt64, byteOrder); diff --git a/src/ScriptEngine.HostedScript/Library/Binary/DataWriter.cs b/src/OneScript.StandardLibrary/Binary/DataWriter.cs similarity index 92% rename from src/ScriptEngine.HostedScript/Library/Binary/DataWriter.cs rename to src/OneScript.StandardLibrary/Binary/DataWriter.cs index cab0f5f99..0bef35027 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/DataWriter.cs +++ b/src/OneScript.StandardLibrary/Binary/DataWriter.cs @@ -1,456 +1,463 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.IO; -using System.Text; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Binary -{ - /// - /// - /// Объект предназначен для удобной записи различных типов данных в приемник. - /// Необходимо соблюдать следующий порядок работы с данным объектом: - /// - /// - Создать или получить приемник даных. - /// - Вызвать из приемника экземпляр объекта ЗаписьДанных. - /// - Выполнить требуемые действия с помощью объекта. - /// - Закрыть экземпляр объекта ЗаписьДанных. - /// При необходимости использовать другие методы для работы с данными, требуется сначала закрыть экземпляр объекта ЗаписьДанных с помощью метода Закрыть, выполнить необходимые действия над данными, установить требуемую позицию для чтения из приемника и создать новый экземпляр ЗаписьДанных. - /// - [ContextClass("ЗаписьДанных", "DataWriter")] - public class DataWriter : AutoContext, IDisposable - { - - private Encoding _workingEncoding; - private IValue _userVisibleEncoding; - private BinaryWriter _binaryWriter; - private readonly bool _writeBOM; - - public DataWriter(string fileName, IValue textEncoding, ByteOrderEnum? byteOrder, string lineSplitter, bool append, string convertibleSplitterOfLines, bool writeBOM) - { - ByteOrder = byteOrder?? ByteOrderEnum.LittleEndian; - LineSplitter = lineSplitter?? "\r\n"; - ConvertibleSplitterOfLines = convertibleSplitterOfLines; - _writeBOM = writeBOM; - TextEncoding = textEncoding; - - var fileSubsystem = new FileStreamsManager(); - var fileStreamContext = append ? fileSubsystem.OpenForAppend(fileName) : fileSubsystem.OpenForWrite(fileName); - - _binaryWriter = new BinaryWriter(fileStreamContext.GetUnderlyingStream(), _workingEncoding); - } - - public DataWriter(IStreamWrapper streamObj, IValue textEncoding, ByteOrderEnum? byteOrder, string lineSplitter, string convertibleSplitterOfLines, bool writeBOM) - { - ByteOrder = byteOrder?? ByteOrderEnum.LittleEndian; - LineSplitter = lineSplitter ?? "\r\n"; - ConvertibleSplitterOfLines = convertibleSplitterOfLines; - _writeBOM = writeBOM; - TextEncoding = textEncoding; - - _binaryWriter = new BinaryWriter(streamObj.GetUnderlyingStream(), _workingEncoding, true); - } - - /// - /// - /// Создает объект ЗаписьДанных для записи в указанный файл. Если файл с таким именем не существует, он будет создан. Параметр <Дописать> определяет, будут ли данные записаны в начало или в конец файла. - /// После завершения работы с объектом, до закрытия потока, переданного в конструктор, объект необходимо закрыть с помощью метода Закрыть или НачатьЗакрытие. При этом используемый файл будет закрыт автоматически. - /// - /// - /// - /// Имя файла или поток, в который будет выполнена запись. - /// - /// Кодировка текста для создаваемого экземпляра ЗаписьДанных. Если не задана, то используется UTF-8. - /// Значение по умолчанию: UTF8. Типы: КодировкаТекста (TextEncoding), Строка (String) - /// - /// Порядок байтов, используемый по умолчанию для кодирования целых чисел при записи в поток. - /// Значение по умолчанию: LittleEndian. - /// - /// Разделитель по умолчанию для строк, записываемых в поток. Если разделитель строк не задан, то используется строка ПС. - /// Значение по умолчанию: ПС. - /// - /// Для файла: - /// Определяет, будут ли данные записаны в начало или в конец файла: - /// - Если Истина, то при открытии существующего файла запись будет выполнена в конец файла. - /// - Иначе данные будут записываться с начала файла, перезаписывая существующие данные. - /// Если заданный файл не существует, будет создан новый файл с указанным именем и значение параметра не повлияет на поведение конструктора. - /// Значение по умолчанию: Ложь. - /// Для потока: - /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС. - /// Значение по умолчанию: ВК + ПС. - /// - /// Для файла: - /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС. - /// Значение по умолчанию: ВК + ПС. - /// Для потока: - /// Если в начало файла или потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста, то данный параметр должен иметь значение Истина. - /// Значение по умолчанию: Ложь. - /// - /// Только для файла: - /// Если в начало файла требуется записать метку порядка байтов (BOM) для используемой кодировки текста, то данный параметр должен иметь значение Истина. - /// Значение по умолчанию: Ложь. - /// - [ScriptConstructor] - public static DataWriter Constructor(IValue file_stream, IValue textEncoding = null, IValue byteOrder = null, IValue lineSplitter = null, IValue param5 = null, IValue param6 = null, IValue param7 = null) - { - if (file_stream.DataType == DataType.String) - return new DataWriter(file_stream.AsString(), textEncoding, - ContextValuesMarshaller.ConvertParam(byteOrder,null), - ContextValuesMarshaller.ConvertParam(lineSplitter), - ContextValuesMarshaller.ConvertParam(param5), - ContextValuesMarshaller.ConvertParam(param6), - ContextValuesMarshaller.ConvertParam(param7)); - else - return ConstructorByStream(file_stream, textEncoding, - ContextValuesMarshaller.ConvertParam(byteOrder,null), - ContextValuesMarshaller.ConvertParam(lineSplitter), - ContextValuesMarshaller.ConvertParam(param5), - ContextValuesMarshaller.ConvertParam(param6)); - } - - /// - /// - /// Объект создается для записи в заданный поток. - /// После завершения работы с объектом, до закрытия потока, переданного в конструктор, объект необходимо закрыть с помощью метода Закрыть или НачатьЗакрытие. - /// - /// - /// - /// Поток, в который производится запись данных. Типы: Поток (Stream), ПотокВПамяти (MemoryStream), ФайловыйПоток (FileStream) - /// - /// Устанавливает кодировку текста для создаваемого экземпляра ЗаписьДанных. Если не задано, используется кодировка UTF-8. - /// Значение по умолчанию: UTF8. Типы: КодировкаТекста (TextEncoding), Строка (String) - /// - /// Порядок байтов, используемый по умолчанию для кодирования целых чисел при записи в поток. - /// Значение по умолчанию: LittleEndian. - /// - /// Разделитель по умолчанию для строк, записываемых в поток. Если разделитель строк не задан, то используется строка ПС. - /// Значение по умолчанию: ПС. - /// - /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС. - /// Значение по умолчанию: ВК + ПС. - /// - /// Если в начало файла или потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста, то данный параметр должен иметь значение Истина. - /// Значение по умолчанию: Ложь. - /// - //[ScriptConstructor(Name = "На основании потока")] - public static DataWriter ConstructorByStream(IValue stream, IValue textEncoding = null, ByteOrderEnum? byteOrder = null, string lineSplitter = null, string convertibleSplitterOfLines = null, bool writeBOM = false) - { - var streamObj = stream.AsObject() as IStreamWrapper; - if (streamObj == null) - { - throw RuntimeException.InvalidArgumentType(nameof(stream)); - } - - return new DataWriter(streamObj, textEncoding, byteOrder, lineSplitter, convertibleSplitterOfLines, writeBOM); - } - - /// - /// - /// Кодировка текста по-умолчанию для данного экземпляра ЗаписьДанных. - /// Кодировка может быть задана как в виде значения перечисления КодировкаТекста, так и в виде строки с указанием названия кодировки. - /// - /// КодировкаТекста (TextEncoding), Строка (String) - [ContextProperty("КодировкаТекста", "TextEncoding")] - public IValue TextEncoding - { - get { return _userVisibleEncoding; } - set - { - if (value != null) - { - _workingEncoding = TextEncodingEnum.GetEncoding(value, _writeBOM); - _userVisibleEncoding = value; - } - else - { - _workingEncoding = new UTF8Encoding(true); - _userVisibleEncoding = ValueFactory.Create("utf-8"); - } - } - } - - /// - /// - /// Конвертируемый разделитель строк. Этот параметр влияет на поведение метода ЗаписатьСимволы. - /// - /// Строка (String) - [ContextProperty("КонвертируемыйРазделительСтрок", "ConvertibleSplitterOfLines")] - public string ConvertibleSplitterOfLines { get; set; } - - /// - /// - /// Порядок байтов по умолчанию. - /// - /// ПорядокБайтов (ByteOrder) - [ContextProperty("ПорядокБайтов", "ByteOrder")] - public ByteOrderEnum ByteOrder { get; private set; } - - - /// - /// - /// Разделитель строк по-умолчанию. Это свойство влияет на поведение метода ЗаписатьСтроку. - /// - /// Строка (String) - [ContextProperty("РазделительСтрок", "LineSplitter")] - public string LineSplitter { get; set; } - - /// - /// - /// Вызывает метод СброситьБуферы. Если целевой поток был создан при создании объекта ЗаписьДанных, целевой поток также закрывается. - /// - /// - /// - [ContextMethod("Закрыть", "Close")] - public void Close() - { - _binaryWriter.Close(); - } - - - /// - /// - /// Записывает данные в целевой поток. - /// - /// - /// - /// - /// Запись двоичных данных - /// - /// - /// - /// Записать экземпляр объекта ДвоичныеДанные в поток. - /// - [ContextMethod("Записать", "Write")] - public void Write(IValue binaryDataOrReadResult) - { - var binData = binaryDataOrReadResult.AsObject() as BinaryDataContext; - if (binData == null) //TODO: Поддержкать класс РезультатЧтенияДанных - throw RuntimeException.InvalidArgumentType(); - - _binaryWriter.Write(binData.Buffer, 0, binData.Size()); - } - - /// - /// - /// Записывает один байт в целевой поток. - /// - /// - /// - /// Целое число, которое будет записано в целевой поток. Значение числа должно находиться в диапазоне от 0 до 255. - /// - [ContextMethod("ЗаписатьБайт", "WriteByte")] - public void WriteByte(byte number) - { - _binaryWriter.Write(number); - } - - /// - /// - /// Записать байты из буфера двоичных данных в целевой поток. - /// - /// - /// - /// - /// Запись части байтов из буфера - /// - /// - /// - /// Буфер двоичных данных, который используется в качестве источника данных для записи в целевой поток. - /// - /// Позиция в буфере, начиная с которой выполняется чтение байтов для записи в целевой поток. - /// - /// Количество байтов, которые требуется записать в целевой поток. - /// - [ContextMethod("ЗаписатьБуферДвоичныхДанных", "WriteBinaryDataBuffer")] - public void WriteBinaryDataBuffer(BinaryDataBuffer buffer, int positionInBuffer = 0, int number = 0) - { - if(positionInBuffer == 0 && number == 0) - _binaryWriter.Write(buffer.Bytes, 0, buffer.Count()); - else - _binaryWriter.Write(buffer.Bytes, positionInBuffer, number); - } - - - /// - /// - /// Записывает символы заданной строки в целевой поток. - /// - /// - /// - /// Строка, символы которой будут записаны в поток. - /// - /// Определяет кодировку текста для записи строки. Если не установлена, используется кодировка, заданная для данного объекта ЗаписьДанных. - /// Кодировка может быть задана как в виде значения перечисления КодировкаТекста, так и в виде строки с указанием названия кодировки. - /// - [ContextMethod("ЗаписатьСимволы", "WriteChars")] - public void WriteChars(string line, IValue encoding = null) - { - if(encoding == null) - _binaryWriter.Write(line.ToCharArray()); - else - { - var enc = TextEncodingEnum.GetEncoding(encoding, _writeBOM); - var bytes = enc.GetBytes(line); - _binaryWriter.Write(bytes,0,bytes.Length); - } - } - - - /// - /// - /// Записывает строку в целевой поток. - /// Сначала записываются все символы строки, затем - разделитель строк. - /// - /// - /// - /// Строка, которая будет записана в поток. - /// - /// Определяет кодировку текста для записи строки. Если не установлена, используется кодировка, заданная для данного объекта ЗаписьДанных. - /// Кодировка может быть задана как в виде значения перечисления КодировкаТекста, так и в виде строки с указанием названия кодировки. - /// Значение по умолчанию: Неопределено. Типы: КодировкаТекста (TextEncoding), Строка (String) - /// - /// Указывает строку, являющуюся разделителем строк в потоке после записи символов строк. Если параметр не указан, используется разделитель строк, указанный для текущего экземпляра объекта ЗаписьДанных. - /// Значение по умолчанию: Неопределено. - /// - [ContextMethod("ЗаписатьСтроку", "WriteLine")] - public void WriteLine(string line, IValue encoding = null, string lineSplitter = null) - { - // TODO: Для экономии времени не поддерживаем пока конвертируемый разделитель строк - // Кому надо - попросит PR. - - if (encoding == null) - _binaryWriter.Write(line.ToCharArray()); - else - { - var enc = TextEncodingEnum.GetEncoding(encoding, _writeBOM); - var bytes = enc.GetBytes(line); - _binaryWriter.Write(bytes, 0, bytes.Length); - } - - if(lineSplitter == null) - _binaryWriter.Write(LineSplitter.ToCharArray()); - else - _binaryWriter.Write(lineSplitter.ToCharArray()); - } - - private byte[] GetBytes(T value, Converter leConverter, Converter beConverter, IValue byteOrder = null) - { - ByteOrderEnum workByteOrder; - if (byteOrder == null) - workByteOrder = ByteOrder; - else - { - var enumVal = byteOrder.GetRawValue() as IObjectWrapper; - if (enumVal == null) - throw RuntimeException.InvalidArgumentType(nameof(byteOrder)); - - try - { - workByteOrder = (ByteOrderEnum) enumVal.UnderlyingObject; - } - catch (InvalidCastException) - { - throw RuntimeException.InvalidArgumentType(nameof(byteOrder)); - } - } - - var converter = workByteOrder == ByteOrderEnum.BigEndian ? beConverter : leConverter; - return converter(value); - } - - /// - /// - /// Записывает 16-разрядное число в целевой поток. - /// - /// - /// - /// Число, которое будет записано в целевой поток. - /// Значение числа должно находиться в диапазоне от 0 до 65535. - /// - /// Порядок байтов, который будет использован для кодировки числа при записи. Если не установлен, то будет использован порядок байтов, заданный для текущего экземпляра объекта ЗаписьДанных. - /// Значение по умолчанию: Неопределено. - /// - [ContextMethod("ЗаписатьЦелое16", "WriteInt16")] - public void WriteInt16(short number, IValue byteOrder = null) - { - var buffer = GetBytes(number, BitConversionFacility.LittleEndian.GetBytes, BitConversionFacility.BigEndian.GetBytes, byteOrder); - _binaryWriter.Write(buffer, 0, buffer.Length); - } - - /// - /// - /// Записать целое 32-битное число в целевой поток. - /// - /// - /// - /// Целое число, которое будет записано в целевой поток. Значение числа должно находиться в диапазоне от 0 до 2^32-1. - /// - /// Порядок байтов, который будет использован для кодировки числа при записи. Если не установлен, то будет использован порядок байтов, заданный для текущего экземпляра объекта ЗаписьДанных. - /// Значение по умолчанию: Неопределено. - /// - [ContextMethod("ЗаписатьЦелое32", "WriteInt32")] - public void WriteInt32(int number, IValue byteOrder = null) - { - var buffer = GetBytes(number, BitConversionFacility.LittleEndian.GetBytes, BitConversionFacility.BigEndian.GetBytes, byteOrder); - _binaryWriter.Write(buffer, 0, buffer.Length); - } - - - /// - /// - /// Записывает целое 16-битное число в целевой поток. - /// - /// - /// - /// Целое число, которое будет записано в целевой поток. Значение числа должно находиться в диапазоне от 0 до 2^64-1. - /// - /// Порядок байтов, который будет использован для кодировки числа при записи. Если не установлен, то будет использован порядок байтов, заданный для текущего экземпляра объекта ЗаписьДанных. - /// Значение по умолчанию: Неопределено. - /// - [ContextMethod("ЗаписатьЦелое64", "WriteInt64")] - public void WriteInt64(long number, IValue byteOrder = null) - { - var buffer = GetBytes(number, BitConversionFacility.LittleEndian.GetBytes, BitConversionFacility.BigEndian.GetBytes, byteOrder); - _binaryWriter.Write(buffer, 0, buffer.Length); - } - - /// - /// - /// Сбрасывает все внутренние буферы в целевой поток, после чего вызывает метод СброситьБуферы целевого потока. - /// - /// - [ContextMethod("СброситьБуферы", "Flush")] - public void Flush() - { - _binaryWriter.Flush(); - } - - - /// - /// - /// Возвращает целевой поток, в который выполняется запись. - /// - /// - /// - /// - /// - [ContextMethod("ЦелевойПоток", "TargetStream")] - public IValue TargetStream() - { - return new GenericStream(_binaryWriter.BaseStream); - } - - public void Dispose() - { - _binaryWriter.Dispose(); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Text; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Text; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// + /// Объект предназначен для удобной записи различных типов данных в приемник. + /// Необходимо соблюдать следующий порядок работы с данным объектом: + /// + /// - Создать или получить приемник даных. + /// - Вызвать из приемника экземпляр объекта ЗаписьДанных. + /// - Выполнить требуемые действия с помощью объекта. + /// - Закрыть экземпляр объекта ЗаписьДанных. + /// При необходимости использовать другие методы для работы с данными, требуется сначала закрыть экземпляр объекта ЗаписьДанных с помощью метода Закрыть, выполнить необходимые действия над данными, установить требуемую позицию для чтения из приемника и создать новый экземпляр ЗаписьДанных. + /// + [ContextClass("ЗаписьДанных", "DataWriter")] + public class DataWriter : AutoContext, IDisposable + { + + private Encoding _workingEncoding; + private IValue _userVisibleEncoding; + private BinaryWriter _binaryWriter; + private readonly bool _writeBOM; + + public DataWriter(string fileName, IValue textEncoding, ByteOrderEnum? byteOrder, string lineSplitter, bool append, string convertibleSplitterOfLines, bool writeBOM) + { + ByteOrder = byteOrder?? ByteOrderEnum.LittleEndian; + LineSplitter = lineSplitter?? "\r\n"; + ConvertibleSplitterOfLines = convertibleSplitterOfLines; + _writeBOM = writeBOM; + TextEncoding = textEncoding; + + var fileSubsystem = new FileStreamsManager(); + var fileStreamContext = append ? fileSubsystem.OpenForAppend(fileName) : fileSubsystem.OpenForWrite(fileName); + + _binaryWriter = new BinaryWriter(fileStreamContext.GetUnderlyingStream(), _workingEncoding); + } + + public DataWriter(IStreamWrapper streamObj, IValue textEncoding, ByteOrderEnum? byteOrder, string lineSplitter, string convertibleSplitterOfLines, bool writeBOM) + { + ByteOrder = byteOrder?? ByteOrderEnum.LittleEndian; + LineSplitter = lineSplitter ?? "\r\n"; + ConvertibleSplitterOfLines = convertibleSplitterOfLines; + _writeBOM = writeBOM; + TextEncoding = textEncoding; + + _binaryWriter = new BinaryWriter(streamObj.GetUnderlyingStream(), _workingEncoding, true); + } + + /// + /// + /// Создает объект ЗаписьДанных для записи в указанный файл. Если файл с таким именем не существует, он будет создан. Параметр <Дописать> определяет, будут ли данные записаны в начало или в конец файла. + /// После завершения работы с объектом, до закрытия потока, переданного в конструктор, объект необходимо закрыть с помощью метода Закрыть или НачатьЗакрытие. При этом используемый файл будет закрыт автоматически. + /// + /// + /// + /// Имя файла или поток, в который будет выполнена запись. + /// + /// Кодировка текста для создаваемого экземпляра ЗаписьДанных. Если не задана, то используется UTF-8. + /// Значение по умолчанию: UTF8. Типы: КодировкаТекста (TextEncoding), Строка (String) + /// + /// Порядок байтов, используемый по умолчанию для кодирования целых чисел при записи в поток. + /// Значение по умолчанию: LittleEndian. + /// + /// Разделитель по умолчанию для строк, записываемых в поток. Если разделитель строк не задан, то используется строка ПС. + /// Значение по умолчанию: ПС. + /// + /// Для файла: + /// Определяет, будут ли данные записаны в начало или в конец файла: + /// - Если Истина, то при открытии существующего файла запись будет выполнена в конец файла. + /// - Иначе данные будут записываться с начала файла, перезаписывая существующие данные. + /// Если заданный файл не существует, будет создан новый файл с указанным именем и значение параметра не повлияет на поведение конструктора. + /// Значение по умолчанию: Ложь. + /// Для потока: + /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС. + /// Значение по умолчанию: ВК + ПС. + /// + /// Для файла: + /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС. + /// Значение по умолчанию: ВК + ПС. + /// Для потока: + /// Если в начало файла или потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста, то данный параметр должен иметь значение Истина. + /// Значение по умолчанию: Ложь. + /// + /// Только для файла: + /// Если в начало файла требуется записать метку порядка байтов (BOM) для используемой кодировки текста, то данный параметр должен иметь значение Истина. + /// Значение по умолчанию: Ложь. + /// + [ScriptConstructor] + public static DataWriter Constructor(IValue file_stream, IValue textEncoding = null, ByteOrderEnum? byteOrder = null, string lineSplitter = null, IValue param5 = null, IValue param6 = null, IValue param7 = null) + { + if (file_stream.SystemType == BasicTypes.String) + return new DataWriter(file_stream.ToString(), + textEncoding, + byteOrder, + lineSplitter, + ContextValuesMarshaller.ConvertValueStrict(param5), + ContextValuesMarshaller.ConvertValueStrict(param6), + ContextValuesMarshaller.ConvertValueStrict(param7)); + else + return ConstructorByStream( + file_stream, + textEncoding, + byteOrder, + lineSplitter, + ContextValuesMarshaller.ConvertValueStrict(param5), + ContextValuesMarshaller.ConvertValueStrict(param6)); + } + + /// + /// + /// Объект создается для записи в заданный поток. + /// После завершения работы с объектом, до закрытия потока, переданного в конструктор, объект необходимо закрыть с помощью метода Закрыть или НачатьЗакрытие. + /// + /// + /// + /// Поток, в который производится запись данных. Типы: Поток (Stream), ПотокВПамяти (MemoryStream), ФайловыйПоток (FileStream) + /// + /// Устанавливает кодировку текста для создаваемого экземпляра ЗаписьДанных. Если не задано, используется кодировка UTF-8. + /// Значение по умолчанию: UTF8. Типы: КодировкаТекста (TextEncoding), Строка (String) + /// + /// Порядок байтов, используемый по умолчанию для кодирования целых чисел при записи в поток. + /// Значение по умолчанию: LittleEndian. + /// + /// Разделитель по умолчанию для строк, записываемых в поток. Если разделитель строк не задан, то используется строка ПС. + /// Значение по умолчанию: ПС. + /// + /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС. + /// Значение по умолчанию: ВК + ПС. + /// + /// Если в начало файла или потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста, то данный параметр должен иметь значение Истина. + /// Значение по умолчанию: Ложь. + /// + //[ScriptConstructor(Name = "На основании потока")] + public static DataWriter ConstructorByStream(IValue stream, IValue textEncoding = null, ByteOrderEnum? byteOrder = null, string lineSplitter = null, string convertibleSplitterOfLines = null, bool writeBOM = false) + { + var streamObj = stream.AsObject() as IStreamWrapper; + if (streamObj == null) + { + throw RuntimeException.InvalidArgumentType(nameof(stream)); + } + + return new DataWriter(streamObj, textEncoding, byteOrder, lineSplitter, convertibleSplitterOfLines, writeBOM); + } + + /// + /// + /// Кодировка текста по-умолчанию для данного экземпляра ЗаписьДанных. + /// Кодировка может быть задана как в виде значения перечисления КодировкаТекста, так и в виде строки с указанием названия кодировки. + /// + /// КодировкаТекста (TextEncoding), Строка (String) + [ContextProperty("КодировкаТекста", "TextEncoding")] + public IValue TextEncoding + { + get { return _userVisibleEncoding; } + set + { + if (value != null) + { + _workingEncoding = TextEncodingEnum.GetEncoding(value, _writeBOM); + _userVisibleEncoding = value; + } + else + { + _workingEncoding = new UTF8Encoding(true); + _userVisibleEncoding = ValueFactory.Create("utf-8"); + } + } + } + + /// + /// + /// Конвертируемый разделитель строк. Этот параметр влияет на поведение метода ЗаписатьСимволы. + /// + /// Строка (String) + [ContextProperty("КонвертируемыйРазделительСтрок", "ConvertibleSplitterOfLines")] + public string ConvertibleSplitterOfLines { get; set; } + + /// + /// + /// Порядок байтов по умолчанию. + /// + /// ПорядокБайтов (ByteOrder) + [ContextProperty("ПорядокБайтов", "ByteOrder")] + public ByteOrderEnum ByteOrder { get; private set; } + + + /// + /// + /// Разделитель строк по-умолчанию. Это свойство влияет на поведение метода ЗаписатьСтроку. + /// + /// Строка (String) + [ContextProperty("РазделительСтрок", "LineSplitter")] + public string LineSplitter { get; set; } + + /// + /// + /// Вызывает метод СброситьБуферы. Если целевой поток был создан при создании объекта ЗаписьДанных, целевой поток также закрывается. + /// + /// + /// + [ContextMethod("Закрыть", "Close")] + public void Close() + { + _binaryWriter.Close(); + } + + + /// + /// + /// Записывает данные в целевой поток. + /// + /// + /// + /// + /// Запись двоичных данных + /// + /// + /// + /// Записать экземпляр объекта ДвоичныеДанные в поток. + /// + [ContextMethod("Записать", "Write")] + public void Write(IValue binaryDataOrReadResult) + { + var binData = binaryDataOrReadResult.AsObject() as BinaryDataContext; + if (binData == null) //TODO: Поддержкать класс РезультатЧтенияДанных + throw RuntimeException.InvalidArgumentType(); + + binData.CopyTo(_binaryWriter.BaseStream); + } + + /// + /// + /// Записывает один байт в целевой поток. + /// + /// + /// + /// Целое число, которое будет записано в целевой поток. Значение числа должно находиться в диапазоне от 0 до 255. + /// + [ContextMethod("ЗаписатьБайт", "WriteByte")] + public void WriteByte(byte number) + { + _binaryWriter.Write(number); + } + + /// + /// + /// Записать байты из буфера двоичных данных в целевой поток. + /// + /// + /// + /// + /// Запись части байтов из буфера + /// + /// + /// + /// Буфер двоичных данных, который используется в качестве источника данных для записи в целевой поток. + /// + /// Позиция в буфере, начиная с которой выполняется чтение байтов для записи в целевой поток. + /// + /// Количество байтов, которые требуется записать в целевой поток. + /// + [ContextMethod("ЗаписатьБуферДвоичныхДанных", "WriteBinaryDataBuffer")] + public void WriteBinaryDataBuffer(BinaryDataBuffer buffer, int positionInBuffer = 0, int number = 0) + { + if(positionInBuffer == 0 && number == 0) + _binaryWriter.Write(buffer.Bytes, 0, buffer.Count()); + else + _binaryWriter.Write(buffer.Bytes, positionInBuffer, number); + } + + + /// + /// + /// Записывает символы заданной строки в целевой поток. + /// + /// + /// + /// Строка, символы которой будут записаны в поток. + /// + /// Определяет кодировку текста для записи строки. Если не установлена, используется кодировка, заданная для данного объекта ЗаписьДанных. + /// Кодировка может быть задана как в виде значения перечисления КодировкаТекста, так и в виде строки с указанием названия кодировки. + /// + [ContextMethod("ЗаписатьСимволы", "WriteChars")] + public void WriteChars(string line, IValue encoding = null) + { + if(encoding == null) + _binaryWriter.Write(line.ToCharArray()); + else + { + var enc = TextEncodingEnum.GetEncoding(encoding, _writeBOM); + var bytes = enc.GetBytes(line); + _binaryWriter.Write(bytes,0,bytes.Length); + } + } + + + /// + /// + /// Записывает строку в целевой поток. + /// Сначала записываются все символы строки, затем - разделитель строк. + /// + /// + /// + /// Строка, которая будет записана в поток. + /// + /// Определяет кодировку текста для записи строки. Если не установлена, используется кодировка, заданная для данного объекта ЗаписьДанных. + /// Кодировка может быть задана как в виде значения перечисления КодировкаТекста, так и в виде строки с указанием названия кодировки. + /// Значение по умолчанию: Неопределено. Типы: КодировкаТекста (TextEncoding), Строка (String) + /// + /// Указывает строку, являющуюся разделителем строк в потоке после записи символов строк. Если параметр не указан, используется разделитель строк, указанный для текущего экземпляра объекта ЗаписьДанных. + /// Значение по умолчанию: Неопределено. + /// + [ContextMethod("ЗаписатьСтроку", "WriteLine")] + public void WriteLine(string line, IValue encoding = null, string lineSplitter = null) + { + // TODO: Для экономии времени не поддерживаем пока конвертируемый разделитель строк + // Кому надо - попросит PR. + + if (encoding == null) + _binaryWriter.Write(line.ToCharArray()); + else + { + var enc = TextEncodingEnum.GetEncoding(encoding, _writeBOM); + var bytes = enc.GetBytes(line); + _binaryWriter.Write(bytes, 0, bytes.Length); + } + + if(lineSplitter == null) + _binaryWriter.Write(LineSplitter.ToCharArray()); + else + _binaryWriter.Write(lineSplitter.ToCharArray()); + } + + private byte[] GetBytes(T value, Converter leConverter, Converter beConverter, BslValue byteOrder = null) + { + ByteOrderEnum workByteOrder; + if (byteOrder == null) + workByteOrder = ByteOrder; + else + { + if (!(byteOrder is IObjectWrapper enumVal)) + throw RuntimeException.InvalidArgumentType(nameof(byteOrder)); + + try + { + workByteOrder = (ByteOrderEnum) enumVal.UnderlyingObject; + } + catch (InvalidCastException) + { + throw RuntimeException.InvalidArgumentType(nameof(byteOrder)); + } + } + + var converter = workByteOrder == ByteOrderEnum.BigEndian ? beConverter : leConverter; + return converter(value); + } + + /// + /// + /// Записывает 16-разрядное число в целевой поток. + /// + /// + /// + /// Число, которое будет записано в целевой поток. + /// Значение числа должно находиться в диапазоне от 0 до 65535. + /// + /// Порядок байтов, который будет использован для кодировки числа при записи. Если не установлен, то будет использован порядок байтов, заданный для текущего экземпляра объекта ЗаписьДанных. + /// Значение по умолчанию: Неопределено. + /// + [ContextMethod("ЗаписатьЦелое16", "WriteInt16")] + public void WriteInt16(ushort number, BslValue byteOrder = null) + { + var buffer = GetBytes(number, BitConversionFacility.LittleEndian.GetBytes, BitConversionFacility.BigEndian.GetBytes, byteOrder); + _binaryWriter.Write(buffer, 0, buffer.Length); + } + + /// + /// + /// Записать целое 32-битное число в целевой поток. + /// + /// + /// + /// Целое число, которое будет записано в целевой поток. Значение числа должно находиться в диапазоне от 0 до 2^32-1. + /// + /// Порядок байтов, который будет использован для кодировки числа при записи. Если не установлен, то будет использован порядок байтов, заданный для текущего экземпляра объекта ЗаписьДанных. + /// Значение по умолчанию: Неопределено. + /// + [ContextMethod("ЗаписатьЦелое32", "WriteInt32")] + public void WriteInt32(uint number, BslValue byteOrder = null) + { + var buffer = GetBytes(number, BitConversionFacility.LittleEndian.GetBytes, BitConversionFacility.BigEndian.GetBytes, byteOrder); + _binaryWriter.Write(buffer, 0, buffer.Length); + } + + + /// + /// + /// Записывает целое 64-битное число в целевой поток. + /// + /// + /// + /// Целое число, которое будет записано в целевой поток. Значение числа должно находиться в диапазоне от 0 до 2^64-1. + /// + /// Порядок байтов, который будет использован для кодировки числа при записи. Если не установлен, то будет использован порядок байтов, заданный для текущего экземпляра объекта ЗаписьДанных. + /// Значение по умолчанию: Неопределено. + /// + [ContextMethod("ЗаписатьЦелое64", "WriteInt64")] + public void WriteInt64(ulong number, BslValue byteOrder = null) + { + var buffer = GetBytes(number, BitConversionFacility.LittleEndian.GetBytes, BitConversionFacility.BigEndian.GetBytes, byteOrder); + _binaryWriter.Write(buffer, 0, buffer.Length); + } + + /// + /// + /// Сбрасывает все внутренние буферы в целевой поток, после чего вызывает метод СброситьБуферы целевого потока. + /// + /// + [ContextMethod("СброситьБуферы", "Flush")] + public void Flush() + { + _binaryWriter.Flush(); + } + + + /// + /// + /// Возвращает целевой поток, в который выполняется запись. + /// + /// + /// + /// + /// + [ContextMethod("ЦелевойПоток", "TargetStream")] + public IValue TargetStream() + { + return new GenericStream(_binaryWriter.BaseStream); + } + + public void Dispose() + { + _binaryWriter.Dispose(); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/EndianBitConverter.cs b/src/OneScript.StandardLibrary/Binary/EndianBitConverter.cs similarity index 99% rename from src/ScriptEngine.HostedScript/Library/Binary/EndianBitConverter.cs rename to src/OneScript.StandardLibrary/Binary/EndianBitConverter.cs index f8b1d5df1..2fa9a373b 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/EndianBitConverter.cs +++ b/src/OneScript.StandardLibrary/Binary/EndianBitConverter.cs @@ -4,11 +4,11 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Linq; - -namespace ScriptEngine.HostedScript.Library.Binary +namespace OneScript.StandardLibrary.Binary { /// /// Converts base data types to an array of bytes, and an array of bytes to base diff --git a/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs new file mode 100644 index 000000000..eeb073ad9 --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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 new file mode 100644 index 000000000..7a0502643 --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/FileBackingStream.cs @@ -0,0 +1,161 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// Поток, который хранит данные в памяти до определенного лимита, потом во временном файле + /// + public sealed class FileBackingStream : Stream + { + private readonly int _inMemoryLimit; + private Stream _backingStream; + + private string _backingFileName; + + public FileBackingStream() : this(FileBackingConstants.DEFAULT_MEMORY_LIMIT) + { + } + + public FileBackingStream(int inMemoryLimit, int capacity = 0) + { + if (inMemoryLimit == FileBackingConstants.SYSTEM_IN_MEMORY_LIMIT) + throw new ArgumentException("Use MemoryStream instead"); + + _inMemoryLimit = inMemoryLimit; + _backingStream = new MemoryStream(capacity); + } + + public override void Flush() + { + _backingStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _backingStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _backingStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + if (value <= _inMemoryLimit) + { + _backingStream.SetLength(value); + SwitchToMemory(); + } + else + { + SwitchToFile(); + _backingStream.SetLength(value); + } + } + + public override void Write(byte[] buffer, int offset, int count) + { + Grow(count); + _backingStream.Write(buffer, offset, count); + } + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => true; + + public override long Length => _backingStream.Length; + + public override long Position + { + get => _backingStream.Position; + set => _backingStream.Position = value; + } + + public bool HasBackingFile => _backingFileName != null; + + public string FileName => _backingFileName; + + public void SwitchToMemory() + { + if (!HasBackingFile) + return; + + if (_backingStream.Length > _inMemoryLimit) + { + throw new InvalidOperationException( + $"Size {_backingStream.Length} is larger than limit {_inMemoryLimit}"); + } + + var memStream = new MemoryStream((int)_backingStream.Length); + var currentPosition = _backingStream.Position; + _backingStream.Position = 0; + _backingStream.CopyTo(memStream); + memStream.Position = currentPosition; + _backingStream.Close(); + + _backingStream = memStream; + _backingFileName = null; + } + + public void SwitchToFile() + { + if (HasBackingFile) + return; + + var currentPosition = _backingStream.Position; + _backingFileName = Path.GetTempFileName(); + var options = new FileStreamOptions + { + Options = FileOptions.DeleteOnClose, + Access = FileAccess.ReadWrite, + Mode = FileMode.Create + }; + + var fileStream = new FileStream(_backingFileName, options); + try + { + _backingStream.Position = 0; + _backingStream.CopyTo(fileStream); + fileStream.Position = currentPosition; + _backingStream.Dispose(); + _backingStream = fileStream; + } + catch + { + fileStream.Dispose(); + throw; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _backingStream.Dispose(); + } + } + + private void Grow(int amount) + { + if (HasBackingFile) + return; + + var newSize = _backingStream.Position + amount; + if (newSize > _inMemoryLimit) + { + SwitchToFile(); + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Binary/FileStreamContext.cs b/src/OneScript.StandardLibrary/Binary/FileStreamContext.cs similarity index 83% rename from src/ScriptEngine.HostedScript/Library/Binary/FileStreamContext.cs rename to src/OneScript.StandardLibrary/Binary/FileStreamContext.cs index 701443769..6386898a6 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/FileStreamContext.cs +++ b/src/OneScript.StandardLibrary/Binary/FileStreamContext.cs @@ -1,301 +1,341 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.IO; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Binary -{ - /// - /// - /// Специализированная версия объекта Поток для работы данными, расположенными в файле на диске. Предоставляет возможность чтения из потока, записи в поток и изменения текущей позиции. - /// По умолчанию, все операции с файловым потоком являются буферизированными, размер буфера по умолчанию - 8 КБ. - /// Размер буфера можно изменить, в том числе - полностью отключить буферизацию при вызове конструктора. - /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. - /// - [ContextClass("ФайловыйПоток", "FileStream")] - public class FileStreamContext : AutoContext, IStreamWrapper, IDisposable - { - - private readonly FileStream _underlyingStream; - private readonly GenericStreamImpl _commonImpl; - - private FileShare FileShareForAccess(FileAccessEnum access) - { - return access == FileAccessEnum.Read ? FileShare.ReadWrite : FileShare.Read; - } - - public FileStreamContext(string filename, FileOpenModeEnum openMode, FileAccessEnum access, int bufferSize = 0) - { - FileName = filename; - - if (bufferSize == 0) - _underlyingStream = new FileStream(filename, - FileStreamsManager.ConvertFileOpenModeToCLR(openMode), - FileStreamsManager.ConvertFileAccessToCLR(access), - FileShareForAccess(access)); - else - _underlyingStream = new FileStream(filename, - FileStreamsManager.ConvertFileOpenModeToCLR(openMode), - FileStreamsManager.ConvertFileAccessToCLR(access), - FileShareForAccess(access), - bufferSize); - - _commonImpl = new GenericStreamImpl(_underlyingStream); - } - - public FileStreamContext(string fileName, FileStream openedStream) - { - FileName = fileName; - _underlyingStream = openedStream; - _commonImpl = new GenericStreamImpl(_underlyingStream); - } - - public bool IsReadOnly => !CanWrite; - - /// - /// - /// Признак доступности записи в поток. - /// - /// Булево (Boolean) - [ContextProperty("ДоступнаЗапись", "CanWrite")] - public bool CanWrite => _underlyingStream.CanWrite; - - /// - /// - /// Признак доступности произвольного изменения позиции чтения/записи в потоке. - /// - /// Булево (Boolean) - [ContextProperty("ДоступноИзменениеПозиции", "CanSeek")] - public bool CanSeek => _underlyingStream.CanSeek; - - - /// - /// - /// Признак доступности чтения из потока. - /// - /// Булево (Boolean) - [ContextProperty("ДоступноЧтение", "CanRead")] - public bool CanRead => _underlyingStream.CanRead; - - /// - /// Содержит полное имя файла, включая путь - /// - [ContextProperty("ИмяФайла")] - public string FileName { get; } - - /// - /// - /// Вызов данного метода завершает работу с потоком. При попытке вызвать любой метод объекта, кроме метода Закрыть, будет вызвано исключение. - /// При повторном вызове данного метода никаких действий выполняться не будет. - /// Выполняемое действие зависит от используемого типа потока. - /// - /// - [ContextMethod("Закрыть", "Close")] - public void Close() - { - _underlyingStream.Close(); - } - - - /// - /// - /// Записывает в поток заданное количество байтов из буфера по заданному смещению. Если в буфере меньше данных, чем требуется записать, вызывается исключение о недостаточном количестве данных в буфере. - /// Запись в поток возможна только, если поток поддерживает запись. В противном случае при вызове метода будет вызвано исключение. - /// - /// - /// - /// Буфер, из которого выбираются данные для записи. - /// - /// Позиция в буфере, начиная с которой данные будут получены для записи в поток. - /// - /// Количество байт, которые требуется записать. - /// - [ContextMethod("Записать", "Write")] - public void Write(BinaryDataBuffer buffer, int positionInBuffer, int number) - { - _commonImpl.Write(buffer, positionInBuffer, number); - } - - - /// - /// - /// Копирует данные из текущего потока в другой поток. - /// - /// - /// - /// Поток, в который будет выполняться копирование. - /// - /// Размер буфера, используемого при копировании. - /// Если параметр не задан, то система подбирает размер буфера автоматически. - /// - [ContextMethod("КопироватьВ", "CopyTo")] - public void CopyTo(IValue targetStream, int bufferSize = 0) - { - _commonImpl.CopyTo(targetStream, bufferSize); - } - - /// - /// - /// Сдвигает текущую позицию потока на заданное количество байтов относительно начальной позиции. Если указано отрицательное смещение, позиция сдвигается в направлении к началу потока. - /// Если изменение позиции недоступно (ДоступноИзменениеПозиции установлено в Ложь), будет сгенерировано исключение. - /// - /// - /// - /// Количество байтов, на которое нужно передвинуть позицию в потоке. - /// - /// Начальная позиция, от которой отсчитывается смещение. - /// - /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. - [ContextMethod("Перейти", "Seek")] - public long Seek(int offset, StreamPositionEnum initialPosition = StreamPositionEnum.Begin) - { - return _commonImpl.Seek(offset, initialPosition); - } - - - /// - /// - /// Возвращает поток, который разделяет данные и текущую позицию с данным потоком, но не разрешает запись. - /// - /// - /// - /// - [ContextMethod("ПолучитьПотокТолькоДляЧтения", "GetReadonlyStream")] - public GenericStream GetReadonlyStream() - { - return _commonImpl.GetReadonlyStream(); - } - - - /// - /// - /// Выполняет чтение заданного количества байтов в указанный буфер по указанному смещению. Текущая позиция смещается вперед на фактическое количество прочитанных байтов. - /// Чтение из потока возможно только, если поток поддерживает чтение. В противном случае, будет вызвано исключение. - /// При чтении размер целевого буфера не меняется, а его содержимое перезаписывается фактически прочитанными данными. Если в буфере недостаточно места для записи прочитанных данных, происходит ошибка переполнения. - /// - /// - /// - /// Буфер, в который выполняется чтение. - /// - /// Позиция в целевом буфере, начиная с которой требуется записывать данные из потока. - /// - /// Количество байт, которые требуется записать в целевой буфер. - /// - /// - /// Возвращает число прочитанных байт - /// - /// - [ContextMethod("Прочитать", "Read")] - public long Read(BinaryDataBuffer buffer, int positionInBuffer, int number) - { - return _commonImpl.Read(buffer, positionInBuffer, number); - } - - - /// - /// - /// Получает размер данных в байтах. - /// - /// - [ContextMethod("Размер", "Size")] - public long Size() - { - return _commonImpl.Size(); - } - - - /// - /// - /// Сбрасывает все промежуточные буферы и производит запись всех незаписанных данных в целевое устройство. - /// - /// - [ContextMethod("СброситьБуферы", "Flush")] - public void Flush() - { - _commonImpl.Flush(); - } - - - /// - /// - /// Возвращает текущую позицию в потоке. - /// - /// - - /// - /// - /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. - - /// - [ContextMethod("ТекущаяПозиция", "CurrentPosition")] - public long CurrentPosition() - { - return _commonImpl.CurrentPosition(); - } - - - /// - /// - /// Устанавливает размер потока. - /// Если текущий размер превышает заданный, поток будет сокращен до заданного размера, а информация, превышающая заданный размер, будет потеряна. - /// Если текущий размер потока меньше заданного, то содержимое потока между старым и новым размером не определено. - /// - /// - /// - /// Устанавливаемый размер потока. - /// - [ContextMethod("УстановитьРазмер", "SetSize")] - public void SetSize(long size) - { - _commonImpl.SetSize(size); - } - - public void Dispose() - { - Close(); - } - - public Stream GetUnderlyingStream() - { - return _underlyingStream; - } - - [ScriptConstructor(Name = "С указанием режима открытия")] - public static FileStreamContext Constructor(IValue filename, IValue openMode, IValue bufferSize = null) - { - if (bufferSize == null || bufferSize.DataType == DataType.Number) - { - return new FileStreamContext( - filename.AsString(), - ContextValuesMarshaller.ConvertParam(openMode), - FileAccessEnum.ReadAndWrite, - ContextValuesMarshaller.ConvertParam(bufferSize)); - } - else - { - // перегрузка методов не позволяет вызвать второй конструктор без доуточнения реальных типов - return Constructor( - filename, - openMode, - new CLREnumValueWrapper(null, FileAccessEnum.ReadAndWrite), - bufferSize); - } - } - - [ScriptConstructor(Name = "С указанием режима открытия и уровня доступа")] - public static FileStreamContext Constructor(IValue filename, IValue openMode, IValue access, IValue bufferSize = null) - { - return new FileStreamContext( - filename.AsString(), - ContextValuesMarshaller.ConvertParam(openMode), - ContextValuesMarshaller.ConvertParam(access), - ContextValuesMarshaller.ConvertParam(bufferSize)); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using OneScript.Contexts; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// + /// Специализированная версия объекта Поток для работы данными, расположенными в файле на диске. Предоставляет возможность чтения из потока, записи в поток и изменения текущей позиции. + /// По умолчанию, все операции с файловым потоком являются буферизированными, размер буфера по умолчанию - 8 КБ. + /// Размер буфера можно изменить, в том числе - полностью отключить буферизацию при вызове конструктора. + /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. + /// + [ContextClass("ФайловыйПоток", "FileStream")] + public class FileStreamContext : AutoContext, IStreamWrapper, IDisposable + { + + private readonly FileStream _underlyingStream; + private readonly GenericStreamImpl _commonImpl; + + private FileShare FileShareForAccess(FileAccessEnum access) + { + return access == FileAccessEnum.Read ? FileShare.ReadWrite : FileShare.Read; + } + + public FileStreamContext(string filename, FileOpenModeEnum openMode, FileAccessEnum access, int bufferSize = 0) + { + FileName = filename; + + if (bufferSize == 0) + _underlyingStream = new FileStream(filename, + FileStreamsManager.ConvertFileOpenModeToCLR(openMode), + FileStreamsManager.ConvertFileAccessToCLR(access), + FileShareForAccess(access)); + else + _underlyingStream = new FileStream(filename, + FileStreamsManager.ConvertFileOpenModeToCLR(openMode), + FileStreamsManager.ConvertFileAccessToCLR(access), + FileShareForAccess(access), + bufferSize); + + _commonImpl = new GenericStreamImpl(_underlyingStream); + } + + public FileStreamContext(string fileName, FileStream openedStream) + { + FileName = fileName; + _underlyingStream = openedStream; + _commonImpl = new GenericStreamImpl(_underlyingStream); + } + + public bool IsReadOnly => !CanWrite; + + /// + /// + /// Признак доступности записи в поток. + /// + /// Булево (Boolean) + [ContextProperty("ДоступнаЗапись", "CanWrite")] + public bool CanWrite => _underlyingStream.CanWrite; + + /// + /// + /// Признак доступности произвольного изменения позиции чтения/записи в потоке. + /// + /// Булево (Boolean) + [ContextProperty("ДоступноИзменениеПозиции", "CanSeek")] + public bool CanSeek => _underlyingStream.CanSeek; + + + /// + /// + /// Признак доступности чтения из потока. + /// + /// Булево (Boolean) + [ContextProperty("ДоступноЧтение", "CanRead")] + public bool CanRead => _underlyingStream.CanRead; + + /// + /// + /// Признак доступности установки таймаута чтения/записи в потоке. + /// + /// Булево (Boolean) + [ContextProperty("ДоступенТаймаут", "CanTimeout")] + public bool CanTimeout => _underlyingStream.CanTimeout; + + /// + /// + /// Время в миллисекундах, отведенное потоку на операцию чтения. + /// + /// Число (int) + [ContextProperty("ТаймаутЧтения", "ReadTimeout")] + public int ReadTimeout + { + get => _underlyingStream.ReadTimeout; + set => _underlyingStream.ReadTimeout = value; + } + /// + /// + /// Время в миллисекундах, отведенное потоку на операцию записи. + /// + /// Число (int) + [ContextProperty("ТаймаутЗаписи", "WriteTimeout")] + public int WriteTimeout + { + get => _underlyingStream.WriteTimeout; + set => _underlyingStream.WriteTimeout = value; + } + + /// + /// Содержит полное имя файла, включая путь + /// + [ContextProperty("ИмяФайла")] + public string FileName { get; } + + /// + /// + /// Вызов данного метода завершает работу с потоком. При попытке вызвать любой метод объекта, кроме метода Закрыть, будет вызвано исключение. + /// При повторном вызове данного метода никаких действий выполняться не будет. + /// Выполняемое действие зависит от используемого типа потока. + /// + /// + [ContextMethod("Закрыть", "Close")] + public void Close() + { + _underlyingStream.Close(); + } + + + /// + /// + /// Записывает в поток заданное количество байтов из буфера по заданному смещению. Если в буфере меньше данных, чем требуется записать, вызывается исключение о недостаточном количестве данных в буфере. + /// Запись в поток возможна только, если поток поддерживает запись. В противном случае при вызове метода будет вызвано исключение. + /// + /// + /// + /// Буфер, из которого выбираются данные для записи. + /// + /// Позиция в буфере, начиная с которой данные будут получены для записи в поток. + /// + /// Количество байт, которые требуется записать. + /// + [ContextMethod("Записать", "Write")] + public void Write(BinaryDataBuffer buffer, int positionInBuffer, int number) + { + _commonImpl.Write(buffer, positionInBuffer, number); + } + + + /// + /// + /// Копирует данные из текущего потока в другой поток. + /// + /// + /// + /// Поток, в который будет выполняться копирование. + /// + /// Размер буфера, используемого при копировании. + /// Если параметр не задан, то система подбирает размер буфера автоматически. + /// + [ContextMethod("КопироватьВ", "CopyTo")] + public void CopyTo(IValue targetStream, int bufferSize = 0) + { + _commonImpl.CopyTo(targetStream, bufferSize); + } + + /// + /// + /// Сдвигает текущую позицию потока на заданное количество байтов относительно начальной позиции. Если указано отрицательное смещение, позиция сдвигается в направлении к началу потока. + /// Если изменение позиции недоступно (ДоступноИзменениеПозиции установлено в Ложь), будет сгенерировано исключение. + /// + /// + /// + /// Количество байтов, на которое нужно передвинуть позицию в потоке. + /// + /// Начальная позиция, от которой отсчитывается смещение. + /// + /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. + [ContextMethod("Перейти", "Seek")] + public long Seek(int offset, StreamPositionEnum initialPosition = StreamPositionEnum.Begin) + { + return _commonImpl.Seek(offset, initialPosition); + } + + + /// + /// + /// Возвращает поток, который разделяет данные и текущую позицию с данным потоком, но не разрешает запись. + /// + /// + /// + /// + [ContextMethod("ПолучитьПотокТолькоДляЧтения", "GetReadonlyStream")] + public GenericStream GetReadonlyStream() + { + return _commonImpl.GetReadonlyStream(); + } + + + /// + /// + /// Выполняет чтение заданного количества байтов в указанный буфер по указанному смещению. Текущая позиция смещается вперед на фактическое количество прочитанных байтов. + /// Чтение из потока возможно только, если поток поддерживает чтение. В противном случае, будет вызвано исключение. + /// При чтении размер целевого буфера не меняется, а его содержимое перезаписывается фактически прочитанными данными. Если в буфере недостаточно места для записи прочитанных данных, происходит ошибка переполнения. + /// + /// + /// + /// Буфер, в который выполняется чтение. + /// + /// Позиция в целевом буфере, начиная с которой требуется записывать данные из потока. + /// + /// Количество байт, которые требуется записать в целевой буфер. + /// + /// + /// Возвращает число прочитанных байт + /// + /// + [ContextMethod("Прочитать", "Read")] + public long Read(BinaryDataBuffer buffer, int positionInBuffer, int number) + { + return _commonImpl.Read(buffer, positionInBuffer, number); + } + + + /// + /// + /// Получает размер данных в байтах. + /// + /// + [ContextMethod("Размер", "Size")] + public long Size() + { + return _commonImpl.Size(); + } + + + /// + /// + /// Сбрасывает все промежуточные буферы и производит запись всех незаписанных данных в целевое устройство. + /// + /// + [ContextMethod("СброситьБуферы", "Flush")] + public void Flush() + { + _commonImpl.Flush(); + } + + + /// + /// + /// Возвращает текущую позицию в потоке. + /// + /// + + /// + /// + /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. + + /// + [ContextMethod("ТекущаяПозиция", "CurrentPosition")] + public long CurrentPosition() + { + return _commonImpl.CurrentPosition(); + } + + + /// + /// + /// Устанавливает размер потока. + /// Если текущий размер превышает заданный, поток будет сокращен до заданного размера, а информация, превышающая заданный размер, будет потеряна. + /// Если текущий размер потока меньше заданного, то содержимое потока между старым и новым размером не определено. + /// + /// + /// + /// Устанавливаемый размер потока. + /// + [ContextMethod("УстановитьРазмер", "SetSize")] + public void SetSize(long size) + { + _commonImpl.SetSize(size); + } + + public void Dispose() + { + Close(); + } + + public Stream GetUnderlyingStream() + { + return _underlyingStream; + } + + [ScriptConstructor(Name = "С указанием режима открытия")] + public static FileStreamContext Constructor(string filename, FileOpenModeEnum openMode, IValue bufferSizeOrAccess = default) + { + // Текущая реализация поиска конструктора смотрит только на количество параметров, но не на их типы. + // Поэтому здесь третий параметр это IValue и это может быть вызов конструктора как с FileAccessEnum, так и с bufferSize + if (bufferSizeOrAccess == null) + { + return new FileStreamContext( + filename, + openMode, + FileAccessEnum.ReadAndWrite); + } + + if (bufferSizeOrAccess.SystemType == BasicTypes.Number) + { + return new FileStreamContext( + filename, + openMode, + FileAccessEnum.ReadAndWrite, + (int)bufferSizeOrAccess.AsNumber()); + } + + return new FileStreamContext( + filename, + openMode, + ContextValuesMarshaller.ConvertValueStrict(bufferSizeOrAccess)); + } + + [ScriptConstructor(Name = "С указанием режима открытия и уровня доступа")] + public static FileStreamContext Constructor(string filename, FileOpenModeEnum openMode, FileAccessEnum access, int bufferSize = default) + { + return new FileStreamContext( + filename, + openMode, + access, + bufferSize); + } + } +} diff --git a/src/OneScript.StandardLibrary/Binary/FileStreamsManager.cs b/src/OneScript.StandardLibrary/Binary/FileStreamsManager.cs new file mode 100644 index 000000000..ca2729029 --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/FileStreamsManager.cs @@ -0,0 +1,238 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// + /// Предоставляет методы для использования в типовых сценариях работы с файлами. + /// + [ContextClass("МенеджерФайловыхПотоков", "FileStreamsManager")] + public class FileStreamsManager : AutoContext + { + + public static FileMode ConvertFileOpenModeToCLR(FileOpenModeEnum value) + { + switch (value) + { + case FileOpenModeEnum.Append: + return FileMode.Append; + case FileOpenModeEnum.Create: + return FileMode.Create; + case FileOpenModeEnum.CreateNew: + return FileMode.CreateNew; + case FileOpenModeEnum.Open: + return FileMode.Open; + case FileOpenModeEnum.OpenOrCreate: + return FileMode.OpenOrCreate; + case FileOpenModeEnum.Truncate: + return FileMode.Truncate; + + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + } + + public static FileAccess ConvertFileAccessToCLR(FileAccessEnum value) + { + switch (value) + { + case FileAccessEnum.Read: + return FileAccess.Read; + case FileAccessEnum.Write: + return FileAccess.Write; + default: + return FileAccess.ReadWrite; + } + } + + public FileStreamsManager() + { + } + + /// + /// + /// Открывает файл в заданном режиме с возможностью чтения и записи. + /// Файл открывается в режиме разделяемого чтения. + /// + /// + /// + /// Режим открытия файла. + /// + /// Имя открываемого файла. + /// + /// Размер буфера для операций с файлом. + /// + /// + /// Специализированная версия объекта Поток для работы данными, расположенными в файле на диске. Предоставляет возможность чтения из потока, записи в поток и изменения текущей позиции. + /// По умолчанию, все операции с файловым потоком являются буферизированными, размер буфера по умолчанию - 8 КБ. + /// Размер буфера можно изменить, в том числе - полностью отключить буферизацию при вызове конструктора. + /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. + [DocumentedMember("Открыть", "Open")] + public FileStreamContext Open(FileOpenModeEnum openingMode, string fileName, int bufferSize = 0) + { + return new FileStreamContext(fileName, openingMode, FileAccessEnum.ReadAndWrite, bufferSize); + } + + /// + /// + /// Открыть файл в выбранном режиме, с заданным уровнем доступа, с общим доступом на чтение. + /// + /// + /// + /// Режим доступа к файлу + /// + /// Имя открываемого файла. + /// + /// Режим открытия файла. + /// + /// Размер буфера для операций с файлом. + /// + /// + /// Специализированная версия объекта Поток для работы данными, расположенными в файле на диске. Предоставляет возможность чтения из потока, записи в поток и изменения текущей позиции. + /// По умолчанию, все операции с файловым потоком являются буферизированными, размер буфера по умолчанию - 8 КБ. + /// Размер буфера можно изменить, в том числе - полностью отключить буферизацию при вызове конструктора. + /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. + [DocumentedMember("Открыть", "Open")] + public FileStreamContext Open(FileAccessEnum fileAccess, string fileName, FileOpenModeEnum openingMode, int bufferSize = 0) + { + return new FileStreamContext(fileName, openingMode, fileAccess, bufferSize); + } + + [ContextMethod("Открыть", "Open", SkipForDocumenter = true)] + public FileStreamContext Open(IValue p1, IValue p2, IValue p3 = null, IValue p4 = null) + { + // Диспетчеризуем перегрузки по типам параметров + if (p1 is ClrEnumValueWrapper fileAccess) + { + // это вариант с 4-мя параметрами + return Open( + fileAccess.UnderlyingValue, + ContextValuesMarshaller.ConvertValueStrict(p2), + ContextValuesMarshaller.ConvertValueStrict(p3), + ContextValuesMarshaller.ConvertValueStrict(p4) + ); + } + else if (p4 == null) + { + // это вариант с 3-мя параметрами + return Open( + ContextValuesMarshaller.ConvertValueStrict(p1), + ContextValuesMarshaller.ConvertValueStrict(p2), + ContextValuesMarshaller.ConvertValueStrict(p3) + ); + } + else + { + throw RuntimeException.InvalidNthArgumentType(4); + } + } + + /// + /// + /// Открыть существующий файл для записи в конец. Если файл не существует, то будет создан новый файл. Запись в существующий файл выполняется с конца файла. Файл открывается в режиме разделяемого чтения. + /// + /// + /// + /// Имя открываемого файла. + /// + [ContextMethod("ОткрытьДляДописывания", "OpenForAppend")] + public FileStreamContext OpenForAppend(string fileName) + { + return new FileStreamContext(fileName, FileOpenModeEnum.Append, FileAccessEnum.ReadAndWrite); + } + + + /// + /// + /// Открывает существующий файл для записи. Файл открывается в режиме разделяемого чтения. Если файл не найден, будет создан новый файл. Запись в существующий файл производится с начала файла, замещая ранее сохраненные данные. + /// + /// + /// + /// Имя открываемого файла. + + /// + /// + /// Специализированная версия объекта Поток для работы данными, расположенными в файле на диске. Предоставляет возможность чтения из потока, записи в поток и изменения текущей позиции. + /// По умолчанию, все операции с файловым потоком являются буферизированными, размер буфера по умолчанию - 8 КБ. + /// Размер буфера можно изменить, в том числе - полностью отключить буферизацию при вызове конструктора. + /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. + + /// + [ContextMethod("ОткрытьДляЗаписи", "OpenForWrite")] + public FileStreamContext OpenForWrite(string fileName) + { + // TODO: Судя по описанию - открывается без обрезки (Truncate). Надо проверить в 1С. + return new FileStreamContext(fileName, FileOpenModeEnum.OpenOrCreate, FileAccessEnum.Write); + } + + + /// + /// + /// Открывает существующий файл для чтения с общим доступом на чтение. + /// + /// + /// + /// Имя открываемого файла. + /// + /// + /// + [ContextMethod("ОткрытьДляЧтения", "OpenForRead")] + public FileStreamContext OpenForRead(string fileName) + { + return new FileStreamContext(fileName, FileOpenModeEnum.Open, FileAccessEnum.Read); + } + + + /// + /// + /// Создает или перезаписывает файл и открывает поток с возможностью чтения и записи в файл. Файл открывается в режиме разделяемого чтения. + /// + /// + /// + /// Имя создаваемого файла. + /// + /// Размер буфера. + /// + /// + [ContextMethod("Создать", "Create")] + public IValue Create(string fileName, int bufferSize = 0) + { + return new FileStreamContext(fileName, new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, bufferSize == 0 ? 8192 : bufferSize)); + } + + + /// + /// НЕ РЕАЛИЗОВАН + /// Создает временный файл и открывает его в монопольном режиме с возможностью чтения и записи. Дополнительно можно установить лимит в байтах, при превышении которого будет создан файл на диске. Пока размер файла не превышает данного лимита - вся работа ведётся в памяти. + /// + /// + /// + /// Максимальный объем памяти (в байтах), при превышении которого будет создан файл на диске. + /// Значение по умолчанию: 65535. + /// + /// Размер буфера для операций с файлом (в байтах). + /// Значение по умолчанию: 8192. + /// + /// + /// + [ContextMethod("СоздатьВременныйФайл", "CreateTempFile")] + public IValue CreateTempFile(int memoryLimit = 0, int bufferSize = 0) + { + throw new NotImplementedException(); + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/GenericStream.cs b/src/OneScript.StandardLibrary/Binary/GenericStream.cs similarity index 87% rename from src/ScriptEngine.HostedScript/Library/Binary/GenericStream.cs rename to src/OneScript.StandardLibrary/Binary/GenericStream.cs index 0ecfda88c..5c00d611c 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/GenericStream.cs +++ b/src/OneScript.StandardLibrary/Binary/GenericStream.cs @@ -4,18 +4,22 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.IO; +using OneScript.Contexts; +using OneScript.Exceptions; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Binary +namespace OneScript.StandardLibrary.Binary { public interface IStreamWrapper { Stream GetUnderlyingStream(); - + bool IsReadOnly { get; } + } /// @@ -46,7 +50,7 @@ public GenericStream(Stream underlyingStream, bool readOnly) } public bool IsReadOnly => !CanWrite; - + /// /// /// Признак доступности записи в поток. @@ -63,7 +67,6 @@ public GenericStream(Stream underlyingStream, bool readOnly) [ContextProperty("ДоступноИзменениеПозиции", "CanSeek")] public bool CanSeek => _underlyingStream.CanSeek; - /// /// /// Признак доступности чтения из потока. @@ -71,7 +74,37 @@ public GenericStream(Stream underlyingStream, bool readOnly) /// Булево (Boolean) [ContextProperty("ДоступноЧтение", "CanRead")] public bool CanRead => _underlyingStream.CanRead; - + + /// + /// + /// Признак доступности установки таймаута чтения/записи в потоке. + /// + /// Булево (Boolean) + [ContextProperty("ДоступенТаймаут", "CanTimeout")] + public bool CanTimeout => _underlyingStream.CanTimeout; + + /// + /// + /// Время в миллисекундах, отведенное потоку на операцию чтения. + /// + /// Число (int) + [ContextProperty("ТаймаутЧтения", "ReadTimeout")] + public int ReadTimeout + { + get => _underlyingStream.ReadTimeout; + set => _underlyingStream.ReadTimeout = value; + } + /// + /// + /// Время в миллисекундах, отведенное потоку на операцию записи. + /// + /// Число (int) + [ContextProperty("ТаймаутЗаписи", "WriteTimeout")] + public int WriteTimeout + { + get => _underlyingStream.WriteTimeout; + set => _underlyingStream.WriteTimeout = value; + } /// /// @@ -103,6 +136,8 @@ public void Close() [ContextMethod("Записать", "Write")] public void Write(BinaryDataBuffer buffer, int positionInBuffer, int number) { + if (!CanWrite) + throw new RuntimeException("Попытка записи в поток не поддерживающий запись", "Cannot write to a stream that does not support writing"); _commonImpl.Write(buffer, positionInBuffer, number); } @@ -239,6 +274,8 @@ public long CurrentPosition() [ContextMethod("УстановитьРазмер", "SetSize")] public void SetSize(long size) { + if (!CanWrite) + throw new RuntimeException("Попытка записи в поток не поддерживающий запись", "Cannot write to a stream that does not support writing"); _commonImpl.SetSize(size); } diff --git a/src/ScriptEngine.HostedScript/Library/Binary/GenericStreamImpl.cs b/src/OneScript.StandardLibrary/Binary/GenericStreamImpl.cs similarity index 94% rename from src/ScriptEngine.HostedScript/Library/Binary/GenericStreamImpl.cs rename to src/OneScript.StandardLibrary/Binary/GenericStreamImpl.cs index 0d4a45a90..42c4df7c6 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/GenericStreamImpl.cs +++ b/src/OneScript.StandardLibrary/Binary/GenericStreamImpl.cs @@ -6,9 +6,10 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.IO; +using OneScript.Exceptions; using ScriptEngine.Machine; -namespace ScriptEngine.HostedScript.Library.Binary +namespace OneScript.StandardLibrary.Binary { internal class GenericStreamImpl { @@ -27,8 +28,7 @@ public void Write(BinaryDataBuffer buffer, int positionInBuffer, int number) public void CopyTo(IValue targetStream, int bufferSize = 0) { - IStreamWrapper sw = targetStream.GetRawValue() as IStreamWrapper; - if (sw == null) + if (!(targetStream is IStreamWrapper sw)) throw RuntimeException.InvalidArgumentType("targetStream"); var stream = sw.GetUnderlyingStream(); diff --git a/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs b/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs new file mode 100644 index 000000000..f5f34141a --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/GlobalBinaryData.cs @@ -0,0 +1,566 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Text; +using System.Text; +using OneScript.Contexts; +using OneScript.Exceptions; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +namespace OneScript.StandardLibrary.Binary +{ + /// + /// Глобальный контекст. Операции с двоичными данными. + /// + [GlobalContext(Category = "Процедуры и функции работы с двоичными данными")] + public sealed class GlobalBinaryData : GlobalContextBase + { + private static byte[] HexStringToByteArray(string hex) + { + var newHex = System.Text.RegularExpressions.Regex.Replace(hex, @"[^0-9A-Fa-f]", ""); + int numberChars = newHex.Length; + if (numberChars % 2 == 1) + throw new FormatException("Неверный формат шестнадцатеричной строки"); + + byte[] bytes = new byte[numberChars / 2]; + for (int i = 0; i < numberChars; i += 2) + bytes[i / 2] = Convert.ToByte(newHex.Substring(i, 2), 16); + + return bytes; + } + + private static int[] hexDigitsValues = new[] + { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,16,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 + }; + private static int CharCodeToHex(byte code) + { + return hexDigitsValues[code]; + } + + private static byte[] HexArrayToByteArray(byte[] hex) + { + var bytes = new byte[hex.Length / 2]; + int pos = 0; + + int hexDig1; + int hexDig2 =-1; + for (int i = 0; i < hex.Length; ++i) + { + hexDig1 = CharCodeToHex(hex[i]); + if (hexDig1 < 0) + continue; + + if (hexDig2 < 0) + { + hexDig2 = hexDig1; + continue; + } + + bytes[pos] = (byte)(hexDig2 * 16 + hexDig1); + ++pos; + hexDig2 = -1; + } + + if (pos < bytes.Length) + Array.Resize(ref bytes, pos); + return bytes; + } + + private static string GetStringFromByteBuffer(byte[] buf, Encoding enc) + { + var bom = enc.GetPreamble(); + + if (bom.Length == 0) + { + return enc.GetString(buf); + } + + int startPos = 0; + if (buf.Length >= 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF) + { + enc = Encoding.UTF8; + startPos = 3; + } + else if (buf.Length >= 2 && buf[0] == 0xFF && buf[1] == 0xFE) + { + if (buf.Length >= 4 && buf[2] == 0x00 && buf[3] == 0x00) + { + enc = Encoding.UTF32; // UTF32LE + startPos = 4; + } + else + { + enc = Encoding.Unicode; // UTF16LE + startPos = 2; + } + } + else if (buf.Length >= 2 && buf[0] == 0xFE && buf[1] == 0xFF) + { + enc = Encoding.BigEndianUnicode; // UTF16BE + startPos = 2; + } + else if (buf.Length >= 4 && buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0xFE && buf[3] == 0xFF) + { + enc = new UTF32Encoding(true, true); // UTF32BE with BOM + startPos = 4; + } + + return enc.GetString(buf, startPos, buf.Length - startPos); + } + + private static void CheckAndThrowIfNull(AutoContext obj) where T : AutoContext + { + if (obj == null) + throw RuntimeException.InvalidArgumentType(); + } + + private static void CheckAndThrowIfNull(AutoContext obj, int argNumber, string argName) where T : AutoContext + { + if (obj == null) + throw RuntimeException.InvalidArgumentType(argNumber, argName); + } + + public static IAttachableContext CreateInstance() + { + return new GlobalBinaryData(); + } + + /// + /// Менеджер файловых потоков. + /// + [ContextProperty("ФайловыеПотоки", "FileStreams")] + public FileStreamsManager FileStreams { get; } = new FileStreamsManager(); + + /// + /// Объединяет несколько объектов типа ДвоичныеДанные в один. + /// + /// Массив объектов типа ДвоичныеДанные. + /// Тип: ДвоичныеДанные. + [DeprecatedName("ConcatenateBinaryData")] + [ContextMethod("СоединитьДвоичныеДанные", "ConcatBinaryData")] + public BinaryDataContext ConcatBinaryData(ArrayImpl array) + { + CheckAndThrowIfNull(array); + + // Сделано на int т.к. BinaryContext.Size имеет тип int; + using var stream = new System.IO.MemoryStream(); + + foreach (var cbd in array) + { + byte[] buffer = ((BinaryDataContext)cbd.AsObject()).Buffer; + stream.Write(buffer, 0, buffer.Length); + } + stream.Position = 0; + + return new BinaryDataContext(stream); + } + + /// + /// Разделяет двоичные данные на части заданного размера. Размер задается в байтах. + /// + /// Объект типа ДвоичныеДанные. + /// Размер одной части данных. + /// Массив объектов типа ДвоичныеДанные. + [ContextMethod("РазделитьДвоичныеДанные", "SplitBinaryData")] + public ArrayImpl SplitBinaryData(BinaryDataContext data, long size) + { + CheckAndThrowIfNull(data, 1, nameof(data)); + + if (size <= 0 || size > Int32.MaxValue) + throw RuntimeException.InvalidNthArgumentValue(2); + + ArrayImpl array = new ArrayImpl(); + long dataSize = data.Size(); + + if (dataSize < size) + { + array.Add(data); + return array; + } + + int readedBytes = 0; + var dataStream = data.GetStream(); + + while (readedBytes < dataSize) + { + int bytesToRead = (int)size; + if (bytesToRead > dataSize - readedBytes) + bytesToRead = (int)(dataSize - readedBytes); + + byte[] buffer = new byte[bytesToRead]; + dataStream.Read(buffer, 0, bytesToRead); + readedBytes += bytesToRead; + array.Add(new BinaryDataContext(buffer)); + } + + return array; + } + + /// + /// Преобразует строку в значение типа ДвоичныеДанные с учетом кодировки текста. + /// + /// Строка, которую требуется преобразовать в ДвоичныеДанные. + /// Кодировка текста + /// Определяет, будет ли добавлена метка порядка байт (BOM) кодировки текста в начало данных. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьДвоичныеДанныеИзСтроки", "GetBinaryDataFromString")] + public BinaryDataContext GetBinaryDataFromString(string str, IValue encoding = null, bool addBOM = false) + { + // Получаем кодировку + // Из синтаксис помощника. Если кодировка не задана, используем UTF8 без BOM. + + System.Text.Encoding enc = new UTF8Encoding(false); + if (encoding != null) + enc = TextEncodingEnum.GetEncoding(encoding, addBOM); + + var stream = new System.IO.MemoryStream(); + + var bom = enc.GetPreamble(); + var inputString = enc.GetBytes(str); + + stream.Write(bom, 0, bom.Length); + stream.Write(inputString, 0, inputString.Length); + stream.Position = 0; + + return new BinaryDataContext(stream); + } + + /// + /// Преобразует строку в буфер двоичных данных с учетом кодировки текста. + /// + /// Строка, которую требуется преобразовать в БуферДвоичныхДанных. + /// Кодировка текста + /// Определяет, будет ли добавлена метка порядка байт (BOM) кодировки текста в начало данных. + /// Тип: БуферДвоичныхДанных. + [ContextMethod("ПолучитьБуферДвоичныхДанныхИзСтроки", "GetBinaryDataBufferFromString")] + public BinaryDataBuffer GetBinaryDataBufferFromString(string str, IValue encoding = null, bool addBOM = false) + { + var enc = (encoding != null)? TextEncodingEnum.GetEncoding(encoding, addBOM) : Encoding.UTF8; + + return new BinaryDataBuffer(enc.GetBytes(str)); + } + + /// + /// Преобразует двоичные данные в строку с заданной кодировкой текста. + /// + /// Двоичные данные, которые требуется преобразовать в строку. + /// Кодировка текста + /// Тип: Строка. + [ContextMethod("ПолучитьСтрокуИзДвоичныхДанных", "GetStringFromBinaryData")] + public string GetStringFromBinaryData(BinaryDataContext data, IValue encoding = null) + { + CheckAndThrowIfNull(data, 1, nameof(data)); + + // Получаем кодировку + // Из синтаксис помощника если кодировка не задана используем UTF8 + var enc = (encoding != null) ? TextEncodingEnum.GetEncoding(encoding) : Encoding.UTF8; + + return GetStringFromByteBuffer(data.Buffer, enc); + } + + /// + /// Преобразует буфер двоичных данных в строку с заданной кодировкой текста. + /// + /// Буфер двоичных данных, который требуется преобразовать в строку. + /// Кодировка текста + /// Тип: Строка. + [ContextMethod("ПолучитьСтрокуИзБуфераДвоичныхДанных", "GetStringFromBinaryDataBuffer")] + public string GetStringFromBinaryDataBuffer(BinaryDataBuffer buffer, IValue encoding = null) + { + CheckAndThrowIfNull(buffer, 1, nameof(buffer)); + + var enc = (encoding != null) ? TextEncodingEnum.GetEncoding(encoding) : Encoding.UTF8; + + return GetStringFromByteBuffer(buffer.Bytes, enc); + } + + /// + /// Преобразует строку формата Base64 в двоичные данные. + /// + /// Строка в формате Base64. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьДвоичныеДанныеИзBase64Строки", "GetBinaryDataFromBase64String")] + public BinaryDataContext GetBinaryDataFromBase64String(string str) + { + try + { + return new BinaryDataContext(Convert.FromBase64String(str)); + } + catch + { + return new BinaryDataContext(new byte[0]); + } + } + + /// + /// Преобразует строку формата Base64 в буфер двоичных данных. + /// + /// Строка в формате Base64. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьБуферДвоичныхДанныхИзBase64Строки", "GetBinaryDataBufferFromBase64String")] + public BinaryDataBuffer GetBinaryDataBufferFromBase64String(string str) + { + try + { + return new BinaryDataBuffer(Convert.FromBase64String(str)); + } + catch + { + return new BinaryDataBuffer(new byte[0]); + } + } + + /// + /// Преобразует двоичные данные в строку формата Base64. + /// Полученный текст разбивается на строки длиной 76 символов. + /// В качестве разделителя строк используется сочетание символов CR+LF. + /// + /// Двоичные данные. + /// Тип: Строка. + [ContextMethod("ПолучитьBase64СтрокуИзДвоичныхДанных", "GetBase64StringFromBinaryData")] + public string GetBase64StringFromBinaryData(BinaryDataContext data) + { + CheckAndThrowIfNull(data, 1, nameof(data)); + + return Convert.ToBase64String(data.Buffer, Base64FormattingOptions.InsertLineBreaks); + } + + /// + /// Преобразует буфер двоичных данных в строку формата Base64. + /// Полученный текст разбивается на строки длиной 76 символов. + /// В качестве разделителя строк используется сочетание символов CR+LF. + /// + /// Буфер двоичных данных. + /// Тип: Строка. + [ContextMethod("ПолучитьBase64СтрокуИзБуфераДвоичныхДанных", "GetBase64StringFromBinaryDataBuffer")] + public string GetBase64StringFromBinaryDataBuffer(BinaryDataBuffer buffer) + { + CheckAndThrowIfNull(buffer); + + return Convert.ToBase64String(buffer.Bytes, Base64FormattingOptions.InsertLineBreaks); + } + + /// + /// Преобразует двоичные данные из формата Base64 в ДвоичныеДанные. + /// + /// Двоичные данные, закодированные по методу Base64. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьДвоичныеДанныеИзBase64ДвоичныхДанных", "GetBinaryDataFromBase64BinaryData")] + public BinaryDataContext GetBinaryDataFromBase64BinaryData(BinaryDataContext data) + { + CheckAndThrowIfNull(data); + + try + { + var enc = new UTF8Encoding(false,true); + var str = enc.GetString(data.Buffer, 0, data.Buffer.Length); + return new BinaryDataContext(Convert.FromBase64String(str)); + } + catch + { + return new BinaryDataContext(new byte[0]); + } + } + + /// + /// Преобразует буфер двоичных данных из формата Base64 в БуферДвоичныхДанных. + /// + /// Буфер двоичных данных. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьБуферДвоичныхДанныхИзBase64БуфераДвоичныхДанных", "GetBinaryDataBufferFromBase64BinaryDataBuffer")] + public BinaryDataBuffer GetBinaryDataBufferFromBase64BinaryDataBuffer(BinaryDataBuffer buffer) + { + CheckAndThrowIfNull(buffer); + + try + { + var enc = new UTF8Encoding(false, true); + var str = enc.GetString(buffer.Bytes, 0, buffer.Bytes.Length); + return new BinaryDataBuffer(Convert.FromBase64String(str)); + } + catch + { + return new BinaryDataBuffer(new byte[0]); + } + } + + /// + /// Преобразует двоичные данные в формат Base64. + /// Полученный текст разбивается на строки длиной 76 символов. + /// В качестве разделителя строк используется сочетание символов CR+LF. + /// + /// Двоичные данные. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьBase64ДвоичныеДанныеИзДвоичныхДанных", "GetBase64BinaryDataFromBinaryData")] + public BinaryDataContext GetBase64BinaryDataFromBinaryData(BinaryDataContext data) + { + CheckAndThrowIfNull(data); + + var base64str = Convert.ToBase64String(data.Buffer, Base64FormattingOptions.InsertLineBreaks); + return new BinaryDataContext(Encoding.ASCII.GetBytes(base64str)); + } + + /// + /// Преобразует буфер двоичных данных в формат Base64. + /// Полученный текст разбивается на строки длиной 76 символов. + /// В качестве разделителя строк используется сочетание символов CR+LF. + /// + /// Буфер двоичных данных. + /// Тип: БуферДвоичныхДанных. + [ContextMethod("ПолучитьBase64БуферДвоичныхДанныхИзБуфераДвоичныхДанных", "GetBase64BinaryDataBufferFromBinaryDataBuffer")] + public BinaryDataBuffer GetBase64BinaryDataBufferFromBinaryDataBuffer(BinaryDataBuffer buffer) + { + CheckAndThrowIfNull(buffer); + + var base64str = Convert.ToBase64String(buffer.Bytes, Base64FormattingOptions.InsertLineBreaks); + return new BinaryDataBuffer(Encoding.ASCII.GetBytes(base64str)); + } + + /// + /// Преобразует строку формата Base 16 (Hex) в двоичные данные. + /// + /// Строка в формате Base 16 (Hex). + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьДвоичныеДанныеИзHexСтроки", "GetBinaryDataFromHexString")] + public BinaryDataContext GetBinaryDataFromHexString(string hex) + { + return new BinaryDataContext(HexStringToByteArray(hex)); + } + + /// + /// Преобразует строку в формате Base 16 (Hex) в буфер двоичных данных. + /// + /// Строка в формате Base 16 (Hex). + /// Тип: БуферДвоичныхДанных. + [ContextMethod("ПолучитьБуферДвоичныхДанныхИзHexСтроки", "GetBinaryDataBufferFromHexString")] + public BinaryDataBuffer GetBinaryDataBufferFromHexString(string hex) + { + return new BinaryDataBuffer(HexStringToByteArray(hex)); + } + + /// + /// Преобразует двоичные данные в строку формата Base 16 (Hex). + /// + /// Двоичные данные. + /// Тип: Строка. + [ContextMethod("ПолучитьHexСтрокуИзДвоичныхДанных", "GetHexStringFromBinaryData")] + public string GetHexStringFromBinaryData(BinaryDataContext data) + { + CheckAndThrowIfNull(data); + + return BitConverter.ToString(data.Buffer).Replace("-",""); + } + + /// + /// Преобразует буфер двоичных данных в строку формата Base 16 (Hex). + /// + /// Буфер двоичных данных. + /// Тип: Строка. + [ContextMethod("ПолучитьHexСтрокуИзБуфераДвоичныхДанных", "GetHexStringFromBinaryDataBuffer")] + public string GetHexStringFromBinaryDataBuffer(BinaryDataBuffer buffer) + { + CheckAndThrowIfNull(buffer); + + return BitConverter.ToString(buffer.Bytes).Replace("-",""); + } + + /// + /// Преобразует двоичные данные из формата Base 16 (Hex) в ДвоичныеДанные. + /// + /// Двоичные данные в формате Base 16 (Hex). + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьДвоичныеДанныеИзHexДвоичныхДанных", "GetBinaryDataFromHexBinaryData")] + public BinaryDataContext GetBinaryDataFromHexBinaryData(BinaryDataContext data) + { + CheckAndThrowIfNull(data); + + return new BinaryDataContext(HexArrayToByteArray(data.Buffer)); + } + + /// + /// Преобразует буфер двоичных данных из формата Base 16 (Hex) в БуферДвоичныхДанных. + /// + /// Буфер двоичных данных в формате Base 16 (Hex). + /// Тип: БуферДвоичныхДанных. + [ContextMethod("ПолучитьБуферДвоичныхДанныхИзHexБуфераДвоичныхДанных", "GetBinaryDataBufferFromHexBinaryDataBuffer")] + public BinaryDataBuffer GetBinaryDataBufferFromHexBinaryDataBuffer(BinaryDataBuffer buffer) + { + CheckAndThrowIfNull(buffer); + + return new BinaryDataBuffer(HexArrayToByteArray(buffer.Bytes)); + } + + /// + /// Преобразует двоичные данные в формат Base 16 (Hex). + /// + /// Двоичные данные. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьHexДвоичныеДанныеИзДвоичныхДанных", "GetHexBinaryDataFromBinaryData")] + public BinaryDataContext GetHexBinaryDataFromBinaryData(BinaryDataContext data) + { + var str = GetHexStringFromBinaryData(data); + return GetBinaryDataFromString(str); + } + + /// + /// Преобразует буфер двоичных данных в формат Base 16 (Hex). + /// + /// Буфер двоичных данных. + /// Тип: БуферДвоичныхДанных. + [ContextMethod("ПолучитьHexБуферДвоичныхДанныхИзБуфераДвоичныхДанных", "GetHexBinaryDataBufferFromBinaryDataBuffer")] + public BinaryDataBuffer GetHexBinaryDataBufferFromBinaryDataBuffer(BinaryDataBuffer buffer) + { + var str = GetHexStringFromBinaryDataBuffer(buffer); + return GetBinaryDataBufferFromString(str); + } + + /// + /// Преобразует двоичные данные в буфер двоичных данных. + /// + /// Двоичные данные. + /// Тип: БуферДвоичныхДанных. + [ContextMethod("ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных", "GetBinaryDataBufferFromBinaryData")] + public BinaryDataBuffer GetBinaryDataBufferFromBinaryData(BinaryDataContext data) + { + CheckAndThrowIfNull(data); + + return new BinaryDataBuffer(data.Buffer); + } + + /// + /// Преобразует буфер двоичных данных в значение типа ДвоичныеДанные. + /// + /// Буфер двоичных данных. + /// Тип: ДвоичныеДанные. + [ContextMethod("ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных", "GetBinaryDataFromBinaryDataBuffer")] + public BinaryDataContext GetBinaryDataFromBinaryDataBuffer(BinaryDataBuffer buffer) + { + CheckAndThrowIfNull(buffer); + + return new BinaryDataContext(buffer.Bytes); + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/MemoryStreamContext.cs b/src/OneScript.StandardLibrary/Binary/MemoryStreamContext.cs similarity index 86% rename from src/ScriptEngine.HostedScript/Library/Binary/MemoryStreamContext.cs rename to src/OneScript.StandardLibrary/Binary/MemoryStreamContext.cs index 2c139a40d..cad44c3fa 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/MemoryStreamContext.cs +++ b/src/OneScript.StandardLibrary/Binary/MemoryStreamContext.cs @@ -1,308 +1,350 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.IO; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Binary -{ - /// - /// - /// Представляет собой поток данных, который можно последовательно читать и/или в который можно последовательно писать. - /// Экземпляры объектов данного типа можно получить с помощью различных методов других объектов. - /// - [ContextClass("ПотокВПамяти", "MemoryStream")] - public class MemoryStreamContext : AutoContext, IDisposable, IStreamWrapper - { - private readonly bool _shouldBeCopiedOnClose; - private readonly MemoryStream _underlyingStream; - private readonly GenericStreamImpl _commonImpl; - - MemoryStreamContext() - { - _underlyingStream = new MemoryStream(); - _commonImpl = new GenericStreamImpl(_underlyingStream); - } - - MemoryStreamContext(BinaryDataBuffer bytes) - { - _underlyingStream = new MemoryStream(bytes.Bytes); - _shouldBeCopiedOnClose = !bytes.ReadOnly; - _commonImpl = new GenericStreamImpl(_underlyingStream); - } - - MemoryStreamContext(int capacity) - { - _underlyingStream = new MemoryStream(capacity); - _commonImpl = new GenericStreamImpl(_underlyingStream); - } - - /// - /// - /// Создает поток, в качестве нижележащего хранилища для которого используется заданный байтовый буфер. Ёмкость потока ограничена размером буфера. При выходе за границы буфера будет сгенерировано исключение. - /// Возможность записи в поток зависит от возможности изменения передаваемого буфера. - /// - /// - /// - /// Буфер, на основании которого будет создан поток или начальная емкость будущего потока. - /// - [ScriptConstructor(Name = "По буферу или начальной емкости")] - public static MemoryStreamContext Constructor(IValue bufferOrCapacity) - { - if (bufferOrCapacity.DataType == DataType.Number) - { - return new MemoryStreamContext((int)bufferOrCapacity.AsNumber()); - } - - var memBuf = ContextValuesMarshaller.ConvertParam(bufferOrCapacity); - return new MemoryStreamContext(memBuf); - } - - /// - /// - /// Создает поток в памяти с расширяемой емкостью. Данный вариант можно использовать для работы с достаточно большими объемами данных, т.к. данные хранятся постранично, а не в виде одного последовательного блока. - /// - /// - /// - [ScriptConstructor] - public static MemoryStreamContext Constructor() - { - return new MemoryStreamContext(); - } - - public bool IsReadOnly => !CanWrite; - - /// - /// - /// Признак доступности записи в поток. - /// - /// Булево (Boolean) - [ContextProperty("ДоступнаЗапись", "CanWrite")] - public bool CanWrite => _underlyingStream.CanWrite; - - /// - /// - /// Признак доступности произвольного изменения позиции чтения/записи в потоке. - /// - /// Булево (Boolean) - [ContextProperty("ДоступноИзменениеПозиции", "CanSeek")] - public bool CanSeek => _underlyingStream.CanSeek; - - - /// - /// - /// Признак доступности чтения из потока. - /// - /// Булево (Boolean) - [ContextProperty("ДоступноЧтение", "CanRead")] - public bool CanRead => _underlyingStream.CanRead; - - - /// - /// - /// Вызов данного метода завершает работу с потоком. При попытке вызвать любой метод объекта, кроме метода Закрыть, будет вызвано исключение. - /// При повторном вызове данного метода никаких действий выполняться не будет. - /// Выполняемое действие зависит от используемого типа потока. - /// - /// - [ContextMethod("Закрыть", "Close")] - public void Close() - { - _underlyingStream.Close(); - } - - - /// - /// - /// Записывает в поток заданное количество байтов из буфера по заданному смещению. Если в буфере меньше данных, чем требуется записать, вызывается исключение о недостаточном количестве данных в буфере. - /// Запись в поток возможна только, если поток поддерживает запись. В противном случае при вызове метода будет вызвано исключение. - /// - /// - /// - /// Буфер, из которого выбираются данные для записи. - /// - /// Позиция в буфере, начиная с которой данные будут получены для записи в поток. - /// - /// Количество байт, которые требуется записать. - /// - [ContextMethod("Записать", "Write")] - public void Write(BinaryDataBuffer buffer, int positionInBuffer, int number) - { - _commonImpl.Write(buffer, positionInBuffer, number); - } - - - /// - /// - /// Копирует данные из текущего потока в другой поток. - /// - /// - /// - /// Поток, в который будет выполняться копирование. - /// - /// Размер буфера, используемого при копировании. - /// Если параметр не задан, то система подбирает размер буфера автоматически. - /// - [ContextMethod("КопироватьВ", "CopyTo")] - public void CopyTo(IValue targetStream, int bufferSize = 0) - { - _commonImpl.CopyTo(targetStream, bufferSize); - } - - /// - /// - /// Сдвигает текущую позицию потока на заданное количество байтов относительно начальной позиции. Если указано отрицательное смещение, позиция сдвигается в направлении к началу потока. - /// Если изменение позиции недоступно (ДоступноИзменениеПозиции установлено в Ложь), будет сгенерировано исключение. - /// - /// - /// - /// Количество байтов, на которое нужно передвинуть позицию в потоке. - /// - /// Начальная позиция, от которой отсчитывается смещение. - /// - /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. - [ContextMethod("Перейти", "Seek")] - public long Seek(int offset, StreamPositionEnum initialPosition = StreamPositionEnum.Begin) - { - return _commonImpl.Seek(offset, initialPosition); - } - - - /// - /// - /// Возвращает поток, который разделяет данные и текущую позицию с данным потоком, но не разрешает запись. - /// - /// - /// - /// - [ContextMethod("ПолучитьПотокТолькоДляЧтения", "GetReadonlyStream")] - public GenericStream GetReadonlyStream() - { - return _commonImpl.GetReadonlyStream(); - } - - - /// - /// - /// Выполняет чтение заданного количества байтов в указанный буфер по указанному смещению. Текущая позиция смещается вперед на фактическое количество прочитанных байтов. - /// Чтение из потока возможно только, если поток поддерживает чтение. В противном случае, будет вызвано исключение. - /// При чтении размер целевого буфера не меняется, а его содержимое перезаписывается фактически прочитанными данными. Если в буфере недостаточно места для записи прочитанных данных, происходит ошибка переполнения. - /// - /// - /// - /// Буфер, в который выполняется чтение. - /// - /// Позиция в целевом буфере, начиная с которой требуется записывать данные из потока. - /// - /// Количество байт, которые требуется записать в целевой буфер. - /// - /// - /// Возвращает число прочитанных байт - /// - /// - [ContextMethod("Прочитать", "Read")] - public long Read(BinaryDataBuffer buffer, int positionInBuffer, int number) - { - return _commonImpl.Read(buffer, positionInBuffer, number); - } - - - /// - /// - /// Получает размер данных в байтах. - /// - /// - [ContextMethod("Размер", "Size")] - public long Size() - { - return _commonImpl.Size(); - } - - - /// - /// - /// Сбрасывает все промежуточные буферы и производит запись всех незаписанных данных в целевое устройство. - /// - /// - [ContextMethod("СброситьБуферы", "Flush")] - public void Flush() - { - _commonImpl.Flush(); - } - - - /// - /// - /// Возвращает текущую позицию в потоке. - /// - /// - - /// - /// - /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. - - /// - [ContextMethod("ТекущаяПозиция", "CurrentPosition")] - public long CurrentPosition() - { - return _commonImpl.CurrentPosition(); - } - - - /// - /// - /// Устанавливает размер потока. - /// Если текущий размер превышает заданный, поток будет сокращен до заданного размера, а информация, превышающая заданный размер, будет потеряна. - /// Если текущий размер потока меньше заданного, то содержимое потока между старым и новым размером не определено. - /// - /// - /// - /// Устанавливаемый размер потока. - /// - [ContextMethod("УстановитьРазмер", "SetSize")] - public void SetSize(long size) - { - _commonImpl.SetSize(size); - } - - /// - /// закрывает поток и возвращает результат в виде двоичных данных - /// - /// - [ContextMethod("ЗакрытьИПолучитьДвоичныеДанные")] - public BinaryDataContext CloseAndGetBinaryData() - { - byte[] bytes; - if (_shouldBeCopiedOnClose) - { - bytes = _underlyingStream.ToArray(); - } - else - { - bytes = _underlyingStream.GetBuffer(); - - if (_underlyingStream.Length < bytes.Length) - Array.Resize(ref bytes, (int)_underlyingStream.Length); - } - - _underlyingStream.Close(); - - return new BinaryDataContext(bytes); - } - - public void Dispose() - { - Close(); - } - - public Stream GetUnderlyingStream() - { - return _underlyingStream; - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// + /// Представляет собой поток данных, который можно последовательно читать и/или в который можно последовательно писать. + /// Экземпляры объектов данного типа можно получить с помощью различных методов других объектов. + /// + [ContextClass("ПотокВПамяти", "MemoryStream")] + public class MemoryStreamContext : AutoContext, IDisposable, IStreamWrapper + { + private readonly bool _shouldBeCopiedOnClose; + private readonly MemoryStream _underlyingStream; + private readonly GenericStreamImpl _commonImpl; + + MemoryStreamContext() + { + _underlyingStream = new MemoryStream(); + _commonImpl = new GenericStreamImpl(_underlyingStream); + } + + MemoryStreamContext(BinaryDataBuffer bytes) + { + // Буфер только для чтения не должен копироваться + _shouldBeCopiedOnClose = !bytes.ReadOnly; + if (bytes.ReadOnly && bytes.Size < int.MaxValue) + { + // Используем конструктор с publicly visible буфером + // Иначе метод GetBuffer упадет в методе ЗакрытьИПолучитьДвоичныеДанные + _underlyingStream = new MemoryStream(bytes.Bytes, 0, (int)bytes.Size, false, true); + } + else + { + _underlyingStream = new MemoryStream(bytes.Bytes); + } + _commonImpl = new GenericStreamImpl(_underlyingStream); + } + + MemoryStreamContext(int capacity) + { + _underlyingStream = new MemoryStream(capacity); + _commonImpl = new GenericStreamImpl(_underlyingStream); + } + + /// + /// + /// Создает поток, в качестве нижележащего хранилища для которого используется заданный байтовый буфер. Ёмкость потока ограничена размером буфера. При выходе за границы буфера будет сгенерировано исключение. + /// Возможность записи в поток зависит от возможности изменения передаваемого буфера. + /// + /// + /// + /// Буфер, на основании которого будет создан поток или начальная емкость будущего потока. + /// + [ScriptConstructor(Name = "По буферу или начальной емкости")] + public static MemoryStreamContext Constructor(IValue bufferOrCapacity) + { + if (bufferOrCapacity is BslNumericValue n) + { + return new MemoryStreamContext((int)n); + } + + var memBuf = ContextValuesMarshaller.ConvertValueStrict(bufferOrCapacity); + return new MemoryStreamContext(memBuf); + } + + /// + /// + /// Создает поток в памяти с расширяемой емкостью. Данный вариант можно использовать для работы с достаточно большими объемами данных, т.к. данные хранятся постранично, а не в виде одного последовательного блока. + /// + /// + /// + [ScriptConstructor] + public static MemoryStreamContext Constructor() + { + return new MemoryStreamContext(); + } + + public bool IsReadOnly => !CanWrite; + + /// + /// + /// Признак доступности записи в поток. + /// + /// Булево (Boolean) + [ContextProperty("ДоступнаЗапись", "CanWrite")] + public bool CanWrite => _underlyingStream.CanWrite; + + /// + /// + /// Признак доступности произвольного изменения позиции чтения/записи в потоке. + /// + /// Булево (Boolean) + [ContextProperty("ДоступноИзменениеПозиции", "CanSeek")] + public bool CanSeek => _underlyingStream.CanSeek; + + /// + /// + /// Признак доступности чтения из потока. + /// + /// Булево (Boolean) + [ContextProperty("ДоступноЧтение", "CanRead")] + public bool CanRead => _underlyingStream.CanRead; + + /// + /// + /// Признак доступности установки таймаута чтения/записи в потоке. + /// + /// Булево (Boolean) + [ContextProperty("ДоступенТаймаут", "CanTimeout")] + public bool CanTimeout => _underlyingStream.CanTimeout; + + /// + /// + /// Время в миллисекундах, отведенное потоку на операцию чтения. + /// + /// Число (int) + [ContextProperty("ТаймаутЧтения", "ReadTimeout")] + public int ReadTimeout + { + get => _underlyingStream.ReadTimeout; + set => _underlyingStream.ReadTimeout = value; + } + /// + /// + /// Время в миллисекундах, отведенное потоку на операцию записи. + /// + /// Число (int) + [ContextProperty("ТаймаутЗаписи", "WriteTimeout")] + public int WriteTimeout + { + get => _underlyingStream.WriteTimeout; + set => _underlyingStream.WriteTimeout = value; + } + + /// + /// + /// Вызов данного метода завершает работу с потоком. При попытке вызвать любой метод объекта, кроме метода Закрыть, будет вызвано исключение. + /// При повторном вызове данного метода никаких действий выполняться не будет. + /// Выполняемое действие зависит от используемого типа потока. + /// + /// + [ContextMethod("Закрыть", "Close")] + public void Close() + { + _underlyingStream.Close(); + } + + + /// + /// + /// Записывает в поток заданное количество байтов из буфера по заданному смещению. Если в буфере меньше данных, чем требуется записать, вызывается исключение о недостаточном количестве данных в буфере. + /// Запись в поток возможна только, если поток поддерживает запись. В противном случае при вызове метода будет вызвано исключение. + /// + /// + /// + /// Буфер, из которого выбираются данные для записи. + /// + /// Позиция в буфере, начиная с которой данные будут получены для записи в поток. + /// + /// Количество байт, которые требуется записать. + /// + [ContextMethod("Записать", "Write")] + public void Write(BinaryDataBuffer buffer, int positionInBuffer, int number) + { + _commonImpl.Write(buffer, positionInBuffer, number); + } + + + /// + /// + /// Копирует данные из текущего потока в другой поток. + /// + /// + /// + /// Поток, в который будет выполняться копирование. + /// + /// Размер буфера, используемого при копировании. + /// Если параметр не задан, то система подбирает размер буфера автоматически. + /// + [ContextMethod("КопироватьВ", "CopyTo")] + public void CopyTo(IValue targetStream, int bufferSize = 0) + { + _commonImpl.CopyTo(targetStream, bufferSize); + } + + /// + /// + /// Сдвигает текущую позицию потока на заданное количество байтов относительно начальной позиции. Если указано отрицательное смещение, позиция сдвигается в направлении к началу потока. + /// Если изменение позиции недоступно (ДоступноИзменениеПозиции установлено в Ложь), будет сгенерировано исключение. + /// + /// + /// + /// Количество байтов, на которое нужно передвинуть позицию в потоке. + /// + /// Начальная позиция, от которой отсчитывается смещение. + /// + /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. + [ContextMethod("Перейти", "Seek")] + public long Seek(int offset, StreamPositionEnum initialPosition = StreamPositionEnum.Begin) + { + return _commonImpl.Seek(offset, initialPosition); + } + + + /// + /// + /// Возвращает поток, который разделяет данные и текущую позицию с данным потоком, но не разрешает запись. + /// + /// + /// + /// + [ContextMethod("ПолучитьПотокТолькоДляЧтения", "GetReadonlyStream")] + public GenericStream GetReadonlyStream() + { + return _commonImpl.GetReadonlyStream(); + } + + + /// + /// + /// Выполняет чтение заданного количества байтов в указанный буфер по указанному смещению. Текущая позиция смещается вперед на фактическое количество прочитанных байтов. + /// Чтение из потока возможно только, если поток поддерживает чтение. В противном случае, будет вызвано исключение. + /// При чтении размер целевого буфера не меняется, а его содержимое перезаписывается фактически прочитанными данными. Если в буфере недостаточно места для записи прочитанных данных, происходит ошибка переполнения. + /// + /// + /// + /// Буфер, в который выполняется чтение. + /// + /// Позиция в целевом буфере, начиная с которой требуется записывать данные из потока. + /// + /// Количество байт, которые требуется записать в целевой буфер. + /// + /// + /// Возвращает число прочитанных байт + /// + /// + [ContextMethod("Прочитать", "Read")] + public long Read(BinaryDataBuffer buffer, int positionInBuffer, int number) + { + return _commonImpl.Read(buffer, positionInBuffer, number); + } + + + /// + /// + /// Получает размер данных в байтах. + /// + /// + [ContextMethod("Размер", "Size")] + public long Size() + { + return _commonImpl.Size(); + } + + + /// + /// + /// Сбрасывает все промежуточные буферы и производит запись всех незаписанных данных в целевое устройство. + /// + /// + [ContextMethod("СброситьБуферы", "Flush")] + public void Flush() + { + _commonImpl.Flush(); + } + + + /// + /// + /// Возвращает текущую позицию в потоке. + /// + /// + + /// + /// + /// Числовым типом может быть представлено любое десятичное число. Над данными числового типа определены основные арифметические операции: сложение, вычитание, умножение и деление. Максимально допустимая разрядность числа 38 знаков. + + /// + [ContextMethod("ТекущаяПозиция", "CurrentPosition")] + public long CurrentPosition() + { + return _commonImpl.CurrentPosition(); + } + + + /// + /// + /// Устанавливает размер потока. + /// Если текущий размер превышает заданный, поток будет сокращен до заданного размера, а информация, превышающая заданный размер, будет потеряна. + /// Если текущий размер потока меньше заданного, то содержимое потока между старым и новым размером не определено. + /// + /// + /// + /// Устанавливаемый размер потока. + /// + [ContextMethod("УстановитьРазмер", "SetSize")] + public void SetSize(long size) + { + _commonImpl.SetSize(size); + } + + /// + /// закрывает поток и возвращает результат в виде двоичных данных + /// + /// + [ContextMethod("ЗакрытьИПолучитьДвоичныеДанные", "CloseAndGetBinaryData")] + public BinaryDataContext CloseAndGetBinaryData() + { + byte[] bytes; + if (_shouldBeCopiedOnClose) + { + bytes = _underlyingStream.ToArray(); + } + else + { + bytes = _underlyingStream.GetBuffer(); + + if (_underlyingStream.Length < bytes.Length) + Array.Resize(ref bytes, (int)_underlyingStream.Length); + } + + _underlyingStream.Close(); + + return new BinaryDataContext(bytes); + } + + public void Dispose() + { + Close(); + } + + public Stream GetUnderlyingStream() + { + return _underlyingStream; + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/ReadDataResult.cs b/src/OneScript.StandardLibrary/Binary/ReadDataResult.cs similarity index 96% rename from src/ScriptEngine.HostedScript/Library/Binary/ReadDataResult.cs rename to src/OneScript.StandardLibrary/Binary/ReadDataResult.cs index 8d9b7015c..c08e283c6 100644 --- a/src/ScriptEngine.HostedScript/Library/Binary/ReadDataResult.cs +++ b/src/OneScript.StandardLibrary/Binary/ReadDataResult.cs @@ -1,109 +1,109 @@ -/*---------------------------------------------------------- -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.IO; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Binary -{ - /// - /// - /// Содержит описание результата чтения данных из потока. - /// - [ContextClass("РезультатЧтенияДанных", "ReadDataResult")] - public class ReadDataResult : AutoContext - { - private readonly byte[] _data; - public ReadDataResult(byte[] data) - { - // Поиск по маркеру на данный момент не реализован. - MarkerIndex = -1; - MarkerFound = false; - - _data = data; - Size = _data.Length; - } - - /// - /// - /// Индекс найденного маркера. - /// - /// Число (Number) - [ContextProperty("ИндексМаркера", "MarkerIndex")] - public int MarkerIndex { get; } - - /// - /// - /// Содержит признак обнаружения маркера: - /// - /// - Истина - если в процессе чтения данных был обнаружен маркер. - /// - Ложь - если маркер не был найден или операция не предполагала поиска маркера. - /// - /// Булево (Boolean) - [ContextProperty("МаркерНайден", "MarkerFound")] - public bool MarkerFound { get; } - - /// - /// - /// Размер данных в байтах. В некоторых случаях может быть равен нулю. Например, при чтении двоичных данных из конца потока или при разделении данных. - /// - /// Число (Number) - [ContextProperty("Размер", "Size")] - public int Size { get; } - - - /// - /// - /// Открывает поток для чтения данных. - /// - /// - /// - /// - /// Представляет собой поток данных, который можно последовательно читать и/или в который можно последовательно писать. - /// Экземпляры объектов данного типа можно получить с помощью различных методов других объектов. - /// - [ContextMethod("ОткрытьПотокДляЧтения", "OpenStreamForRead")] - public GenericStream OpenStreamForRead() - { - var stream = new MemoryStream(_data); - return new GenericStream(stream); - } - - /// - /// - /// Получает результат в виде буфера двоичных данных. Необходимо учитывать, что при этом данные будут полностью загружены в оперативную память. Если требуется избежать загрузки оперативной памяти, следует использовать другие методы получения двоичных данных. - /// - /// - /// - /// Коллекция байтов фиксированного размера с возможностью произвольного доступа и изменения по месту. - /// - [ContextMethod("ПолучитьБуферДвоичныхДанных", "GetBinaryDataBuffer")] - public IValue GetBinaryDataBuffer() - { - // вроде бы, 1С делает копию данных для данного метода. - // требуется уточнить правильное поведение - return new BinaryDataBuffer((byte[])_data.Clone()); - } - - /// - /// - /// Получает результат чтения в виде двоичных данных. - /// - /// - /// - /// - /// - [ContextMethod("ПолучитьДвоичныеДанные", "GetBinaryData")] - public IValue GetBinaryData() - { - return new BinaryDataContext(_data); - } - - } -} +/*---------------------------------------------------------- +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.IO; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Binary +{ + /// + /// + /// Содержит описание результата чтения данных из потока. + /// + [ContextClass("РезультатЧтенияДанных", "ReadDataResult")] + public class ReadDataResult : AutoContext + { + private readonly byte[] _data; + public ReadDataResult(byte[] data) + { + // Поиск по маркеру на данный момент не реализован. + MarkerIndex = -1; + MarkerFound = false; + + _data = data; + Size = _data.Length; + } + + /// + /// + /// Индекс найденного маркера. + /// + /// Число (Number) + [ContextProperty("ИндексМаркера", "MarkerIndex")] + public int MarkerIndex { get; } + + /// + /// + /// Содержит признак обнаружения маркера: + /// + /// - Истина - если в процессе чтения данных был обнаружен маркер. + /// - Ложь - если маркер не был найден или операция не предполагала поиска маркера. + /// + /// Булево (Boolean) + [ContextProperty("МаркерНайден", "MarkerFound")] + public bool MarkerFound { get; } + + /// + /// + /// Размер данных в байтах. В некоторых случаях может быть равен нулю. Например, при чтении двоичных данных из конца потока или при разделении данных. + /// + /// Число (Number) + [ContextProperty("Размер", "Size")] + public int Size { get; } + + + /// + /// + /// Открывает поток для чтения данных. + /// + /// + /// + /// + /// Представляет собой поток данных, который можно последовательно читать и/или в который можно последовательно писать. + /// Экземпляры объектов данного типа можно получить с помощью различных методов других объектов. + /// + [ContextMethod("ОткрытьПотокДляЧтения", "OpenStreamForRead")] + public GenericStream OpenStreamForRead() + { + var stream = new MemoryStream(_data); + return new GenericStream(stream); + } + + /// + /// + /// Получает результат в виде буфера двоичных данных. Необходимо учитывать, что при этом данные будут полностью загружены в оперативную память. Если требуется избежать загрузки оперативной памяти, следует использовать другие методы получения двоичных данных. + /// + /// + /// + /// Коллекция байтов фиксированного размера с возможностью произвольного доступа и изменения по месту. + /// + [ContextMethod("ПолучитьБуферДвоичныхДанных", "GetBinaryDataBuffer")] + public IValue GetBinaryDataBuffer() + { + // вроде бы, 1С делает копию данных для данного метода. + // требуется уточнить правильное поведение + return new BinaryDataBuffer((byte[])_data.Clone()); + } + + /// + /// + /// Получает результат чтения в виде двоичных данных. + /// + /// + /// + /// + /// + [ContextMethod("ПолучитьДвоичныеДанные", "GetBinaryData")] + public IValue GetBinaryData() + { + return new BinaryDataContext(_data); + } + + } +} diff --git a/src/OneScript.StandardLibrary/Binary/StreamEnums.cs b/src/OneScript.StandardLibrary/Binary/StreamEnums.cs new file mode 100644 index 000000000..6af701493 --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/StreamEnums.cs @@ -0,0 +1,61 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Binary +{ + [EnumerationType("РежимОткрытияФайла", "FileOpenMode")] + public enum FileOpenModeEnum + { + [EnumValue("Дописать", "Append")] + Append, + [EnumValue("Обрезать", "Truncate")] + Truncate, + [EnumValue("Открыть", "Open")] + Open, + [EnumValue("ОткрытьИлиСоздать", "OpenOrCreate")] + OpenOrCreate, + [EnumValue("Создать", "Create")] + Create, + [EnumValue("СоздатьНовый", "CreateNew")] + CreateNew + } + + [EnumerationType("ДоступКФайлу", "FileAccess")] + public enum FileAccessEnum + { + [EnumValue("Запись", "Write")] + Write, + [EnumValue("Чтение", "Read")] + Read, + [EnumValue("ЧтениеИЗапись", "ReadAndWrite")] + ReadAndWrite + } + + [DeprecatedName("StreamPosition")] + [EnumerationType("ПозицияВПотоке", "PositionInStream")] + public enum StreamPositionEnum + { + [EnumValue("Начало", "Begin")] + Begin, + [EnumValue("Конец", "End")] + End, + [EnumValue("Текущая", "Current")] + Current + } + + [EnumerationType("ПорядокБайтов", "ByteOrder")] + public enum ByteOrderEnum + { + [EnumValue("BigEndian")] + BigEndian, + [EnumValue("LittleEndian")] + LittleEndian + } +} diff --git a/src/OneScript.StandardLibrary/Binary/StreamWithTimeout.cs b/src/OneScript.StandardLibrary/Binary/StreamWithTimeout.cs new file mode 100644 index 000000000..f4fc06c65 --- /dev/null +++ b/src/OneScript.StandardLibrary/Binary/StreamWithTimeout.cs @@ -0,0 +1,127 @@ +/*---------------------------------------------------------- +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.IO; +using System.Threading; + +namespace OneScript.StandardLibrary.Binary +{ + class StreamWithTimeout : Stream + { + private readonly Stream _underlyingStream; + private int _readTimeout; + + public StreamWithTimeout(Stream underlyingStream) + { + _underlyingStream = underlyingStream; + } + + public override bool CanRead => _underlyingStream.CanRead; + + public override bool CanSeek => _underlyingStream.CanSeek; + + public override bool CanWrite => _underlyingStream.CanWrite; + + public override bool CanTimeout => true; + + public override long Length => _underlyingStream.Length; + + public override long Position + { + get + { + return _underlyingStream.Position; + } + set + { + _underlyingStream.Position = value; + } + } + + public override int ReadTimeout + { + get + { + return _readTimeout; + } + set + { + _readTimeout = value; + if (_underlyingStream.CanTimeout) + _underlyingStream.ReadTimeout = value; + } + } + + public override int WriteTimeout + { + get => _underlyingStream.WriteTimeout; + set => _underlyingStream.WriteTimeout = value; + } + + public override void Flush() + { + _underlyingStream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_readTimeout > 0 && !_underlyingStream.CanTimeout) + { + return ReadWithTimeout(buffer, offset, count); + } + else + return _underlyingStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _underlyingStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _underlyingStream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + _underlyingStream.Write(buffer, offset, count); + } + + private int ReadWithTimeout(byte[] buffer, int offset, int count) + { + int read = 0; + + AutoResetEvent gotInput = new AutoResetEvent(false); + Thread inputThread = new Thread(() => + { + try + { + read = _underlyingStream.Read(buffer, offset, count); + gotInput.Set(); + } + catch (ThreadInterruptedException) + { + } + }) + { + IsBackground = true + }; + + inputThread.Start(); + + // Timeout expired? + if (!gotInput.WaitOne(_readTimeout)) + { + inputThread.Interrupt(); + } + + return read; + + } + + } +} diff --git a/src/OneScript.StandardLibrary/Collections/ArrayImpl.cs b/src/OneScript.StandardLibrary/Collections/ArrayImpl.cs new file mode 100644 index 000000000..388125736 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ArrayImpl.cs @@ -0,0 +1,238 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections +{ + [ContextClass("Массив", "Array")] + public class ArrayImpl : AutoCollectionContext, IValueArray + { + private readonly List _values; + + public ArrayImpl() + { + _values = new List(); + } + + public ArrayImpl(IEnumerable values) + { + _values = new List(values); + } + + public override bool IsIndexed => true; + + #region Native Runtime Bridge + + public BslValue this[int index] + { + get => (BslValue)_values[index]; + set => _values[index] = value; + } + + #endregion + + public override IValue GetIndexedValue(IValue index) + { + if(index.SystemType == BasicTypes.Number) + return Get((int)index.AsNumber()); + + return base.GetIndexedValue(index); + } + + public override void SetIndexedValue(IValue index, IValue val) + { + if (index.SystemType == BasicTypes.Number) + Set((int)index.AsNumber(), val); + else + base.SetIndexedValue(index, val); + } + + #region ICollectionContext Members + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _values.Count; + } + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _values.Clear(); + } + + #endregion + + #region IEnumerable Members + + public override IEnumerator GetEnumerator() + { + foreach (var item in _values) + { + yield return item; + } + } + + #endregion + + [ContextMethod("Добавить", "Add")] + public void Add(IValue value = null) + { + if (value == null) + _values.Add(ValueFactory.Create()); + else + _values.Add(value); + } + + [ContextMethod("Вставить", "Insert")] + public void Insert(int index, IValue value = null) + { + if (index < 0) + throw RuntimeException.IndexOutOfRange(); + + if (index > _values.Count) + Extend(index - _values.Count); + + if (value == null) + _values.Insert(index, ValueFactory.Create()); + else + _values.Insert(index, value); + } + + [ContextMethod("Найти", "Find")] + public IValue Find(IValue what) + { + var idx = _values.FindIndex(x => x.StrictEquals(what)); + if(idx < 0) + { + return ValueFactory.Create(); + } + else + { + return ValueFactory.Create(idx); + } + } + + [ContextMethod("Удалить", "Delete")] + public void Remove(int index) + { + if (index < 0 || index >= _values.Count) + throw RuntimeException.IndexOutOfRange(); + + _values.RemoveAt(index); + } + + [ContextMethod("ВГраница", "UBound")] + public int UpperBound() + { + return _values.Count - 1; + } + + [ContextMethod("Получить", "Get")] + public IValue Get(int index) + { + if (index < 0 || index >= _values.Count) + throw RuntimeException.IndexOutOfRange(); + + return _values[index]; + } + + [ContextMethod("Установить", "Set")] + public void Set(int index, IValue value) + { + if (index < 0 || index >= _values.Count) + throw RuntimeException.IndexOutOfRange(); + + _values[index] = value; + } + + private void Extend(int count) + { + for (int i = 0; i < count; ++i) + { + _values.Add(ValueFactory.Create()); + } + } + + private static void FillArray(ArrayImpl currentArray, int bound) + { + for (int i = 0; i < bound; i++) + { + currentArray._values.Add(ValueFactory.Create()); + } + } + + private static IValue CloneArray(ArrayImpl cloneable) + { + ArrayImpl clone = new ArrayImpl(); + foreach (var item in cloneable._values) + { + clone._values.Add(item ?? ValueFactory.Create()); + } + return clone; + } + + [ScriptConstructor] + public static ArrayImpl Constructor() + { + return new ArrayImpl(); + } + + /// + /// Позволяет задать измерения массива при его создании + /// + /// Числовые размерности массива. Например, "Массив(2,3)", создает двумерный массив 2х3. + /// + [ScriptConstructor(Name = "По количеству элементов")] + public static ArrayImpl Constructor(IValue[] dimensions) + { + if (dimensions.Length == 1 && dimensions[0] is FixedArrayImpl fa) + { + return Constructor(fa); + } + + ArrayImpl cloneable = null; + for (int dim = dimensions.Length - 1; dim >= 0; dim--) + { + if (dimensions[dim] == null) + throw RuntimeException.InvalidNthArgumentType(dim + 1); + + int bound = (int)dimensions[dim].AsNumber(); + if (bound <= 0) + throw RuntimeException.InvalidNthArgumentValue(dim + 1); + + var newInst = new ArrayImpl(); + FillArray(newInst, bound); + if(cloneable != null) + { + for (int i = 0; i < bound; i++) + { + newInst._values[i] = CloneArray(cloneable); + } + } + cloneable = newInst; + + } + + return cloneable; + + } + + [ScriptConstructor(Name = "На основании фиксированного массива")] + public static ArrayImpl Constructor(FixedArrayImpl fixedArray) + { + return new ArrayImpl(fixedArray); + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/Exceptions/ColumnException.cs b/src/OneScript.StandardLibrary/Collections/Exceptions/ColumnException.cs new file mode 100644 index 000000000..7867f1015 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/Exceptions/ColumnException.cs @@ -0,0 +1,54 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Exceptions; +using OneScript.Localization; + +namespace OneScript.StandardLibrary.Collections.Exceptions +{ + public class ColumnException : RuntimeException + { + public ColumnException(BilingualString message, Exception innerException) : base(message, + innerException) + { + } + + public ColumnException(BilingualString message) : base(message) + { + } + + public static ColumnException WrongColumnName() + { + return new ColumnException(new BilingualString( + "Неверное имя колонки", + "Wrong column name")); + } + + public static ColumnException WrongColumnName(string columnName) + { + return new ColumnException(new BilingualString( + $"Неверное имя колонки '{columnName}'", + $"Wrong column name '{columnName}'")); + } + + public static ColumnException DuplicatedColumnName(string columnName) + { + return new ColumnException(new BilingualString( + $"Колонка '{columnName}' уже есть", + $"Column '{columnName}' already exists")); + } + + + public static ColumnException ColumnsMixed(string columnName) + { + return new ColumnException(new BilingualString( + $"Колонка '{columnName}' не может одновременно быть колонкой группировки и колонкой суммирования", + $"Column '{columnName}' cannot be both grouping column and summation column")); + } + + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Collections/FixedArrayImpl.cs b/src/OneScript.StandardLibrary/Collections/FixedArrayImpl.cs new file mode 100644 index 000000000..fcedb13bc --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/FixedArrayImpl.cs @@ -0,0 +1,88 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections +{ + [ContextClass("ФиксированныйМассив", "FixedArray")] + public class FixedArrayImpl : AutoCollectionContext, IValueArray + { + private readonly ArrayImpl _array; + + public FixedArrayImpl(ArrayImpl source) + { + _array = new ArrayImpl(); + foreach (var Value in source) + { + _array.Add(Value); + } + } + + public override bool IsIndexed + { + get + { + return true; + } + } + + public override IValue GetIndexedValue(IValue index) + { + return _array.GetIndexedValue(index); + } + + public override void SetIndexedValue(IValue index, IValue val) + { + throw new RuntimeException("Индексированное значение доступно только для чтения"); + } + + #region ICollectionContext Members + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _array.Count(); + } + + public override IEnumerator GetEnumerator() + { + return _array.GetEnumerator(); + } + + #endregion + + + [ContextMethod("Найти", "Find")] + public IValue Find(IValue what) + { + return _array.Find(what); + } + + [ContextMethod("ВГраница", "UBound")] + public int UpperBound() + { + return _array.UpperBound(); + } + + [ContextMethod("Получить", "Get")] + public IValue Get(int index) + { + return _array.Get(index); + } + + [ScriptConstructor(Name = "На основании обычного массива")] + public static FixedArrayImpl Constructor(ArrayImpl source) + { + return new FixedArrayImpl(source); + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/FixedMapImpl.cs b/src/OneScript.StandardLibrary/Collections/FixedMapImpl.cs new file mode 100644 index 000000000..37962bc73 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/FixedMapImpl.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 OneScript.Contexts; +using OneScript.Exceptions; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections +{ + [ContextClass("ФиксированноеСоответствие", "FixedMap")] + public class FixedMapImpl : AutoCollectionContext + { + + private readonly MapImpl _map; + + public FixedMapImpl(MapImpl source) + { + _map = new MapImpl(); + foreach (KeyAndValueImpl KV in source) + { + _map.Insert(KV.Key, KV.Value); + } + } + + public override bool IsIndexed + { + get + { + return true; + } + } + + public override IValue GetIndexedValue(IValue index) + { + if(_map.ContainsKey(index)) + return _map.GetIndexedValue(index); + + throw new RuntimeException("Значение, соответствующее ключу, не задано"); + } + + public override void SetIndexedValue(IValue index, IValue val) + { + throw new RuntimeException("Индексированное значение доступно только для чтения"); + } + + public override bool IsPropReadable(int propNum) + { + return _map.IsPropReadable(propNum); + } + + public override bool IsPropWritable(int propNum) + { + return _map.IsPropWritable(propNum); + } + + #region ICollectionContext Members + + [ContextMethod("Получить", "Get")] + public IValue Retrieve(IValue key) + { + return _map.GetIndexedValue(key); + } + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _map.Count(); + } + + public override IEnumerator GetEnumerator() + { + return _map.GetEnumerator(); + } + + #endregion + + [ScriptConstructor(Name = "Из соответствия")] + public static FixedMapImpl Constructor(MapImpl source) + { + return new FixedMapImpl(source); + } + } + +} diff --git a/src/OneScript.StandardLibrary/Collections/FixedStructureImpl.cs b/src/OneScript.StandardLibrary/Collections/FixedStructureImpl.cs new file mode 100644 index 000000000..a208576d9 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/FixedStructureImpl.cs @@ -0,0 +1,195 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Types; + +namespace OneScript.StandardLibrary.Collections +{ + [ContextClass("ФиксированнаяСтруктура", "FixedStructure")] + public class FixedStructureImpl : DynamicPropertiesAccessor, ICollectionContext + { + private readonly StructureImpl _structure = new StructureImpl(); + + private static readonly TypeDescriptor typeDescriptor = typeof(StructureImpl).GetTypeFromClassMarkup(); + + public FixedStructureImpl(StructureImpl structure) + { + DefineType(typeDescriptor); + foreach (KeyAndValueImpl keyValue in structure) + _structure.Insert(keyValue.Key.ToString(), keyValue.Value); + } + + public FixedStructureImpl(string strProperties, params IValue[] values) + { + DefineType(typeDescriptor); + _structure = new StructureImpl(strProperties, values); + } + + [ContextMethod("Свойство", "Property")] + public bool HasProperty(string name, [ByRef] IVariable value = null) + { + return _structure.HasProperty(name, value); + } + + public override bool IsPropWritable(int propNum) + { + return false; + } + + public override IValue GetPropValue(int propNum) + { + return _structure.GetPropValue(propNum); + } + + public override void SetPropValue(int propNum, IValue newVal) + { + throw new RuntimeException("Свойство только для чтения"); + } + + public override int GetPropertyNumber(string name) + { + return _structure.GetPropertyNumber(name); + } + + public override int GetPropCount() + { + return _structure.GetPropCount(); + } + + public override BslMethodInfo GetMethodInfo(int methodNumber) + { + return _methods.GetRuntimeMethod(methodNumber); + } + + public override BslPropertyInfo GetPropertyInfo(int propertyNumber) + { + var realProp = _structure.GetPropertyInfo(propertyNumber); + return BslPropertyBuilder.Create() + .Name(realProp.Name) + .CanRead(true) + .CanWrite(false) + .ReturnType(realProp.PropertyType) + .DeclaringType(GetType()) + .Build(); + } + + public override string GetPropName(int propNum) + { + return _structure.GetPropName(propNum); + } + + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + var binding = _methods.GetCallableDelegate(methodNumber); + try + { + binding(this, arguments, process); + } + catch (System.Reflection.TargetInvocationException e) + { + throw e.InnerException; + } + } + + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + var binding = _methods.GetCallableDelegate(methodNumber); + try + { + retValue = binding(this, arguments, process); + } + catch (System.Reflection.TargetInvocationException e) + { + throw e.InnerException; + } + } + + public override int GetMethodNumber(string name) + { + return _methods.FindMethod(name); + } + + #region IReflectableContext Members + + public override int GetMethodsCount() + { + return _methods.Count; + } + + #endregion + + #region ICollectionContext Members + + [ContextMethod("Количество", "Count")] + public int Count() + { + return _structure.Count(); + } + + public int Count(IBslProcess process) => Count(); + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + return _structure.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return _structure.GetEnumerator(); + } + + #endregion + + private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); + + /// + /// Создает фиксированную структуру по исходной структуре + /// + /// Исходная структура + //[ScriptConstructor(Name = "Из структуры")] + private static FixedStructureImpl Constructor(StructureImpl structObject) + { + return new FixedStructureImpl(structObject); + } + + /// + /// Создает фиксированную структуру по структуре либо заданному перечню свойств и значений + /// + /// Структура либо строка с именами свойств, указанными через запятую. + /// Только для перечня свойств: + /// Значения свойств. Каждое значение передается, как отдельный параметр. + [ScriptConstructor(Name = "По ключам и значениям")] + public static FixedStructureImpl Constructor(IValue param1, IValue[] args) + { + return param1 switch + { + null => new FixedStructureImpl(""), + BslStringValue s => new FixedStructureImpl(s, args), + StructureImpl structure => new FixedStructureImpl(structure), + + _ => throw new RuntimeException("В качестве параметра для конструктора можно передавать только Структура или Ключи и Значения") + }; + } + + } +} diff --git a/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs new file mode 100644 index 000000000..73f13347e --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndex.cs @@ -0,0 +1,148 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.Indexes +{ + [ContextClass("ИндексКоллекции", "CollectionIndex")] + public class CollectionIndex : AutoCollectionContext + { + private readonly List _fields = new List(); + private readonly IIndexCollectionSource _source; + + private readonly Dictionary> _data = + new Dictionary>(); + + public CollectionIndex(IIndexCollectionSource source, IEnumerable fields) + { + foreach (var field in fields) + { + if (field is ValueTable.ValueTableColumn column) + column.AddToIndex(); + _fields.Add(field); + } + + _source = source; + foreach (var value in _source) + { + ElementAdded(value); + } + } + + internal bool CanBeUsedFor(IEnumerable searchFields) + { + return _fields.Count != 0 && _fields.All(f => searchFields.Contains(f)); + } + + private CollectionIndexKey IndexKey(PropertyNameIndexAccessor source) + { + return CollectionIndexKey.Extract(source, _fields); + } + + public override string ToString() + { + return string.Join(", ", _fields.Select(field => _source.GetName(field))); + } + + public IEnumerable GetData(PropertyNameIndexAccessor searchCriteria) + { + var key = IndexKey(searchCriteria); + return _data.TryGetValue(key, out var filteredData) ? filteredData : Enumerable.Empty(); + } + + internal void FieldRemoved(IValue field) + { + if (_fields.Contains(field)) + { + while (_fields.Contains(field)) + { + if (field is ValueTable.ValueTableColumn column) + column.DeleteFromIndex(); + + _fields.Remove(field); + } + Rebuild(); + } + } + + internal void ExcludeFields() + { + foreach (var field in _fields) + { + if (field is ValueTable.ValueTableColumn column) + column.DeleteFromIndex(); + } + } + + internal void ElementAdded(PropertyNameIndexAccessor element) + { + var key = CollectionIndexKey.Extract(element, _fields); + if (_data.TryGetValue(key, out var set)) + { + set.Add(element); + } + else + { + _data.Add(key, new HashSet { element }); + } + } + + internal void ElementRemoved(PropertyNameIndexAccessor element) + { + var key = CollectionIndexKey.Extract(element, _fields); + if (_data.TryGetValue(key, out var set)) + { + set.Remove(element); + } + } + + internal void Clear() => _data.Clear(); + + internal void Rebuild() + { + _data.Clear(); + foreach (var value in _source) + { + ElementAdded(value); + } + } + + public override IValue GetIndexedValue(IValue index) + { + if (index is BslNumericValue numericValue) + { + var numeric = numericValue.AsNumber(); + if (numeric >= 0 && numeric < _fields.Count) + { + + return ValueFactory.Create(_source.GetName(_fields[decimal.ToInt32(numeric)])); + } + } + throw RuntimeException.InvalidArgumentValue(); + } + + public override int Count() + { + return _fields.Count; + } + + public override IEnumerator GetEnumerator() + { + foreach (var field in _fields) + { + yield return ValueFactory.Create(_source.GetName(field)); + } + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndexKey.cs b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndexKey.cs new file mode 100644 index 000000000..6e716a355 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/Indexes/CollectionIndexKey.cs @@ -0,0 +1,83 @@ +/*---------------------------------------------------------- +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 ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.Indexes +{ + public class CollectionIndexKey + { + private readonly IDictionary _values; + private readonly int _hashCode; + + private CollectionIndexKey(IDictionary values) + { + _values = values; + _hashCode = EvalHashCode(values.Values); + } + + private static int EvalHashCode(IEnumerable values) + { + var result = 0; + foreach (var value in values) + { + result ^= value.GetHashCode(); + } + + return result; + } + + public static CollectionIndexKey Extract(PropertyNameIndexAccessor source, IEnumerable fields) + { + var values = new Dictionary(); + + foreach (var field in fields) + { + var value = source.GetIndexedValue(field); + values.TryAdd(field, value); + } + + return new CollectionIndexKey(values); + } + + public override bool Equals(object obj) + { + if (object.ReferenceEquals(this, obj)) return true; + if (obj is CollectionIndexKey casted) + { + var allKeys = CombinedKeysSet(casted); + + foreach (var key in allKeys) + { + var thisValue = _values[key]; + var otherValue = casted._values[key]; + + if (!thisValue.Equals(otherValue)) return false; + } + + return true; + } + + return false; + } + + public override int GetHashCode() + { + return _hashCode; + } + + private ISet CombinedKeysSet(CollectionIndexKey other) + { + var allKeys = new HashSet(_values.Keys.ToHashSet()); + allKeys.UnionWith(other._values.Keys.ToHashSet()); + return allKeys; + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Collections/Indexes/IIndexCollectionSource.cs b/src/OneScript.StandardLibrary/Collections/Indexes/IIndexCollectionSource.cs new file mode 100644 index 000000000..9ffd3d8f3 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/Indexes/IIndexCollectionSource.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.Indexes +{ + public interface IIndexCollectionSource : ICollectionContext + { + /// + /// Возвращает имя поля + /// + /// Поле + /// Строка. Имя поля + string GetName(IValue field); + + /// + /// Возвращает поле по имени. + /// + /// Имя поля + /// Поле + IValue GetField(string name); + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Collections/KeyAndValueImpl.cs b/src/OneScript.StandardLibrary/Collections/KeyAndValueImpl.cs new file mode 100644 index 000000000..c3390eaad --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/KeyAndValueImpl.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Diagnostics; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections +{ + [ContextClass("КлючИЗначение", "KeyAndValue")] + public class KeyAndValueImpl : AutoContext + { + public KeyAndValueImpl(IValue key, IValue value) + { + Debug.Assert(!(key is IValueReference)); + Debug.Assert(!(value is IValueReference)); + + Key = key; + Value = value; + } + + [ContextProperty("Ключ", "Key")] + public IValue Key { get; } + + [ContextProperty("Значение", "Value")] + public IValue Value { get; } + + public override IValue GetPropValue(int propNum) + { + return propNum == 0 ? Key : Value; + } + + } +} diff --git a/src/OneScript.StandardLibrary/Collections/MapImpl.cs b/src/OneScript.StandardLibrary/Collections/MapImpl.cs new file mode 100644 index 000000000..0166a9f1e --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/MapImpl.cs @@ -0,0 +1,136 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections +{ + /// + /// Стандартный ассоциативный массив (словарь), соответствие вида Ключ->Значение. + /// Ключом данной коллекции может выступать произвольный объект. + /// Доступен обход в цикле Для Каждого Из, элементами коллекции выступают объекты вида КлючИЗначение. + /// + /// + [ContextClass("Соответствие", "Map")] + public class MapImpl : AutoCollectionContext + { + private readonly Dictionary _content = new Dictionary(new GenericIValueComparer()); + + public MapImpl() + { + } + + public MapImpl(IEnumerable source) + { + foreach (var kv in source) + { + _content.Add(kv.Key, kv.Value); + } + } + + public override bool IsIndexed => true; + + public override IValue GetIndexedValue(IValue index) + { + IValue result; + if (!_content.TryGetValue(index, out result)) + { + result = ValueFactory.Create(); + } + + return result; + } + + public override void SetIndexedValue(IValue index, IValue val) + { + _content[index] = val; + } + + public override bool IsPropReadable(int propNum) + { + return false; + } + + public override bool IsPropWritable(int propNum) + { + return false; + } + + internal bool ContainsKey(IValue key) + { + return _content.ContainsKey(key); + } + + public IEnumerable Keys() + { + foreach (var key in _content.Keys) + { + yield return key; + } + } + + #region ICollectionContext Members + + [ContextMethod("Вставить", "Insert")] + public void Insert(IValue key, IValue val = null) + { + SetIndexedValue(key, val ?? ValueFactory.Create() ); + } + + [ContextMethod("Получить", "Get")] + public IValue Retrieve(IValue key) + { + return GetIndexedValue(key); + } + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _content.Count; + } + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _content.Clear(); + } + + [ContextMethod("Удалить", "Delete")] + public void Delete(IValue key) + { + _content.Remove(key); + } + #endregion + + #region IEnumerable Members + + public override IEnumerator GetEnumerator() + { + foreach (var item in _content) + { + yield return new KeyAndValueImpl(item.Key, item.Value); + } + } + + #endregion + + [ScriptConstructor] + public static MapImpl Constructor() + { + return new MapImpl(); + } + + [ScriptConstructor(Name = "Из фиксированного соответствия")] + public static MapImpl Constructor(FixedMapImpl source) + { + return new MapImpl(source); + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/MapWrapper.cs b/src/OneScript.StandardLibrary/Collections/MapWrapper.cs new file mode 100644 index 000000000..814007714 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/MapWrapper.cs @@ -0,0 +1,139 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Execution; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections +{ + /// + /// Класс Соответствие, который оборачивает произвольный Dictionary + /// + public class MapWrapper : AutoCollectionContext + { + private readonly IDictionary _originalMap; + + private static ContextMethodsMapper> _methods = + new ContextMethodsMapper>(); + + public static MapWrapper Create(ITypeManager typeManager, IDictionary originalMap) + { + var type = typeManager.GetTypeByFrameworkType(typeof(MapImpl)); + return new MapWrapper(type, originalMap); + } + + private MapWrapper( + TypeDescriptor mapType, + IDictionary originalMap) : base(mapType) + { + _originalMap = originalMap; + } + + public override bool IsIndexed => true; + + [ContextMethod("Количество", "Count")] + public override int Count() => _originalMap.Count; + + public override IEnumerator GetEnumerator() + { + return _originalMap.Select(entry => new KeyAndValueImpl( + ContextValuesMarshaller.ConvertReturnValue(entry.Key), + ContextValuesMarshaller.ConvertReturnValue(entry.Value))).GetEnumerator(); + } + + public override IValue GetIndexedValue(IValue index) + { + if (!_originalMap.TryGetValue(ContextValuesMarshaller.ConvertParam(index), out var mapValue)) + { + return ValueFactory.Create(); + } + + return ContextValuesMarshaller.ConvertReturnValue(mapValue); + } + + public override void SetIndexedValue(IValue index, IValue val) + { + if (index.SystemType != BasicTypes.Undefined) + { + var mapKey = ContextValuesMarshaller.ConvertParam(index); + var mapVal = ContextValuesMarshaller.ConvertParam(val); + _originalMap[mapKey] = mapVal; + } + } + + public override bool IsPropReadable(int propNum) + { + return false; + } + + public override bool IsPropWritable(int propNum) + { + return false; + } + + [ContextMethod("Вставить", "Insert")] + public void Insert(IValue key, IValue val = null) + { + SetIndexedValue(key, val ?? ValueFactory.Create()); + } + + [ContextMethod("Получить", "Get")] + public IValue Retrieve(IValue key) + { + return GetIndexedValue(key); + } + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _originalMap.Clear(); + } + + [ContextMethod("Удалить", "Delete")] + public void Delete(IValue key) + { + _originalMap.Remove(ContextValuesMarshaller.ConvertParam(key)); + } + + public override int GetMethodNumber(string name) => _methods.FindMethod(name); + + public override int GetMethodsCount() => _methods.Count; + + public override BslMethodInfo GetMethodInfo(int methodNumber) => _methods.GetRuntimeMethod(methodNumber); + + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + var binding = _methods.GetCallableDelegate(methodNumber); + try + { + binding(this, arguments, process); + } + catch (System.Reflection.TargetInvocationException e) + { + throw e.InnerException!; + } + } + + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + var binding = _methods.GetCallableDelegate(methodNumber); + try + { + retValue = binding(this, arguments, process); + } + catch (System.Reflection.TargetInvocationException e) + { + throw e.InnerException!; + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/SortDirectionEnum.cs b/src/OneScript.StandardLibrary/Collections/SortDirectionEnum.cs similarity index 75% rename from src/ScriptEngine.HostedScript/Library/SortDirectionEnum.cs rename to src/OneScript.StandardLibrary/Collections/SortDirectionEnum.cs index 53fc5e45a..bb5a454cd 100644 --- a/src/ScriptEngine.HostedScript/Library/SortDirectionEnum.cs +++ b/src/OneScript.StandardLibrary/Collections/SortDirectionEnum.cs @@ -5,15 +5,17 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Collections { [EnumerationType("НаправлениеСортировки", "SortDirection")] public enum SortDirectionEnum { - [EnumItem("Возр", "ASC")] + [EnumValue("Возр", "ASC")] Asc, - [EnumItem("Убыв", "DESC")] + [EnumValue("Убыв", "DESC")] Desc } } diff --git a/src/OneScript.StandardLibrary/Collections/StructureImpl.cs b/src/OneScript.StandardLibrary/Collections/StructureImpl.cs new file mode 100644 index 000000000..e3e8a4be4 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/StructureImpl.cs @@ -0,0 +1,286 @@ +/*---------------------------------------------------------- +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 OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Types; + +namespace OneScript.StandardLibrary.Collections +{ + /// + /// Универсальный объект, хранящий значения по именам свойств. + /// Доступ к элементам структуры возможен через точку, или по имени свойства в квадратных скобках [] + /// + /// Возможен обход в цикле Для Каждого Из. При обходе, элементами коллекции являются объекты типа КлючИЗначение + /// + /// + /// Пользователь = Новый Структура("Логин, Адрес, Пароль"); + /// Пользователь.Логин = "user"; + /// Пользователь.Адрес = "somemail@server.com"; + /// Пользователь.Пароль = "password"; + /// + /// + [ContextClass("Структура", "Structure")] + public class StructureImpl : DynamicPropertiesAccessor, ICollectionContext, IDebugPresentationAcceptor + { + private readonly List _values = new List(); + private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); + + private static readonly TypeDescriptor typeDescriptor = typeof(StructureImpl).GetTypeFromClassMarkup(); + + public StructureImpl() + { + DefineType(typeDescriptor); + } + + public StructureImpl(string strProperties, params IValue[] values) + { + var nprop = 0; + foreach (var item in strProperties.Split(',')) + { + var prop = item.Trim(); + if (prop.Equals(string.Empty)) + continue; + + Insert(prop, nprop < values.Length ? values[nprop] : null); + ++nprop; + } + } + + public StructureImpl(FixedStructureImpl structure) + { + foreach (KeyAndValueImpl keyValue in structure) + { + Insert(keyValue.Key.ToString(), keyValue.Value); + } + } + + [ContextMethod("Вставить")] + public void Insert(string name, IValue val = null) + { + if (!Utils.IsValidIdentifier(name)) + throw InvalidPropertyNameException(name); + + var num = RegisterProperty(name); + if (num == _values.Count) + { + _values.Add(null); + } + + if (val == null) + { + val = ValueFactory.Create(); + } + + SetPropValue(num, val); + } + + [ContextMethod("Удалить", "Delete")] + public void Remove(string name) + { + if (!Utils.IsValidIdentifier(name)) + throw InvalidPropertyNameException(name); + + int propIndex; + try + { + propIndex = GetPropertyNumber(name); + } + catch (PropertyAccessException) + { + return; + } + + _values.RemoveAt(propIndex); + RemoveProperty(name); + } + + [ContextMethod("Свойство", "Property")] + public bool HasProperty(string name, [ByRef] IVariable value = null) + { + if (!Utils.IsValidIdentifier(name)) + throw InvalidPropertyNameException(name); + + int propIndex; + try + { + propIndex = GetPropertyNumber(name); + } + catch (PropertyAccessException) + { + if(value != null) + value.Value = ValueFactory.Create(); + return false; + } + + if(value != null) + value.Value = GetPropValue(propIndex); + + return true; + } + + public override IValue GetPropValue(int propNum) + { + return _values[propNum]; + } + + public override void SetPropValue(int propNum, IValue newVal) + { + _values[propNum] = newVal; + } + + public override int GetPropCount() + { + return _values.Count; + } + + public override string GetPropName(int propNum) + { + return GetPropertyName(propNum); + } + + public override BslMethodInfo GetMethodInfo(int methodNumber) + { + return _methods.GetRuntimeMethod(methodNumber); + } + + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + var binding = _methods.GetCallableDelegate(methodNumber); + try + { + binding(this, arguments, process); + } + catch (System.Reflection.TargetInvocationException e) + { + throw e.InnerException; + } + } + + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + var binding = _methods.GetCallableDelegate(methodNumber); + try + { + retValue = binding(this, arguments, process); + } + catch (System.Reflection.TargetInvocationException e) + { + throw e.InnerException; + } + } + + public override int GetMethodNumber(string name) + { + return _methods.FindMethod(name); + } + + #region IReflectableContext Members + + public override int GetMethodsCount() + { + return _methods.Count; + } + + #endregion + + #region ICollectionContext Members + + [ContextMethod("Количество", "Count")] + public int Count() + { + return _values.Count; + } + + public int Count(IBslProcess process) => Count(); + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + ClearProperties(); + _values.Clear(); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() + { + foreach (var item in GetDynamicProperties()) + { + yield return new KeyAndValueImpl( + ValueFactory.Create(item.Key), + GetPropValue(item.Value)); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + [ScriptConstructor] + public static StructureImpl Constructor() + { + return new StructureImpl(); + } + + /// + /// Создает структуру по фиксированной структуре + /// + /// Исходная структура + //[ScriptConstructor(Name = "Из фиксированной структуры")] + private static StructureImpl Constructor(FixedStructureImpl fixedStruct) + { + return new StructureImpl(fixedStruct); + } + + /// + /// Создает структуру по заданному перечню свойств и значений + /// + /// Фиксированная структура либо строка с именами свойств, указанными через запятую. + /// Только для перечня свойств: + /// Значения свойств. Каждое значение передается, как отдельный параметр. + [ScriptConstructor(Name = "По ключам и значениям")] + public static StructureImpl Constructor(IValue param1, IValue[] args) + { + return param1 switch + { + null => new StructureImpl(), + BslStringValue s => new StructureImpl((string)s, args), + FixedStructureImpl fixedstr => new StructureImpl(fixedstr), + + _ => throw new RuntimeException("В качестве параметра для конструктора можно передавать только ФиксированнаяСтруктура или Ключи и Значения") + }; + } + + + private static RuntimeException InvalidPropertyNameException( string name ) + { + return new RuntimeException($"Задано неправильное имя атрибута структуры '{name}'"); + } + + void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) + { + visitor.ShowProperties(this); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/ValueList/ValueListImpl.cs b/src/OneScript.StandardLibrary/Collections/ValueList/ValueListImpl.cs similarity index 87% rename from src/ScriptEngine.HostedScript/Library/ValueList/ValueListImpl.cs rename to src/OneScript.StandardLibrary/Collections/ValueList/ValueListImpl.cs index ae85ea2e2..6f3c1b288 100644 --- a/src/ScriptEngine.HostedScript/Library/ValueList/ValueListImpl.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueList/ValueListImpl.cs @@ -4,18 +4,25 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; + +using System; using System.Collections.Generic; using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.ValueList +namespace OneScript.StandardLibrary.Collections.ValueList { /// /// Стандартная универсальная коллекция системы 1С:Предприятие 8 /// [ContextClass("СписокЗначений", "ValueList")] - public class ValueListImpl : AutoContext, ICollectionContext, IEnumerable + public class ValueListImpl : AutoCollectionContext { readonly List _items; public ValueListImpl() @@ -33,7 +40,7 @@ public override bool IsIndexed public override IValue GetIndexedValue(IValue index) { - if (index.DataType == Machine.DataType.Number) + if (index.SystemType == BasicTypes.Number) { return GetValue(index); } @@ -45,7 +52,7 @@ public override IValue GetIndexedValue(IValue index) public override void SetIndexedValue(IValue index, IValue val) { - if (index.DataType == Machine.DataType.Number) + if (index.SystemType == BasicTypes.Number) { throw new RuntimeException("Индексированное значение доступно только для чтения"); } @@ -76,7 +83,7 @@ public ValueListItem GetValue(IValue index) /// Картинка (необязательный) - Визуальное представление добавляемого значения /// ЭлементСпискаЗначений [ContextMethod("Добавить", "Add")] - public ValueListItem Add(IValue value, string presentation = null, bool check = false, IValue picture = null) + public ValueListItem Add(IValue value = null, string presentation = null, bool check = false, IValue picture = null) { var newItem = CreateNewListItem(value, presentation, check, picture); @@ -94,7 +101,7 @@ public ValueListItem Add(IValue value, string presentation = null, bool check = /// Картинка (необязательный) - Визуальное представление добавляемого значения /// ЭлементСпискаЗначений [ContextMethod("Вставить", "Insert")] - public ValueListItem Insert(int index, IValue value, string presentation = null, bool check = false, IValue picture = null) + public ValueListItem Insert(int index, IValue value = null, string presentation = null, bool check = false, IValue picture = null) { var newItem = CreateNewListItem(value, presentation, check, picture); _items.Insert(index, newItem); @@ -104,11 +111,13 @@ public ValueListItem Insert(int index, IValue value, string presentation = null, private static ValueListItem CreateNewListItem(IValue value, string presentation, bool check, IValue picture) { - var newItem = new ValueListItem(); - newItem.Value = value; - newItem.Presentation = presentation; - newItem.Check = check; - newItem.Picture = picture; + var newItem = new ValueListItem + { + Value = value ?? BslUndefinedValue.Instance, + Presentation = presentation, + Check = check, + Picture = picture + }; return newItem; } @@ -174,17 +183,15 @@ public int IndexOf(ValueListItem item) [ContextMethod("НайтиПоЗначению", "FindByValue")] public IValue FindByValue(IValue val) { - var item = _items.FirstOrDefault(x => x.Value.Equals(val)); + var item = _items.FirstOrDefault(x => x.Value.StrictEquals(val)); if(item == null) return ValueFactory.Create(); return item; } - private int IndexByValue(IValue item) + private int IndexByValue(BslValue item) { - item = item.GetRawValue(); - int index; if (item is ValueListItem) @@ -205,7 +212,7 @@ private int IndexByValue(IValue item) } if (index < 0 || index >= _items.Count()) - throw new RuntimeException("Значение индекса выходит за пределы диапазона"); + throw RuntimeException.IndexOutOfRange(); } return index; @@ -220,7 +227,7 @@ private int IndexByValue(IValue item) /// /// Количество позиций, на которое сдвигается элемент. Если значение положительное - сдвиг вниз, иначе вверх [ContextMethod("Сдвинуть", "Move")] - public void Move(IValue item, int offset) + public void Move(BslValue item, int offset) { int index_source = IndexByValue(item); @@ -264,19 +271,19 @@ public ValueListImpl Copy() /// /// НаправлениеСортировки (необязательный) - Направление сортировки элементов. По умолчанию - по возрастанию. [ContextMethod("СортироватьПоЗначению", "SortByValue")] - public void SortByValue(SortDirectionEnum? direction = null) + public void SortByValue(IBslProcess process, SortDirectionEnum? direction = null) { if (direction == null || direction == SortDirectionEnum.Asc) { - _items.Sort((x, y) => SafeCompare(x.Value, y.Value)); + _items.Sort((x, y) => SafeCompare(process, x.Value, y.Value)); } else { - _items.Sort((x, y) => SafeCompare(y.Value, x.Value)); + _items.Sort((x, y) => SafeCompare(process, y.Value, x.Value)); } } - private int SafeCompare(IValue x, IValue y) + private int SafeCompare(IBslProcess p, IValue x, IValue y) { try { @@ -285,7 +292,7 @@ private int SafeCompare(IValue x, IValue y) catch(RuntimeException) { // Сравнение типов не поддерживается - return x.AsString().CompareTo(y.AsString()); + return string.Compare(x?.AsString(p), y?.AsString(p), StringComparison.Ordinal); } } @@ -314,7 +321,7 @@ public void SortByPresentation(SortDirectionEnum? direction = null) /// Число - Индекс удаляемого элемента /// [ContextMethod("Удалить", "Delete")] - public void Delete(IValue item) + public void Delete(BslValue item) { int indexSource = IndexByValue(item); @@ -324,26 +331,16 @@ public void Delete(IValue item) #region Collection Context [ContextMethod("Количество", "Count")] - public int Count() + public override int Count() { return _items.Count; } - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { return _items.GetEnumerator(); } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - #endregion [ScriptConstructor] diff --git a/src/OneScript.StandardLibrary/Collections/ValueList/ValueListItem.cs b/src/OneScript.StandardLibrary/Collections/ValueList/ValueListItem.cs new file mode 100644 index 000000000..c015ee277 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueList/ValueListItem.cs @@ -0,0 +1,53 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueList +{ + /// + /// Используется для доступа к свойствам и методам элемента списка значений + /// + [ContextClass("ЭлементСпискаЗначений", "ValueListItem")] + public class ValueListItem : AutoContext + { + private string _presentationHolder; + private IValue _pictureHolder; + + public ValueListItem() + { + _pictureHolder = ValueFactory.Create(); + _presentationHolder = String.Empty; + } + + [ContextProperty("Значение", "Value")] + public IValue Value { get; set; } + + [ContextProperty("Представление", "Presentation")] + public string Presentation + { + get => _presentationHolder; + set => _presentationHolder = value ?? String.Empty; + } + + [ContextProperty("Пометка", "Check")] + public bool Check { get; set; } + + [ContextProperty("Картинка", "Picture")] + public IValue Picture + { + get => _pictureHolder; + set => _pictureHolder = value ?? ValueFactory.Create(); + } + + public override string ToString() + => !String.IsNullOrEmpty(_presentationHolder) ? _presentationHolder : Value.ToString(); + } +} diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/CollectionIndexes.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/CollectionIndexes.cs new file mode 100644 index 000000000..08c5328e4 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/CollectionIndexes.cs @@ -0,0 +1,162 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections.Exceptions; +using OneScript.StandardLibrary.Collections.Indexes; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTable +{ + [ContextClass("ИндексыКоллекции", "CollectionIndexes")] + public class CollectionIndexes : AutoCollectionContext + { + readonly List _indexes = new List(); + private readonly IIndexCollectionSource _owner; + + public CollectionIndexes(IIndexCollectionSource owner) + { + _owner = owner; + } + + [ContextMethod("Добавить", "Add")] + public CollectionIndex Add(string columns) + { + var newIndex = new CollectionIndex(_owner, BuildFieldList(_owner, columns)); + _indexes.Add(newIndex); + return newIndex; + } + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _indexes.Count; + } + + [ContextMethod("Удалить", "Delete")] + public void Delete(IValue index) + { + var idx = GetIndex(index); + idx.ExcludeFields(); + _indexes.Remove(idx); + } + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + foreach (var idx in _indexes) + idx.ExcludeFields(); + + _indexes.Clear(); + } + + public override IValue GetIndexedValue(IValue index) + { + return GetIndex(index); + } + + private CollectionIndex GetIndex(IValue index) + { + if (index is CollectionIndex collectionIndex) + { + if (_indexes.Contains(collectionIndex)) + { + return collectionIndex; + } + throw RuntimeException.InvalidArgumentValue(); + } + + if (index is BslNumericValue numberValue) + { + var number = numberValue.AsNumber(); + if (number >= 0 && number < _indexes.Count) + { + return _indexes[decimal.ToInt32(number)]; + } + + throw RuntimeException.InvalidArgumentValue(); + } + + throw RuntimeException.InvalidArgumentType(); + } + + public void Rebuild() + { + foreach (var index in _indexes) + { + index.Rebuild(); + } + } + + internal void ClearIndexes() + { + foreach (var index in _indexes) + { + index.Clear(); + } + } + + internal void FieldRemoved(IValue field) + { + foreach (var index in _indexes) + { + index.FieldRemoved(field); + } + } + + internal void ElementAdded(PropertyNameIndexAccessor element) + { + foreach (var index in _indexes) + { + index.ElementAdded(element); + } + } + + internal void ElementRemoved(PropertyNameIndexAccessor element) + { + foreach (var index in _indexes) + { + index.ElementRemoved(element); + } + } + + public CollectionIndex FindSuitableIndex(IEnumerable searchFields) + { + return _indexes.FirstOrDefault(index => index.CanBeUsedFor(searchFields)); + } + + private static List BuildFieldList(IIndexCollectionSource source, string fieldList) + { + var fields = new List(); + if (string.IsNullOrEmpty(fieldList)) + return fields; + + var fieldNames = fieldList.Split(',', System.StringSplitOptions.RemoveEmptyEntries); + foreach (var fieldName in fieldNames) + { + var name = fieldName.Trim(); + var field = source.GetField(name) ?? throw ColumnException.WrongColumnName(fieldName); + fields.Add(field); + } + + return fields; + } + + public override IEnumerator GetEnumerator() + { + foreach (var item in _indexes) + { + yield return item; + } + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs new file mode 100644 index 000000000..3fdbff358 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTable.cs @@ -0,0 +1,788 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.StandardLibrary.Collections.Exceptions; +using OneScript.StandardLibrary.Collections.Indexes; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OneScript.StandardLibrary.Collections.ValueTable +{ + /// + /// Объект для работы с данными в табличном виде. + /// Представляет из себя коллекцию строк с заранее заданной структурой. + /// + [ContextClass("ТаблицаЗначений", "ValueTable")] + public class ValueTable : AutoCollectionContext, IIndexCollectionSource + { + private readonly List _rows; + + public ValueTable() + { + Columns = new ValueTableColumnCollection(this); + _rows = new List(); + Indexes = new CollectionIndexes(this); + } + + /// + /// Коллекция колонок + /// + /// КоллекцияКолонокТаблицыЗначений + [ContextProperty("Колонки", "Columns")] + public ValueTableColumnCollection Columns { get; } + + /// + /// Коллекция индексов + /// + /// ИндексыКоллекции + [ContextProperty("Индексы", "Indexes")] + public CollectionIndexes Indexes { get; } + + /// + /// Количество строк в Таблице значений + /// + /// Число + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _rows.Count; + } + + /// + /// Добавляет строку в конец Таблицы значений + /// + /// СтрокаТаблицыЗначений + [ContextMethod("Добавить", "Add")] + public ValueTableRow Add() + { + var row = new ValueTableRow(this); + _rows.Add(row); + if (Indexes.Count() > 0) + Indexes.ElementAdded(row); + return row; + } + + /// + /// Вставляет строку в указанную позицию + /// + /// Число - Индекс позиции куда будет произведена вставка. + /// Если индекс вне размера Таблицы значений, строка добавляется в конец + /// СтрокаТаблицыЗначений + [ContextMethod("Вставить", "Insert")] + public ValueTableRow Insert(int index) + { + if (index < 0 || index > _rows.Count) + return Add(); // для совместимости с 1С, хотя логичней было бы исключение + + var row = new ValueTableRow(this); + _rows.Insert(index, row); + + if (Indexes.Count() > 0) + Indexes.ElementAdded(row); + return row; + } + + /// + /// Удаляет строку + /// + /// + /// СтрокаТаблицыЗначений - Удаляемая строка + /// Число - Индекс удаляемой строки + /// + [ContextMethod("Удалить", "Delete")] + public void Delete(BslValue row) + { + var index = IndexByValue(row); + Indexes.ElementRemoved(_rows[index]); + _rows.RemoveAt(index); + } + + /// + /// Загружает значения в колонку + /// + /// Массив - Значения для загрузки в колонку + /// + /// Строка - Имя колонки для загрузки + /// Число - Индекс колонки для загрузки + /// КолонкаТаблицыЗначений - Колонка для загрузки + /// + [ContextMethod("ЗагрузитьКолонку", "LoadColumn")] + public void LoadColumn(IValue values, IValue columnIndex) + { + // ValueTableColumn Column = Columns.GetColumnByIIndex(ColumnIndex); + var row_iterator = _rows.GetEnumerator(); + var array_iterator = (values as ArrayImpl)?.GetEnumerator() + ?? throw RuntimeException.InvalidNthArgumentType(1); + + Indexes.ClearIndexes(); + + while (row_iterator.MoveNext() && array_iterator.MoveNext()) + { + row_iterator.Current.Set(columnIndex, array_iterator.Current); + } + + Indexes.Rebuild(); + } + + /// + /// Выгружает значения колонки в новый массив + /// + /// + /// Строка - Имя колонки для выгрузки + /// Число - Индекс колонки для выгрузки + /// КолонкаТаблицыЗначений - Колонка для выгрузки + /// + /// Массив + [ContextMethod("ВыгрузитьКолонку", "UnloadColumn")] + public ArrayImpl UnloadColumn(IValue column) + { + var result = new ArrayImpl(); + + foreach (var row in _rows) + { + result.Add(row.Get(column)); + } + + return result; + } + + private List GetProcessingColumnList(string columnNames, bool emptyListInCaseOfNull = false) + { + var processing_list = new List(); + if (string.IsNullOrEmpty(columnNames)) // Передали пустую строку вместо списка колонок + { + if (!emptyListInCaseOfNull) + { + processing_list.AddRange(Columns); + } + return processing_list; + } + + foreach (var column_name in columnNames.Split(',')) + { + if (column_name == String.Empty) + continue; + + var name = column_name.Trim(); + + var Column = Columns.FindColumnByName(name); + if (Column == null) + throw ColumnException.WrongColumnName(column_name); + + if (processing_list.Find(x => x.Name == name) == null) + processing_list.Add(Column); + } + + return processing_list; + } + + /// + /// Заполнить колонку/колонки указанным значением + /// + /// Произвольный - Устанавливаемое значение + /// Строка - Список имен колонок для установки значения (разделены запятыми). + /// Если параметр не указан или передана пустая строка, будут заполнены все колонки + [ContextMethod("ЗаполнитьЗначения", "FillValues")] + public void FillValues(IValue value, string columnNames = null) + { + var processing_list = GetProcessingColumnList(columnNames); + Indexes.ClearIndexes(); + foreach (var row in _rows) + { + foreach (var col in processing_list) + { + row.Set(col, value); + } + } + Indexes.Rebuild(); + } + + /// + /// Получить индекс указанной строки + /// + /// СтрокаТаблицыЗначений - Строка таблицы значений, для которой необходимо определить индекс + /// Число - Индекс в коллекции, если не найдено возвращает -1 + [ContextMethod("Индекс", "IndexOf")] + public int IndexOf(IValue row) + { + if (row is ValueTableRow tableRow) + return _rows.IndexOf(tableRow); + + throw RuntimeException.InvalidArgumentType(); + } + + /// + /// Сумма значений всех строк указанной колонки + /// + /// + /// Строка - Имя колонки для суммирования + /// Число - Индекс колонки для суммирования + /// КолонкаТаблицыЗначений - Колонка для суммирования + /// + /// + /// Если в колонке установлен тип и он единственный, + /// то при суммировании будет попытка преобразования значения к типу Число. + ///
Если колонке не присвоены типы + /// или в колонке несколько типов и среди них есть тип Число, + /// то в суммироваться будут только значения типа Число + ///
Если в колонке несколько типов и среди них нет типа Число, то результатом будет Неопределено. + ///
+ /// Число + [ContextMethod("Итог", "Total")] + public IValue Total(IValue columnIndex) + { + var column = Columns.GetColumnByIIndex(columnIndex); + + var types = column.ValueType.Types(); + if (types.Count() == 1) // единственный тип + { + return TotalAllAsNumber(column); + } + + if (types.Count() == 0 // нет типов + || types.Any(x => ((BslTypeValue)x).TypeValue == BasicTypes.Number)) // среди типов есть Число + { + return TotalNumbersOnly(column); + } + + // несколько типов и нет типа Число + return ValueFactory.Create(); + } + + private IValue TotalAllAsNumber(ValueTableColumn column) + { + decimal result = 0; + foreach (var row in _rows) + { + try + { + result += row.Get(column).AsNumber(); + } + catch (RuntimeException) + { + // игнорировать неприводимые к числу + } + } + return ValueFactory.Create(result); + } + + private IValue TotalNumbersOnly(ValueTableColumn column) + { + decimal result = 0; + foreach (var row in _rows) + { + var current_value = row.Get(column); + if (current_value.SystemType == BasicTypes.Number) + { + result += current_value.AsNumber(); + } + } + return ValueFactory.Create(result); + } + + /// + /// Осуществляет поиск значения в указанных колонках + /// + /// Произвольный - Искомое значение + /// Строка - Список имен колонок для поиска значения (разделены запятыми). + /// Если параметр не указан - ищет по всем колонкам. По умолчанию: пустая строка + /// СтрокаТаблицыЗначений - если строка найдена, иначе Неопределено + [ContextMethod("Найти", "Find")] + public IValue Find(IValue value, string columnNames = null) + { + var processing_list = GetProcessingColumnList(columnNames); + foreach (ValueTableRow row in _rows) + { + foreach (var col in processing_list) + { + var current = row.Get(col); + if (value.StrictEquals(current)) + return row; + } + } + return ValueFactory.Create(); + } + + private ValueTableColumn GetColumnOrThrow(string column_name) + => this.Columns.FindColumnByName(column_name) + ?? throw ColumnException.WrongColumnName(column_name); + + + private bool CheckFilterCriteria(ValueTableRow Row, StructureImpl Filter) + { + foreach (var kv in Filter) + { + var Column = GetColumnOrThrow(kv.Key.ToString()); + IValue current = Row.Get(Column); + if (!current.StrictEquals(kv.Value)) + return false; + } + return true; + } + + /// + /// Поиск строк по условию + /// + /// Структура - Условия поиска. Ключ - имя колонки, значение - искомое значение + /// Массив - Массив ссылок на строки, удовлетворяющих условию поиска + [ContextMethod("НайтиСтроки", "FindRows")] + public ArrayImpl FindRows(StructureImpl filter) + { + if (filter == null) + throw RuntimeException.InvalidArgumentType(); + + var result = new ArrayImpl(); + + var mapped = ColumnsMap(filter); + var suitableIndex = Indexes.FindSuitableIndex(mapped.Keys()); + var dataToScan = suitableIndex?.GetData(mapped) ?? _rows; + + foreach (var element in dataToScan) + { + var row = (ValueTableRow)element; + if (CheckFilterCriteria(row, filter)) + result.Add(row); + } + + return result; + } + + private MapImpl ColumnsMap(StructureImpl filter) + { + var result = new MapImpl(); + foreach (var kv in filter) + { + var key = GetColumnOrThrow(kv.Key.ToString()); + result.Insert(key, kv.Value); + } + + return result; + } + + /// + /// Удаляет все строки. Структура колонок не меняется. + /// + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _rows.Clear(); + Indexes.Clear(); + } + + /// + /// Получить строку по индексу + /// + /// Число - Индекс строки + /// СтрокаТаблицыЗначений + [ContextMethod("Получить", "Get")] + public ValueTableRow Get(int index) + { + if (index < 0 || index >= Count()) + throw RuntimeException.IndexOutOfRange(); + return _rows[index]; + } + + /// + /// Сворачиваются (группируются) строки по указанным колонкам измерениям, суммируются колонки ресурсов. + /// Колонки не указанные ни в измерениях ни в ресурсах удаляются. + /// + /// Строка - Имена колонок для сворачивания (изменения), разделены запятыми + /// Строка - Имена колонок для суммирования (ресурсы), разделены запятыми + [ContextMethod("Свернуть", "GroupBy")] + public void GroupBy(IBslProcess process, string groupColumnNames, string aggregateColumnNames = null) + { + var GroupColumns = GetProcessingColumnList(groupColumnNames, true); + var AggregateColumns = GetProcessingColumnList(aggregateColumnNames, true); + + CheckMixedColumns(GroupColumns, AggregateColumns); + + var uniqueRows = new Dictionary(new RowsByColumnsEqComparer(process, GroupColumns)); + int new_idx = 0; + + foreach (var row in _rows) + { + if (!uniqueRows.TryGetValue(row, out var destination)) + { + destination = _rows[new_idx++]; + CopyRowData(row, destination, GroupColumns); + CopyRowData(row, destination, AggregateColumns); + uniqueRows.Add(destination, destination); + } + else + { + AppendRowData(row, destination, AggregateColumns); + } + } + + _rows.RemoveRange(new_idx, _rows.Count - new_idx); + + int i = 0; + while (i < Columns.Count()) + { + var column = Columns.FindColumnByIndex(i); + if (GroupColumns.IndexOf(column) == -1 && AggregateColumns.IndexOf(column) == -1) + Columns.Delete(column); + else + ++i; + } + } + + private static void CheckMixedColumns(List groupColumns, List aggregateColumns) + { + foreach (var groupColumn in groupColumns) + if (aggregateColumns.Find(x => x.Name == groupColumn.Name) != null) + throw ColumnException.ColumnsMixed(groupColumn.Name); + } + + private static void CopyRowData(ValueTableRow source, ValueTableRow dest, IEnumerable columns) + { + foreach (var column in columns) + dest.Set(column, source.Get(column)); + } + + private static void AppendRowData(ValueTableRow source, ValueTableRow dest, IEnumerable columns) + { + foreach (var column in columns) + { + var value1 = GetNumeric(source, column); + var value2 = GetNumeric(dest, column); + dest.Set(column, BslNumericValue.Create(value1 + value2)); + } + } + + private static decimal GetNumeric(ValueTableRow row, ValueTableColumn column) + { + var value = row.Get(column); + return value.SystemType == BasicTypes.Number ? value.AsNumber() : 0; + } + + private class RowsByColumnsEqComparer : IEqualityComparer + { + private readonly IBslProcess _process; + private readonly List _columns; + + public RowsByColumnsEqComparer(IBslProcess process, List columns) + { + _process = process; + _columns = columns; + } + + public bool Equals(ValueTableRow row1, ValueTableRow row2) + { + foreach (var column in _columns) + { + if (!row1.Get(column).Equals(row2.Get(column))) + return false; + } + return true; + } + + public int GetHashCode(ValueTableRow row) + { + int hash = 0; + foreach (var column in _columns) + hash ^= row.Get(column).AsString(_process).GetHashCode(); + return hash; + } + } + + private int IndexByValue(BslValue item) + { + int index; + + if (item is ValueTableRow row) + { + index = IndexOf(row); + if (index == -1) + throw ValueTableException.RowDoesntBelongTo(); + } + else + { + try + { + index = decimal.ToInt32(item.AsNumber()); + } + catch (RuntimeException) + { + throw RuntimeException.InvalidArgumentType(); + } + + if (index < 0 || index >= _rows.Count) + throw RuntimeException.IndexOutOfRange(); + } + + return index; + } + + /// + /// Сдвигает строку на указанное количество позиций. + /// + /// + /// СтрокаТаблицыЗначений - Строка которую сдвигаем + /// Число - Индекс сдвигаемой строки + /// + /// Количество строк, на которое сдвигается строка. + /// Если значение положительное - сдвиг вниз, иначе вверх + [ContextMethod("Сдвинуть", "Move")] + public void Move(BslValue row, int offset) + { + int index_source = IndexByValue(row); + + int index_dest = index_source + offset; + + if (index_dest < 0 || index_dest >= _rows.Count) + throw RuntimeException.IncorrectOffset(); + + ValueTableRow tmp = _rows[index_source]; + + if (index_source < index_dest) + { + _rows.Insert(index_dest + 1, tmp); + _rows.RemoveAt(index_source); + } + else + { + _rows.RemoveAt(index_source); + _rows.Insert(index_dest, tmp); + } + } + + /// + /// Создает новую таблицу значений с указанными колонками. Данные не копируются. + /// + /// Строка - Имена колонок для копирования, разделены запятыми + /// Если параметр не указан или передана пустая строка, будут скопированы все колонки + /// ТаблицаЗначений + [ContextMethod("СкопироватьКолонки", "CopyColumns")] + public ValueTable CopyColumns(string columnNames = null) + { + var Result = new ValueTable(); + var columns = GetProcessingColumnList(columnNames); + + foreach (var Column in columns) + { + Result.Columns.Add(Column.Name, Column.ValueType, Column.Title, Column.Width); + } + + return Result; + } + + /// + /// Создает новую таблицу значений с указанными строками и колонками. Если передан отбор - копирует строки удовлетворяющие отбору. + /// Если не указаны строки - будут скопированы все строки. Если не указаны колонки - будут скопированы все колонки. + /// Если не указаны оба параметра - будет создана полная копия таблицы значений. + /// + /// + /// Массив - Массив строк для отбора + /// Структура - Параметры отбора. Ключ - Колонка, Значение - Значение отбора + /// + /// Строка - Имена колонок для копирования, разделены запятыми + /// ТаблицаЗначений + [ContextMethod("Скопировать", "Copy")] + public ValueTable Copy(IValue rows = null, string columnNames = null) + { + var result = CopyColumns(columnNames); + var columns = GetProcessingColumnList(columnNames); + + IEnumerable requestedRows = rows switch + { + null => _rows, + StructureImpl structure => FindRows(structure).Select(x => x as ValueTableRow), + ArrayImpl array => GetRowsEnumByArray(array), + _ => throw RuntimeException.InvalidArgumentType(), + }; + + var columnMap = new Dictionary(); + foreach (var column in columns) + { + var destinationColumn = result.Columns.FindColumnByName(column.Name); + columnMap.Add(column, destinationColumn); + } + + foreach (var row in requestedRows) + { + var new_row = result.Add(); + foreach (var Column in columns) + { + new_row.Set(columnMap[Column], row.Get(Column)); + } + } + + return result; + } + + private IEnumerable GetRowsEnumByArray(ArrayImpl rowsArray) + { + if (rowsArray == null) + throw RuntimeException.InvalidArgumentType(); + + return rowsArray.Select(x => x is ValueTableRow vtr && vtr.Owner() == this ? vtr + : throw RuntimeException.InvalidArgumentValue()); + } + + private struct ValueTableSortRule + { + public ValueTableColumn Column; + public int direction; // 1 = asc, -1 = desc + } + + private List GetSortRules(string Columns) + { + + string[] a_columns = Columns.Split(','); + + List Rules = new List(); + + foreach (string column in a_columns) + { + string[] description = column.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (description.Length == 0) + throw ColumnException.WrongColumnName(); + if (description.Length > 2) + throw RuntimeException.InvalidNthArgumentValue(1); + + var sortColumn = GetColumnOrThrow(description[0]); + var rule = new ValueTableSortRule + { + Column = sortColumn, + direction = (description.Length > 1 && description[1].BilingualEquals("УБЫВ", "DESC")) ? -1 : 1 + }; + + Rules.Add(rule); + } + + return Rules; + } + + private class RowComparator : IComparer + { + readonly List Rules; + + readonly GenericIValueComparer _comparer; + + public RowComparator(IBslProcess process, List Rules) + { + if (Rules.Count == 0) + throw RuntimeException.InvalidArgumentValue(); + + this.Rules = Rules; + _comparer = new GenericIValueComparer(process); + } + + private int OneCompare(ValueTableRow x, ValueTableRow y, ValueTableSortRule Rule) + { + IValue xValue = x.Get(Rule.Column); + IValue yValue = y.Get(Rule.Column); + + int result = _comparer.Compare(xValue, yValue) * Rule.direction; + + return result; + } + + public int Compare(ValueTableRow x, ValueTableRow y) + { + int i = 0, r; + while ((r = OneCompare(x, y, Rules[i])) == 0) + { + if (++i >= Rules.Count) + return 0; + } + + return r; + } + } + + /// + /// Сортировать строки в таблице значений. Строки сортируются по порядку следования колонок для сортировки, с учетом варианта сортировки. + /// + /// Строка - Имена колонок для сортировки. + /// После имени колонки, через пробел, можно указать направление сортировки: "Убыв" ("Desc") - по убыванию. Возр" ("Asc") - по возрастанию + /// По умолчанию - по возрастанию. + /// + /// СравнениеЗначений - правила сравнения значений при наличии различных типов данных в колонке. + [ContextMethod("Сортировать", "Sort")] + public void Sort(IBslProcess process, string columns, IValue comparator = null) + { + _rows.Sort(new RowComparator(process, GetSortRules(columns))); + } + + /// + /// Не поддерживается + /// + /// + /// + [ContextMethod("ВыбратьСтроку", "ChooseRow")] + public void ChooseRow(string title = null, IValue startRow = null) + { + throw new NotSupportedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override IEnumerator GetEnumerator() + { + foreach (var item in _rows) + { + yield return item; + } + } + + public override IValue GetIndexedValue(IValue index) + { + return Get((int)index.AsNumber()); + } + + [ScriptConstructor] + public static ValueTable Constructor() + { + return new ValueTable(); + } + + public string GetName(IValue field) + { + if (field is ValueTableColumn column) + return column.Name; + throw RuntimeException.InvalidArgumentType(nameof(field)); + } + + public IValue GetField(string name) + { + return Columns.FindColumnByName(name); + } + } + + public sealed class ValueTableException : RuntimeException + { + public ValueTableException(BilingualString message, Exception innerException) : base(message, + innerException) + { + } + + public ValueTableException(BilingualString message) : base(message) + { + } + + public static ValueTableException RowDoesntBelongTo() + { + return new ValueTableException(new BilingualString( + "Строка не принадлежит таблице значений", + "Row does not belong to table")); + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumn.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumn.cs new file mode 100644 index 000000000..8070951bb --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumn.cs @@ -0,0 +1,87 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections.Exceptions; +using OneScript.StandardLibrary.TypeDescriptions; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTable +{ + /// + /// Колонка таблицы значений. + /// + [ContextClass("КолонкаТаблицыЗначений", "ValueTableColumn")] + public class ValueTableColumn : AutoContext + { + private string _title; + private string _name; + private readonly TypeDescription _valueType; + private readonly WeakReference _owner; + + private int _indicesCount = 0; + public bool IsIndexable => _indicesCount != 0; + public void AddToIndex() { _indicesCount++; } + public void DeleteFromIndex() { if (_indicesCount != 0) _indicesCount--; } + + public ValueTableColumn(ValueTableColumnCollection owner, string name, string title, TypeDescription type, int width) + { + _name = name; + _title = title; + _valueType = type ?? new TypeDescription(); + Width = width; + + _owner = new WeakReference(owner); + } + + /// + /// Заголовок колонки + /// + /// Строка + [ContextProperty("Заголовок", "Title")] + public string Title + { + get { return _title ?? _name; } + set { _title = value; } + } + + /// + /// Имя колонки + /// + /// Строка + [ContextProperty("Имя", "Name")] + public string Name + { + get { return _name; } + set + { + ValueTableColumnCollection Owner = _owner.Target as ValueTableColumnCollection; + if (Owner.FindColumnByName(value) != null) + throw ColumnException.WrongColumnName(); + + if (_title == _name) + _title = value; + + _name = value; + } + } + /// + /// Тип значения колонки + /// + /// ОписаниеТипа + [ContextProperty("ТипЗначения", "ValueType")] + public TypeDescription ValueType => _valueType; + + /// + /// Ширина колонки + /// + /// Число + [ContextProperty("Ширина", "Width")] + public int Width { get; set; } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumnCollection.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumnCollection.cs new file mode 100644 index 000000000..bea283327 --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableColumnCollection.cs @@ -0,0 +1,271 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary.Collections.Exceptions; +using OneScript.StandardLibrary.TypeDescriptions; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTable +{ + /// + /// Коллекция колонок таблицы значений + /// + [ContextClass("КоллекцияКолонокТаблицыЗначений", "ValueTableColumnCollection")] + public class ValueTableColumnCollection : AutoContext, ICollectionContext, IDebugPresentationAcceptor + { + private static readonly StringComparer _namesComparer = StringComparer.OrdinalIgnoreCase; + + private readonly List _columns = new List(); + private readonly ValueTable _owner; + + public ValueTableColumnCollection(ValueTable owner) + { + _owner = owner; + } + + public ValueTableColumn AddUnchecked(string name, string title, TypeDescription type = null) + { + var column = new ValueTableColumn(this, name, title, type, 0); + _columns.Add(column); + return column; + } + + public ValueTableColumn AddUnchecked(string name, string title = null) + { + var column = new ValueTableColumn(this, name, title ?? name, null, 0); + _columns.Add(column); + return column; + } + + + private void CheckColumnName(string name) + { + if (!Utils.IsValidIdentifier(name)) + throw ColumnException.WrongColumnName(name); + + if (FindColumnByName(name) != null) + throw ColumnException.DuplicatedColumnName(name); + } + + /// + /// Добавляет колонку в таблицу значений + /// + /// Строка - Имя колонки + /// ОписаниеТипов - Тип данных колонки + /// Строка - Заголовок колонки + /// Число - Ширина колонки + /// КолонкаТаблицыЗначений + [ContextMethod("Добавить", "Add")] + public ValueTableColumn Add(string name, TypeDescription type = null, string title = null, int width = 0) + { + CheckColumnName(name); + + var column = new ValueTableColumn(this, name, title, type, width); + _columns.Add(column); + + return column; + } + + /// + /// Вставить колонку в указанную позицию + /// + /// Число - Индекс расположения колонки + /// Строка - Имя колонки + /// ОписаниеТипов - Тип данных колонки + /// Строка - Заголовок колонки + /// Число - Ширина колонки + /// КолонкаТаблицыЗначений + [ContextMethod("Вставить", "Insert")] + public ValueTableColumn Insert(int index, string name, TypeDescription type = null, string title = null, int width = 0) + { + CheckColumnName(name); + + ValueTableColumn column = new ValueTableColumn(this, name, title, type, width); + _columns.Insert(index, column); + + return column; + } + + /// + /// Индекс указанной колонки + /// + /// КолонкаТаблицыЗначений - Колонка, для которой определяется индекс + /// Число + [ContextMethod("Индекс", "IndexOf")] + public int IndexOf(ValueTableColumn column) + { + return _columns.IndexOf(column); + } + + /// + /// Количество колонок в таблице значений + /// + /// Число + [ContextMethod("Количество", "Count")] + public int Count() + { + return _columns.Count; + } + + public int Count(IBslProcess process) => Count(); + + /// + /// Поиск колонки по имени + /// + /// Строка - Имя колонки + /// КолонкаТаблицыЗначений - Найденная колонка таблицы значений, иначе Неопределено. + [ContextMethod("Найти", "Find")] + public IValue Find(string name) + { + return FindColumnByName(name) ?? ValueFactory.Create(); + } + + /// + /// Удалить колонку значений + /// + /// + /// Строка - Имя колонки для удаления + /// Число - Индекс колонки для удаления + /// КолонкаТаблицыЗначений - Колонка для удаления + /// + [ContextMethod("Удалить", "Delete")] + public void Delete(IValue column) + { + var vtColumn = GetColumnByIIndex(column); + _owner.ForEach((ValueTableRow x) => + { + x.OnOwnerColumnRemoval(vtColumn); + }); + _owner.Indexes.FieldRemoved(column); + _columns.Remove(vtColumn); + } + + /// + /// Удаляет все колонки + /// + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + while (_columns.Count != 0) + { + Delete(_columns[0]); + } + } + + public ValueTableColumn FindColumnByName(string name) + { + return _columns.Find(column => _namesComparer.Equals(name, column.Name)); + } + + public ValueTableColumn FindColumnByIndex(int index) + { + if (index < 0 || index >= _columns.Count) + throw RuntimeException.IndexOutOfRange(); + return _columns[index]; + } + + + public IEnumerator GetEnumerator() + { + foreach (var item in _columns) + { + yield return item; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override bool IsIndexed => true; + + public override IValue GetIndexedValue(IValue index) => GetColumnByIIndex(index); + + public override int GetPropertyNumber(string name) + { + int idx = _columns.FindIndex(column => _namesComparer.Equals(name, column.Name)); + if (idx == -1) + throw PropertyAccessException.PropNotFoundException(name); + return idx; + } + + public override int GetPropCount() => _columns.Count; + + public override string GetPropName(int propNum) + { + return FindColumnByIndex(propNum).Name; + } + + public override IValue GetPropValue(int propNum) + { + return FindColumnByIndex(propNum); + } + + public override bool IsPropWritable(int propNum) => false; + + public override bool IsPropReadable(int propNum) => true; + + public ValueTableColumn GetColumnByIIndex(IValue index) + { + if (index.SystemType == BasicTypes.String) + { + return FindColumnByName(index.ToString()) + ?? throw PropertyAccessException.PropNotFoundException(index.ToString()); + } + + if (index.SystemType == BasicTypes.Number) + { + return FindColumnByIndex(decimal.ToInt32(index.AsNumber())); + } + + if (index is ValueTableColumn column) + { + return column; + } + + throw RuntimeException.InvalidArgumentType(); + } + + public int GetColumnNumericIndex(IValue index) + { + if (index.SystemType == BasicTypes.String) + { + return GetPropertyNumber(index.ToString()); + } + + if (index.SystemType == BasicTypes.Number) + { + int iIndex = Decimal.ToInt32(index.AsNumber()); + if (iIndex < 0 || iIndex >= Count()) + throw RuntimeException.IndexOutOfRange(); + + return iIndex; + } + + if (index is ValueTableColumn column) + { + return IndexOf(column); + } + + throw RuntimeException.InvalidArgumentType(); + } + + void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) + { + visitor.ShowProperties(this); + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableRow.cs b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableRow.cs new file mode 100644 index 000000000..8bd94093d --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueTable/ValueTableRow.cs @@ -0,0 +1,167 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Execution; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTable +{ + [ContextClass("СтрокаТаблицыЗначений", "ValueTableRow")] + public class ValueTableRow : AutoContext, ICollectionContext, IDebugPresentationAcceptor + { + private readonly Dictionary _data = new Dictionary(); + private readonly ValueTable _owner; + + public ValueTableRow(ValueTable owner) + { + _owner = owner; + } + + public int Count() => _owner.Columns.Count(); + + public int Count(IBslProcess process) => Count(); + + /// + /// Владелец строки + /// + /// ТаблицаЗначений + [ContextMethod("Владелец", "Owner")] + public ValueTable Owner() + { + return _owner; + } + + private IValue TryValue(ValueTableColumn Column) + { + if (_data.TryGetValue(Column, out IValue Value)) + { + return Value; + } + return Column.ValueType.AdjustValue(); + } + + /// + /// Получает значение по индексу + /// + /// Число - Индекс колонки + /// Произвольный - Значение колонки + [ContextMethod("Получить", "Get")] + public IValue Get(int index) + { + return TryValue(_owner.Columns.FindColumnByIndex(index)); + } + + public IValue Get(IValue index) + { + return TryValue(_owner.Columns.GetColumnByIIndex(index)); + } + + public IValue Get(ValueTableColumn column) + { + return TryValue(column); + } + + /// + /// Установить значение + /// + /// Число - Индекс колонки + /// Произвольный - значение для установки + [ContextMethod("Установить", "Set")] + public void Set(int index, IValue value) + { + Set(_owner.Columns.FindColumnByIndex(index), value); + } + + public void Set(IValue index, IValue value) + { + Set(_owner.Columns.GetColumnByIIndex(index), value); + } + + public void Set(ValueTableColumn column, IValue value) + { + if (column.IsIndexable) + { + _owner.Indexes.ElementRemoved(this); + _data[column] = column.ValueType.AdjustValue(value); + _owner.Indexes.ElementAdded(this); + } + else + _data[column] = column.ValueType.AdjustValue(value); + } + + public void OnOwnerColumnRemoval(ValueTableColumn column) + { + _data.Remove(column); + } + + public IEnumerator GetEnumerator() + { + foreach (var item in _owner.Columns) + { + yield return TryValue(item); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override int GetPropCount() + { + return Count(); + } + + public override string GetPropName(int propNum) + { + return _owner.Columns.GetPropName(propNum); + } + + public override int GetPropertyNumber(string name) + { + return _owner.Columns.GetPropertyNumber(name); + } + + public override bool IsPropReadable(int propNum) => true; + + public override bool IsPropWritable(int propNum) => true; + + public override IValue GetPropValue(int propNum) + { + return TryValue(_owner.Columns.FindColumnByIndex(propNum)); + } + + public override void SetPropValue(int propNum, IValue newVal) + { + Set(_owner.Columns.FindColumnByIndex(propNum), newVal); + } + + private ValueTableColumn GetColumnByIIndex(IValue index) + { + return _owner.Columns.GetColumnByIIndex(index); + } + + public override IValue GetIndexedValue(IValue index) + { + return TryValue(GetColumnByIIndex(index)); + } + + public override void SetIndexedValue(IValue index, IValue val) + { + Set(GetColumnByIIndex(index), val); + } + + void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) + { + visitor.ShowProperties(this); + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTree.cs b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTree.cs similarity index 95% rename from src/ScriptEngine.HostedScript/Library/ValueTree/ValueTree.cs rename to src/OneScript.StandardLibrary/Collections/ValueTree/ValueTree.cs index c3483758e..c1877a0e2 100644 --- a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTree.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTree.cs @@ -1,66 +1,67 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTree -{ - /// - /// Дерево значений. - /// Древовидная структура с фунциональностью подобно таблице значений. - /// - [ContextClass("ДеревоЗначений", "ValueTree")] - public class ValueTree : AutoContext - { - private readonly ValueTreeColumnCollection _columns = new ValueTreeColumnCollection(); - private readonly ValueTreeRowCollection _rows; - - public ValueTree() - { - _rows = new ValueTreeRowCollection(this, null, 0); - } - - [ContextProperty("Колонки", "Columns")] - public ValueTreeColumnCollection Columns - { - get { return _columns; } - } - - [ContextProperty("Строки", "Rows")] - public ValueTreeRowCollection Rows - { - get { return _rows; } - } - - /// - /// Создаёт копию дерева значений. - /// - /// Массив. Строки для копирования. Если не указан, копируются все строки. Необязательный параметр. - /// Строка. Список колонок через запятую, которые должны быть скопированы. Необязательный параметр. - /// ДеревоЗначений. Копия исходного дерева значений. - [ContextMethod("Скопировать", "Copy")] - public ValueTree Copy(IValue rows = null, string columnNames = null) - { - - // TODO: отрабатывать параметр Rows - // TODO: отрабатывать параметр ColumnNames - - ValueTree result = new ValueTree(); - result._columns.CopyFrom(_columns); - result._rows.CopyFrom(_rows); - return result; - } - - - [ScriptConstructor] - public static ValueTree Constructor() - { - return new ValueTree(); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTree +{ + /// + /// Дерево значений. + /// Древовидная структура с фунциональностью подобно таблице значений. + /// + [ContextClass("ДеревоЗначений", "ValueTree")] + public class ValueTree : AutoContext + { + private readonly ValueTreeColumnCollection _columns = new ValueTreeColumnCollection(); + private readonly ValueTreeRowCollection _rows; + + public ValueTree() + { + _rows = new ValueTreeRowCollection(this, null, 0); + } + + [ContextProperty("Колонки", "Columns")] + public ValueTreeColumnCollection Columns + { + get { return _columns; } + } + + [ContextProperty("Строки", "Rows")] + public ValueTreeRowCollection Rows + { + get { return _rows; } + } + + /// + /// Создаёт копию дерева значений. + /// + /// Массив. Строки для копирования. Если не указан, копируются все строки. Необязательный параметр. + /// Строка. Список колонок через запятую, которые должны быть скопированы. Необязательный параметр. + /// ДеревоЗначений. Копия исходного дерева значений. + [ContextMethod("Скопировать", "Copy")] + public ValueTree Copy(IValue rows = null, string columnNames = null) + { + + // TODO: отрабатывать параметр Rows + // TODO: отрабатывать параметр ColumnNames + + ValueTree result = new ValueTree(); + result._columns.CopyFrom(_columns); + result._rows.CopyFrom(_rows); + return result; + } + + + [ScriptConstructor] + public static ValueTree Constructor() + { + return new ValueTree(); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeColumn.cs b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeColumn.cs similarity index 89% rename from src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeColumn.cs rename to src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeColumn.cs index 8a22ff32e..201f5187b 100644 --- a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeColumn.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeColumn.cs @@ -1,82 +1,84 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTree -{ - /// - /// Колонка дерева значений. - /// - [ContextClass("КолонкаДереваЗначений", "ValueTreeColumn")] - public class ValueTreeColumn : AutoContext - { - private string _title; - private string _name; - private TypeDescription _valueType; - private int _width; - private readonly ValueTreeColumnCollection _owner; - - public ValueTreeColumn(ValueTreeColumnCollection owner, string name, string title, TypeDescription type, int width) - { - _name = name; - _title = title ?? name; - _valueType = type ?? new TypeDescription(); - _width = width; - - _owner = owner; - } - - public ValueTreeColumn(ValueTreeColumnCollection owner, ValueTreeColumn src) - { - _name = src._name; - _title = src._title; - _valueType = src._valueType; - _width = src._width; - - _owner = owner; - } - - [ContextProperty("Заголовок", "Title")] - public string Title - { - get { return _title ?? _name; } - set { _title = value; } - } - - [ContextProperty("Имя", "Name")] - public string Name - { - get { return _name; } - set - { - if (_owner.FindColumnByName(value) != null) - throw new RuntimeException("Неверное имя колонки!"); - - if (_title == _name) - _title = value; - - _name = value; - } - } - - [ContextProperty("ТипЗначения", "ValueType")] - public TypeDescription ValueType - { - get { return _valueType; } - } - - [ContextProperty("Ширина", "Width")] - public int Width - { - get { return _width; } - set { _width = value; } // TOOD: Проверить неотрицательность значения - } - - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections.Exceptions; +using OneScript.StandardLibrary.TypeDescriptions; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTree +{ + /// + /// Колонка дерева значений. + /// + [ContextClass("КолонкаДереваЗначений", "ValueTreeColumn")] + public class ValueTreeColumn : AutoContext + { + private string _title; + private string _name; + private TypeDescription _valueType; + private int _width; + private readonly ValueTreeColumnCollection _owner; + + public ValueTreeColumn(ValueTreeColumnCollection owner, string name, string title, TypeDescription type, int width) + { + _name = name; + _title = title ?? name; + _valueType = type ?? new TypeDescription(); + _width = width; + + _owner = owner; + } + + public ValueTreeColumn(ValueTreeColumnCollection owner, ValueTreeColumn src) + { + _name = src._name; + _title = src._title; + _valueType = src._valueType; + _width = src._width; + + _owner = owner; + } + + [ContextProperty("Заголовок", "Title")] + public string Title + { + get { return _title ?? _name; } + set { _title = value; } + } + + [ContextProperty("Имя", "Name")] + public string Name + { + get { return _name; } + set + { + if (_owner.FindColumnByName(value) != null) + throw ColumnException.WrongColumnName(); + + if (_title == _name) + _title = value; + + _name = value; + } + } + + [ContextProperty("ТипЗначения", "ValueType")] + public TypeDescription ValueType + { + get { return _valueType; } + } + + [ContextProperty("Ширина", "Width")] + public int Width + { + get { return _width; } + set { _width = value; } // TOOD: Проверить неотрицательность значения + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeColumnCollection.cs b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeColumnCollection.cs similarity index 78% rename from src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeColumnCollection.cs rename to src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeColumnCollection.cs index 92b2c72e6..fcb66fa3c 100644 --- a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeColumnCollection.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeColumnCollection.cs @@ -1,342 +1,309 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTree -{ - /// - /// Коллекция колонок дерева значений. - /// - [ContextClass("КоллекцияКолонокДереваЗначений", "ValueTreeColumnCollection")] - public class ValueTreeColumnCollection : DynamicPropertiesAccessor, ICollectionContext, IEnumerable, IDebugPresentationAcceptor - { - private readonly List _columns = new List(); - - public ValueTreeColumnCollection() - { - } - - /// - /// Добавляет новую колонку. - /// - /// Строка. Имя колонки. - /// ОписаниеТипов. Доступные типы значений для колонки. Необязательный параметр. - /// Строка. Заголовок колонки. Необязательный параметр. - /// Число. Ширина колонки. Необязательный параметр. - /// КолонкаДереваЗначений. Добавленная колонка. - [ContextMethod("Добавить", "Add")] - public ValueTreeColumn Add(string name, TypeDescription type = null, string title = null, int width = 0) - { - if (FindColumnByName(name) != null) - throw new RuntimeException("Неверное имя колонки " + name); - - ValueTreeColumn column = new ValueTreeColumn(this, name, title, type, width); - _columns.Add(column); - - return column; - } - - /// - /// Вставляет новую колонку по указанному индексу. - /// - /// Число. Индекс новой колонки. - /// Строка. Имя колонки. - /// ОписаниеТипов. Доступные типы значений для колонки. Необязательный параметр. - /// Строка. Заголовок колонки. Необязательный параметр. - /// Число. Ширина колонки. Необязательный параметр. - /// КолонкаДереваЗначений. Добавленная колонка. - [ContextMethod("Вставить", "Insert")] - public ValueTreeColumn Insert(int index, string name, TypeDescription type = null, string title = null, int width = 0) - { - if (FindColumnByName(name) != null) - throw new RuntimeException("Неверное имя колонки " + name); - - ValueTreeColumn column = new ValueTreeColumn(this, name, title, type, width); - _columns.Insert(index, column); - - return column; - } - - /// - /// Определяет индекс колонки. - /// - /// КолонкаДереваЗначений. Колонка. - /// Число. Индекс колонки в коллекции. Если колонка не найдена, возвращается -1. - [ContextMethod("Индекс", "IndexOf")] - public int IndexOf(ValueTreeColumn column) - { - return _columns.IndexOf(column); - } - - /// - /// Возвращает количество колонок. - /// - /// Число. Количество колонокs. - [ContextMethod("Количество", "Count")] - public int Count() - { - return _columns.Count; - } - - /// - /// Ищет колонку по имени. - /// - /// Строка. Имя искомой колонки. - /// КолонкаДереваЗначений, Неопределено. Найденная колонка или Неопределено, если колонка не найдена. - [ContextMethod("Найти", "Find")] - public IValue Find(string name) - { - var column = FindColumnByName(name); - if (column == null) - return ValueFactory.Create(); - return column; - } - - /// - /// Удаляет колонку. - /// - /// КолонкаДереваЗначений. Колонка. - [ContextMethod("Удалить", "Delete")] - public void Delete(IValue column) - { - column = column.GetRawValue(); - _columns.Remove(GetColumnByIIndex(column)); - } - - /// - /// Получает колонку по индексу. - /// - /// Число. Индекс колонки. - /// КолонкаДереваЗначений. Колонка. - [ContextMethod("Получить", "Get")] - public ValueTreeColumn Get(int index) - { - if (index >= 0 && index < _columns.Count) - { - return _columns[index]; - } - throw RuntimeException.InvalidArgumentValue(); - } - - /// - /// Удаляет все колонки. - /// - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _columns.Clear(); - } - - /// - /// Сдвигает колонку на указанное смещение. - /// - /// КолонкаДереваЗначений. Колонка. - /// Число. Смещение. - [ContextMethod("Сдвинуть", "Move")] - public void Move(IValue column, int offset) - { - var treeColumn = GetColumnByIIndex(column); - int indexSource = _columns.IndexOf(treeColumn); - - int indexDestination = (indexSource + offset) % _columns.Count(); - while (indexDestination < 0) - { - indexDestination += _columns.Count(); - } - - - if (indexSource < indexDestination) - { - _columns.Insert(indexDestination + 1, treeColumn); - _columns.RemoveAt(indexSource); - } - else - { - _columns.RemoveAt(indexSource); - _columns.Insert(indexDestination, treeColumn); - } - - } - - internal void CopyFrom(ValueTreeColumnCollection src) - { - _columns.Clear(); - foreach (ValueTreeColumn column in src._columns) - { - _columns.Add(new ValueTreeColumn(this, column)); - } - } - - public ValueTreeColumn FindColumnByName(string name) - { - var comparer = StringComparer.OrdinalIgnoreCase; - return _columns.Find(column => comparer.Equals(name, column.Name)); - } - - public ValueTreeColumn FindColumnByIndex(int index) - { - return _columns[index]; - } - - public IEnumerator GetEnumerator() - { - foreach (var item in _columns) - { - yield return item; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public override int FindProperty(string name) - { - var column = FindColumnByName(name); - if (column == null) - throw RuntimeException.PropNotFoundException(name); - return _columns.IndexOf(column); - } - - public override int GetPropCount() - { - return _columns.Count; - } - - public override string GetPropName(int propNum) - { - return FindColumnByIndex(propNum).Name; - } - - public override IValue GetPropValue(int propNum) - { - return _columns[propNum]; - } - - public override bool IsPropWritable(int propNum) - { - return false; - } - - public ValueTreeColumn GetColumnByIIndex(IValue index) - { - if (index.DataType == DataType.String) - { - var column = FindColumnByName(index.AsString()); - if (column == null) - throw RuntimeException.PropNotFoundException(index.AsString()); - return column; - } - - if (index.DataType == DataType.Number) - { - int indexNum = Decimal.ToInt32(index.AsNumber()); - if (indexNum < 0 || indexNum >= Count()) - throw RuntimeException.InvalidArgumentValue(); - - var column = FindColumnByIndex(indexNum); - return column; - } - - if (index is ValueTreeColumn) - { - return index as ValueTreeColumn; - } - - throw RuntimeException.InvalidArgumentType(); - } - - public override IValue GetIndexedValue(IValue index) - { - return GetColumnByIIndex(index); - } - - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - - public override MethodInfo GetMethodInfo(int methodNumber) - { - return _methods.GetMethodInfo(methodNumber); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - var binding = _methods.GetMethod(methodNumber); - try - { - binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - var binding = _methods.GetMethod(methodNumber); - try - { - retValue = binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override int FindMethod(string name) - { - return _methods.FindMethod(name); - } - - internal List GetProcessingColumnList(string columnNamesString) - { - List processingList = new List(); - if (columnNamesString != null) - { - - if (columnNamesString.Trim().Length == 0) - { - // Передали пустую строку вместо списка колонок - return processingList; - } - - string[] columnNames = columnNamesString.Split(','); - foreach (string name in columnNames) - { - var column = FindColumnByName(name.Trim()); - - if (column == null) - throw RuntimeException.PropNotFoundException(name.Trim()); - - processingList.Add(column); - } - } - else - { - foreach (var column in _columns) - processingList.Add(column); - } - return processingList; - } - - void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) - { - visitor.ShowProperties(this); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary.Collections.Exceptions; +using OneScript.StandardLibrary.TypeDescriptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTree +{ + /// + /// Коллекция колонок дерева значений. + /// + [ContextClass("КоллекцияКолонокДереваЗначений", "ValueTreeColumnCollection")] + public class ValueTreeColumnCollection : AutoContext, ICollectionContext, IDebugPresentationAcceptor + { + private readonly List _columns = new List(); + + /// + /// Добавляет новую колонку. + /// + /// Строка. Имя колонки. + /// ОписаниеТипов. Доступные типы значений для колонки. Необязательный параметр. + /// Строка. Заголовок колонки. Необязательный параметр. + /// Число. Ширина колонки. Необязательный параметр. + /// КолонкаДереваЗначений. Добавленная колонка. + [ContextMethod("Добавить", "Add")] + public ValueTreeColumn Add(string name, TypeDescription type = null, string title = null, int width = 0) + { + if (FindColumnByName(name) != null) + throw ColumnException.DuplicatedColumnName(name); + + ValueTreeColumn column = new ValueTreeColumn(this, name, title, type, width); + _columns.Add(column); + + return column; + } + + /// + /// Вставляет новую колонку по указанному индексу. + /// + /// Число. Индекс новой колонки. + /// Строка. Имя колонки. + /// ОписаниеТипов. Доступные типы значений для колонки. Необязательный параметр. + /// Строка. Заголовок колонки. Необязательный параметр. + /// Число. Ширина колонки. Необязательный параметр. + /// КолонкаДереваЗначений. Добавленная колонка. + [ContextMethod("Вставить", "Insert")] + public ValueTreeColumn Insert(int index, string name, TypeDescription type = null, string title = null, int width = 0) + { + if (FindColumnByName(name) != null) + throw ColumnException.DuplicatedColumnName(name); + + ValueTreeColumn column = new ValueTreeColumn(this, name, title, type, width); + _columns.Insert(index, column); + + return column; + } + + /// + /// Определяет индекс колонки. + /// + /// КолонкаДереваЗначений. Колонка. + /// Число. Индекс колонки в коллекции. Если колонка не найдена, возвращается -1. + [ContextMethod("Индекс", "IndexOf")] + public int IndexOf(ValueTreeColumn column) + { + return _columns.IndexOf(column); + } + + /// + /// Возвращает количество колонок. + /// + /// Число. Количество колонокs. + [ContextMethod("Количество", "Count")] + public int Count() + { + return _columns.Count; + } + + public int Count(IBslProcess process) => Count(); + + /// + /// Ищет колонку по имени. + /// + /// Строка. Имя искомой колонки. + /// КолонкаДереваЗначений, Неопределено. Найденная колонка или Неопределено, если колонка не найдена. + [ContextMethod("Найти", "Find")] + public IValue Find(string name) + { + var column = FindColumnByName(name); + if (column == null) + return ValueFactory.Create(); + return column; + } + + /// + /// Удаляет колонку. + /// + /// КолонкаДереваЗначений. Колонка. + [ContextMethod("Удалить", "Delete")] + public void Delete(BslValue column) + { + _columns.Remove(GetColumnByIIndex(column)); + } + + /// + /// Получает колонку по индексу. + /// + /// Число. Индекс колонки. + /// КолонкаДереваЗначений. Колонка. + [ContextMethod("Получить", "Get")] + public ValueTreeColumn Get(int index) + { + if (index >= 0 && index < _columns.Count) + { + return _columns[index]; + } + throw RuntimeException.InvalidArgumentValue(); + } + + /// + /// Удаляет все колонки. + /// + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _columns.Clear(); + } + + /// + /// Сдвигает колонку на указанное смещение. + /// + /// КолонкаДереваЗначений. Колонка. + /// Число. Смещение. + [ContextMethod("Сдвинуть", "Move")] + public void Move(BslValue column, int offset) + { + var treeColumn = GetColumnByIIndex(column); + int indexSource = _columns.IndexOf(treeColumn); + + int indexDestination = (indexSource + offset) % _columns.Count(); + while (indexDestination < 0) + { + indexDestination += _columns.Count(); + } + + + if (indexSource < indexDestination) + { + _columns.Insert(indexDestination + 1, treeColumn); + _columns.RemoveAt(indexSource); + } + else + { + _columns.RemoveAt(indexSource); + _columns.Insert(indexDestination, treeColumn); + } + + } + + internal void CopyFrom(ValueTreeColumnCollection src) + { + _columns.Clear(); + foreach (ValueTreeColumn column in src._columns) + { + _columns.Add(new ValueTreeColumn(this, column)); + } + } + + public ValueTreeColumn FindColumnByName(string name) + { + var comparer = StringComparer.OrdinalIgnoreCase; + return _columns.Find(column => comparer.Equals(name, column.Name)); + } + + public ValueTreeColumn FindColumnByIndex(int index) + { + return _columns[index]; + } + + public IEnumerator GetEnumerator() + { + foreach (var item in _columns) + { + yield return item; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override int GetPropertyNumber(string name) + { + var column = FindColumnByName(name); + if (column == null) + throw PropertyAccessException.PropNotFoundException(name); + return _columns.IndexOf(column); + } + + public override int GetPropCount() + { + return _columns.Count; + } + + public override string GetPropName(int propNum) + { + return FindColumnByIndex(propNum).Name; + } + + public override IValue GetPropValue(int propNum) + { + return _columns[propNum]; + } + + public override bool IsPropWritable(int propNum) + { + return false; + } + + public override bool IsPropReadable(int propNum) + { + return true; + } + + public ValueTreeColumn GetColumnByIIndex(BslValue index) + { + if (index.SystemType == BasicTypes.String) + { + var column = FindColumnByName(index.ToString()); + if (column == null) + throw PropertyAccessException.PropNotFoundException(index.ToString()); + return column; + } + + if (index.SystemType == BasicTypes.Number) + { + int indexNum = Decimal.ToInt32(index.AsNumber()); + if (indexNum < 0 || indexNum >= Count()) + throw RuntimeException.InvalidArgumentValue(); + + var column = FindColumnByIndex(indexNum); + return column; + } + + if (index is ValueTreeColumn) + { + return index as ValueTreeColumn; + } + + throw RuntimeException.InvalidArgumentType(); + } + + public override IValue GetIndexedValue(IValue index) + { + return GetColumnByIIndex((BslValue)index); + } + + internal List GetProcessingColumnList(string columnNamesString) + { + List processingList = new List(); + if (columnNamesString != null) + { + + if (columnNamesString.Trim().Length == 0) + { + // Передали пустую строку вместо списка колонок + return processingList; + } + + string[] columnNames = columnNamesString.Split(','); + foreach (string name in columnNames) + { + var column = FindColumnByName(name.Trim()); + + if (column == null) + throw PropertyAccessException.PropNotFoundException(name.Trim()); + + processingList.Add(column); + } + } + else + { + foreach (var column in _columns) + processingList.Add(column); + } + return processingList; + } + + void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) + { + visitor.ShowProperties(this); + } + } +} diff --git a/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRow.cs b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRow.cs new file mode 100644 index 000000000..0d8eccd6a --- /dev/null +++ b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRow.cs @@ -0,0 +1,253 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTree +{ + /// + /// Строка дерева значений. + /// + [ContextClass("СтрокаДереваЗначений", "ValueTreeRow")] + public class ValueTreeRow : AutoContext, ICollectionContext, IDebugPresentationAcceptor + { + private readonly Dictionary _data = new Dictionary(); + private readonly ValueTreeRow _parent; + private readonly ValueTree _owner; + private readonly int _level; + private readonly ValueTreeRowCollection _rows; + + public ValueTreeRow(ValueTree owner, ValueTreeRow parent, int level) + { + _owner = owner; + _parent = parent; + _level = level; + _rows = new ValueTreeRowCollection(owner, this, level + 1); + } + + public int Count() + { + return base.GetPropCount() + _owner.Columns.Count(); + } + + public int Count(IBslProcess process) => Count(); + + [ContextProperty("Родитель", "Parent")] + public IValue Parent + { + get + { + if (_parent != null) + return _parent; + return ValueFactory.Create(); + } + } + + [ContextProperty("Строки", "Rows")] + public ValueTreeRowCollection Rows + { + get { return _rows; } + } + + /// + /// Возвращает дерево значений, в которе входит строка. + /// + /// ДеревоЗначений. Владелец строки. + [ContextMethod("Владелец", "Owner")] + public ValueTree Owner() + { + return _owner; + } + + private IValue TryValue(ValueTreeColumn column) + { + IValue value; + if (_data.TryGetValue(column, out value)) + { + return value; + } + return column.ValueType.AdjustValue(); + } + + /// + /// Получает значение по индексу. + /// + /// Число. Индекс получаемого параметра. + /// Произвольный. Получаемое значение. + [ContextMethod("Получить", "Get")] + public IValue Get(int index) + { + var column = Owner().Columns.FindColumnByIndex(index); + return TryValue(column); + } + + public IValue Get(IValue index) + { + var column = Owner().Columns.GetColumnByIIndex((BslValue)index); + return TryValue(column); + } + + public IValue Get(ValueTreeColumn column) + { + return TryValue(column); + } + + /// + /// Устанавливает значение по индексу. + /// + /// Число. Индекс параметра, которому задаётся значение. + /// Произвольный. Новое значение. + [ContextMethod("Установить", "Set")] + public void Set(int index, IValue value) + { + var column = Owner().Columns.FindColumnByIndex(index); + _data[column] = column.ValueType.AdjustValue(value); + } + + public void Set(IValue index, IValue value) + { + var column = Owner().Columns.GetColumnByIIndex((BslValue)index); + _data[column] = column.ValueType.AdjustValue(value); + } + + public void Set(ValueTreeColumn column, IValue value) + { + _data[column] = column.ValueType.AdjustValue(value); + } + + /// + /// Возвращает уровень вложенности строки в дереве. + /// Строки верхнего уровня имеют значение 0. + /// + /// Число. Уровень вложенности строки. + [ContextMethod("Уровень", "Level")] + public int Level() + { + return _level; + } + + public IEnumerator GetEnumerator() + { + foreach (ValueTreeColumn item in Owner().Columns) + { + yield return TryValue(item); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override int GetPropCount() + { + return Count(); + } + + public override string GetPropName(int propNum) + { + if (IsOwnProp(propNum)) + return base.GetPropName(propNum); + return Owner().Columns.GetPropName(GetColumnIndex(propNum)); + } + + public override int GetPropertyNumber(string name) + { + if (PropertyMapper.ContainsProperty(name)) + { + return base.GetPropertyNumber(name); + } + + var cols = Owner().Columns; + var column = cols.FindColumnByName(name); + if (column != null) + { + return GetColumnPropIndex(cols.IndexOf(column)); + } + + throw PropertyAccessException.PropNotFoundException(name); + } + + public override bool IsPropReadable(int propNum) + { + return true; + } + + public override bool IsPropWritable(int propNum) + { + return !IsOwnProp(propNum); + } + + public override IValue GetPropValue(int propNum) + { + if (IsOwnProp(propNum)) + { + return base.GetPropValue(propNum); + } + + var column = Owner().Columns.FindColumnByIndex(GetColumnIndex(propNum)); + return TryValue(column); + } + + public override void SetPropValue(int propNum, IValue newVal) + { + if (IsOwnProp(propNum)) + { + base.SetPropValue(propNum, newVal); + } + else + { + var column = Owner().Columns.FindColumnByIndex(GetColumnIndex(propNum)); + _data[column] = column.ValueType.AdjustValue(newVal); + } + } + + private ValueTreeColumn GetColumnByIIndex(IValue index) + { + return Owner().Columns.GetColumnByIIndex((BslValue)index); + } + + public override IValue GetIndexedValue(IValue index) + { + var column = GetColumnByIIndex(index); + return TryValue(column); + } + + public override void SetIndexedValue(IValue index, IValue val) + { + var column = GetColumnByIIndex(index); + _data[GetColumnByIIndex(index)] = column.ValueType.AdjustValue(val); + } + + private bool IsOwnProp(int propNum) + { + return propNum < base.GetPropCount(); + } + + private int GetColumnPropIndex(int index) + { + return base.GetPropCount() + index; + } + + private int GetColumnIndex(int propIndex) + { + return propIndex - base.GetPropCount(); + } + + void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) + { + visitor.ShowProperties(this); + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeRowCollection.cs b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRowCollection.cs similarity index 83% rename from src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeRowCollection.cs rename to src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRowCollection.cs index 8a39c4b53..10a1c8259 100644 --- a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeRowCollection.cs +++ b/src/OneScript.StandardLibrary/Collections/ValueTree/ValueTreeRowCollection.cs @@ -1,500 +1,492 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTree -{ - /// - /// Коллекция строк дерева значений. - /// - [ContextClass("КоллекцияСтрокДереваЗначений", "ValueTreeRowCollection")] - public class ValueTreeRowCollection : AutoContext, ICollectionContext, IEnumerable - { - - private readonly List _rows = new List(); - private readonly ValueTreeRow _parent; - private readonly ValueTree _owner; - private readonly int _level; - - public ValueTreeRowCollection(ValueTree owner, ValueTreeRow parent, int level) - { - _owner = owner; - _parent = parent; - _level = level; +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary.Collections.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Collections.ValueTree +{ + /// + /// Коллекция строк дерева значений. + /// + [ContextClass("КоллекцияСтрокДереваЗначений", "ValueTreeRowCollection")] + public class ValueTreeRowCollection : AutoCollectionContext + { + private readonly List _rows = new List(); + private readonly ValueTreeRow _parent; + private readonly ValueTree _owner; + private readonly int _level; + + public ValueTreeRowCollection(ValueTree owner, ValueTreeRow parent, int level) + { + _owner = owner; + _parent = parent; + _level = level; + } + + [ContextProperty("Родитель", "Parent")] + public IValue Parent + { + get + { + if (_parent != null) + return _parent; + return ValueFactory.Create(); + } + } + + private ValueTreeColumnCollection Columns + { + get + { + return _owner.Columns; + } + } + + /// + /// Возвращает дерево значений, в которе входит строка. + /// + /// ДеревоЗначений. Владелец строки. + [ContextMethod("Владелец", "Owner")] + public ValueTree Owner() + { + return _owner; + } + + /// + /// Возвращает количество строк. + /// + /// Число. Количество строк. + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _rows.Count; + } + + /// + /// Добавляет строку в коллекцию. + /// + /// СтрокаДереваЗначений. Добавленная строка. + [ContextMethod("Добавить", "Add")] + public ValueTreeRow Add() + { + ValueTreeRow row = new ValueTreeRow(Owner(), _parent, _level); + _rows.Add(row); + return row; + } + + /// + /// Добавляет строку в коллекцию. + /// + /// Число. Индекс новой строки. + /// СтрокаДереваЗначений. Добавленная строка. + [ContextMethod("Вставить", "Insert")] + public ValueTreeRow Insert(int index) + { + ValueTreeRow row = new ValueTreeRow(Owner(), _parent, _level); + _rows.Insert(index, row); + return row; + } + + /// + /// Удаляет строку из коллекции. + /// + /// СтрокаДереваЗначений, Число. Удаляемая строка или её индекс. + [ContextMethod("Удалить", "Delete")] + public void Delete(BslValue row) + { + int index; + if (row is ValueTreeRow) + { + index = _rows.IndexOf(row as ValueTreeRow); + if (index == -1) + throw RuntimeException.InvalidArgumentValue(); + } + else + { + index = Decimal.ToInt32(row.AsNumber()); + } + _rows.RemoveAt(index); + } + + /// + /// Загружает значения из массива в колонку. + /// + /// Массив. Значения. + /// КолонкаДереваЗначений, Число, Строка. Колонка, в которую будут загружены значения, её имя или индекс. + [ContextMethod("ЗагрузитьКолонку", "LoadColumn")] + public void LoadColumn(ArrayImpl values, IValue columnIndex) + { + var rowIterator = _rows.GetEnumerator(); + using var arrayIterator = values.GetEnumerator(); + + while (rowIterator.MoveNext() && arrayIterator.MoveNext()) + { + rowIterator.Current.Set(columnIndex, arrayIterator.Current); + } + } + + /// + /// Загружает значения из массива в колонку. + /// + /// КолонкаДереваЗначений, Число, Строка. Колонка, из которой будут выгружены значения, её имя или индекс. + /// Массив. Массив значений. + [ContextMethod("ВыгрузитьКолонку", "UnloadColumn")] + public ArrayImpl UnloadColumn(IValue column) + { + ArrayImpl result = new ArrayImpl(); + + foreach (ValueTreeRow row in _rows) + { + result.Add(row.Get(column)); + } + + return result; + } + + /// + /// Определяет индекс строки. + /// + /// СтрокаДереваЗначений. Строка. + /// Число. Индекс строки в коллекции. Если строка не найдена, возвращается -1. + [ContextMethod("Индекс", "IndexOf")] + public int IndexOf(BslValue row) + { + if (row is ValueTreeRow treeRow) + return _rows.IndexOf(treeRow); + + return -1; + } + + /// + /// Суммирует значения в строках. + /// + /// КолонкаДереваЗначений, Строка, Число. Колонка, значения которой будут суммироваться. + /// Булево. Если Истина, в расчёт будут включены все вложенные строки. + /// Число. Вычисленная сумма. + [ContextMethod("Итог", "Total")] + public IValue Total(BslValue columnIndex, bool includeChildren = false) + { + ValueTreeColumn column = Columns.GetColumnByIIndex(columnIndex); + decimal result = 0; + + foreach (ValueTreeRow row in _rows) + { + IValue currentValue = row.Get(column); + if (currentValue.SystemType == BasicTypes.Number) + { + result += currentValue.AsNumber(); + } + + if (includeChildren) + { + IValue childrenTotal = row.Rows.Total(columnIndex, includeChildren); + if (childrenTotal.SystemType == BasicTypes.Number) + { + result += childrenTotal.AsNumber(); + } + } + } + + return ValueFactory.Create(result); + } + + /// + /// Ищет значение в строках дерева значений. + /// + /// Произвольный. Искомое значение. + /// Строка. Список колонок через запятую, в которых будет производиться поиск. Необязательный параметр. + /// Булево. Если Истина, в поиск будут включены все вложенные строки. Необязательный параметр. + /// СтрокаДереваЗначений, Неопределено. Найденная строка или Неопределено, если строка не найдена. + [ContextMethod("Найти", "Find")] + public IValue Find(IValue value, string columnNames = null, bool includeChildren = false) + { + List processingList = Columns.GetProcessingColumnList(columnNames); + foreach (ValueTreeRow row in _rows) + { + foreach (ValueTreeColumn col in processingList) + { + IValue current = row.Get(col); + if (value.StrictEquals(current)) + return row; + } + if (includeChildren) + { + IValue childrenResult = row.Rows.Find(value, columnNames, includeChildren); + if (childrenResult.SystemType != BasicTypes.Undefined) + { + return childrenResult; + } + } + } + return ValueFactory.Create(); + } + + private bool CheckFilterCriteria(ValueTreeRow row, StructureImpl filter) + { + foreach (KeyAndValueImpl kv in filter) + { + ValueTreeColumn column = Columns.FindColumnByName(kv.Key.ToString()); + if (column == null) + throw PropertyAccessException.PropNotFoundException(kv.Key.ToString()); + + IValue current = row.Get(column); + if (!current.StrictEquals(kv.Value)) + return false; + } + return true; + } + + /// + /// Ищет строки, отвечающие критериям отбора. + /// + /// Структура. Структура, в которой Ключ - это имя колонки, а Значение - искомое значение. + /// Булево. Если Истина, в поиск будут включены все вложенные строки. Необязательный параметр. + /// Массив. Найденные строки. + [ContextMethod("НайтиСтроки", "FindRows")] + public ArrayImpl FindRows(BslValue filter, bool includeChildren = false) + { + var filterStruct = filter as StructureImpl; + + if (filterStruct == null) + throw RuntimeException.InvalidArgumentType(); + + ArrayImpl result = new ArrayImpl(); + + foreach (ValueTreeRow row in _rows) + { + if (CheckFilterCriteria(row, filterStruct)) + { + result.Add(row); + } + + if (includeChildren) + { + ArrayImpl childrenResult = row.Rows.FindRows(filter, includeChildren); + foreach (IValue value in childrenResult) + { + result.Add(value); + } + } + } + + return result; + } + + /// + /// Удаляет все строки. + /// + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _rows.Clear(); + } + + /// + /// Получает строку по индексу. + /// + /// Число. Индекс строки. + /// СтрокаДереваЗначений. Строка. + [ContextMethod("Получить", "Get")] + public ValueTreeRow Get(int index) + { + if (index < 0 || index >= Count()) + throw RuntimeException.InvalidArgumentValue(); + return _rows[index]; + } + + /// + /// Сдвигает строку на указанное смещение. + /// + /// СтрокаДереваЗначений. Строка. + /// Число. Смещение. + [ContextMethod("Сдвинуть", "Move")] + public void Move(BslValue row, int offset) + { + int indexSource; + if (row is ValueTreeRow) + indexSource = _rows.IndexOf(row as ValueTreeRow); + else if (row.SystemType == BasicTypes.Number) + indexSource = decimal.ToInt32(row.AsNumber()); + else + throw RuntimeException.InvalidArgumentType(); + + if (indexSource < 0 || indexSource >= _rows.Count) + throw RuntimeException.InvalidArgumentValue(); + + int indexDestination = (indexSource + offset) % _rows.Count; + while (indexDestination < 0) + indexDestination += _rows.Count; + + ValueTreeRow tmp = _rows[indexSource]; + + if (indexSource < indexDestination) + { + _rows.Insert(indexDestination + 1, tmp); + _rows.RemoveAt(indexSource); + } + else + { + _rows.RemoveAt(indexSource); + _rows.Insert(indexDestination, tmp); + } + + } + + private struct ValueTreeSortRule + { + public ValueTreeColumn Column; + public int direction; // 1 = asc, -1 = desc + } + + private List GetSortRules(string columns) + { + + string[] aColumns = columns.Split(','); + + List rules = new List(); + + foreach (string column in aColumns) + { + string[] description = column.Trim().Split(' '); + if (description.Length == 0) + throw ColumnException.WrongColumnName(); + + ValueTreeSortRule desc = new ValueTreeSortRule(); + desc.Column = this.Columns.FindColumnByName(description[0]); + if (desc.Column == null) + throw ColumnException.WrongColumnName(description[0]); + + if (description.Length > 1) + { + if (String.Compare(description[1], "DESC", true) == 0 || String.Compare(description[1], "УБЫВ", true) == 0) + desc.direction = -1; + else + desc.direction = 1; + } + else + desc.direction = 1; + + rules.Add(desc); + } + + return rules; + } + + private class RowComparator : IComparer + { + readonly List _rules; + + readonly GenericIValueComparer _comparer; + + public RowComparator(IBslProcess process, List rules) + { + if (rules.Count == 0) + throw RuntimeException.InvalidArgumentValue(); + + this._rules = rules; + _comparer = new GenericIValueComparer(process); + } + + private int OneCompare(ValueTreeRow x, ValueTreeRow y, ValueTreeSortRule rule) + { + IValue xValue = x.Get(rule.Column); + IValue yValue = y.Get(rule.Column); + + int result = _comparer.Compare(xValue, yValue) * rule.direction; + + return result; + } + + public int Compare(ValueTreeRow x, ValueTreeRow y) + { + int i = 0, r; + while ((r = OneCompare(x, y, _rules[i])) == 0) + { + if (++i >= _rules.Count) + return 0; + } + + return r; + } + } + + /// + /// Сортирует строки по указанному правилу. + /// + /// Строка. Правило сортировки: список имён колонок, разделённых запятой. После имени через + /// пробел может указываться направление сортировки: Возр(Asc) - по возрастанию, Убыв(Desc) - по убыванию. + /// Булево. Если Истина, сортировка будет применена также к вложенным строкам. + /// СравнениеЗначений. Не используется. + [ContextMethod("Сортировать", "Sort")] + public void Sort(IBslProcess process, string columns, bool sortChildren = false, IValue comparator = null) + { + Sort(process, new RowComparator(process, GetSortRules(columns)), sortChildren); + } + + private void Sort(IBslProcess process, RowComparator comparator, bool sortChildren) + { + _rows.Sort(comparator); + + if (sortChildren) + { + foreach (var row in _rows) + { + row.Rows.Sort(process, comparator, sortChildren); + } + } + } + + /// + /// Не поддерживается. + /// + [ContextMethod("ВыбратьСтроку", "ChooseRow")] + public void ChooseRow(string title = null, IValue startRow = null) + { + throw new NotSupportedException(); + } + + internal void CopyFrom(ValueTreeRowCollection src) + { + _rows.Clear(); + ValueTreeColumnCollection columns = Owner().Columns; + + foreach (ValueTreeRow row in src._rows) + { + ValueTreeRow newRow = Add(); + foreach (ValueTreeColumn column in columns) + { + newRow.Set(column, row.Get(ValueFactory.Create(column.Name))); + } + newRow.Rows.CopyFrom(row.Rows); + } + } + + + public override IEnumerator GetEnumerator() + { + foreach (var item in _rows) + { + yield return item; + } + } + + + public override IValue GetIndexedValue(IValue index) + { + return Get((int)index.AsNumber()); } - - [ContextProperty("Родитель", "Parent")] - public IValue Parent - { - get - { - if (_parent != null) - return _parent; - return ValueFactory.Create(); - } - } - - private ValueTreeColumnCollection Columns - { - get - { - return _owner.Columns; - } - } - - /// - /// Возвращает дерево значений, в которе входит строка. - /// - /// ДеревоЗначений. Владелец строки. - [ContextMethod("Владелец", "Owner")] - public ValueTree Owner() - { - return _owner; - } - - /// - /// Возвращает количество строк. - /// - /// Число. Количество строк. - [ContextMethod("Количество", "Count")] - public int Count() - { - return _rows.Count(); - } - - /// - /// Добавляет строку в коллекцию. - /// - /// СтрокаДереваЗначений. Добавленная строка. - [ContextMethod("Добавить", "Add")] - public ValueTreeRow Add() - { - ValueTreeRow row = new ValueTreeRow(Owner(), _parent, _level); - _rows.Add(row); - return row; - } - - /// - /// Добавляет строку в коллекцию. - /// - /// Число. Индекс новой строки. - /// СтрокаДереваЗначений. Добавленная строка. - [ContextMethod("Вставить", "Insert")] - public ValueTreeRow Insert(int index) - { - ValueTreeRow row = new ValueTreeRow(Owner(), _parent, _level); - _rows.Insert(index, row); - return row; - } - - /// - /// Удаляет строку из коллекции. - /// - /// СтрокаДереваЗначений, Число. Удаляемая строка или её индекс. - [ContextMethod("Удалить", "Delete")] - public void Delete(IValue row) - { - row = row.GetRawValue(); - int index; - if (row is ValueTreeRow) - { - index = _rows.IndexOf(row as ValueTreeRow); - if (index == -1) - throw RuntimeException.InvalidArgumentValue(); - } - else - { - index = Decimal.ToInt32(row.AsNumber()); - } - _rows.RemoveAt(index); - } - - /// - /// Загружает значения из массива в колонку. - /// - /// Массив. Значения. - /// КолонкаДереваЗначений, Число, Строка. Колонка, в которую будут загружены значения, её имя или индекс. - [ContextMethod("ЗагрузитьКолонку", "LoadColumn")] - public void LoadColumn(IValue values, IValue columnIndex) - { - var rowIterator = _rows.GetEnumerator(); - var arrayIterator = (values as ArrayImpl).GetEnumerator(); - - while (rowIterator.MoveNext() && arrayIterator.MoveNext()) - { - rowIterator.Current.Set(columnIndex, arrayIterator.Current); - } - } - - /// - /// Загружает значения из массива в колонку. - /// - /// КолонкаДереваЗначений, Число, Строка. Колонка, из которой будут выгружены значения, её имя или индекс. - /// Массив. Массив значений. - [ContextMethod("ВыгрузитьКолонку", "UnloadColumn")] - public ArrayImpl UnloadColumn(IValue column) - { - ArrayImpl result = new ArrayImpl(); - - foreach (ValueTreeRow row in _rows) - { - result.Add(row.Get(column)); - } - - return result; - } - - /// - /// Определяет индекс строки. - /// - /// СтрокаДереваЗначений. Строка. - /// Число. Индекс строки в коллекции. Если строка не найдена, возвращается -1. - [ContextMethod("Индекс", "IndexOf")] - public int IndexOf(IValue row) - { - row = row.GetRawValue(); - - if (row is ValueTreeRow) - return _rows.IndexOf(row as ValueTreeRow); - - return -1; - } - - /// - /// Суммирует значения в строках. - /// - /// КолонкаДереваЗначений, Строка, Число. Колонка, значения которой будут суммироваться. - /// Булево. Если Истина, в расчёт будут включены все вложенные строки. - /// Число. Вычисленная сумма. - [ContextMethod("Итог", "Total")] - public IValue Total(IValue columnIndex, bool includeChildren = false) - { - ValueTreeColumn column = Columns.GetColumnByIIndex(columnIndex); - decimal result = 0; - - foreach (ValueTreeRow row in _rows) - { - IValue currentValue = row.Get(column); - if (currentValue.DataType == Machine.DataType.Number) - { - result += currentValue.AsNumber(); - } - - if (includeChildren) - { - IValue childrenTotal = row.Rows.Total(columnIndex, includeChildren); - if (childrenTotal.DataType == Machine.DataType.Number) - { - result += childrenTotal.AsNumber(); - } - } - } - - return ValueFactory.Create(result); - } - - /// - /// Ищет значение в строках дерева значений. - /// - /// Произвольный. Искомое значение. - /// Строка. Список колонок через запятую, в которых будет производиться поиск. Необязательный параметр. - /// Булево. Если Истина, в поиск будут включены все вложенные строки. Необязательный параметр. - /// СтрокаДереваЗначений, Неопределено. Найденная строка или Неопределено, если строка не найдена. - [ContextMethod("Найти", "Find")] - public IValue Find(IValue value, string columnNames = null, bool includeChildren = false) - { - List processingList = Columns.GetProcessingColumnList(columnNames); - foreach (ValueTreeRow row in _rows) - { - foreach (ValueTreeColumn col in processingList) - { - IValue current = row.Get(col); - if (value.Equals(current)) - return row; - } - if (includeChildren) - { - IValue childrenResult = row.Rows.Find(value, columnNames, includeChildren); - if (childrenResult.DataType != Machine.DataType.Undefined) - { - return childrenResult; - } - } - } - return ValueFactory.Create(); - } - - private bool CheckFilterCriteria(ValueTreeRow row, StructureImpl filter) - { - foreach (KeyAndValueImpl kv in filter) - { - ValueTreeColumn column = Columns.FindColumnByName(kv.Key.AsString()); - if (column == null) - throw RuntimeException.PropNotFoundException(kv.Key.AsString()); - - IValue current = row.Get(column); - if (!current.Equals(kv.Value)) - return false; - } - return true; - } - - /// - /// Ищет строки, отвечающие критериям отбора. - /// - /// Структура. Структура, в которой Ключ - это имя колонки, а Значение - искомое значение. - /// Булево. Если Истина, в поиск будут включены все вложенные строки. Необязательный параметр. - /// Массив. Найденные строки. - [ContextMethod("НайтиСтроки", "FindRows")] - public ArrayImpl FindRows(IValue filter, bool includeChildren = false) - { - var filterStruct = filter.GetRawValue() as StructureImpl; - - if (filterStruct == null) - throw RuntimeException.InvalidArgumentType(); - - ArrayImpl result = new ArrayImpl(); - - foreach (ValueTreeRow row in _rows) - { - if (CheckFilterCriteria(row, filterStruct)) - { - result.Add(row); - } - - if (includeChildren) - { - ArrayImpl childrenResult = row.Rows.FindRows(filter, includeChildren); - foreach (IValue value in childrenResult) - { - result.Add(value); - } - } - } - - return result; - } - - /// - /// Удаляет все строки. - /// - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _rows.Clear(); - } - - /// - /// Получает строку по индексу. - /// - /// Число. Индекс строки. - /// СтрокаДереваЗначений. Строка. - [ContextMethod("Получить", "Get")] - public ValueTreeRow Get(int index) - { - if (index < 0 || index >= Count()) - throw RuntimeException.InvalidArgumentValue(); - return _rows[index]; - } - - /// - /// Сдвигает строку на указанное смещение. - /// - /// СтрокаДереваЗначений. Строка. - /// Число. Смещение. - [ContextMethod("Сдвинуть", "Move")] - public void Move(IValue row, int offset) - { - row = row.GetRawValue(); - - int indexSource; - if (row is ValueTreeRow) - indexSource = _rows.IndexOf(row as ValueTreeRow); - else if (row.DataType == Machine.DataType.Number) - indexSource = decimal.ToInt32(row.AsNumber()); - else - throw RuntimeException.InvalidArgumentType(); - - if (indexSource < 0 || indexSource >= _rows.Count()) - throw RuntimeException.InvalidArgumentValue(); - - int indexDestination = (indexSource + offset) % _rows.Count(); - while (indexDestination < 0) - indexDestination += _rows.Count(); - - ValueTreeRow tmp = _rows[indexSource]; - - if (indexSource < indexDestination) - { - _rows.Insert(indexDestination + 1, tmp); - _rows.RemoveAt(indexSource); - } - else - { - _rows.RemoveAt(indexSource); - _rows.Insert(indexDestination, tmp); - } - - } - - private struct ValueTreeSortRule - { - public ValueTreeColumn Column; - public int direction; // 1 = asc, -1 = desc - } - - private List GetSortRules(string columns) - { - - string[] aColumns = columns.Split(','); - - List rules = new List(); - - foreach (string column in aColumns) - { - string[] description = column.Trim().Split(' '); - if (description.Count() == 0) - throw RuntimeException.PropNotFoundException(""); // TODO: WrongColumnNameException - - ValueTreeSortRule desc = new ValueTreeSortRule(); - desc.Column = this.Columns.FindColumnByName(description[0]); - if (desc.Column == null) - throw RuntimeException.PropNotFoundException(description[0]); - - if (description.Count() > 1) - { - if (String.Compare(description[1], "DESC", true) == 0 || String.Compare(description[1], "УБЫВ", true) == 0) - desc.direction = -1; - else - desc.direction = 1; - } - else - desc.direction = 1; - - rules.Add(desc); - } - - return rules; - } - - private class RowComparator : IComparer - { - readonly List _rules; - - readonly GenericIValueComparer _comparer = new GenericIValueComparer(); - - public RowComparator(List rules) - { - if (rules.Count() == 0) - throw RuntimeException.InvalidArgumentValue(); - - this._rules = rules; - } - - private int OneCompare(ValueTreeRow x, ValueTreeRow y, ValueTreeSortRule rule) - { - IValue xValue = x.Get(rule.Column); - IValue yValue = y.Get(rule.Column); - - int result = _comparer.Compare(xValue, yValue) * rule.direction; - - return result; - } - - public int Compare(ValueTreeRow x, ValueTreeRow y) - { - int i = 0, r; - while ((r = OneCompare(x, y, _rules[i])) == 0) - { - if (++i >= _rules.Count()) - return 0; - } - - return r; - } - } - - /// - /// Сортирует строки по указанному правилу. - /// - /// Строка. Правило сортировки: список имён колонок, разделённых запятой. После имени через - /// пробел может указываться направление сортировки: Возр(Asc) - по возрастанию, Убыв(Desc) - по убыванию. - /// Булево. Если Истина, сортировка будет применена также к вложенным строкам. - /// СравнениеЗначений. Не используется. - [ContextMethod("Сортировать", "Sort")] - public void Sort(string columns, bool sortChildren = false, IValue comparator = null) - { - Sort(new RowComparator(GetSortRules(columns)), sortChildren); - } - - private void Sort(RowComparator comparator, bool sortChildren) - { - _rows.Sort(comparator); - - if (sortChildren) - { - foreach (var row in _rows) - { - row.Rows.Sort(comparator, sortChildren); - } - } - } - - /// - /// Не поддерживается. - /// - [ContextMethod("ВыбратьСтроку", "ChooseRow")] - public void ChooseRow(string title = null, IValue startRow = null) - { - throw new NotSupportedException(); - } - - internal void CopyFrom(ValueTreeRowCollection src) - { - _rows.Clear(); - ValueTreeColumnCollection columns = Owner().Columns; - - foreach (ValueTreeRow row in src._rows) - { - ValueTreeRow newRow = Add(); - foreach (ValueTreeColumn column in columns) - { - newRow.Set(column, row.Get(ValueFactory.Create(column.Name))); - } - newRow.Rows.CopyFrom(row.Rows); - } - } - - - public IEnumerator GetEnumerator() - { - foreach (var item in _rows) - { - yield return item; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public override IValue GetIndexedValue(IValue index) - { - return Get((int)index.AsNumber()); - } - } -} + } +} diff --git a/src/ScriptEngine.HostedScript/Library/CustomLineFeedStreamReader.cs b/src/OneScript.StandardLibrary/CustomLineFeedStreamReader.cs similarity index 82% rename from src/ScriptEngine.HostedScript/Library/CustomLineFeedStreamReader.cs rename to src/OneScript.StandardLibrary/CustomLineFeedStreamReader.cs index a7a264ec5..f087c5d26 100644 --- a/src/ScriptEngine.HostedScript/Library/CustomLineFeedStreamReader.cs +++ b/src/OneScript.StandardLibrary/CustomLineFeedStreamReader.cs @@ -4,12 +4,13 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; -using System.IO; using System.Collections.Generic; +using System.IO; using System.Text; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary { public class CustomLineFeedStreamReader : IDisposable @@ -44,17 +45,18 @@ private void UpdateCharQueue (int minimalLentgh = 1) public int Read () { if (_buffer.Count == 0) + { UpdateCharQueue (); - - if (_buffer.Count == 0) - return -1; + if (_buffer.Count == 0) + return -1; + } if (_analyzeDefaults && _buffer.Peek () == '\r') { _buffer.Dequeue (); UpdateCharQueue (); - if (_buffer.Count > 0 && _buffer.Peek () == '\n') { + if (_buffer.Count != 0 && _buffer.Peek () == '\n') { _buffer.Dequeue (); UpdateCharQueue (); } @@ -62,30 +64,28 @@ public int Read () return '\n'; } - if (_eolDelimiter.Length > 0 && _buffer.Peek() == _eolDelimiter [0]) { - bool isEol = true; + if (_eolDelimiter.Length > 0 && _buffer.Peek() == _eolDelimiter [0]) + { UpdateCharQueue (_eolDelimiter.Length); var eolIndex = 0; - foreach (var bufChar in _buffer) { - - if (eolIndex >= _eolDelimiter.Length) + foreach (var bufChar in _buffer) + { + if (bufChar != _eolDelimiter[eolIndex]) break; - - if (bufChar != _eolDelimiter [eolIndex]) { - isEol = false; - break; - } ++eolIndex; - } - if (isEol) { - - foreach (var eolChar in _eolDelimiter) - _buffer.Dequeue (); - - return '\n'; + if (eolIndex == _eolDelimiter.Length) + { + while (eolIndex > 0) + { + _buffer.Dequeue(); + --eolIndex; + } + + return '\n'; + } } } diff --git a/src/OneScript.StandardLibrary/DelegateAction.cs b/src/OneScript.StandardLibrary/DelegateAction.cs new file mode 100644 index 000000000..32fb02697 --- /dev/null +++ b/src/OneScript.StandardLibrary/DelegateAction.cs @@ -0,0 +1,100 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + /// + /// Делегат для выполнения метода в другом объекте + /// + [ContextClass("Действие","Action")] + public class DelegateAction : ContextIValueImpl + { + private readonly Func _action; + private const string MethodName_Ru = "Выполнить"; + private const string MethodName_En = "Execute"; + + private static BslMethodInfo ExecuteMethodInfo; + + static DelegateAction() + { + var builder = BslMethodBuilder.Create() + .DeclaringType(typeof(DelegateAction)) + .ReturnType(typeof(BslValue)) + .SetNames(MethodName_Ru, MethodName_En); + + ExecuteMethodInfo = builder.Build(); + } + + public DelegateAction(Func action) + { + _action = action; + } + + public DelegateAction(Func action) + { + _action = (process, parameters) => action(process, parameters + .Select(x => x is IValueReference r ? r.BslValue : (BslValue)x) + .ToArray() + ); + } + + public override bool DynamicMethodSignatures => true; + + public override int GetMethodNumber(string name) + { + if (string.Compare(name, MethodName_En, StringComparison.OrdinalIgnoreCase) == 0 + || string.Compare(name, MethodName_Ru, StringComparison.OrdinalIgnoreCase) == 0) + { + return 0; + } + + return base.GetMethodNumber(name); + } + + public override int GetMethodsCount() + { + return 1; + } + + public override BslMethodInfo GetMethodInfo(int methodNumber) + { + return ExecuteMethodInfo; + } + + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + retValue = _action(process, arguments); + } + + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + _action(process, arguments); + } + + [ScriptConstructor] + public static DelegateAction Create(IRuntimeContextInstance target, string methodName) + { + var method = target.GetMethodNumber(methodName); + + Func action = (process, parameters) => + { + target.CallAsFunction(method, parameters, out var retVal, process); + return retVal; + }; + + return new DelegateAction(action); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/DriveInfo/DriveInfo.cs b/src/OneScript.StandardLibrary/DriveInfo/DriveInfo.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/DriveInfo/DriveInfo.cs rename to src/OneScript.StandardLibrary/DriveInfo/DriveInfo.cs index eb009b2c6..ea65253e6 100644 --- a/src/ScriptEngine.HostedScript/Library/DriveInfo/DriveInfo.cs +++ b/src/OneScript.StandardLibrary/DriveInfo/DriveInfo.cs @@ -1,145 +1,151 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; - -namespace ScriptEngine.HostedScript.Library.DriveInfo -{ - [ContextClass("ИнформацияОДиске", "DriveInfo")] - public class DriveInfo : AutoContext - { - private System.IO.DriveInfo _driveInfo; - - public DriveInfo(string driveName) - { - SystemDriveInfo = new System.IO.DriveInfo(driveName); - } - - [ScriptConstructor] - public static DriveInfo Constructor(IValue driveName) - { - return new DriveInfo(driveName.AsString()); - } - - - /// - /// Указывает объем доступного свободного места на диске в байтах. - /// - [ContextProperty("Доступно")] - public Int64 AvailableFreeSpace - { - get { return SystemDriveInfo.AvailableFreeSpace; } - } - - /// - /// Получает имя файловой системы - /// - [ContextProperty("ИмяФС")] - public string DriveFormat - { - get { return SystemDriveInfo.DriveFormat ?? "unknown"; } // на mono может быть null - } - - /// - /// Возвращает тип диска - /// - /// ТипДиска - [ContextProperty("ТипДиска", "DriveType")] - public IValue DriveTypeProp - { - get { - var dte = DriveTypeEnum.CreateInstance(); - return dte.GetPropValue((int)_driveInfo.DriveType); - } - } - - /// - /// Получает значение, указывающее состояние готовности диска. - /// - [ContextProperty("Готов")] - public bool IsReady - { - get - { - return SystemDriveInfo.IsReady; - } - } - - /// - /// Возвращает имя диска - /// - [ContextProperty("Имя")] - public string Name - { - get - { - return SystemDriveInfo.Name; - } - } - - /// - /// Возвращает корневой каталог диска. - /// - [ContextProperty("КорневойКаталог")] - public IValue RootDirectory - { - get - { - return new FileContext(SystemDriveInfo.RootDirectory.FullName); - } - } - - /// - /// Возвращает общий объем свободного места, доступного на диске, в байтах - /// - [ContextProperty("ОбщийОбъемСвободногоМеста")] - public Int64 TotalFreeSpace - { - get { return SystemDriveInfo.TotalFreeSpace; } - } - - /// - /// Возвращает общий размер места для хранения на диске в байтах. - /// - [ContextProperty("РазмерДиска")] - public Int64 TotalSize - { - get { return SystemDriveInfo.TotalSize; } - } - - /// - /// Возвращает или задает метку тома диска. - /// - [ContextProperty("МеткаТома")] - public string VolumeLabel - { - get - { - return SystemDriveInfo.VolumeLabel; - } - set - { - SystemDriveInfo.VolumeLabel = value; - } - } - - public System.IO.DriveInfo SystemDriveInfo - { - get - { - return _driveInfo; - } - - set - { - _driveInfo = value; - } - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Types; +using ScriptEngine; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.DriveInfo +{ + [ContextClass("ИнформацияОДиске", "DriveInfo")] + public class DriveInfo : AutoContext + { + private readonly IGlobalsManager _globals; + private System.IO.DriveInfo _driveInfo; + + private DriveInfo(IGlobalsManager globals, string driveName) + { + _globals = globals; + SystemDriveInfo = new System.IO.DriveInfo(driveName); + } + + [ScriptConstructor] + public static DriveInfo Constructor(TypeActivationContext activation, string driveName) + { + return new DriveInfo(activation.Services.Resolve(), driveName); + } + + + /// + /// Указывает объем доступного свободного места на диске в байтах. + /// + [ContextProperty("Доступно")] + public Int64 AvailableFreeSpace + { + get { return SystemDriveInfo.AvailableFreeSpace; } + } + + /// + /// Получает имя файловой системы + /// + [ContextProperty("ИмяФС")] + public string DriveFormat + { + get { return SystemDriveInfo.DriveFormat ?? "unknown"; } // на mono может быть null + } + + /// + /// Возвращает тип диска + /// + /// ТипДиска + [ContextProperty("ТипДиска", "DriveType")] + public IValue DriveTypeProp + { + get + { + var dte = _globals.GetInstance(); + return dte.GetPropValue((int)_driveInfo.DriveType); + } + } + + /// + /// Получает значение, указывающее состояние готовности диска. + /// + [ContextProperty("Готов")] + public bool IsReady + { + get + { + return SystemDriveInfo.IsReady; + } + } + + /// + /// Возвращает имя диска + /// + [ContextProperty("Имя")] + public string Name + { + get + { + return SystemDriveInfo.Name; + } + } + + /// + /// Возвращает корневой каталог диска. + /// + [ContextProperty("КорневойКаталог")] + public IValue RootDirectory + { + get + { + return new FileContext(SystemDriveInfo.RootDirectory.FullName); + } + } + + /// + /// Возвращает общий объем свободного места, доступного на диске, в байтах + /// + [ContextProperty("ОбщийОбъемСвободногоМеста")] + public Int64 TotalFreeSpace + { + get { return SystemDriveInfo.TotalFreeSpace; } + } + + /// + /// Возвращает общий размер места для хранения на диске в байтах. + /// + [ContextProperty("РазмерДиска")] + public Int64 TotalSize + { + get { return SystemDriveInfo.TotalSize; } + } + + /// + /// Возвращает или задает метку тома диска. + /// + [ContextProperty("МеткаТома")] + public string VolumeLabel + { + get + { + return SystemDriveInfo.VolumeLabel; + } + set + { + SystemDriveInfo.VolumeLabel = value; + } + } + + public System.IO.DriveInfo SystemDriveInfo + { + get + { + return _driveInfo; + } + + set + { + _driveInfo = value; + } + } + } +} diff --git a/src/OneScript.StandardLibrary/DriveInfo/DriveTypeEnum.cs b/src/OneScript.StandardLibrary/DriveInfo/DriveTypeEnum.cs new file mode 100644 index 000000000..f8320c1e7 --- /dev/null +++ b/src/OneScript.StandardLibrary/DriveInfo/DriveTypeEnum.cs @@ -0,0 +1,45 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.DriveInfo +{ + /// + /// Типы дисков: + /// - Диск является устройством оптических дисков, такие как компакт-ДИСК или DVD-диск. + /// - Диск является жестким диском. + /// - Диск является сетевым диском. + /// - Диск не имеет корневой каталог. + /// - Диск является диском ОЗУ. + /// - Диск является съемное запоминающее устройство, например, дисковод гибких дисков или USB-устройство флэш-памяти. + /// - Тип диска неизвестен. + /// + [SystemEnum("ТипДиска", "DriveType")] + public class DriveTypeEnum : EnumerationContext + { + private DriveTypeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + this.WrapClrValue("Неизвестный", "Unknown", System.IO.DriveType.Unknown); + this.WrapClrValue("НеИмеетКорневойКаталог", "NoRootDirectory", System.IO.DriveType.NoRootDirectory); + this.WrapClrValue("СъемноеЗапоминающееУстройство", "Removable", System.IO.DriveType.Removable); + this.WrapClrValue("ЖесткийДиск", "Fixed", System.IO.DriveType.Fixed); + this.WrapClrValue("СетевойДиск", "Network", System.IO.DriveType.Network); + this.WrapClrValue("ОптическийДиск", "CDRom", System.IO.DriveType.CDRom); + this.WrapClrValue("ДискОЗУ", "Ram", System.IO.DriveType.Ram); + } + + public static DriveTypeEnum CreateInstance(ITypeManager typeManager) + { + return EnumContextHelper.CreateClrEnumInstance( + typeManager, (t, v) => new DriveTypeEnum(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs new file mode 100644 index 000000000..a1fc47fa3 --- /dev/null +++ b/src/OneScript.StandardLibrary/DynamicLoadingFunctions.cs @@ -0,0 +1,193 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.NativeApi; +using ScriptEngine; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + /// + /// Динамическое подключение сценариев + /// + [GlobalContext(ManualRegistration = true, Category = "Динамическое подключение сценариев")] + public class DynamicLoadingFunctions : GlobalContextBase + { + private readonly ScriptingEngine _engine; + + public DynamicLoadingFunctions(ScriptingEngine engine) + { + _engine = engine; + } + + /// + /// Подключает сторонний файл сценария к текущей системе типов. + /// Подключенный сценарий выступает, как самостоятельный класс, создаваемый оператором Новый + /// + /// Путь к подключаемому сценарию + /// Имя типа, которое будет иметь новый класс. Экземпляры класса создаются оператором Новый. + /// ПодключитьСценарий("C:\file.os", "МойОбъект"); + /// А = Новый МойОбъект(); + [ContextMethod("ПодключитьСценарий", "AttachScript")] + public void AttachScript(IBslProcess process, string path, string typeName) + { + var compiler = _engine.GetCompilerService(); + try + { + _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); + } + } + + /// + /// Создает экземпляр объекта на основании стороннего файла сценария. + /// Загруженный сценарий возвращается, как самостоятельный объект. + /// Экспортные свойства и методы скрипта доступны для вызова. + /// + /// + /// Текст сценария + /// Структура. Глобальные свойства, которые будут инжектированы в область видимости загружаемого скрипта. (Необязательный) + /// + /// Контекст = Новый Структура("ЧислоПи", 3.1415); // 4 знака хватит всем + /// ЗагрузитьСценарийИзСтроки("Сообщить(ЧислоПи);", Контекст); + [ContextMethod("ЗагрузитьСценарийИзСтроки", "LoadScriptFromString")] + public UserScriptContextInstance LoadScriptFromString(IBslProcess process, + string code, + StructureImpl externalContext = null) + { + var compiler = _engine.GetCompilerService(); + if (externalContext == null) + { + return _engine.AttachedScriptsFactory.LoadFromString(compiler, code, process); + } + else + { + var extData = new ExternalContextData(); + + foreach (var item in externalContext) + { + extData.Add(item.Key.ToString()!, item.Value); + } + + 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); + } + } + } + + /// + /// Создает экземпляр объекта на основании стороннего файла сценария. + /// Загруженный сценарий возвращается, как самостоятельный объект. + /// Экспортные свойства и методы скрипта доступны для вызова. + /// + /// Путь к подключаемому сценарию + /// + /// Структура. Глобальные свойства, которые будут инжектированы в область видимости загружаемого скрипта. (Необязательный) + /// + /// Контекст = Новый Структура("ЧислоПи", 3.1415); // 4 знака хватит + /// // В коде скрипта somescript.os будет доступна глобальная переменная "ЧислоПи" + /// Объект = ЗагрузитьСценарий("somescript.os", Контекст); + [ContextMethod("ЗагрузитьСценарий", "LoadScript")] + public IRuntimeContextInstance 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); + } + + return _engine.AttachedScriptsFactory.LoadFromPath(compiler, path, extData, process); + + } + } + + /// + /// Подключает внешнюю сборку среды .NET (*.dll) и регистрирует классы 1Script, объявленные в этой сборке. + /// Публичные классы, отмеченные в dll атрибутом ContextClass, будут импортированы аналогично встроенным классам 1Script. + /// Загружаемая сборка должна ссылаться на сборку ScriptEngine.dll + /// + /// Также подключает вншение компонеты, разработанные по технологии Native API, + /// поставляемые в виде отдельных DLL или упакованные в ZIP-архив. + /// + /// + /// Путь к внешней компоненте + /// Символическое имя подключаемой внешней компоненты (только для Native API) + /// Тип подключаемой внешней компоненты (для совместимости, необязательно) + /// + /// //Подключает внешнюю сборку среды .NET (*.dll) + /// ПодключитьВнешнююКомпоненту("C:\MyAssembly.dll"); + /// КлассИзКомпоненты = Новый КлассИзКомпоненты(); // тип объявлен внутри компоненты + /// + /// //Подключает вншение компонеты Native API, упакованные в ZIP-архив + /// ПодключитьВнешнююКомпоненту("C:\AddInNative.zip", "AddInNative"); + /// ЭкземплярВнешнейКомпоненты = Новый ("AddIn.AddInNative.NativeComponent", ТипВнешнейКомпоненты.Native); + /// + /// //Подключает вншение компонеты Native API в виде отдельных DLL-файлов + /// ПодключитьВнешнююКомпоненту("C:\AddInNative.dll", "SimpleAddIn", ТипВнешнейКомпоненты.Native); + /// ЭкземплярВнешнейКомпоненты = Новый ("AddIn.SimpleAddIn.SimpleComponent"); + /// + [ContextMethod("ПодключитьВнешнююКомпоненту", "AttachAddIn")] + public bool AttachAddIn(string dllPath, string name = "", NativeApiEnums type = NativeApiEnums.OneScript) + { + if (type == NativeApiEnums.OneScript) + { + var assembly = System.Reflection.Assembly.LoadFrom(dllPath); + _engine.AttachExternalAssembly(assembly); + return true; + } + else { + if (!Utils.IsValidIdentifier(name)) + { + throw RuntimeException.InvalidArgumentValue(name); + } + return NativeApiFactory.Register(dllPath, name, _engine.TypeManager); + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs new file mode 100644 index 000000000..d38e2fbb6 --- /dev/null +++ b/src/OneScript.StandardLibrary/EngineBuilderExtensions.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; + +namespace OneScript.StandardLibrary +{ + public static class EngineBuilderExtensions + { + 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/EnvironmentVariableTargetEnum.cs b/src/OneScript.StandardLibrary/EnvironmentVariableTargetEnum.cs new file mode 100644 index 000000000..1372fcaf5 --- /dev/null +++ b/src/OneScript.StandardLibrary/EnvironmentVariableTargetEnum.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary +{ + [EnumerationType("РасположениеПеременнойСреды", "EnvironmentVariableTarget")] + public enum EnvironmentVariableTargetEnum + { + [EnumValue("Процесс", "Process")] + Process, + + [EnumValue("Пользователь", "User")] + User, + + [EnumValue("Машина", "Machine")] + Machine + } +} diff --git a/src/ScriptEngine.HostedScript/Library/EnvironmentVariablesImpl.cs b/src/OneScript.StandardLibrary/EnvironmentVariablesImpl.cs similarity index 97% rename from src/ScriptEngine.HostedScript/Library/EnvironmentVariablesImpl.cs rename to src/OneScript.StandardLibrary/EnvironmentVariablesImpl.cs index ea2fdc76b..0d5be795a 100644 --- a/src/ScriptEngine.HostedScript/Library/EnvironmentVariablesImpl.cs +++ b/src/OneScript.StandardLibrary/EnvironmentVariablesImpl.cs @@ -1,100 +1,102 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -using System; -using System.Collections; - -namespace ScriptEngine.HostedScript.Library -{ - [GlobalContext(Category = "Работа с переменными окружения")] - public class EnvironmentVariablesImpl : GlobalContextBase - { - - /// - /// Возвращает соответствие переменных среды. Ключом является имя переменной, а значением - значение переменной - /// - /// Расположение переменной среды - /// - /// Для Каждого Переменная Из ПеременныеСреды() Цикл - /// Сообщить(Переменная.Ключ + " = " + Переменная.Значение); - /// КонецЦикла; - /// - /// Соответствие - [ContextMethod("ПеременныеСреды", "EnvironmentVariables")] - public MapImpl EnvironmentVariables(EnvironmentVariableTargetEnum target = EnvironmentVariableTargetEnum.Process) - { - EnvironmentVariableTarget targetParam = GetSystemEnvVariableTarget(target); - var varsMap = new MapImpl(); - var allVars = System.Environment.GetEnvironmentVariables(targetParam); - foreach (DictionaryEntry item in allVars) - { - varsMap.Insert( - ValueFactory.Create((string)item.Key), - ValueFactory.Create((string)item.Value)); - } - - return varsMap; - } - - /// - /// Позволяет установить переменную среды. - /// По умолчанию переменная устанавливается в области видимости процесса и очищается после его завершения. - /// - /// Имя переменной - /// Значение переменной - /// Расположение переменной среды - [ContextMethod("УстановитьПеременнуюСреды", "SetEnvironmentVariable")] - public void SetEnvironmentVariable(string varName, string value, EnvironmentVariableTargetEnum target = EnvironmentVariableTargetEnum.Process) - { - EnvironmentVariableTarget targetParam = GetSystemEnvVariableTarget(target); - System.Environment.SetEnvironmentVariable(varName, value, targetParam); - } - - /// - /// Получить значение переменной среды. - /// - /// Имя переменной - /// Расположение переменной среды - /// Строка. Значение переменной - [ContextMethod("ПолучитьПеременнуюСреды", "GetEnvironmentVariable")] - public IValue GetEnvironmentVariable(string varName, EnvironmentVariableTargetEnum target = EnvironmentVariableTargetEnum.Process) - { - EnvironmentVariableTarget targetParam = GetSystemEnvVariableTarget(target); - string value = System.Environment.GetEnvironmentVariable(varName, targetParam); - if (value == null) - return ValueFactory.Create(); - else - return ValueFactory.Create(value); - - } - - public static IAttachableContext CreateInstance() - { - return new EnvironmentVariablesImpl(); - } - - private static EnvironmentVariableTarget GetSystemEnvVariableTarget(EnvironmentVariableTargetEnum target) - { - EnvironmentVariableTarget targetParam = EnvironmentVariableTarget.Process; - switch (target) - { - case EnvironmentVariableTargetEnum.Process: - targetParam = EnvironmentVariableTarget.Process; - break; - case EnvironmentVariableTargetEnum.User: - targetParam = EnvironmentVariableTarget.User; - break; - case EnvironmentVariableTargetEnum.Machine: - targetParam = EnvironmentVariableTarget.Machine; - break; - } - return targetParam; - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + [GlobalContext(Category = "Работа с переменными окружения")] + public class EnvironmentVariablesImpl : GlobalContextBase + { + + /// + /// Возвращает соответствие переменных среды. Ключом является имя переменной, а значением - значение переменной + /// + /// Расположение переменной среды + /// + /// Для Каждого Переменная Из ПеременныеСреды() Цикл + /// Сообщить(Переменная.Ключ + " = " + Переменная.Значение); + /// КонецЦикла; + /// + /// Соответствие + [ContextMethod("ПеременныеСреды", "EnvironmentVariables")] + public MapImpl EnvironmentVariables(EnvironmentVariableTargetEnum target = EnvironmentVariableTargetEnum.Process) + { + EnvironmentVariableTarget targetParam = GetSystemEnvVariableTarget(target); + var varsMap = new MapImpl(); + var allVars = System.Environment.GetEnvironmentVariables(targetParam); + foreach (DictionaryEntry item in allVars) + { + varsMap.Insert( + ValueFactory.Create((string)item.Key), + ValueFactory.Create((string)item.Value)); + } + + return varsMap; + } + + /// + /// Позволяет установить переменную среды. + /// По умолчанию переменная устанавливается в области видимости процесса и очищается после его завершения. + /// + /// Имя переменной + /// Значение переменной + /// Расположение переменной среды + [ContextMethod("УстановитьПеременнуюСреды", "SetEnvironmentVariable")] + public void SetEnvironmentVariable(string varName, string value, EnvironmentVariableTargetEnum target = EnvironmentVariableTargetEnum.Process) + { + EnvironmentVariableTarget targetParam = GetSystemEnvVariableTarget(target); + System.Environment.SetEnvironmentVariable(varName, value, targetParam); + } + + /// + /// Получить значение переменной среды. + /// + /// Имя переменной + /// Расположение переменной среды + /// Строка. Значение переменной + [ContextMethod("ПолучитьПеременнуюСреды", "GetEnvironmentVariable")] + public IValue GetEnvironmentVariable(string varName, EnvironmentVariableTargetEnum target = EnvironmentVariableTargetEnum.Process) + { + EnvironmentVariableTarget targetParam = GetSystemEnvVariableTarget(target); + string value = System.Environment.GetEnvironmentVariable(varName, targetParam); + if (value == null) + return ValueFactory.Create(); + else + return ValueFactory.Create(value); + + } + + public static IAttachableContext CreateInstance() + { + return new EnvironmentVariablesImpl(); + } + + private static EnvironmentVariableTarget GetSystemEnvVariableTarget(EnvironmentVariableTargetEnum target) + { + EnvironmentVariableTarget targetParam = EnvironmentVariableTarget.Process; + switch (target) + { + case EnvironmentVariableTargetEnum.Process: + targetParam = EnvironmentVariableTarget.Process; + break; + case EnvironmentVariableTargetEnum.User: + targetParam = EnvironmentVariableTarget.User; + break; + case EnvironmentVariableTargetEnum.Machine: + targetParam = EnvironmentVariableTarget.Machine; + break; + } + return targetParam; + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/FileContext.cs b/src/OneScript.StandardLibrary/FileContext.cs similarity index 93% rename from src/ScriptEngine.HostedScript/Library/FileContext.cs rename to src/OneScript.StandardLibrary/FileContext.cs index 6b62b298a..9ba9f0abd 100644 --- a/src/ScriptEngine.HostedScript/Library/FileContext.cs +++ b/src/OneScript.StandardLibrary/FileContext.cs @@ -1,219 +1,225 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.IO; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("Файл","File")] - public class FileContext : AutoContext - { - private readonly string _givenName; - private string _name; - private string _baseName; - private string _fullName; - private string _path; - private string _extension; - - public FileContext(string name) - { - if (String.IsNullOrWhiteSpace(name)) - { - _name = ""; - _baseName = ""; - _fullName = ""; - _path = ""; - _extension = ""; - } - - _givenName = name; - } - - private string LazyField(ref string value, Func algo) - { - if (value == null) - value = algo(_givenName); - - return value; - } - - [ContextProperty("Имя","Name")] - public string Name - { - get - { - return LazyField(ref _name, GetFileNameV8Compatible); - } - } - - private string GetFileNameV8Compatible(string arg) - { - return System.IO.Path.GetFileName(arg.TrimEnd(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar)); - } - - [ContextProperty("ИмяБезРасширения", "BaseName")] - public string BaseName - { - get - { - return LazyField(ref _baseName, System.IO.Path.GetFileNameWithoutExtension); - } - } - - [ContextProperty("ПолноеИмя", "FullName")] - public string FullName - { - get - { - return LazyField(ref _fullName, System.IO.Path.GetFullPath); - } - } - - [ContextProperty("Путь", "Path")] - public string Path - { - get - { - return LazyField(ref _path, GetPathWithEndingDelimiter); - } - } - - private string GetPathWithEndingDelimiter(string src) - { - src = src.Trim (); - if (src.Length == 1 && src[0] == System.IO.Path.DirectorySeparatorChar) - return src; // корневой каталог - - var path = System.IO.Path.GetDirectoryName(src.TrimEnd(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar)); - if (path == null) - return src; // корневой каталог - - if (path.Length > 0 && path[path.Length - 1] != System.IO.Path.DirectorySeparatorChar) - path += System.IO.Path.DirectorySeparatorChar; - - return path; - } - - [ContextProperty("Расширение", "Extension")] - public string Extension - { - get - { - return LazyField(ref _extension, System.IO.Path.GetExtension); - } - } - - [ContextMethod("Существует","Exist")] - public bool Exist() - { - if (_givenName == String.Empty) - return false; - - try - { - File.GetAttributes(FullName); - return true; - } - catch (FileNotFoundException) { } - catch (DirectoryNotFoundException) { } - catch (ArgumentException) { } - catch (NotSupportedException) { } - catch (PathTooLongException) { } - catch (UnauthorizedAccessException) { } - catch (IOException) { } - - return false; - } - - [ContextMethod("Размер", "Size")] - public long Size() - { - return new FileInfo(FullName).Length; - } - - [ContextMethod("ПолучитьНевидимость", "GetHidden")] - public bool GetHidden() - { - var attr = File.GetAttributes(FullName); - return attr.HasFlag(System.IO.FileAttributes.Hidden); - } - - [ContextMethod("ПолучитьТолькоЧтение", "GetReadOnly")] - public bool GetReadOnly() - { - var attr = File.GetAttributes(FullName); - return attr.HasFlag(System.IO.FileAttributes.ReadOnly); - } - - [ContextMethod("ПолучитьВремяИзменения", "GetModificationTime")] - public DateTime GetModificationTime() - { - return File.GetLastWriteTime(FullName); - } - - [ContextMethod("ПолучитьВремяСоздания", "GetCreationTime")] - public DateTime GetCreationTime() - { - return File.GetCreationTime(FullName); - } - - [ContextMethod("УстановитьНевидимость", "SetHidden")] - public void SetHidden(bool value) - { - FileSystemInfo entry = new FileInfo(FullName); - - if(value) - entry.Attributes |= System.IO.FileAttributes.Hidden; - else - entry.Attributes &= ~System.IO.FileAttributes.Hidden; - } - - [ContextMethod("УстановитьТолькоЧтение", "SetReadOnly")] - public void SetReadOnly(bool value) - { - FileSystemInfo entry = new FileInfo(FullName); - if (value) - entry.Attributes |= System.IO.FileAttributes.ReadOnly; - else - entry.Attributes &= ~System.IO.FileAttributes.ReadOnly; - } - - [ContextMethod("УстановитьВремяИзменения", "SetModificationTime")] - public void SetModificationTime(DateTime dateTime) - { - FileSystemInfo entry = new FileInfo(FullName); - entry.LastWriteTime = dateTime; - } - - [ContextMethod("ЭтоКаталог", "IsDirectory")] - public bool IsDirectory() - { - var attr = File.GetAttributes(FullName); - return attr.HasFlag(FileAttributes.Directory); - } - - [ContextMethod("ЭтоФайл", "IsFile")] - public bool IsFile() - { - var attr = File.GetAttributes(FullName); - return !attr.HasFlag(FileAttributes.Directory); - } - - public FileAttributes GetAttributes() - { - return File.GetAttributes(FullName); - } - - [ScriptConstructor(Name = "По имени файла")] - public static FileContext Constructor(IValue name) - { - return new FileContext(name.AsString()); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + [ContextClass("Файл","File")] + public class FileContext : AutoContext + { + private readonly string _givenName; + private string _name; + private string _baseName; + private string _fullName; + private string _path; + private string _extension; + + public FileContext(string name) + { + // Strip null characters that can be added by Windows WebDAV client + // to maintain compatibility with 1.x behavior + name = PathHelper.StripNullCharacters(name); + + if (String.IsNullOrWhiteSpace(name)) + { + _name = ""; + _baseName = ""; + _fullName = ""; + _path = ""; + _extension = ""; + } + + _givenName = name; + } + + private string LazyField(ref string value, Func algo) + { + if (value == null) + value = algo(_givenName); + + return value; + } + + [ContextProperty("Имя","Name")] + public string Name + { + get + { + return LazyField(ref _name, GetFileNameV8Compatible); + } + } + + private string GetFileNameV8Compatible(string arg) + { + return System.IO.Path.GetFileName(arg.TrimEnd(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar)); + } + + [ContextProperty("ИмяБезРасширения", "BaseName")] + public string BaseName + { + get + { + return LazyField(ref _baseName, System.IO.Path.GetFileNameWithoutExtension); + } + } + + [ContextProperty("ПолноеИмя", "FullName")] + public string FullName + { + get + { + return LazyField(ref _fullName, System.IO.Path.GetFullPath); + } + } + + [ContextProperty("Путь", "Path")] + public string Path + { + get + { + return LazyField(ref _path, GetPathWithEndingDelimiter); + } + } + + private string GetPathWithEndingDelimiter(string src) + { + src = src.Trim (); + if (src.Length == 1 && src[0] == System.IO.Path.DirectorySeparatorChar) + return src; // корневой каталог + + var path = System.IO.Path.GetDirectoryName(src.TrimEnd(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar)); + if (path == null) + return src; // корневой каталог + + if (path.Length > 0 && path[path.Length - 1] != System.IO.Path.DirectorySeparatorChar) + path += System.IO.Path.DirectorySeparatorChar; + + return path; + } + + [ContextProperty("Расширение", "Extension")] + public string Extension + { + get + { + return LazyField(ref _extension, System.IO.Path.GetExtension); + } + } + + [DeprecatedName("Exist")] + [ContextMethod("Существует","Exists")] + public bool Exists() + { + if (_givenName == String.Empty) + return false; + + try + { + File.GetAttributes(FullName); + return true; + } + catch (FileNotFoundException) { } + catch (DirectoryNotFoundException) { } + catch (ArgumentException) { } + catch (NotSupportedException) { } + catch (PathTooLongException) { } + catch (UnauthorizedAccessException) { } + catch (IOException) { } + + return false; + } + + [ContextMethod("Размер", "Size")] + public long Size() + { + return new FileInfo(FullName).Length; + } + + [ContextMethod("ПолучитьНевидимость", "GetHidden")] + public bool GetHidden() + { + var attr = File.GetAttributes(FullName); + return attr.HasFlag(System.IO.FileAttributes.Hidden); + } + + [ContextMethod("ПолучитьТолькоЧтение", "GetReadOnly")] + public bool GetReadOnly() + { + var attr = File.GetAttributes(FullName); + return attr.HasFlag(System.IO.FileAttributes.ReadOnly); + } + + [ContextMethod("ПолучитьВремяИзменения", "GetModificationTime")] + public DateTime GetModificationTime() + { + return File.GetLastWriteTime(FullName); + } + + [ContextMethod("ПолучитьВремяСоздания", "GetCreationTime")] + public DateTime GetCreationTime() + { + return File.GetCreationTime(FullName); + } + + [ContextMethod("УстановитьНевидимость", "SetHidden")] + public void SetHidden(bool value) + { + FileSystemInfo entry = new FileInfo(FullName); + + if(value) + entry.Attributes |= System.IO.FileAttributes.Hidden; + else + entry.Attributes &= ~System.IO.FileAttributes.Hidden; + } + + [ContextMethod("УстановитьТолькоЧтение", "SetReadOnly")] + public void SetReadOnly(bool value) + { + FileSystemInfo entry = new FileInfo(FullName); + if (value) + entry.Attributes |= System.IO.FileAttributes.ReadOnly; + else + entry.Attributes &= ~System.IO.FileAttributes.ReadOnly; + } + + [ContextMethod("УстановитьВремяИзменения", "SetModificationTime")] + public void SetModificationTime(DateTime dateTime) + { + FileSystemInfo entry = new FileInfo(FullName); + entry.LastWriteTime = dateTime; + } + + [ContextMethod("ЭтоКаталог", "IsDirectory")] + public bool IsDirectory() + { + var attr = File.GetAttributes(FullName); + return attr.HasFlag(FileAttributes.Directory); + } + + [ContextMethod("ЭтоФайл", "IsFile")] + public bool IsFile() + { + var attr = File.GetAttributes(FullName); + return !attr.HasFlag(FileAttributes.Directory); + } + + public FileAttributes GetAttributes() + { + return File.GetAttributes(FullName); + } + + [ScriptConstructor(Name = "По имени файла")] + public static FileContext Constructor(string name) + { + return new FileContext(name); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/FileOperations.cs b/src/OneScript.StandardLibrary/FileOperations.cs similarity index 96% rename from src/ScriptEngine.HostedScript/Library/FileOperations.cs rename to src/OneScript.StandardLibrary/FileOperations.cs index af6b093a9..7d6188851 100644 --- a/src/ScriptEngine.HostedScript/Library/FileOperations.cs +++ b/src/OneScript.StandardLibrary/FileOperations.cs @@ -1,381 +1,388 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Security; - -namespace ScriptEngine.HostedScript.Library -{ - [GlobalContext(Category="Файловые операции")] - public class FileOperations : GlobalContextBase - { - - /// - /// Копирует файл из одного расположения в другое. Перезаписывает приемник, если он существует. - /// - /// Имя файла-источника - /// Имя файла приемника - [ContextMethod("КопироватьФайл", "CopyFile")] - public void CopyFile(string source, string destination) - { - var scheme = PathScheme(source); - - if(scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps) - DownloadFromRemote(source, - destination, WebRequestMethods.Http.Get); - else if(scheme == Uri.UriSchemeFtp) - DownloadFromRemote(source, - destination, WebRequestMethods.Ftp.DownloadFile); - else - File.Copy(source, destination, true); - - } - - /// - /// Перемещает файл из одного расположения в другое. - /// - /// Имя файла-источника - /// Имя файла приемника - [ContextMethod("ПереместитьФайл", "MoveFile")] - public void MoveFile(string source, string destination) - { - var scheme = PathScheme(source); - - if (scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps) - { - DownloadFromRemote(source, - destination, WebRequestMethods.Http.Get); - DeleteFromRemote(source, "DELETE"); - } - else if (scheme == Uri.UriSchemeFtp) - { - DownloadFromRemote(source, - destination, WebRequestMethods.Ftp.DownloadFile); - DeleteFromRemote(source, WebRequestMethods.Ftp.DeleteFile); - } - else - File.Move(source, destination); - } - - public string PathScheme(string path) - { - if(Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out Uri uri) && uri.IsAbsoluteUri) - { - return uri.Scheme; - } - return Uri.UriSchemeFile; - } - - private void DownloadFromRemote(string source, - string destination, string method) where T: WebRequest - { - var req = (T)WebRequest.Create(source); - req.Method = method; - - using (var respStream = req.GetResponse().GetResponseStream()) - using (var fs = File.Create(destination)) - respStream.CopyTo(fs); - } - - private void DeleteFromRemote(string source, string method) where T : WebRequest - { - var req = (T)WebRequest.Create(source); - req.Method = method; - - using (var resp = req.GetResponse()) { }; - } - - /// - /// Возвращает каталог временных файлов ОС - /// - /// Строка. Путь к каталогу временных файлов - [ContextMethod("КаталогВременныхФайлов", "TempFilesDir")] - public string TempFilesDir() - { - return Path.GetTempPath(); - } - - /// - /// Получает имя файла во временом каталоге. - /// - /// Расширение будущего файла. Если не указано, то по умолчанию расширение равно ".tmp" - /// Строка. Полный путь ко временному файлу. - [ContextMethod("ПолучитьИмяВременногоФайла", "GetTempFileName")] - public string GetTempFilename(string ext = null) - { - // примитивная реализация "в лоб" - var fn = Path.GetRandomFileName(); - if (ext != null && !String.IsNullOrWhiteSpace(ext)) - { - if(ext[0] == '.') - fn += ext; - else - fn += "." + ext; - } - - return Path.Combine(TempFilesDir(), fn); - - } - - /// - /// Выполняет поиск файлов по маске - /// - /// Каталог, в котором выполняется поиск - /// Маска имени файла (включая символы * и ?) - /// Флаг рекурсивного поиска в поддиректориях - /// Массив объектов Файл, которые были найдены. - [ContextMethod("НайтиФайлы", "FindFiles")] - public ArrayImpl FindFiles(string dir, string mask = null, bool recursive = false) - { - if (mask == null) - { - // fix 225, 227, 228 - var fObj = new FileContext(dir); - if(fObj.Exist()) - { - return new ArrayImpl(new[] { fObj }); - } - else - { - return new ArrayImpl(); - } - } - else if (File.Exists(dir)) - { - return new ArrayImpl(); - } - - if(!Directory.Exists(dir)) - return new ArrayImpl(); - - var filesFound = FindFilesV8Compatible(dir, mask, recursive); - - return new ArrayImpl(filesFound); - - } - - private static IEnumerable FindFilesV8Compatible(string dir, string mask, bool recursive) - { - var collectedFiles = new List(); - IEnumerable entries; - IEnumerable folders = null; - try - { - if (recursive) - folders = Directory.GetDirectories(dir).Select(x => new FileContext(x)); - - entries = Directory.EnumerateFileSystemEntries(dir, mask) - .Select(x => new FileContext(x)); - } - catch (SecurityException) - { - return collectedFiles; - } - catch (UnauthorizedAccessException) - { - return collectedFiles; - } - - if (recursive) - { - foreach (var fileFound in entries) - { - try - { - var attrs = fileFound.GetAttributes(); - if (attrs.HasFlag(FileAttributes.ReparsePoint)) - { - collectedFiles.Add(fileFound); - continue; - } - } - catch (SecurityException) - { - continue; - } - catch (UnauthorizedAccessException) - { - continue; - } - - collectedFiles.Add(fileFound); - } - - foreach (var folder in folders) - { - try - { - var attrs = folder.GetAttributes(); - if (!attrs.HasFlag(FileAttributes.ReparsePoint)) - { - collectedFiles.AddRange(FindFilesV8Compatible(folder.FullName, mask, true)); - } - } - catch (SecurityException) - { - } - catch (UnauthorizedAccessException) - { - } - } - } - else - { - collectedFiles.AddRange(entries); - } - - return collectedFiles; - } - - /// - /// Удаление файлов - /// - /// Каталог из которого удаляются файлы, или сам файл. - /// Маска файлов. Необязательный параметр. Если указан, то первый параметр трактуется, как каталог. - [ContextMethod("УдалитьФайлы", "DeleteFiles")] - public void DeleteFiles(string path, string mask = null) - { - if (mask == null) - { - if (Directory.Exists(path)) - { - System.IO.Directory.Delete(path, true); - } - else - { - System.IO.File.Delete(path); - } - } - else - { - // bugfix #419 - if (!Directory.Exists(path)) - return; - - var entries = System.IO.Directory.EnumerateFileSystemEntries(path, mask) - .AsParallel() - .ToArray(); - foreach (var item in entries) - { - System.IO.FileInfo finfo = new System.IO.FileInfo(item); - if (finfo.Attributes.HasFlag(System.IO.FileAttributes.Directory)) - { - //recursively delete directory - DeleteDirectory(item, true); - } - else - { - System.IO.File.Delete(item); - } - } - } - } - - public static void DeleteDirectory(string path, bool recursive) - { - if (recursive) - { - var subfolders = Directory.GetDirectories(path); - foreach (var s in subfolders) - { - DeleteDirectory(s, recursive); - } - } - - var files = Directory.GetFiles(path); - foreach (var f in files) - { - File.Delete(f); - } - - Directory.Delete(path); - } - - /// - /// Создать каталог - /// - /// Имя нового каталога - [ContextMethod("СоздатьКаталог", "CreateDirectory")] - public void CreateDirectory(string path) - { - System.IO.Directory.CreateDirectory(path); - } - - /// - /// Получить текущий каталог - /// - [ContextMethod("ТекущийКаталог", "CurrentDirectory")] - public string CurrentDirectory() - { - return System.IO.Directory.GetCurrentDirectory(); - } - - /// - /// Установить каталог текущим - /// - /// Имя нового текущего каталога - [ContextMethod("УстановитьТекущийКаталог", "SetCurrentDirectory")] - public void SetCurrentDirectory(string path) - { - System.IO.Directory.SetCurrentDirectory(path); - } - - /// - /// Получает разделитель пути в соответствии с текущей операционной системой - /// - [ContextMethod("ПолучитьРазделительПути","GetPathSeparator")] - public string GetPathSeparator() - { - return new string(new char[]{Path.DirectorySeparatorChar}); - } - - /// - /// Получает маску "все файлы" для текущей операционной системы. - /// В Windows маска принимает значение "*.*", в nix - "*". - /// - [ContextMethod("ПолучитьМаскуВсеФайлы", "GetAllFilesMask")] - public string GetAllFilesMask() - { - var platform = System.Environment.OSVersion.Platform; - if (platform == PlatformID.Win32NT || platform == PlatformID.Win32Windows) - return "*.*"; - else - return "*"; - } - - /// - /// Объединяет компоненты файлового пути, с учетом разделителей, принятых в данной ОС. - /// При этом корректно, без дублирования, обрабатываются уже существующие разделители пути. - /// - /// Первая часть пути - /// Вторая часть пути - /// Третья часть пути (необязательно) - /// Четвертая часть пути (необязательно) - /// Объединенный путь. - [ContextMethod("ОбъединитьПути", "CombinePath")] - public string CombinePath(string path1, string path2, string path3 = null, string path4 = null) - { - if (path3 == null) - return Path.Combine(path1, path2); - else if (path4 == null) - return Path.Combine(path1, path2, path3); - else - return Path.Combine(path1, path2, path3, path4); - } - - public static IAttachableContext CreateInstance() - { - return new FileOperations(); - } - - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Security; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + [GlobalContext(Category="Файловые операции")] + public class FileOperations : GlobalContextBase + { + + /// + /// Копирует файл из одного расположения в другое. Перезаписывает приемник, если он существует. + /// + /// Имя файла-источника + /// Имя файла приемника + [ContextMethod("КопироватьФайл", "CopyFile")] + public void CopyFile(string source, string destination) + { + var scheme = PathScheme(source); + + if(scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps) + DownloadFromRemote(source, + destination, WebRequestMethods.Http.Get); + else if(scheme == Uri.UriSchemeFtp) + DownloadFromRemote(source, + destination, WebRequestMethods.Ftp.DownloadFile); + else + File.Copy(source, destination, true); + + } + + /// + /// Перемещает файл из одного расположения в другое. + /// + /// Имя файла-источника + /// Имя файла приемника + [ContextMethod("ПереместитьФайл", "MoveFile")] + public void MoveFile(string source, string destination) + { + var scheme = PathScheme(source); + + if (scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeHttps) + { + DownloadFromRemote(source, + destination, WebRequestMethods.Http.Get); + DeleteFromRemote(source, "DELETE"); + } + else if (scheme == Uri.UriSchemeFtp) + { + DownloadFromRemote(source, + destination, WebRequestMethods.Ftp.DownloadFile); + DeleteFromRemote(source, WebRequestMethods.Ftp.DeleteFile); + } + else + File.Move(source, destination); + } + + public string PathScheme(string path) + { + if(Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out Uri uri) && uri.IsAbsoluteUri) + { + return uri.Scheme; + } + return Uri.UriSchemeFile; + } + + private void DownloadFromRemote(string source, + string destination, string method) where T: WebRequest + { + var req = (T)WebRequest.Create(source); + req.Method = method; + + using (var respStream = req.GetResponse().GetResponseStream()) + using (var fs = File.Create(destination)) + respStream.CopyTo(fs); + } + + private void DeleteFromRemote(string source, string method) where T : WebRequest + { + var req = (T)WebRequest.Create(source); + req.Method = method; + + using (var resp = req.GetResponse()) { }; + } + + /// + /// Возвращает каталог временных файлов ОС + /// + /// Строка. Путь к каталогу временных файлов + [ContextMethod("КаталогВременныхФайлов", "TempFilesDir")] + public string TempFilesDir() + { + return Path.GetTempPath(); + } + + /// + /// Получает имя файла во временом каталоге. + /// + /// Расширение будущего файла. Если не указано, то по умолчанию расширение равно ".tmp" + /// Строка. Полный путь ко временному файлу. + [ContextMethod("ПолучитьИмяВременногоФайла", "GetTempFileName")] + public string GetTempFilename(string ext = null) + { + // примитивная реализация "в лоб" + var fn = Path.GetRandomFileName(); + if (ext != null && !String.IsNullOrWhiteSpace(ext)) + { + if(ext[0] == '.') + fn += ext; + else + fn += "." + ext; + } + + return Path.Combine(TempFilesDir(), fn); + + } + + /// + /// Выполняет поиск файлов по маске + /// + /// Каталог, в котором выполняется поиск + /// Маска имени файла (включая символы * и ?) + /// Флаг рекурсивного поиска в поддиректориях + /// Массив объектов Файл, которые были найдены. + [ContextMethod("НайтиФайлы", "FindFiles")] + public ArrayImpl FindFiles(string dir, string mask = null, bool recursive = false) + { + // Strip null characters that can be added by Windows WebDAV client + // to maintain compatibility with 1.x behavior + dir = PathHelper.StripNullCharacters(dir); + mask = PathHelper.StripNullCharacters(mask); + + if (mask == null) + { + // fix 225, 227, 228 + var fObj = new FileContext(dir); + if(fObj.Exists()) + { + return new ArrayImpl(new[] { fObj }); + } + else + { + return new ArrayImpl(); + } + } + else if (File.Exists(dir)) + { + return new ArrayImpl(); + } + + if(!Directory.Exists(dir)) + return new ArrayImpl(); + + var filesFound = FindFilesV8Compatible(dir, mask, recursive); + + return new ArrayImpl(filesFound); + + } + + private static IEnumerable FindFilesV8Compatible(string dir, string mask, bool recursive) + { + var collectedFiles = new List(); + IEnumerable entries; + IEnumerable folders = null; + try + { + if (recursive) + folders = Directory.GetDirectories(dir).Select(x => new FileContext(x)); + + entries = Directory.EnumerateFileSystemEntries(dir, mask) + .Select(x => new FileContext(x)); + } + catch (SecurityException) + { + return collectedFiles; + } + catch (UnauthorizedAccessException) + { + return collectedFiles; + } + + if (recursive) + { + foreach (var fileFound in entries) + { + try + { + var attrs = fileFound.GetAttributes(); + if (attrs.HasFlag(FileAttributes.ReparsePoint)) + { + collectedFiles.Add(fileFound); + continue; + } + } + catch (SecurityException) + { + continue; + } + catch (UnauthorizedAccessException) + { + continue; + } + + collectedFiles.Add(fileFound); + } + + foreach (var folder in folders) + { + try + { + var attrs = folder.GetAttributes(); + if (!attrs.HasFlag(FileAttributes.ReparsePoint)) + { + collectedFiles.AddRange(FindFilesV8Compatible(folder.FullName, mask, true)); + } + } + catch (SecurityException) + { + } + catch (UnauthorizedAccessException) + { + } + } + } + else + { + collectedFiles.AddRange(entries); + } + + return collectedFiles; + } + + /// + /// Удаление файлов + /// + /// Каталог из которого удаляются файлы, или сам файл. + /// Маска файлов. Необязательный параметр. Если указан, то первый параметр трактуется, как каталог. + [ContextMethod("УдалитьФайлы", "DeleteFiles")] + public void DeleteFiles(string path, string mask = null) + { + if (mask == null) + { + if (Directory.Exists(path)) + { + System.IO.Directory.Delete(path, true); + } + else + { + System.IO.File.Delete(path); + } + } + else + { + // bugfix #419 + if (!Directory.Exists(path)) + return; + + var entries = System.IO.Directory.EnumerateFileSystemEntries(path, mask) + .AsParallel() + .ToArray(); + foreach (var item in entries) + { + System.IO.FileInfo finfo = new System.IO.FileInfo(item); + if (finfo.Attributes.HasFlag(System.IO.FileAttributes.Directory)) + { + //recursively delete directory + DeleteDirectory(item, true); + } + else + { + System.IO.File.Delete(item); + } + } + } + } + + public static void DeleteDirectory(string path, bool recursive) + { + if (recursive) + { + var subfolders = Directory.GetDirectories(path); + foreach (var s in subfolders) + { + DeleteDirectory(s, true); + } + } + + var files = Directory.GetFiles(path); + foreach (var f in files) + { + File.Delete(f); + } + + Directory.Delete(path); + } + + /// + /// Создать каталог + /// + /// Имя нового каталога + [ContextMethod("СоздатьКаталог", "CreateDirectory")] + public void CreateDirectory(string path) + { + System.IO.Directory.CreateDirectory(path); + } + + /// + /// Получить текущий каталог + /// + [ContextMethod("ТекущийКаталог", "CurrentDirectory")] + public string CurrentDirectory() + { + return System.IO.Directory.GetCurrentDirectory(); + } + + /// + /// Установить каталог текущим + /// + /// Имя нового текущего каталога + [ContextMethod("УстановитьТекущийКаталог", "SetCurrentDirectory")] + public void SetCurrentDirectory(string path) + { + System.IO.Directory.SetCurrentDirectory(path); + } + + /// + /// Получает разделитель пути в соответствии с текущей операционной системой + /// + [ContextMethod("ПолучитьРазделительПути","GetPathSeparator")] + public string GetPathSeparator() + { + return new string(new char[]{Path.DirectorySeparatorChar}); + } + + /// + /// Получает маску "все файлы" для текущей операционной системы. + /// В Windows маска принимает значение "*.*", в nix - "*". + /// + [ContextMethod("ПолучитьМаскуВсеФайлы", "GetAllFilesMask")] + public string GetAllFilesMask() + { + var platform = System.Environment.OSVersion.Platform; + if (platform == PlatformID.Win32NT || platform == PlatformID.Win32Windows) + return "*.*"; + else + return "*"; + } + + /// + /// Объединяет компоненты файлового пути, с учетом разделителей, принятых в данной ОС. + /// При этом корректно, без дублирования, обрабатываются уже существующие разделители пути. + /// + /// Первая часть пути + /// Вторая часть пути + /// Третья часть пути (необязательно) + /// Четвертая часть пути (необязательно) + /// Объединенный путь. + [ContextMethod("ОбъединитьПути", "CombinePath")] + public string CombinePath(string path1, string path2, string path3 = null, string path4 = null) + { + if (path3 == null) + return Path.Combine(path1, path2); + else if (path4 == null) + return Path.Combine(path1, path2, path3); + else + return Path.Combine(path1, path2, path3, path4); + } + + public static IAttachableContext CreateInstance() + { + return new FileOperations(); + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/GlobalBitFunctions.cs b/src/OneScript.StandardLibrary/GlobalBitFunctions.cs similarity index 98% rename from src/ScriptEngine.HostedScript/Library/GlobalBitFunctions.cs rename to src/OneScript.StandardLibrary/GlobalBitFunctions.cs index 1ea4bb904..302950b5a 100644 --- a/src/ScriptEngine.HostedScript/Library/GlobalBitFunctions.cs +++ b/src/OneScript.StandardLibrary/GlobalBitFunctions.cs @@ -5,10 +5,11 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.Exceptions; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary { /// /// Глобальный контекст. Побитовые операции с целыми числами. diff --git a/src/OneScript.StandardLibrary/GuidWrapper.cs b/src/OneScript.StandardLibrary/GuidWrapper.cs new file mode 100644 index 000000000..346e53c0d --- /dev/null +++ b/src/OneScript.StandardLibrary/GuidWrapper.cs @@ -0,0 +1,82 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Types; + +namespace OneScript.StandardLibrary +{ + [ContextClass("УникальныйИдентификатор","UUID")] + public class GuidWrapper : BslPrimitiveValue, IObjectWrapper, IEmptyValueCheck + { + Guid _value; + private static readonly TypeDescriptor InstanceType; + + static GuidWrapper() + { + var attr = typeof(GuidWrapper).GetCustomAttribute(); + InstanceType = typeof(GuidWrapper).GetTypeFromClassMarkup(); + } + + public GuidWrapper() + { + _value = Guid.NewGuid(); + } + + public GuidWrapper(string uuidString) + { + _value = Guid.Parse(uuidString); + } + + [ScriptConstructor] + public static GuidWrapper Create() + { + return new GuidWrapper(); + } + + [ScriptConstructor(Name = "Из строки")] + public static GuidWrapper Create(string uuidString) + { + return new GuidWrapper(uuidString); + } + + public override TypeDescriptor SystemType => InstanceType; + + public override string ToString() + { + return _value.ToString(); + } + + public override int CompareTo(BslValue other) + { + GuidWrapper otherUuid = other.GetRawValue() as GuidWrapper; + if (otherUuid == null) + throw ComparisonException.NotSupported(); + + return _value.CompareTo(otherUuid._value); + } + + public override bool Equals(BslValue other) + { + GuidWrapper otherUuid = other?.GetRawValue() as GuidWrapper; + if (otherUuid == null) + return false; + else + return _value.Equals(otherUuid._value); + } + + public bool IsEmpty => _value.Equals(Guid.Empty); + + object IObjectWrapper.UnderlyingObject => _value; + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Hash/CombinedStream.cs b/src/OneScript.StandardLibrary/Hash/CombinedStream.cs similarity index 98% rename from src/ScriptEngine.HostedScript/Library/Hash/CombinedStream.cs rename to src/OneScript.StandardLibrary/Hash/CombinedStream.cs index 8d8a6edb9..c8969f999 100644 --- a/src/ScriptEngine.HostedScript/Library/Hash/CombinedStream.cs +++ b/src/OneScript.StandardLibrary/Hash/CombinedStream.cs @@ -7,10 +7,10 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; -using System.Linq; using System.IO; +using System.Linq; -namespace ScriptEngine.HostedScript.Library.Hash +namespace OneScript.StandardLibrary.Hash { public class CombinedStream : Stream { diff --git a/src/ScriptEngine.HostedScript/Library/Hash/Crc32.cs b/src/OneScript.StandardLibrary/Hash/Crc32.cs similarity index 98% rename from src/ScriptEngine.HostedScript/Library/Hash/Crc32.cs rename to src/OneScript.StandardLibrary/Hash/Crc32.cs index e433f6d43..3b1f812c8 100644 --- a/src/ScriptEngine.HostedScript/Library/Hash/Crc32.cs +++ b/src/OneScript.StandardLibrary/Hash/Crc32.cs @@ -4,10 +4,11 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Security.Cryptography; -namespace ScriptEngine.HostedScript.Library.Hash +namespace OneScript.StandardLibrary.Hash { class Crc32:HashAlgorithm { diff --git a/src/OneScript.StandardLibrary/Hash/HashFunctionEnum.cs b/src/OneScript.StandardLibrary/Hash/HashFunctionEnum.cs new file mode 100644 index 000000000..78b11fc98 --- /dev/null +++ b/src/OneScript.StandardLibrary/Hash/HashFunctionEnum.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Hash +{ + [EnumerationType("ХешФункция", "HashFunction", + TypeUUID = "FCD487D9-9C9A-423D-B4E8-90FB3248AACF", + ValueTypeUUID = "39B708B2-98ED-4B14-B112-60985A547526")] + public enum HashFunctionEnum + { + [EnumValue("MD5")] + MD5, + + [EnumValue("SHA1")] + SHA1, + + [EnumValue("SHA256")] + SHA256, + + [EnumValue("SHA384")] + SHA384, + + [EnumValue("SHA512")] + SHA512, + + [EnumValue("CRC32")] + CRC32 + + } +} diff --git a/src/OneScript.StandardLibrary/Hash/HashImpl.cs b/src/OneScript.StandardLibrary/Hash/HashImpl.cs new file mode 100644 index 000000000..78889b659 --- /dev/null +++ b/src/OneScript.StandardLibrary/Hash/HashImpl.cs @@ -0,0 +1,160 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.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; + +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 byte[] _hash; + + public HashImpl(HashAlgorithm provider, HashFunctionEnum enumValue) + { + _provider = provider; + _enumValue = enumValue; + _calculated = false; + } + + public byte[] InternalHash + { + get + { + if (!_calculated) + { + _hash = _provider.ComputeHash(_toCalculate); + _calculated = true; + } + return _hash; + } + } + + [ContextProperty("ХешФункция", "HashFunction")] + public HashFunctionEnum Extension => _enumValue; + + [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); + } + } + + [ContextProperty("ХешСуммаСтрокой", "HashSumOfString")] + public string HashString + { + get + { + var sb = new StringBuilder(); + for (int i = 0; i < InternalHash.Length; i++) + sb.Append(InternalHash[i].ToString("X2")); + return sb.ToString(); + } + } + + + [ContextMethod("Добавить", "Append")] + public void Append(BslValue toAdd, uint count = 0) + { + switch (toAdd) + { + case BslStringValue s: + AddStream(new MemoryStream(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)); + break; + case BslObjectValue obj when obj is BinaryDataContext binaryData: + AddStream(binaryData.GetStream()); + break; + default: + throw RuntimeException.InvalidArgumentType(nameof(toAdd)); + } + } + + [ContextMethod("ДобавитьФайл", "AppendFile")] + public void AppendFile(string path) + { + if (!File.Exists(path)) + throw RuntimeException.InvalidArgumentType(); + AddStream(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + } + + [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); + return new HashImpl(objectProvider, providerEnum); + } + + private static HashAlgorithm GetProvider(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; + } + } + + public void Dispose() + { + _toCalculate.Close(); + _toCalculate.Dispose(); + } + + private void AddStream(Stream stream) + { + _toCalculate.AddStream(stream); + _toCalculate.Seek(0, SeekOrigin.Begin); + _calculated = false; + + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Http/HttpConnectionContext.cs b/src/OneScript.StandardLibrary/Http/HttpConnectionContext.cs similarity index 94% rename from src/ScriptEngine.HostedScript/Library/Http/HttpConnectionContext.cs rename to src/OneScript.StandardLibrary/Http/HttpConnectionContext.cs index b3fd8d94e..78472bfbc 100644 --- a/src/ScriptEngine.HostedScript/Library/Http/HttpConnectionContext.cs +++ b/src/OneScript.StandardLibrary/Http/HttpConnectionContext.cs @@ -4,16 +4,22 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; + using System; using System.Collections.Generic; using System.Linq; using System.Net; +using System.Reflection; using System.Text; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; using RegExp = System.Text.RegularExpressions; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Http +namespace OneScript.StandardLibrary.Http { /// /// Объект доступа к протоколу HTTP/HTTPS. @@ -40,7 +46,7 @@ public HttpConnectionContext(string host, IValue ssl = null, bool useOSAuth = false) { - if (ssl != null && !(ssl.DataType == Machine.DataType.Undefined || ssl.DataType == Machine.DataType.NotAValidValue)) + if (ssl != null && !(ssl.SystemType == BasicTypes.Undefined || ssl.IsSkippedArgument())) throw new RuntimeException("Защищенное соединение по произвольным сертификатам не поддерживается. Если необходим доступ по https, просто укажите протокол https в адресе хоста."); var uriBuilder = new UriBuilder(host); @@ -259,76 +265,76 @@ private static bool ContentBodyAllowed(string method) return !methods.Contains(method, StringComparer.OrdinalIgnoreCase); } - private class Range - { - public string RangeSpecifier - { - get; private set; - } - public Int64 From - { - get; private set; - } - public Int64 To - { - get; private set; - } - - public Range(string rangeSpecifier, Int64 from, Int64 to) - { - RangeSpecifier = rangeSpecifier; - From = from; - To = to; - - } + private class Range + { + public string RangeSpecifier + { + get; private set; + } + public Int64 From + { + get; private set; + } + public Int64 To + { + get; private set; + } + + public Range(string rangeSpecifier, Int64 from, Int64 to) + { + RangeSpecifier = rangeSpecifier; + From = from; + To = to; + + } + } + + private static List ParseRange(string rangeHeader) + { + + List range = new List(); + + if (rangeHeader.Length == 0) + return range; + + RegExp.MatchCollection matches = RegExp.Regex.Matches(rangeHeader, @"^(.+)=([^;]+)"); + + string rangeSpecifier = matches[0].Groups[1].Value; + + string stringrange = matches[0].Groups[2].Value; + + string[] ranges = stringrange.Split(','); + + for (int i = 0; i < ranges.Length; i++) + { + string range_spec = ranges[i].Trim(); + + Int64 from = 0; + Int64 to = 0; + RegExp.MatchCollection fromMatches = RegExp.Regex.Matches(range_spec, @"^(\d+)\-$"); + RegExp.MatchCollection fromToMatches = RegExp.Regex.Matches(range_spec, @"^(\d+)\-(\d+)$"); + RegExp.MatchCollection toMatches = RegExp.Regex.Matches(range_spec, @"^\-(\d+)$"); + + if (fromMatches.Count > 0) + { + from = Int64.Parse(fromMatches[0].Groups[1].Value); + to = 0; + } + else if (fromToMatches.Count > 0) + { + from = Int64.Parse(fromToMatches[0].Groups[1].Value); + to = Int64.Parse(fromToMatches[0].Groups[2].Value); + } + else if (toMatches.Count > 0) + { + from = Int64.Parse(toMatches[0].Groups[1].Value); + to = 0; + } + range.Add(new Range(rangeSpecifier, from, to)); + } + return range; } - private static List ParseRange(string rangeHeader) - { - - List range = new List(); - - if (rangeHeader.Length == 0) - return range; - - RegExp.MatchCollection matches = RegExp.Regex.Matches(rangeHeader, @"^(.+)=([^;]+)"); - - string rangeSpecifier = matches[0].Groups[1].Value; - - string stringrange = matches[0].Groups[2].Value; - - string[] ranges = stringrange.Split(','); - - for (int i = 0; i < ranges.Length; i++) - { - string range_spec = ranges[i].Trim(); - - Int64 from = 0; - Int64 to = 0; - RegExp.MatchCollection fromMatches = RegExp.Regex.Matches(range_spec, @"^(\d+)\-$"); - RegExp.MatchCollection fromToMatches = RegExp.Regex.Matches(range_spec, @"^(\d+)\-(\d+)$"); - RegExp.MatchCollection toMatches = RegExp.Regex.Matches(range_spec, @"^\-(\d+)$"); - - if (fromMatches.Count > 0) - { - from = Int64.Parse(fromMatches[0].Groups[1].Value); - to = 0; - } - else if (fromToMatches.Count > 0) - { - from = Int64.Parse(fromToMatches[0].Groups[1].Value); - to = Int64.Parse(fromToMatches[0].Groups[2].Value); - } - else if (toMatches.Count > 0) - { - from = Int64.Parse(toMatches[0].Groups[1].Value); - to = 0; - } - range.Add(new Range(rangeSpecifier, from, to)); - } - return range; - } - private HttpResponseContext GetResponse(HttpRequestContext request, string method, string output = null) { var webRequest = CreateRequest(request.ResourceAddress); @@ -385,8 +391,8 @@ private static void SetRequestHeaders(HttpRequestContext request, HttpWebRequest { System.Diagnostics.Trace.Assert(item != null); - var key = item.Key.AsString(); - var value = item.Value.AsString(); + var key = item.Key.ToString(); + var value = item.Value.ToString(); switch (key.ToUpperInvariant()) { @@ -450,19 +456,19 @@ private static void SetRequestHeaders(HttpRequestContext request, HttpWebRequest } break; case "RANGE": - try - { - List range_list = ParseRange(value); - foreach (Range range in range_list) - { - if (range.To == 0) - webRequest.AddRange(range.RangeSpecifier, range.From); - else - webRequest.AddRange(range.RangeSpecifier, range.From, range.To); + try + { + List range_list = ParseRange(value); + foreach (Range range in range_list) + { + if (range.To == 0) + webRequest.AddRange(range.RangeSpecifier, range.From); + else + webRequest.AddRange(range.RangeSpecifier, range.From, range.To); } } catch - { + { throw new RuntimeException("Заголовок Range задан неправильно"); } break; @@ -479,8 +485,12 @@ private static void SetRequestHeaders(HttpRequestContext request, HttpWebRequest break; } - - + + // fix #1151 + if (webRequest.UserAgent == default) + { + webRequest.UserAgent = $"1Script v${Assembly.GetExecutingAssembly().GetName().Version}"; + } } } @@ -501,24 +511,25 @@ private static void SetRequestHeaders(HttpRequestContext request, HttpWebRequest /// Использовать аутентификацию ОС. /// [ScriptConstructor(Name = "По указанному серверу")] - public static HttpConnectionContext Constructor(IValue host, - IValue port = null, - IValue user = null, - IValue password = null, - IValue proxy = null, - IValue timeout = null, + public static HttpConnectionContext Constructor( + string host, + int port = default, + string user = null, + string password = null, + InternetProxyContext proxy = null, + int timeout = default, IValue ssl = null, - IValue useOSAuthentication = null) + bool useOSAuthentication = default) { - return new HttpConnectionContext(host.AsString(), - ContextValuesMarshaller.ConvertParam(port), - ContextValuesMarshaller.ConvertParam(user), - ContextValuesMarshaller.ConvertParam(password), - ContextValuesMarshaller.ConvertParam(proxy), - ContextValuesMarshaller.ConvertParam(timeout), - ContextValuesMarshaller.ConvertParam(ssl), - ContextValuesMarshaller.ConvertParam(useOSAuthentication) - ); + return new HttpConnectionContext( + host, + port, + user, + password, + proxy, + timeout, + ssl, + useOSAuthentication); } } diff --git a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestBody.cs b/src/OneScript.StandardLibrary/Http/HttpRequestBody.cs similarity index 92% rename from src/ScriptEngine.HostedScript/Library/Http/HttpRequestBody.cs rename to src/OneScript.StandardLibrary/Http/HttpRequestBody.cs index cd8f19d3c..795ba3127 100644 --- a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestBody.cs +++ b/src/OneScript.StandardLibrary/Http/HttpRequestBody.cs @@ -4,11 +4,12 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; + using System; using System.IO; +using ScriptEngine.Machine; -namespace ScriptEngine.HostedScript.Library.Http +namespace OneScript.StandardLibrary.Http { interface IHttpRequestBody : IDisposable { diff --git a/src/OneScript.StandardLibrary/Http/HttpRequestBodyBinary.cs b/src/OneScript.StandardLibrary/Http/HttpRequestBodyBinary.cs new file mode 100644 index 000000000..0057e2b79 --- /dev/null +++ b/src/OneScript.StandardLibrary/Http/HttpRequestBodyBinary.cs @@ -0,0 +1,85 @@ +/*---------------------------------------------------------- +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.IO; +using System.Text; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Text; +using OneScript.Types; +using ScriptEngine.Machine; + +namespace OneScript.StandardLibrary.Http +{ + class HttpRequestBodyBinary : IHttpRequestBody + { + private readonly FileBackingStream _storage = new FileBackingStream(); + + public HttpRequestBodyBinary() + { + } + + public HttpRequestBodyBinary(BinaryDataContext data) + { + 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); + var reader = new StreamReader(_storage); + return ValueFactory.Create(reader.ReadToEnd()); + } + + public IValue GetAsBinary() + { + _storage.Seek(0, SeekOrigin.Begin); + return new BinaryDataContext(_storage); + } + + public IValue GetAsFilename() + { + return ValueFactory.Create(); + } + + public Stream GetDataStream() + { + _storage.Seek(0, SeekOrigin.Begin); + return _storage; + } + + public void Dispose() + { + _storage.Close(); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestBodyFile.cs b/src/OneScript.StandardLibrary/Http/HttpRequestBodyFile.cs similarity index 95% rename from src/ScriptEngine.HostedScript/Library/Http/HttpRequestBodyFile.cs rename to src/OneScript.StandardLibrary/Http/HttpRequestBodyFile.cs index 237256a9f..a741ab93a 100644 --- a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestBodyFile.cs +++ b/src/OneScript.StandardLibrary/Http/HttpRequestBodyFile.cs @@ -4,11 +4,12 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; + using System; using System.IO; +using ScriptEngine.Machine; -namespace ScriptEngine.HostedScript.Library.Http +namespace OneScript.StandardLibrary.Http { class HttpRequestBodyFile : IHttpRequestBody, IDisposable { diff --git a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestContext.cs b/src/OneScript.StandardLibrary/Http/HttpRequestContext.cs similarity index 89% rename from src/ScriptEngine.HostedScript/Library/Http/HttpRequestContext.cs rename to src/OneScript.StandardLibrary/Http/HttpRequestContext.cs index b97b1921c..d28946b1a 100644 --- a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestContext.cs +++ b/src/OneScript.StandardLibrary/Http/HttpRequestContext.cs @@ -4,13 +4,18 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; + using System; using System.IO; -using ScriptEngine.HostedScript.Library.Binary; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Text; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Http +namespace OneScript.StandardLibrary.Http { /// /// Данные и заголоки HTTP запроса. @@ -71,13 +76,15 @@ public IValue GetBodyFileName() /// Установить тело запроса из объекта ДвоичныеДанные /// /// - [ContextMethod("УстановитьТелоИзДвоичныхДанных", "SetBodyFromBinary")] + [DeprecatedName("SetBodyFromBinary")] + [ContextMethod("УстановитьТелоИзДвоичныхДанных", "SetBodyFromBinaryData")] public void SetBodyFromBinary(BinaryDataContext data) { SetBody(new HttpRequestBodyBinary(data)); } - [ContextMethod("ПолучитьТелоКакДвоичныеДанные", "GetBodyAsBinary")] + [DeprecatedName("GetBodyAsBinary")] + [ContextMethod("ПолучитьТелоКакДвоичныеДанные", "GetBodyAsBinaryData")] public IValue GetBodyFromBinary() { return _body?.GetAsBinary(); @@ -115,11 +122,13 @@ public static HttpRequestContext Constructor() } [ScriptConstructor(Name = "По адресу ресурса и заголовкам")] - public static HttpRequestContext Constructor(IValue resource, IValue headers = null) + public static HttpRequestContext Constructor(string resource, IValue headers = null) { - var ctx = new HttpRequestContext {ResourceAddress = resource.AsString()}; - if (headers == null) return ctx; - if (!(headers.GetRawValue() is MapImpl headersMap)) + var ctx = new HttpRequestContext {ResourceAddress = resource}; + if (headers == null) + return ctx; + + if (!(headers is MapImpl headersMap)) throw RuntimeException.InvalidArgumentType(); ctx.Headers = headersMap; diff --git a/src/OneScript.StandardLibrary/Http/HttpResponseBody.cs b/src/OneScript.StandardLibrary/Http/HttpResponseBody.cs new file mode 100644 index 000000000..92e7dab39 --- /dev/null +++ b/src/OneScript.StandardLibrary/Http/HttpResponseBody.cs @@ -0,0 +1,248 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.IO.Compression; +using System.Net; +using System.Net.Http; + +namespace OneScript.StandardLibrary.Http +{ + class HttpResponseBody : IDisposable + { + private const int INMEMORY_BODY_LIMIT = 1024 * 1024 * 5; // 5 Mb + private const int UNDEFINED_LENGTH = -1; + private const int CHUNK_SIZE = 0x8000; + + string _backingFileName; + bool _backFileIsTemp = false; + byte[] _inMemBody; + + private readonly bool _autoDecompress; + private long _contentSize = 0; + private readonly Stream _rawStream; + private bool _inMemoryResponseInited; + + public HttpResponseBody(HttpWebResponse response, string dumpToFile) + { + if (response.Method == HttpMethod.Head.Method) + { + _inMemBody = Array.Empty(); + return; + } + + _inMemoryResponseInited = false; + _rawStream = response.GetResponseStream(); + _autoDecompress = string.Equals(response.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase); + _contentSize = _autoDecompress ? -1 : response.ContentLength; + + if (!String.IsNullOrEmpty(dumpToFile)) + { + InitFileBackedResponse(dumpToFile); + } + else if(_autoDecompress) + { + InitInMemoryResponse(); + } + } + + private void InitInMemoryResponse() + { + if(_contentSize > INMEMORY_BODY_LIMIT) + { + var filename = Path.GetTempFileName(); + _backFileIsTemp = true; + InitFileBackedResponse(filename); + } + else + { + if(_contentSize == UNDEFINED_LENGTH) + { + ReadToStream(); + } + else + { + ReadToArray(); + } + } + _inMemoryResponseInited = true; + } + + public bool AutoDecompress => _autoDecompress; + + public long ContentSize => _contentSize < 0 ? 0 : _contentSize; + + public Stream OpenReadStream(bool raw = false) + { + if (raw) + { + return GetResponseStream(); + } + + if (!_inMemoryResponseInited) + { + InitInMemoryResponse(); + } + + if (_backingFileName != null) + { + return new FileStream(_backingFileName, FileMode.Open, FileAccess.Read); + } + else if (_inMemBody != null) + { + return new MemoryStream(_inMemBody); + } + else + throw new InvalidOperationException("No response body"); + } + + private Stream GetResponseStream() + { + if (_autoDecompress) + return new GZipStream(_rawStream, CompressionMode.Decompress); + return _rawStream; + } + + private void ReadToStream() + { + using (var responseStream = GetResponseStream()) + using(var ms = new MemoryStream()) + { + bool memStreamIsAlive = true; + + int readTotal = 0; + byte[] buffer = new byte[CHUNK_SIZE]; + while (true) + { + var bytesRead = responseStream.Read(buffer, 0, CHUNK_SIZE); + if (bytesRead == 0) + break; + + ms.Write(buffer, 0, bytesRead); + + readTotal += bytesRead; + + if(readTotal > INMEMORY_BODY_LIMIT) + { + var filename = Path.GetTempFileName(); + _backFileIsTemp = true; + _backingFileName = filename; + + ms.Position = 0; + using (var file = new FileStream(filename, FileMode.Create)) + { + StreamToStreamCopy(ms, file); + ms.Dispose(); + memStreamIsAlive = false; + StreamToStreamCopy(responseStream, file); + } + + break; + + } + } + + if(memStreamIsAlive) + { + _inMemBody = new byte[ms.Length]; + ms.Position = 0; + ms.Read(_inMemBody, 0, _inMemBody.Length); + } + + if (_autoDecompress) + _contentSize = readTotal; + } + } + + private void ReadToArray() + { + System.Diagnostics.Debug.Assert(_contentSize <= INMEMORY_BODY_LIMIT); + + using var stream = GetResponseStream(); + var mustRead = (int)_contentSize; + _inMemBody = new byte[mustRead]; + int offset = 0; + + while (mustRead > 0) + { + int portion = Math.Min(CHUNK_SIZE, (int)mustRead); + var read = stream.Read(_inMemBody, offset, portion); + + if (read == 0) + break; + + mustRead -= read; + offset += read; + } + } + + private void InitFileBackedResponse(string backingFileName) + { + _backingFileName = backingFileName; + using (var responseStream = GetResponseStream()) + { + using(var file = new FileStream(backingFileName, FileMode.Create)) + { + StreamToStreamCopy(responseStream, file); + } + } + } + + private static void StreamToStreamCopy(Stream responseStream, Stream acceptor) + { + byte[] buffer = new byte[CHUNK_SIZE]; + while (true) + { + var bytesRead = responseStream.Read(buffer, 0, CHUNK_SIZE); + if (bytesRead == 0) + break; + + acceptor.Write(buffer, 0, bytesRead); + } + } + + private void Dispose(bool manualDispose) + { + if (manualDispose) + { + GC.SuppressFinalize(this); + _inMemBody = null; + } + + KillTemporaryFile(); + } + + private void KillTemporaryFile() + { + if(_backFileIsTemp && _backingFileName != null) + { + if(File.Exists(_backingFileName)) + { + try + { + File.Delete(_backingFileName); + } + catch + { + // нипавезло :( + } + } + } + } + + public void Dispose() + { + Dispose(true); + } + + ~HttpResponseBody() + { + Dispose(false); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Http/HttpResponseContext.cs b/src/OneScript.StandardLibrary/Http/HttpResponseContext.cs similarity index 77% rename from src/ScriptEngine.HostedScript/Library/Http/HttpResponseContext.cs rename to src/OneScript.StandardLibrary/Http/HttpResponseContext.cs index 9fe778076..247844387 100644 --- a/src/ScriptEngine.HostedScript/Library/Http/HttpResponseContext.cs +++ b/src/OneScript.StandardLibrary/Http/HttpResponseContext.cs @@ -1,19 +1,22 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; + using System; using System.IO; using System.Net; using System.Text; +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Text; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -using ScriptEngine.HostedScript.Library.Binary; - -namespace ScriptEngine.HostedScript.Library.Http +namespace OneScript.StandardLibrary.Http { /// /// Ответ от HTTP-сервера @@ -25,34 +28,24 @@ public class HttpResponseContext : AutoContext, IDisposable // TODO: Нельзя выделить массив размером больше чем 2GB // поэтому функционал сохранения в файл не должен использовать промежуточный буфер _body private HttpResponseBody _body; - + private HttpWebResponse _response; + private string _defaultCharset; private string _filename; - public HttpResponseContext(HttpWebResponse response) - { - RetrieveResponseData(response, null); - } - public HttpResponseContext(HttpWebResponse response, string dumpToFile) { - RetrieveResponseData(response, dumpToFile); - } + StatusCode = (int)response.StatusCode; + _defaultCharset = response.CharacterSet; - private void RetrieveResponseData(HttpWebResponse response, string dumpToFile) - { - using(response) + ProcessHeaders(response.Headers); + ProcessResponseBody(response, dumpToFile); + _response = response; + + if (_body != null && _body.AutoDecompress) { - StatusCode = (int)response.StatusCode; - _defaultCharset = response.CharacterSet; - - ProcessHeaders(response.Headers); - ProcessResponseBody(response, dumpToFile); - if (_body != null && _body.AutoDecompress) - { - _headers.Delete(ValueFactory.Create("Content-Encoding")); - _headers.SetIndexedValue(ValueFactory.Create("Content-Length"), ValueFactory.Create(_body.ContentSize)); - } + _headers.Delete(ValueFactory.Create("Content-Encoding")); + _headers.SetIndexedValue(ValueFactory.Create("Content-Length"), ValueFactory.Create(_body.ContentSize)); } } @@ -80,13 +73,7 @@ private void ProcessResponseBody(HttpWebResponse response, string dumpToFile) /// Соответствие. Заголовки ответа сервера. /// [ContextProperty("Заголовки", "Headers")] - public MapImpl Headers - { - get - { - return _headers; - } - } + public MapImpl Headers => _headers; /// /// Код состояния HTTP ответа. Число. @@ -134,24 +121,24 @@ public IValue GetBodyAsBinaryData() return ValueFactory.Create(); using (var stream = _body.OpenReadStream()) - { - var data = new byte[stream.Length]; - stream.Read(data, 0, data.Length); - return new BinaryDataContext(data); + using (var memoryStream = new MemoryStream()) + { + stream.CopyTo(memoryStream); + return new BinaryDataContext(memoryStream.ToArray()); } } /// /// Интерпретировать ответ, как Поток /// + /// Булево. Будет получен сырой поток без дополнительной обработки. По умолчанию Ложь /// Поток [ContextMethod("ПолучитьТелоКакПоток", "GetBodyAsStream")] - public IValue GetBodyAsStream() + public IValue GetBodyAsStream(bool rawStream = false) { if (_body == null) return ValueFactory.Create(); - - return new GenericStream(_body.OpenReadStream(), true); + return new GenericStream(_body.OpenReadStream(rawStream), true); } /// @@ -178,8 +165,11 @@ public void Close() public void Dispose() { - if (_body != null) - _body.Dispose(); + _response?.Dispose(); + _response = null; + + _body?.Dispose(); + _body = null; } } } diff --git a/src/OneScript.StandardLibrary/Http/InternetProxyContext.cs b/src/OneScript.StandardLibrary/Http/InternetProxyContext.cs new file mode 100644 index 000000000..5e3d71207 --- /dev/null +++ b/src/OneScript.StandardLibrary/Http/InternetProxyContext.cs @@ -0,0 +1,195 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Http +{ + /// + /// Параметры прокси-сервера для доступа в Интернет. + /// В текущей реализации поддерживается только HTTP прокси. Стандартные методы объекта ИнтернетПрокси из 1С:Предприятие для FTP и SOCKS не реализованы. + /// + [ContextClass("ИнтернетПрокси", "InternetProxy")] + public class InternetProxyContext : AutoContext + { + private Dictionary _proxies = new Dictionary(); + private const string LINUX_ENV_HTTP = "http_proxy"; + private const string LINUX_ENV_HTTPS = "https_proxy"; + private const string LINUX_ENV_NO_PROXY = "no_proxy"; + + private ArrayImpl _bypassProxyOnAddresses; + private bool _bypassLocal; + + public InternetProxyContext(bool useDefault) + { + var emptyProxy = new WebProxy(); + _bypassLocal = false; + _bypassProxyOnAddresses = new ArrayImpl(); + + if (useDefault) + { + if (System.Environment.OSVersion.Platform == System.PlatformID.Unix) + { + var httpEnv = System.Environment.GetEnvironmentVariable(LINUX_ENV_HTTP); + _proxies[Uri.UriSchemeHttp] = httpEnv == null ? emptyProxy : + _proxies[Uri.UriSchemeHttp] = GetProxyFromEnvironmentVariable(httpEnv); + + var httpsEnv = System.Environment.GetEnvironmentVariable(LINUX_ENV_HTTPS); + _proxies[Uri.UriSchemeHttps] = httpsEnv == null ? emptyProxy : + _proxies[Uri.UriSchemeHttps] = GetProxyFromEnvironmentVariable(httpEnv); + + var noProxy = System.Environment.GetEnvironmentVariable(LINUX_ENV_NO_PROXY) ?? string.Empty; + var separator = new[] {',', ' '}; + var byPassList = noProxy.Split(separator, StringSplitOptions.RemoveEmptyEntries); + foreach (var uri in byPassList) + _bypassProxyOnAddresses.Add(ValueFactory.Create(uri)); + foreach (var proxy in _proxies.Values.Cast()) + proxy.BypassList = byPassList; + } + else + { + var defaultProxy = WebRequest.GetSystemWebProxy(); + defaultProxy.Credentials = CredentialCache.DefaultNetworkCredentials; + + _proxies[Uri.UriSchemeHttp] = defaultProxy; + _proxies[Uri.UriSchemeHttps] = defaultProxy; + } + } + else + { + _proxies[Uri.UriSchemeHttp] = emptyProxy; + _proxies[Uri.UriSchemeHttps] = emptyProxy; + } + } + + private static WebProxy GetProxyFromEnvironmentVariable(string envVariable) + { + var proxyBuilder = new UriBuilder(envVariable); + var proxyUri = new Uri(proxyBuilder.Uri.GetComponents(UriComponents.HttpRequestUrl, UriFormat.UriEscaped)); + var proxyCredentials = proxyBuilder.UserName.Equals(string.Empty) + ? CredentialCache.DefaultNetworkCredentials + : GetBasicCredential(proxyUri, proxyBuilder.UserName, proxyBuilder.Password); + return new WebProxy(proxyUri, true, new string[]{}, proxyCredentials); + } + + private static ICredentials GetBasicCredential(Uri uri, string username, string password) + { + var credential = new NetworkCredential(username, password); + var cache = new CredentialCache {{uri, "Basic", credential}}; + return cache; + } + + public IWebProxy GetProxy(string protocol) + { + if(!ProtocolNameIsValid(protocol)) + throw RuntimeException.InvalidArgumentValue(); + return _proxies[protocol]; + } + + [ContextMethod("Пользователь","User")] + public string User(string protocol) + { + var proxy = GetProxy(protocol) as WebProxy; + return proxy?.Credentials.GetCredential(proxy.Address, "Basic").UserName ?? string.Empty; + } + + [ContextMethod("Пароль", "Password")] + public string Password(string protocol) + { + var proxy = GetProxy(protocol) as WebProxy; + return proxy?.Credentials.GetCredential(proxy.Address, "Basic").Password ?? string.Empty; + } + + [ContextMethod("Сервер", "Server")] + public string Server(string protocol) + { + const UriComponents serverComponents = UriComponents.Scheme | UriComponents.Host | UriComponents.PathAndQuery; + var proxy = GetProxy(protocol) as WebProxy; + return proxy?.Address.GetComponents(serverComponents, UriFormat.UriEscaped) ?? string.Empty; + } + + [ContextMethod("Порт", "Port")] + public int Port(string protocol) + { + var proxy = GetProxy(protocol) as WebProxy; + return proxy?.Address.Port ?? 0; + } + + [ContextMethod("Установить", "Set")] + public void Set(string protocol, string server, int port = 0, string username = "", string password = "", bool useOSAuthentication = true) + { + protocol = protocol.ToLower(); + if(!ProtocolNameIsValid(protocol)) + throw RuntimeException.InvalidArgumentValue(); + + var builderServer = new UriBuilder(server); + if (builderServer.Scheme.Equals(string.Empty)) + builderServer.Scheme = protocol; + + if (port != 0) + builderServer.Port = port; + + var proxyCredentials = useOSAuthentication ? CredentialCache.DefaultNetworkCredentials : + GetBasicCredential(builderServer.Uri, username, password); + + _proxies[protocol] = new WebProxy(builderServer.Uri, _bypassLocal, + _bypassProxyOnAddresses?.Select(x => x.ToString()).ToArray() ?? new string[] {}, proxyCredentials); + } + + [ContextProperty("НеИспользоватьПроксиДляАдресов","BypassProxyOnAddresses")] + public ArrayImpl BypassProxyList + { + get => _bypassProxyOnAddresses; + set + { + _bypassProxyOnAddresses = value; + var bypassList = _bypassProxyOnAddresses?.Select(x => x.ToString()).ToArray() ?? new string[] {}; + foreach (var kv in _proxies) + if (kv.Value is WebProxy proxy) + proxy.BypassList = bypassList; + } + } + + [ContextProperty("НеИспользоватьПроксиДляЛокальныхАдресов", "BypassProxyOnLocal")] + public bool BypassProxyOnLocal + { + get => _bypassLocal; + set + { + _bypassLocal = value; + foreach (var kv in _proxies) + if (kv.Value is WebProxy proxy) + proxy.BypassProxyOnLocal = _bypassLocal; + } + } + + private static bool ProtocolNameIsValid(string protocol) + { + return Uri.UriSchemeHttp.Equals(protocol) || Uri.UriSchemeHttps.Equals(protocol); + } + + [ScriptConstructor(Name = "Формирование неинициализированного объекта")] + public static InternetProxyContext Constructor() + { + return Constructor(ValueFactory.Create(false)); + } + + [ScriptConstructor(Name = "Конструктор для системных настроек прокси")] + public static InternetProxyContext Constructor(IValue useDefault) + { + return new InternetProxyContext(useDefault.AsBoolean()); + } + } +} diff --git a/src/OneScript.StandardLibrary/Json/GlobalJsonFunctions.cs b/src/OneScript.StandardLibrary/Json/GlobalJsonFunctions.cs new file mode 100644 index 000000000..fe19b479e --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/GlobalJsonFunctions.cs @@ -0,0 +1,346 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Json +{ + + /// + /// Глобальный контекст. Операции с Json. + /// + [GlobalContext(Category = "Процедуры и функции работы с JSON")] + public sealed class GlobalJsonFunctions : GlobalContextBase + { + public static IAttachableContext CreateInstance() + { + return new GlobalJsonFunctions(); + } + + /// + /// + /// Считывает значение из JSON-текста или файла. JSON-текст должен быть корректным. + /// + /// + /// + /// Объект чтения JSON. + /// + /// Если установлено Истина, чтение объекта JSON будет выполнено в Соответствие. + /// Если установлено Ложь, объекты будут считываться в объект типа Структура. + /// Значение по умолчанию: Ложь. + /// + /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. + /// + /// Значение не обрабатывается в текущей версии. Значение по умолчанию: ISO. + /// + /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. + /// + /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. + /// + /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. + /// + /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. + /// + /// Значение не обрабатывается в текущей версии. + /// + /// + /// + [ContextMethod("ПрочитатьJSON", "ReadJSON")] + public IValue ReadJSON(JSONReader Reader, bool ReadToMap = false, IValue PropertiesWithDateValuesNames = null, IValue ExpectedDateFormat = null, string ReviverFunctionName = null, IValue ReviverFunctionModule = null, IValue ReviverFunctionAdditionalParameters = null, IValue RetriverPropertiesNames = null, int MaximumNesting = 500) + { + var jsonReader = new JsonReaderInternal(Reader); + return ReadToMap ? jsonReader.Read() : jsonReader.Read(); + } + + internal class JsonReaderInternal + { + private readonly JSONReader _reader; + private Func _builder; + private Action _inserter; + + private void Init() + { + if (typeof(TStructure) == typeof(StructureImpl)) + { + _builder = () => new StructureImpl(); + _inserter = (o, s, v) => ((StructureImpl)o).Insert(s, v); + } + else if (typeof(TStructure) == typeof(MapImpl)) + { + _builder = () => new MapImpl(); + _inserter = (o, s, v) =>((MapImpl)o).Insert(ValueFactory.Create(s), v); + } + else + { + throw new InvalidOperationException(); + } + } + + public JsonReaderInternal(JSONReader reader) + { + _reader = reader; + } + + private IValue Create() => _builder(); + + private void AddProperty(IValue obj, string str, IValue val) => _inserter(obj, str, val); + + public IValue Read() where T: IEnumerable + { + System.Diagnostics.Debug.Assert(typeof(T)==typeof(StructureImpl)||typeof(T)==typeof(MapImpl)); + Init(); + + try + { + if (ReadJsonValue(out var value)) + return value; + } + catch (JSONReaderException) + { + throw; + } + catch (Exception exc) + { + throw InvalidJsonException(exc.Message); + } + + throw InvalidJsonException(); + } + + private JsonToken ReadJsonToken() + { + while (_reader.Read()) + { + var tok = _reader.CurrentJsonTokenType; + if (tok != JsonToken.Comment) + return tok; + } + + return JsonToken.None; + } + + private bool ReadJsonValue(out IValue value) + { + switch (ReadJsonToken()) + { + case JsonToken.StartObject: + var jsonObject = Create(); + + while (ReadJsonToken() == JsonToken.PropertyName) + { + var propertyName = (string)_reader.ReaderValue; + if (!ReadJsonValue(out value)) + return false; + + AddProperty(jsonObject, propertyName, value); + } + + if (_reader.CurrentJsonTokenType == JsonToken.EndObject) + { + value = jsonObject; + return true; + } + break; + + case JsonToken.StartArray: + var resArray = new ArrayImpl(); + + while (ReadJsonValue(out value)) + { + resArray.Add(value); + } + + if (_reader.CurrentJsonTokenType == JsonToken.EndArray) + { + value = resArray; + return true; + } + break; + + case JsonToken.EndArray: + case JsonToken.EndObject: + case JsonToken.None: + break; + + default: + value = _reader.CurrentValue; + return true; + } + + value = null; + return false; + } + + private RuntimeException InvalidJsonException(string message=null) + { + var addition = string.IsNullOrWhiteSpace(message) ? string.Empty : $"\n({message})"; + return new RuntimeException(string.Format(Locale.NStr + ("ru='Недопустимое состояние потока чтения JSON в строке {0} позиции {1}{2}'" + + "en='Invalid JSON reader state at line {0} position {1}{2}'"), + _reader.CurrentLine, _reader.CurrentPosition, addition)); + } + } + + public IValue ReadJSONInMap(JSONReader reader) + { + var jsonReader = new JsonReaderInternal(reader); + return jsonReader.Read(); + } + + /// + /// + /// Выполняет преобразование строки, прочитанной в JSON-формате, в значение типа Дата. + /// + /// + /// + /// Строка, которую требуется преобразовать в дату. + /// + /// Формат, в котором представлена дата в строке, подлежащей преобразованию. + /// + /// + /// Значения данного типа содержит дату григорианского календаря (с 01 января 0001 года) и время с точностью до секунды. + /// + [ContextMethod("ПрочитатьДатуJSON", "ReadJSONDate")] + public IValue ReadJSONDate(string String, JSONDateFormatEnum? format) + { + DateFormatHandling dateFormatHandling; + + switch (format) + { + case JSONDateFormatEnum.ISO: + case null: + dateFormatHandling = DateFormatHandling.IsoDateFormat; + break; + case JSONDateFormatEnum.Microsoft: + dateFormatHandling = DateFormatHandling.MicrosoftDateFormat; + break; + case JSONDateFormatEnum.JavaScript: + default: + throw new RuntimeException(Locale.NStr( + "ru='Формат даты JavaScript не поддерживается.'; en='JavaScript date format is not supported'")); + } + + string json = @"{""Date"":""" + String + @"""}"; + + var settings = new JsonSerializerSettings + { + DateFormatHandling = dateFormatHandling + }; + + try + { + var result = JsonConvert.DeserializeObject(json, settings); + return ValueFactory.Create((DateTime)result.Date); + } + catch (JsonException) + { + throw new RuntimeException(Locale.NStr("ru='Представление даты имеет неверный формат.'; en='Invalid date presentation format'")); + } + } + + /// + /// + /// Выполняет сериализацию Значение в формат JSON. Результат помещает в объект ЗаписьJSON. + /// Если методу требуется передать значение недопустимого типа, то можно использовать функцию преобразования значения (параметры ИмяФункцииПреобразования и МодульФункцииПреобразования). + /// + /// + /// + /// Объект, через который осуществляется запись JSON. Поток JSON должен быть подготовлен для записи значения. + /// + /// Объект записи JSON. Меняет состояние потока записи. + /// + /// В текущий версии не обрабатывается. Настройки сериализации в JSON. + /// + /// В текущий версии не обрабатывается. Значение по умолчанию: Неопределено. + /// + /// Указывает контекст, в котором реализована функция преобразования значения в значение формата JSON. + /// В текущий версии не обрабатывается. Значение по умолчанию: Неопределено. + /// + /// В текущий версии не обрабатывается. Значение по умолчанию: Неопределено. + /// + /// + [ContextMethod("ЗаписатьJSON", "WriteJSON")] + public void WriteJSON(IBslProcess process, JSONWriter writer, IValue value, IValue serializationSettings = null, string conversionFunctionName = null, IValue conversionFunctionModule = null, IValue conversionFunctionAdditionalParameters = null) + { + if (value is ArrayImpl) + { + writer.WriteStartArray(); + foreach (var item in (ArrayImpl)value) + { + WriteJSON(process, writer, item); + } + writer.WriteEndArray(); + } + else if (value is FixedArrayImpl) + { + writer.WriteStartArray(); + foreach (var item in (FixedArrayImpl)value) + { + WriteJSON(process, writer, item); + } + writer.WriteEndArray(); + } + else if (value is StructureImpl) + { + writer.WriteStartObject(); + foreach (var item in (StructureImpl)value) + { + writer.WritePropertyName(item.Key.ToString()); + WriteJSON(process, writer, item.Value); + } + writer.WriteEndObject(); + } + else if (value is FixedStructureImpl) + { + writer.WriteStartObject(); + foreach (var item in (FixedStructureImpl)value) + { + writer.WritePropertyName(item.Key.ToString()); + WriteJSON(process, writer, item.Value); + } + writer.WriteEndObject(); + } + else if (value is MapImpl) + { + writer.WriteStartObject(); + foreach (var item in (MapImpl)value) + { + writer.WritePropertyName(item.Key.AsString(process)); + WriteJSON(process, writer, item.Value); + } + writer.WriteEndObject(); + } + else if (value is FixedMapImpl) + { + writer.WriteStartObject(); + foreach (var item in (FixedMapImpl)value) + { + writer.WritePropertyName(item.Key.AsString(process)); + WriteJSON(process, writer, item.Value); + } + writer.WriteEndObject(); + } + else + writer.WriteValue(value); + } + } + + class ConvertedDate + { + public DateTime Date { get; set; } + } + +} diff --git a/src/OneScript.StandardLibrary/Json/JSONCharactersEscapeMode.cs b/src/OneScript.StandardLibrary/Json/JSONCharactersEscapeMode.cs new file mode 100644 index 000000000..acae880f9 --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/JSONCharactersEscapeMode.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Json +{ + [EnumerationType("ЭкранированиеСимволовJSON", "JSONCharactersEscapeMode", + TypeUUID = "A7FA438B-AC4D-4811-BB88-29F16BB9594D", + ValueTypeUUID = "064950E9-91D3-4FF4-836D-BE6C8BF173E3")] + public enum JSONCharactersEscapeModeEnum + { + [EnumValue("Нет", "None")] + None, + + [EnumValue("СимволыВнеASCII", "NotASCIISymbols")] + NotASCIISymbols, + + [EnumValue("СимволыВнеBMP", "SymbolsNotInBMP")] + SymbolsNotInBMP + } + +} diff --git a/src/OneScript.StandardLibrary/Json/JSONDataExtractor.cs b/src/OneScript.StandardLibrary/Json/JSONDataExtractor.cs new file mode 100644 index 000000000..b96bd01a9 --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/JSONDataExtractor.cs @@ -0,0 +1,536 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Text; +using OneScript.StandardLibrary.Binary; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OneScript.Exceptions; +using OneScript.Execution; + +namespace OneScript.StandardLibrary.Json +{ + /// + /// Предоставляет методы для извлечения данных из JSON по запросу JSON-path + /// + [ContextClass("ИзвлечениеДанныхJSON", "JSONDataExtractor")] + public class JSONDataExtractor : AutoContext + { + private JToken _jsonData; + + /// + /// Устанавливает строку JSON для последующей обработки + /// + /// Строка. Строка JSON. + [ContextMethod("УстановитьСтроку", "SetString")] + public void SetString(string JSONString) + { + + _jsonData = JToken.Parse(JSONString); + + } + + /// + /// Читает файл, содержащий данные JSON для последующей обработки + /// + /// Строка. Путь к файлу. + /// КодировкаТекста. кодировка файла. + [ContextMethod("ОткрытьФайл", "OpenFile")] + public void OpenFile(string JSONFileName, IValue encoding = null) + { + + StreamReader _fileReader; + + try + { + if (encoding == null) + _fileReader = FileOpener.OpenReader(JSONFileName, Encoding.Default); + else + _fileReader = FileOpener.OpenReader(JSONFileName, TextEncodingEnum.GetEncoding(encoding)); + } + catch (Exception e) + { + throw new RuntimeException(e.Message, e); + } + + _jsonData = JToken.Parse(_fileReader.ReadToEnd()); + + _fileReader.Close(); + + } + + /// + /// Читает данные из потока JSON для последующей обработки + /// + /// Поток. поток с данными JSON. + /// КодировкаТекста. кодировка файла. + [ContextMethod("ОткрытьПоток", "OpenStream")] + public void OpenStream(IValue JSONStream, IValue encoding = null) + { + + string _JSONString = "{}"; + Encoding _encoding; + + if (encoding == null) + _encoding = Encoding.Default; + else + _encoding = TextEncodingEnum.GetEncoding(encoding); + + using (Stream _underlyingStream = ((IStreamWrapper)JSONStream).GetUnderlyingStream()) + { + byte[] buffer = new byte[1000]; + StringBuilder builder = new StringBuilder(); + int read = -1; + + while (true) + { + AutoResetEvent gotInput = new AutoResetEvent(false); + Thread inputThread = new Thread(() => + { + try + { + read = _underlyingStream.Read(buffer, 0, buffer.Length); + gotInput.Set(); + } + catch (ThreadAbortException) + { + Thread.ResetAbort(); + } + }) + { + IsBackground = true + }; + + inputThread.Start(); + + // Timeout expired? + if (!gotInput.WaitOne(100)) + { + inputThread.Abort(); + break; + } + + // End of stream? + if (read == 0) + { + _JSONString = builder.ToString(); + break; + } + + // Got data + builder.Append(_encoding.GetString(buffer, 0, read)); + } + } + + string _BOMMarkUTF8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()); + + if (_JSONString.StartsWith(_BOMMarkUTF8, StringComparison.Ordinal)) + _JSONString = _JSONString.Remove(0, _BOMMarkUTF8.Length); + + _jsonData = JToken.Parse(_JSONString.Trim()); + + } + + /// + /// Выполняет выборку из JSON по указанному JSON-path + /// + /// Строка. JSON-path. + /// Булево. Если результирующий массив содержит единственное значение, то: + /// Истина - будет возвращено значение; + /// Ложь - будет возвращен массив. + /// Булево. Истина - объекты будут возвращены в виде строки JSON; + /// Ложь - Объекты будут возвращены в виде соответствия. + /// Строка - Выбранные данные + [ContextMethod("Выбрать", "Select")] + public IValue Select(IBslProcess p, string path, bool extractSingleValue = true, bool getObjectAsJSON = true) + { + IValue result; + + AggregateFuncEnum aggregateFunc = GetAggregateFunc(path, out path); + + List parseResult = _jsonData.SelectTokens(path).ToList(); + + if (parseResult.Count() == 0) + { + result = ValueFactory.Create(); + } + else + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter writer = new JsonTextWriter(sw)) + { + writer.Formatting = Formatting.Indented; + + writer.WriteStartArray(); + foreach (JToken token in parseResult) + { + writer.WriteToken(token.CreateReader(), true); + } + writer.WriteEndArray(); + } + result = JSONToDataStructure(sb.ToString()); + } + + if (aggregateFunc != AggregateFuncEnum.none) + result = CalculateAggregateFunc(result, aggregateFunc); + + if (result is ArrayImpl && ((ArrayImpl)result.AsObject()).Count() == 1 && extractSingleValue) + { + result = ((ArrayImpl)result.AsObject()).Get(0); + } + + if ((result is ArrayImpl || result is MapImpl) && getObjectAsJSON) + { + result = ValueFactory.Create(DataStructureToJSON(p, result)); + } + + return result; + } + + #region JSON conversion + + /// + /// Преобразует строку JSON в соответствие или массив + /// + /// Строка. Строка JSON. + /// Соответствие, Массив - Результат преобразования строки JSON в соответствие или массив + private IValue JSONToDataStructure(string JSONString) + { + JSONReader reader = new JSONReader(); + reader.SetString(JSONString); + + return ((GlobalJsonFunctions)GlobalJsonFunctions.CreateInstance()).ReadJSONInMap(reader); + } + + /// + /// Преобразует соответствие или массив в строку JSON + /// + /// Соответствие, Массив. Соответствие или массив для преобразования в строку JSON. + /// Строка - Результат преобразования соответствия или массива в строку JSON + private string DataStructureToJSON(IBslProcess p, IValue inputStruct) + { + JSONWriter writer = new JSONWriter(); + writer.SetString(); + + ((GlobalJsonFunctions)GlobalJsonFunctions.CreateInstance()).WriteJSON(p, writer, inputStruct); + + return writer.Close(); + } + + #endregion + + #region Aggregate calculation + + /// + /// Вычисляет агрегатную функцию над переданными данными + /// + /// Соответствие, Массив. Соответствие или массив для вычисления агрегатной функции. + /// Агрегатнаная функция. + /// Строка - Результат вычисления функции + private IValue CalculateAggregateFunc(IValue sourceData, AggregateFuncEnum aggregateFunc) + { + + IValue result; + + if (aggregateFunc == AggregateFuncEnum.length) + result = CalculateLength(sourceData); + else if (aggregateFunc == AggregateFuncEnum.sum) + result = CalculateSum(sourceData); + else if (aggregateFunc == AggregateFuncEnum.avg) + result = CalculateAvg(sourceData); + else if (aggregateFunc == AggregateFuncEnum.min) + result = CalculateMin(sourceData); + else if (aggregateFunc == AggregateFuncEnum.max) + result = CalculateMax(sourceData); + else if (aggregateFunc == AggregateFuncEnum.first) + result = CalculateFirst(sourceData); + else if (aggregateFunc == AggregateFuncEnum.last) + result = CalculateLast(sourceData); + else if (aggregateFunc == AggregateFuncEnum.keys) + result = CalculateKeys(sourceData); + else + result = sourceData; + + return result; + } + + /// + /// Вычисляет размер переданного массива или соответствия + /// + /// Соответствие, Массив. Соответствие или массив для обработки. + /// Число - Размер переданного массива или соответствия + private IValue CalculateLength(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (sourceData is ArrayImpl) + result = ValueFactory.Create(((ArrayImpl)sourceData).Count()); + else if (sourceData is MapImpl) + result = ValueFactory.Create(((MapImpl)sourceData).Count()); + + return result; + } + + /// + /// Вычисляет сумму чисел в переданном массиве + /// + /// Массив. Соответствие или массив для обработки. + /// Число - Сумма чисел в переданном массиве + private IValue CalculateSum(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (((ArrayImpl)sourceData).Count() == 0) + return result; + + decimal fullSum = 0; + + foreach (IValue value in (ArrayImpl)sourceData) + fullSum += value.AsNumber(); + + result = ValueFactory.Create(fullSum); + + return result; + } + + /// + /// Вычисляет среднее значение в переданном массиве + /// + /// Массив. Соответствие или массив для обработки. + /// Число - Среднее значение в переданном массиве + private IValue CalculateAvg(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (!(sourceData is ArrayImpl)) + return result; + + if (((ArrayImpl)sourceData).Count() == 0) + return result; + + decimal fullSum = 0; + + foreach (IValue value in (ArrayImpl)sourceData) + fullSum += value.AsNumber(); + + result = ValueFactory.Create(fullSum / ((ArrayImpl)sourceData).Count()); + + return result; + } + + /// + /// Вычисляет минимально значение в переданном массиве + /// + /// Массив. Соответствие или массив для обработки. + /// Число - Минимальное значение в переданном массиве + private IValue CalculateMin(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (!(sourceData is ArrayImpl)) + return result; + + if (((ArrayImpl)sourceData).Count() == 0) + return result; + + decimal numericResult = ((ArrayImpl)sourceData).Get(0).AsNumber(); + + foreach (IValue value in (ArrayImpl)sourceData) + numericResult = value.AsNumber() < numericResult ? value.AsNumber() : numericResult; + + result = ValueFactory.Create(numericResult); + + return result; + } + + /// + /// Вычисляет максимальное значение в переданном массиве + /// + /// Массив. Соответствие или массив для обработки. + /// Число - Максимальное значение в переданном массиве + private IValue CalculateMax(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (!(sourceData is ArrayImpl)) + return result; + + if (((ArrayImpl)sourceData).Count() == 0) + return result; + + decimal numericResult = ((ArrayImpl)sourceData).Get(0).AsNumber(); + + foreach (IValue value in (ArrayImpl)sourceData) + numericResult = value.AsNumber() > numericResult ? value.AsNumber() : numericResult; + + result = ValueFactory.Create(numericResult); + + return result; + } + + /// + /// Получает первое значение из переданного массива + /// + /// Массив. Массив для обработки. + /// Произвольный - Первое значение из переданного массива + private IValue CalculateFirst(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (!(sourceData is ArrayImpl)) + return result; + + if (((ArrayImpl)sourceData).Count() == 0) + return result; + + result = ((ArrayImpl)sourceData).Get(0); + + return result; + } + + /// + /// Получает последнее значение из переданного массива + /// + /// Массив. Массив для обработки. + /// Произвольный - Последнее значение из переданного массива + private IValue CalculateLast(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (!(sourceData is ArrayImpl)) + return result; + + if (((ArrayImpl)sourceData).Count() == 0) + return result; + + int lastIndex = ((ArrayImpl)sourceData).UpperBound(); + result = ((ArrayImpl)sourceData).Get(lastIndex); + + return result; + } + + /// + /// Получает массив ключей из переданного соответствия или массив индексов из переданного массива + /// если переданный массив содержит единственное соответствие, то возвращаются ключи соответствия + /// + /// Соответствие, Массив. Соответствие или массив для обработки. + /// Массив - Ключи соответствия или индексы массива + private IValue CalculateKeys(IValue sourceData) + { + + IValue result = ValueFactory.Create(); + + if (sourceData is ArrayImpl && ((ArrayImpl)sourceData).Count() == 1) + sourceData = ((ArrayImpl)sourceData).Get(0); + + if (!(sourceData is MapImpl)) + return result; + + ArrayImpl keyArray = new ArrayImpl(); + if (sourceData is MapImpl) + foreach (KeyAndValueImpl KeyValue in (MapImpl)sourceData) + keyArray.Add(KeyValue.Key); + else if (sourceData is ArrayImpl) + for (int i = 0; i < ((ArrayImpl)sourceData).Count(); i++) + keyArray.Add(ValueFactory.Create(i)); + + return keyArray; + } + + /// + /// Доступные агрегатные функции + /// + private enum AggregateFuncEnum + { + none, + length, + sum, + avg, + min, + max, + first, + last, + keys + } + + /// + /// Получает используемую агрегатную функцию из переданного пути JSON path + /// + /// Строка. исходный путь JSON-path. + /// Строка. путь JSON-path без агрегатной функции. + /// AggregateFuncEnum - используемуя агрегатная функция + private AggregateFuncEnum GetAggregateFunc(string path, out string mainPath) + { + Dictionary aggregateFuncs = new Dictionary(); + aggregateFuncs.Add("count", AggregateFuncEnum.length); + aggregateFuncs.Add("length", AggregateFuncEnum.length); + aggregateFuncs.Add("sum", AggregateFuncEnum.sum); + aggregateFuncs.Add("avg", AggregateFuncEnum.avg); + aggregateFuncs.Add("min", AggregateFuncEnum.min); + aggregateFuncs.Add("max", AggregateFuncEnum.max); + aggregateFuncs.Add("first", AggregateFuncEnum.first); + aggregateFuncs.Add("last", AggregateFuncEnum.last); + aggregateFuncs.Add("keys", AggregateFuncEnum.keys); + + AggregateFuncEnum result = AggregateFuncEnum.none; + mainPath = path; + + string[] parts = path.Split('.'); + + if (parts.Length == 0) + return result; + + string aggregateFunc = parts[parts.GetUpperBound(0)].Trim(); + + if (!aggregateFunc.Trim().EndsWith("()")) + return result; + + aggregateFunc = aggregateFunc.Substring(0, aggregateFunc.Length - 2); + + if (aggregateFuncs.TryGetValue(aggregateFunc.ToLower(), out result)) + { + mainPath = String.Join(".", parts, 0, parts.Length - 1); + return result; + } + + return result; + } + + #endregion + + /// + /// Создает ИзвлечениеДанныхJSON + /// + /// ИзвлечениеДанныхJSON + [ScriptConstructor] + public static IRuntimeContextInstance Constructor() + { + return new JSONDataExtractor(); + } + + } +} diff --git a/src/OneScript.StandardLibrary/Json/JSONDateFormatEnum.cs b/src/OneScript.StandardLibrary/Json/JSONDateFormatEnum.cs new file mode 100644 index 000000000..f47e98459 --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/JSONDateFormatEnum.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Json +{ + [EnumerationType("ФорматДатыJSON", "JSONDateFormat", + TypeUUID = "CDBA878E-FF5C-40A5-ADF3-E473176812BD", + ValueTypeUUID = "29E40EE6-57C7-4E5B-98D8-9B5DDE8CF667")] + public enum JSONDateFormatEnum + { + [EnumValue("ISO")] + ISO, + [EnumValue("JavaScript")] + JavaScript, + [EnumValue("Microsoft")] + Microsoft + } +} diff --git a/src/OneScript.StandardLibrary/Json/JSONLineBreak.cs b/src/OneScript.StandardLibrary/Json/JSONLineBreak.cs new file mode 100644 index 000000000..40bb6c3e8 --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/JSONLineBreak.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Json +{ + [EnumerationType("ПереносСтрокJSON", "JSONLineBreak", + TypeUUID = "C0049501-11D3-41F8-8FAD-767AE8CD7C7E", + ValueTypeUUID = "623C2D3A-01B4-43E3-AF2B-9EB922D0D838")] + public enum JSONLineBreakEnum + { + [EnumValue("Авто", "Auto")] + Auto, + + [EnumValue("Unix")] + Unix, + + [EnumValue("Windows")] + Windows, + + [EnumValue("Нет", "None")] + None + } + +} diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs new file mode 100644 index 000000000..444907ee2 --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -0,0 +1,346 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using Newtonsoft.Json; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Text; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Json +{ + internal class JsonReaderInternal: JsonTextReader // из библиотеки Newtonsoft + { + public JsonReaderInternal(TextReader reader) : base(reader) + { + Finished = false; + } + + public override bool Read() + { + if (!base.Read()) + { + Finished = true; + return false; + } + if (TokenType != JsonToken.Undefined) + return true; + + throw JSONReaderException.UnexpectedSymbol(); + } + public bool Finished { get; private set; } + } + + /// + /// + /// Предназначен для последовательного чтения JSON-данных из файла или строки. + /// + [ContextClass("ЧтениеJSON", "JSONReader")] + public class JSONReader : AutoContext + { + + private JsonReaderInternal _reader; + + /// + /// + /// Возвращает true если для объекта чтения json был задан текст для парсинга. + /// + private bool IsOpen() => _reader != null; + + private void CheckIfOpen() + { + if (_reader == null) throw JSONReaderException.NotOpen(); + } + + public JSONReader() + { + } + + [ScriptConstructor] + public static JSONReader Constructor() + { + return new JSONReader(); + } + + /// + /// + /// Указывает на позицию, находящуюся сразу после прочитанного значения. + /// При ошибке чтение остается на позиции последнего успешно считанного символа. + /// + /// Число (Number), Неопределено (Undefined) + [ContextProperty("ТекущаяПозиция", "CurrentPosition")] + public IValue CurrentPosition + { + get + { + if (IsOpen()) + { + return ValueFactory.Create(_reader.LinePosition); + } + + return ValueFactory.Create(); // Неопределено + } + } + + /// + /// + /// Указывает на позицию сразу после прочитанного значения. + /// Например, перед чтением первого элемента - 0, после чтения первого элемента -1 . + /// + /// Число (Number), Неопределено (Undefined) + [ContextProperty("ТекущаяСтрока", "CurrentLine")] + public IValue CurrentLine + { + get + { + if (IsOpen()) + { + return ValueFactory.Create(_reader.LineNumber); + } + + return ValueFactory.Create(); // Неопределено + } + } + + /// + /// + /// Содержит текущее значение: + /// + /// - Число - если ТипТекущегоЗначения имеет значение Число; + /// - Строка - если ТипТекущегоЗначения имеет одно из следующих значений: + /// - Комментарий, + /// - ИмяСвойства, + /// - Строка; + /// - Булево - если ТипТекущегоЗначения имеет значение Булево, + /// - Неопределено - если ТипТекущегоЗначения имеет значение Null. + /// Исключение генерируется в случае, если ТипТекущегоЗначения имеет одно из следующих значений: + /// - НачалоМассива, + /// - КонецМассива, + /// - НачалоОбъекта, + /// - КонецОбъекта, + /// - Ничего. + /// + /// Число (Number), Строка (String), Булево (Boolean), Неопределено (Undefined) + [ContextProperty("ТекущееЗначение", "CurrentValue")] + public IValue CurrentValue + { + get + { + CheckIfOpen(); + if (_reader.Finished) + throw JSONReaderException.CannotGetValue(); + + switch (_reader.TokenType) + { + case JsonToken.String: + case JsonToken.Comment: + case JsonToken.PropertyName: + return ValueFactory.Create((string)_reader.Value); + + case JsonToken.Integer: + case JsonToken.Float: + return ValueFactory.Create(Convert.ToDecimal(_reader.Value)); + + case JsonToken.Boolean: + return ValueFactory.Create((bool)_reader.Value); + + case JsonToken.Date: + return ValueFactory.Create((DateTime)_reader.Value); + + case JsonToken.Null: + return ValueFactory.Create(); + + default: + throw JSONReaderException.CannotGetValue(); + ; + } + } + } + + public object ReaderValue => _reader.Value; + + /// + /// + /// Тип текущего значения в документе JSON во внутреннем формате. + /// null - если чтение еще не началось или достигнут конец файла. + /// + /// CurrentJsonTokenType + public JsonToken CurrentJsonTokenType + { + get + { + CheckIfOpen(); + return _reader.TokenType; + } + } + + /// + /// + /// Тип текущего значения в документе JSON. + /// Неопределено - если чтение еще не началось или достигнут конец файла. + /// + /// ТипЗначенияJSON (JSONValueType) + [ContextProperty("ТипТекущегоЗначения", "CurrentValueType")] + public JSONValueTypeEnum CurrentValueType + { + get + { + CheckIfOpen(); + + if (_reader.Finished) + return JSONValueTypeEnum.None; + + return _reader.TokenType switch + { + JsonToken.Null => JSONValueTypeEnum.Null, + JsonToken.StartObject => JSONValueTypeEnum.ObjectStart, + JsonToken.StartArray => JSONValueTypeEnum.ArrayStart, + JsonToken.PropertyName => JSONValueTypeEnum.PropertyName, + JsonToken.Comment => JSONValueTypeEnum.Comment, + JsonToken.Integer => JSONValueTypeEnum.Number, + JsonToken.Float => JSONValueTypeEnum.Number, + JsonToken.String => JSONValueTypeEnum.String, + JsonToken.Boolean => JSONValueTypeEnum.Boolean, + JsonToken.EndObject => JSONValueTypeEnum.ObjectEnd, + JsonToken.EndArray => JSONValueTypeEnum.ArrayEnd, + _ => JSONValueTypeEnum.None + }; + } + } + + /// + /// + /// Завершает чтение текста JSON из файла или строки. + /// + /// + [ContextMethod("Закрыть", "Close")] + public void Close() + { + if (_reader != null) + { + _reader.Close(); + _reader = null; + } + } + + /// + /// + /// Открывает JSON-файл для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанного файла. + /// + /// + /// + /// Имя файла, содержащего текст JSON. + /// + /// Позволяет задать кодировку входного файла. + [ContextMethod("ОткрытьФайл", "OpenFile")] + public void OpenFile(string JSONFileName, IValue encoding = null) + { + if (IsOpen()) + Close(); + + StreamReader _fileReader; + + try + { + if (encoding != null) + _fileReader = FileOpener.OpenReader(JSONFileName, TextEncodingEnum.GetEncoding(encoding)); + else + _fileReader = FileOpener.OpenReader(JSONFileName, System.Text.Encoding.UTF8); + } + catch (Exception e) + { + throw new RuntimeException(e.Message, e); + } + + _reader = new JsonReaderInternal(_fileReader) + { + SupportMultipleContent = true + }; + } + + /// + /// Если текущее значение – начало массива или объекта, то пропускает его содержимое и конец. + /// Для остальных типов значений работает аналогично методу Прочитать(). + /// + /// + [ContextMethod("Пропустить", "Skip")] + public bool Skip() + { + CheckIfOpen(); + + if (_reader.TokenType == JsonToken.StartArray || _reader.TokenType == JsonToken.StartObject) + { + _reader.Skip(); + } + + return _reader.Read(); + } + + /// + /// Выполняет чтение значения JSON. + /// + /// + [ContextMethod("Прочитать", "Read")] + public bool Read() + { + CheckIfOpen(); + return _reader.Read(); + + } + + /// + /// + /// Устанавливает строку, содержащую текст JSON для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанной строки. + /// + /// + /// + /// Строка, содержащая текст в формате JSON. + /// + /// + [ContextMethod("УстановитьСтроку", "SetString")] + public void SetString(string JSONString) + { + if (IsOpen()) + Close(); + + _reader = new JsonReaderInternal(new StringReader(JSONString)) + { + SupportMultipleContent = true + }; + } + + } + + public class JSONReaderException : RuntimeException + { + public JSONReaderException(string message) : base(message) + { + } + + public static JSONReaderException NotOpen() + { + return new JSONReaderException(Locale.NStr + ("ru='Источник данных JSON не открыт'; en='JSON data source is not opened'")); + } + + public static JSONReaderException CannotGetValue() + { + return new JSONReaderException(Locale.NStr + ("ru='Текущее значение JSON не может быть получено';en='Cannot get current JSON value'")); + } + + public static JSONReaderException UnexpectedSymbol() + { + return new JSONReaderException(Locale.NStr + ("ru='Непредвиденный символ при чтении JSON';en='Unexpected symbol during JSON reading'")); + } + } +} diff --git a/src/OneScript.StandardLibrary/Json/JSONValueTypeEnum.cs b/src/OneScript.StandardLibrary/Json/JSONValueTypeEnum.cs new file mode 100644 index 000000000..068cc543d --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/JSONValueTypeEnum.cs @@ -0,0 +1,50 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Json +{ + [EnumerationType("ТипЗначенияJSON", "JSONValueType", + TypeUUID = "90EC0979-11CF-4688-972B-3530FA16EEF5", + ValueTypeUUID = "D6089B59-1730-452C-B142-C2EF6C75299C")] + public enum JSONValueTypeEnum + { + [EnumValue("Ничего", "None")] + None, + + [EnumValue("Null")] + Null, + + [EnumValue("Булево", "Boolean")] + Boolean, + + [EnumValue("ИмяСвойства", "PropertyName")] + PropertyName, + + [EnumValue("Комментарий", "Comment")] + Comment, + + [EnumValue("КонецМассива", "ArrayEnd")] + ArrayEnd, + + [EnumValue("КонецОбъекта", "ObjectEnd")] + ObjectEnd, + + [EnumValue("НачалоМассива", "ArrayStart")] + ArrayStart, + + [EnumValue("НачалоОбъекта", "ObjectStart")] + ObjectStart, + + [EnumValue("Строка", "String")] + String, + + [EnumValue("Число", "Number")] + Number + } +} diff --git a/src/OneScript.StandardLibrary/Json/JSONWriter.cs b/src/OneScript.StandardLibrary/Json/JSONWriter.cs new file mode 100644 index 000000000..a31842fc2 --- /dev/null +++ b/src/OneScript.StandardLibrary/Json/JSONWriter.cs @@ -0,0 +1,502 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Text; +using System.Threading; +using Newtonsoft.Json; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Text; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Json +{ + /// + /// + /// Предназначен для организации последовательной записи объектов и текстов JSON. + /// + [ContextClass("ЗаписьJSON", "JSONWriter")] + public class JSONWriter : AutoContext + { + private const int INDENT_SIZE = 0; + + private JSONWriterSettings _settings; + private JsonTextWriter _writer; // Объект из библиотеки Newtonsoft для работы с форматом JSON + + StringWriter _stringWriter; + + public JSONWriter() + { + + } + + private bool _escapeNonAscii; + + /// + /// + /// Возвращает true если для объекта чтения json был задан текст для парсинга. + /// + private bool IsOpen() + { + return _writer != null; + } + + /// + /// + /// Возвращает true если для объекта чтения json был задан текст для парсинга. + /// + private bool IsOpenForString() + { + return _stringWriter != null; + } + + private void SetDefaultOptions() + { + _writer.Indentation = INDENT_SIZE; + _writer.Formatting = Formatting.Indented; + _settings = new JSONWriterSettings(); + _escapeNonAscii = false; + } + + private void SetOptions(JSONWriterSettings settings) + { + _settings = settings; + if (_settings.UseDoubleQuotes) + _writer.QuoteChar = '\"'; + else { + _writer.QuoteChar = '\''; + } + + _writer.IndentChar = !string.IsNullOrEmpty(_settings.PaddingSymbols) ? _settings.PaddingSymbols[0] : ' '; + _writer.Indentation = !string.IsNullOrEmpty(_settings.PaddingSymbols) ? 1 : INDENT_SIZE; + _writer.Formatting = Formatting.Indented; + + if (_settings.EscapeCharacters != JSONCharactersEscapeModeEnum.None) + { + var jsonCharactersEscapeMode = _settings.EscapeCharacters; + if (jsonCharactersEscapeMode == JSONCharactersEscapeModeEnum.NotASCIISymbols) + { + _escapeNonAscii = true; + _writer.QuoteChar = '\"'; + _writer.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii; + } + else if (jsonCharactersEscapeMode == JSONCharactersEscapeModeEnum.SymbolsNotInBMP) + throw new NotImplementedException(); + } + } + + void WriteStringValue(string val) + { + if (_settings.EscapeCharacters != JSONCharactersEscapeModeEnum.None && _escapeNonAscii) + { + StringWriter wr = new StringWriter(); + var jsonWriter = new JsonTextWriter(wr); + jsonWriter.QuoteChar = '\"'; + jsonWriter.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii; + new JsonSerializer().Serialize(jsonWriter, val); + string str = wr.ToString(); + _writer.WriteRawValue(EscapeCharacters(str.Substring(1, str.Length - 2), false)); + + } + else + _writer.WriteRawValue(EscapeCharacters(val, _settings.EscapeSlash)); + } + + string EscapeCharacters(string sval, bool EscapeSlash) + { + var sb = new StringBuilder(); + + int length = sval.Length; + int start = 0; + + for (var i = 0; i < length; i++) + { + char c = sval[i]; + string? escapedValue = null; + + if (EscapeSlash && c == '/') + { + escapedValue = "\\/"; + } + else if (_settings.EscapeAmpersand && c == '&') + { + escapedValue = "\\&"; + } + else if ((_settings.EscapeSingleQuotes || !_settings.UseDoubleQuotes) && c == '\'') + { + escapedValue = "\\u0027"; + } + else if (_settings.EscapeAngleBrackets && c == '<') + { + escapedValue = "\\u003C"; + } + else if (_settings.EscapeAngleBrackets && c == '>') + { + escapedValue = "\\u003E"; + } + else if (c == '\r') + { + escapedValue = "\\r"; + } + else if (c == '\n') + { + escapedValue = "\\n"; + } + else if (c == '\f') + { + escapedValue = "\\f"; + } + else if (c == '\"') + { + escapedValue = "\\\""; + } + else if (c == '\b') + { + escapedValue = "\\b"; + } + else if (c == '\t') + { + escapedValue = "\\t"; + } + else if (c == '\\') + { + escapedValue = "\\\\"; + } + + // Спец. символы: \u0000, \u0001, \u0002, ... , \u001e, \u001f; + else if ((int)c >= 0 && (int)c <= 31) + { + escapedValue = "\\u" + ((int)c).ToString("x4"); + } + + if (escapedValue != null) + { + sb.Append(sval, start, i - start); + sb.Append(escapedValue); + start = i + 1; + } + } + + sb.Insert(0, _writer.QuoteChar); + sb.Append(sval, start, length - start); + sb.Append(_writer.QuoteChar); + return sb.ToString(); + } + + void SetNewLineChars(TextWriter textWriter) + { + if (_settings != null) + { + switch (_settings.NewLines) + { + case JSONLineBreakEnum.Unix: + textWriter.NewLine = "\n"; + break; + case JSONLineBreakEnum.Windows: + textWriter.NewLine = "\r\n"; + break; + case JSONLineBreakEnum.Auto when Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX: + textWriter.NewLine = "\n"; + break; + case JSONLineBreakEnum.Auto: + textWriter.NewLine = "\r\n"; + break; + default: + textWriter.NewLine = ""; //Нет + _writer.Formatting = Formatting.None; + break; + } + } + } + [ScriptConstructor] + public static JSONWriter Constructor() + { + return new JSONWriter(); + } + + /// + /// + /// Определяет текущие параметры записи JSON. + /// + /// ПараметрыЗаписиJSON (JSONWriterSettings) + [ContextProperty("Параметры", "Settings")] + public IValue Settings + { + get { throw new NotImplementedException(); } + + } + + + /// + /// + /// Показывает, будет ли проводиться проверка правильности структуры записываемого JSON объекта. В случае обнаружение ошибки, будет сгенерировано исключение. Например: при попытке записать значение без имени вне массива или записать окончание объекта без начала. Установка данного свойства не имеет немедленного эффекта. Установленное значение свойства будет использовано только после открытия файла или установки строки. + /// После создания объекта данное свойство имеет значение Истина. + /// + /// Булево (Boolean) + [ContextProperty("ПроверятьСтруктуру", "ValidateStructure")] + public bool ValidateStructure + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + + /// + /// + /// Завершает запись текста JSON. Если производилась запись в файл, то файл закрывается. + /// Если производилась запись в строку, то результирующая строка будет получена в качестве возвращаемого значения метода. + /// Если производилась запись в файл, то метод вернет пустую строку. + /// + /// + /// + /// Значения данного типа содержат строку в формате Unicode произвольной длины. + [ContextMethod("Закрыть", "Close")] + public string Close() + { + string res = ""; + + if (IsOpenForString()) + { + res = _stringWriter.ToString(); + _stringWriter.Close(); + _stringWriter = null; + } + + if (_writer != null) + { + _writer.Close(); + _writer = null; + } + + return res; + } + + + /// + /// + /// Выполняет запись произвольной строки в документ, при этом проверка структуры документа не выполняется. + /// Если при использовании метода свойство ПроверятьСтруктуру установлено в значение Истина, то проверка структуры продолжается на следующем элементе. + /// + /// + /// + /// Строка, записываемая в документ JSON. + [ContextMethod("ЗаписатьБезОбработки", "WriteRaw")] + public void WriteRaw(string stringValue) + { + if (!IsOpen()) + throw NotOpenException(); + + _writer.WriteRaw(stringValue); + } + + + /// + /// + /// Записывает значение свойства JSON. + /// + /// + /// + /// Записываемое значение. Типы: Строка (String), Число (Number), Булево (Boolean), Неопределено (Undefined) + /// + /// Использование экспоненциальной формы записи для числовых значений. Параметр имеет смысл только если записывается значение числового типа. + /// Значение по умолчанию: Ложь. + [ContextMethod("ЗаписатьЗначение", "WriteValue")] + public void WriteValue(IValue value, bool useFormatWithExponent = false) + { + if (!IsOpen()) + throw NotOpenException(); + + var clrValue = value.UnwrapToClrObject(); + switch (clrValue) + { + case string v: + WriteStringValue(v); + break; + case decimal v: + if (v == Math.Round(v)) + { + Int64 i = Convert.ToInt64(v); + if (useFormatWithExponent) + _writer.WriteRawValue(string.Format(Thread.CurrentThread.CurrentCulture, "{0:E}", i)); + else + _writer.WriteValue(i); + } + else + { + if (useFormatWithExponent) + _writer.WriteRawValue(string.Format(string.Format(Thread.CurrentThread.CurrentCulture, "{0:E}", v))); + else + _writer.WriteValue(v); + } + break; + case bool v: + _writer.WriteValue(v); + break; + case DateTime v: + _writer.WriteValue(v); + break; + + case null: + _writer.WriteNull(); + break; + + default: + throw TypeNotSupportedException(value?.GetType()); + } + } + + /// + /// + /// Записывает имя свойства JSON. + /// + /// + /// + /// Имя свойства. + [ContextMethod("ЗаписатьИмяСвойства", "WritePropertyName")] + public void WritePropertyName(string propertyName) + { + _writer.WritePropertyName(propertyName); + } + + + /// + /// + /// Записывает конец массива JSON. + /// + /// + [ContextMethod("ЗаписатьКонецМассива", "WriteEndArray")] + public void WriteEndArray() + { + _writer.WriteEndArray(); + } + + + /// + /// + /// Записывает конец объекта JSON. + /// + /// + [ContextMethod("ЗаписатьКонецОбъекта", "WriteEndObject")] + public void WriteEndObject() + { + _writer.WriteEndObject(); + } + + + /// + /// + /// Записывает начало массива JSON. + /// + /// + [ContextMethod("ЗаписатьНачалоМассива", "WriteStartArray")] + public void WriteStartArray() + { + _writer.WriteStartArray(); + } + + + /// + /// + /// Записывает начало объекта JSON. + /// + /// + [ContextMethod("ЗаписатьНачалоОбъекта", "WriteStartObject")] + public void WriteStartObject() + { + _writer.WriteStartObject(); + } + + + /// + /// + /// Открывает файл для записи JSON. Позволяет указать тип кодировки, который будет использован для записи файла JSON, а также использование BOM. + /// + /// + /// + /// Имя файла, в который будет записываться текст JSON. + /// + /// В качестве типа кодировки может быть указана одна из возможных кодировок текста. В этом случае файл будет записан в соответствующей кодировке. Если же в качестве параметра указана пустая строка или ничего не указано, то для записи файла будет использована кодировка UTF8. + /// Поддерживаемые коды кодировок: + /// + /// Значение по умолчанию: UTF-8. + /// + /// Определяет, будет ли добавлен маркер порядка байт (BOM) к результирующему файлу JSON. + /// Внимание. Стандарт RFC7159 настоятельно рекомендует не добавлять маркер порядка байт (BOM) к документу JSON . + /// Значение по умолчанию: Ложь. + /// + /// Параметры, используемые при открытии файла для настройки записи в формате JSON. + [ContextMethod("ОткрытьФайл", "OpenFile")] + public void OpenFile(string fileName, string encoding = null, bool addBOM = false, JSONWriterSettings settings = null) + { + StreamWriter streamWriter; + + try + { + if (encoding != null) + streamWriter = FileOpener.OpenWriter(fileName, TextEncodingEnum.GetEncodingByName(encoding, addBOM)); + else + streamWriter = FileOpener.OpenWriter(fileName, TextEncodingEnum.GetEncodingByName("UTF-8", addBOM)); + } + catch (Exception e) + { + throw new RuntimeException(e.Message, e); + } + + if (IsOpen()) + Close(); + + _writer = new JsonTextWriter(streamWriter); + if (settings == null) + SetDefaultOptions(); + else + SetOptions(settings); + + SetNewLineChars(streamWriter); + } + + + /// + /// + /// Инициализирует объект для вывода результирующего JSON текста в строку. + /// + /// + /// + /// Параметры, используемые при записи объекта JSON. + /// По умолчанию, содержит ПараметрыЗаписиJSON, сгенерированные автоматически. + [ContextMethod("УстановитьСтроку", "SetString")] + public void SetString(JSONWriterSettings settings = null) + { + if (IsOpen()) + Close(); + _stringWriter = new StringWriter(); + _writer = new JsonTextWriter(_stringWriter); + if (settings == null) + SetDefaultOptions(); + else + SetOptions(settings); + + SetNewLineChars(_stringWriter); + } + + RuntimeException NotOpenException() + { + return new RuntimeException(Locale.NStr + ("ru='Приемник данных JSON не открыт';en='JSON data target is not opened'")); + } + + RuntimeException TypeNotSupportedException(Type type) + { + return new RuntimeException(Locale.NStr + ($"ru='Запись значения типа {type} не поддерживается.'; en='Can not write value of type {type}'")); + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Json/JSONWriterSettings.cs b/src/OneScript.StandardLibrary/Json/JSONWriterSettings.cs similarity index 78% rename from src/ScriptEngine.HostedScript/Library/Json/JSONWriterSettings.cs rename to src/OneScript.StandardLibrary/Json/JSONWriterSettings.cs index c3bd6be31..ba7825f2d 100644 --- a/src/ScriptEngine.HostedScript/Library/Json/JSONWriterSettings.cs +++ b/src/OneScript.StandardLibrary/Json/JSONWriterSettings.cs @@ -1,240 +1,213 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Json -{ - /// - /// - /// Определяет набор параметров, используемых при записи JSON. - /// - [ContextClass("ПараметрыЗаписиJSON", "JSONWriterSettings")] - public class JSONWriterSettings : AutoContext - { - - private bool _useDoubleQuotes; - - private IValue _newLines; - - private string _paddingSymbols; - - private IValue _escapeCharacters; - - private bool _escapeAmpersand; - - private bool _escapeSingleQuotes; - - private bool _escapeLineTerminators; - - private bool _escapeSlash; - - private bool _escapeAngleBrackets; - - public JSONWriterSettings(IValue NewLines = null, string PaddingSymbols = null, bool UseDoubleQuotes = true, IValue EscapeCharacters = null, bool EscapeAngleBrackets = false, bool EscapeLineTerminators = true, bool EscapeAmpersand = false, bool EscapeSingleQuotes = false, bool EscapeSlash = false) - { - _newLines = NewLines; - _paddingSymbols = PaddingSymbols; - _useDoubleQuotes = UseDoubleQuotes; - _escapeCharacters = EscapeCharacters; - _escapeAngleBrackets = EscapeAngleBrackets; - _escapeLineTerminators = EscapeLineTerminators; - _escapeAmpersand = EscapeAmpersand; - _escapeSingleQuotes = EscapeSingleQuotes; - _escapeSlash = EscapeSlash; - } - - - /// - /// - /// Создает объект параметров записи JSON. - /// - /// - /// - /// Определяет способ переноса строк, который будет использован при записи данных JSON. - /// Значение по умолчанию: Авто. - /// - /// Определяет символы отступа, используемые при записи данных JSON. - /// Применяется только, если значение ПереносСтрокJSON отлично от Нет. - /// Значение по умолчанию: " ". - /// - /// Определяет, будут ли при записи имена свойств JSON записываться в двойных кавычках. - /// Значение по умолчанию: Истина. - /// - /// Определяет используемый способ экранирования (замены) символов при записи данных JSON. - /// Значение по умолчанию: Нет. - /// - /// Определяет, будут ли при записи экранироваться символы '<' и '>'. - /// Значение по умолчанию: Ложь. - /// - /// Определяет, будут ли экранироваться разделители строк - U+2028 (line-separator) и U+2029 (page-separator). - /// Значение по умолчанию: Истина. - /// - /// Определяет, будет ли при записи экранироваться символ амперсанда '&'. - /// Значение по умолчанию: Ложь. - /// - /// Определяет, будут ли экранироваться одинарные кавычки. - /// Устанавливается в значение Истина, если ИспользоватьДвойныеКавычки установлено в Ложь. - /// Значение по умолчанию: Ложь. - /// - /// Определяет, будет ли экранироваться слеш (косая черта) при записи значения. - /// Значение по умолчанию: Ложь. - [ScriptConstructor(Name = "По описанию параметров записи")] - public static JSONWriterSettings Constructor(IValue newLines = null, IValue paddingSymbols = null, IValue useDoubleQuotes = null, IValue escapeCharacters = null, IValue escapeAngleBrackets = null, IValue escapeLineTerminators = null, IValue escapeAmpersand = null, IValue escapeSingleQuotes = null, IValue escapeSlash = null) - { - return new JSONWriterSettings(newLines, - (paddingSymbols == null ? null : paddingSymbols.AsString()), - (useDoubleQuotes == null? true: useDoubleQuotes.AsBoolean()), - escapeCharacters, - (escapeAngleBrackets == null ? false : escapeAngleBrackets.AsBoolean()), - (escapeLineTerminators == null ? true : escapeLineTerminators.AsBoolean()), - (escapeAmpersand == null ? false : escapeAmpersand.AsBoolean()), - (escapeSingleQuotes == null ? false : escapeSingleQuotes.AsBoolean()), - (escapeSlash == null ? false : escapeSlash.AsBoolean())); - } - - /// - /// - /// - /// - /// - [ScriptConstructor] - public static JSONWriterSettings Constructor() - { - return new JSONWriterSettings(); - } - - /// - /// - /// Определяет использование двойных кавычек при записи свойств и значений JSON. - /// После создания объекта данное свойство имеет значение Истина. - /// - /// Булево (Boolean) - [ContextProperty("ИспользоватьДвойныеКавычки", "UseDoubleQuotes")] - public bool UseDoubleQuotes - { - get { return _useDoubleQuotes; } - - } - - - /// - /// - /// Управляет размещением начала и конца объектов и массивов, ключей и значений на новой строке. - /// Также установка данного свойства в значение, отличное от Нет, добавляет пробел между именем свойства, двоеточием и значением. - /// После создания объекта данное свойство имеет значение Авто. - /// - /// ПереносСтрокJSON (JSONLineBreak) - [ContextProperty("ПереносСтрок", "NewLines")] - public IValue NewLines - { - get { return _newLines; } - - } - - - /// - /// - /// Определяет символы отступа, используемые при записи документа JSON. - /// Свойство не используется, если свойство ПереносСтрокJSON установлено в значение Нет. - /// После создания объекта данное свойство имеет значение - один пробельный символ. - /// - /// Строка (String) - [ContextProperty("СимволыОтступа", "PaddingSymbols")] - public string PaddingSymbols - { - get { return _paddingSymbols; } - - } - - - /// - /// - /// Определяет способ экранирования символов при записи документа JSON. - /// После создания объекта данное свойство имеет значение СимволыВнеASCII. - /// - /// ЭкранированиеСимволовJSON (JSONCharactersEscapeMode) - [ContextProperty("ЭкранированиеСимволов", "EscapeCharacters")] - public IValue EscapeCharacters - { - get { return _escapeCharacters; } - - } - - - /// - /// - /// Определяет, будет ли экранироваться знак амперсанда при записи документа JSON. - /// После создания объекта данное свойство имеет значение Ложь. - /// - /// Булево (Boolean) - [ContextProperty("ЭкранироватьАмперсанд", "EscapeAmpersand")] - public bool EscapeAmpersand - { - get { return _escapeAmpersand; } - - } - - - /// - /// - /// Определяет, будет ли экранирован знак одинарной кавычки при записи документа JSON. - /// Имеет значение Истина, если ИспользоватьДвойныеКавычки установлен Ложь. - /// После создания объекта данное свойство имеет значение Ложь - /// - /// Булево (Boolean) - [ContextProperty("ЭкранироватьОдинарныеКавычки", "EscapeSingleQuotes")] - public bool EscapeSingleQuotes - { - get { return _escapeSingleQuotes; } - } - - - /// - /// - /// Определяет экранирование символов "U+2028" (разделитель строк) и "U+2029" (разделитель абзацев) для совместимости с JavaScript. - /// После создания объекта данное свойство имеет значение Истина. - /// - /// Булево (Boolean) - [ContextProperty("ЭкранироватьРазделителиСтрок", "EscapeLineTerminators")] - public bool EscapeLineTerminators - { - get { return _escapeLineTerminators; } - - } - - - /// - /// - /// Определяет, будет ли экранироваться слеш (косая черта) при записи значения. - /// После создания объекта данное свойство имеет значение Ложь. - /// - /// Булево (Boolean) - [ContextProperty("ЭкранироватьСлеш", "EscapeSlash")] - public bool EscapeSlash - { - get { return _escapeSlash; } - - } - - - /// - /// - /// Определяет, будут ли экранированы знаки угловых скобок при записи документа JSON. - /// После создания объекта данное свойство имеет значение Ложь. - /// - /// Булево (Boolean) - [ContextProperty("ЭкранироватьУгловыеСкобки", "EscapeAngleBrackets")] - public bool EscapeAngleBrackets - { - get { return _escapeAngleBrackets; } - - } - - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Json +{ + /// + /// + /// Определяет набор параметров, используемых при записи JSON. + /// + [ContextClass("ПараметрыЗаписиJSON", "JSONWriterSettings")] + public class JSONWriterSettings : AutoContext + { + + private bool _useDoubleQuotes; + + private JSONLineBreakEnum _newLines; + + private string _paddingSymbols; + + private JSONCharactersEscapeModeEnum _escapeCharacters; + + private bool _escapeAmpersand; + + private bool _escapeSingleQuotes; + + private bool _escapeLineTerminators; + + private bool _escapeSlash; + + private bool _escapeAngleBrackets; + + public JSONWriterSettings(JSONLineBreakEnum NewLines = JSONLineBreakEnum.Auto, string PaddingSymbols = null, bool UseDoubleQuotes = true, JSONCharactersEscapeModeEnum EscapeCharacters = JSONCharactersEscapeModeEnum.None, bool EscapeAngleBrackets = false, bool EscapeLineTerminators = true, bool EscapeAmpersand = false, bool EscapeSingleQuotes = false, bool EscapeSlash = false) + { + _newLines = NewLines; + _paddingSymbols = PaddingSymbols; + _useDoubleQuotes = UseDoubleQuotes; + _escapeCharacters = EscapeCharacters; + _escapeAngleBrackets = EscapeAngleBrackets; + _escapeLineTerminators = EscapeLineTerminators; + _escapeAmpersand = EscapeAmpersand; + _escapeSingleQuotes = EscapeSingleQuotes; + _escapeSlash = EscapeSlash; + } + + + /// + /// + /// Создает объект параметров записи JSON. + /// + /// + /// + /// Определяет способ переноса строк, который будет использован при записи данных JSON. + /// Значение по умолчанию: Авто. + /// + /// Определяет символы отступа, используемые при записи данных JSON. + /// Применяется только, если значение ПереносСтрокJSON отлично от Нет. + /// Значение по умолчанию: " ". + /// + /// Определяет, будут ли при записи имена свойств JSON записываться в двойных кавычках. + /// Значение по умолчанию: Истина. + /// + /// Определяет используемый способ экранирования (замены) символов при записи данных JSON. + /// Значение по умолчанию: Нет. + /// + /// Определяет, будут ли при записи экранироваться символы '<' и '>'. + /// Значение по умолчанию: Ложь. + /// + /// Определяет, будут ли экранироваться разделители строк - U+2028 (line-separator) и U+2029 (page-separator). + /// Значение по умолчанию: Истина. + /// + /// Определяет, будет ли при записи экранироваться символ амперсанда '&'. + /// Значение по умолчанию: Ложь. + /// + /// Определяет, будут ли экранироваться одинарные кавычки. + /// Устанавливается в значение Истина, если ИспользоватьДвойныеКавычки установлено в Ложь. + /// Значение по умолчанию: Ложь. + /// + /// Определяет, будет ли экранироваться слеш (косая черта) при записи значения. + /// Значение по умолчанию: Ложь. + [ScriptConstructor(Name = "По описанию параметров записи")] + public static JSONWriterSettings ParametrizedConstructor( + JSONLineBreakEnum newLines = JSONLineBreakEnum.None, + string paddingSymbols = null, + bool useDoubleQuotes = true, + JSONCharactersEscapeModeEnum escapeCharacters = JSONCharactersEscapeModeEnum.None, + bool escapeAngleBrackets = false, + bool escapeLineTerminators = true, + bool escapeAmpersand = false, + bool escapeSingleQuotes = false, + bool escapeSlash = false) + { + return new JSONWriterSettings(newLines, + paddingSymbols, + useDoubleQuotes, + escapeCharacters, + escapeAngleBrackets, + escapeLineTerminators, + escapeAmpersand, + escapeSingleQuotes, + escapeSlash); + } + + /// + /// + /// + /// + /// + [ScriptConstructor] + public static JSONWriterSettings Constructor() + { + return new JSONWriterSettings(); + } + + /// + /// + /// Определяет использование двойных кавычек при записи свойств и значений JSON. + /// После создания объекта данное свойство имеет значение Истина. + /// + /// Булево (Boolean) + [ContextProperty("ИспользоватьДвойныеКавычки", "UseDoubleQuotes")] + public bool UseDoubleQuotes => _useDoubleQuotes; + + + /// + /// + /// Управляет размещением начала и конца объектов и массивов, ключей и значений на новой строке. + /// Также установка данного свойства в значение, отличное от Нет, добавляет пробел между именем свойства, двоеточием и значением. + /// После создания объекта данное свойство имеет значение Авто. + /// + /// ПереносСтрокJSON (JSONLineBreak) + [ContextProperty("ПереносСтрок", "NewLines")] + public JSONLineBreakEnum NewLines => _newLines; + + + /// + /// + /// Определяет символы отступа, используемые при записи документа JSON. + /// Свойство не используется, если свойство ПереносСтрокJSON установлено в значение Нет. + /// После создания объекта данное свойство имеет значение - один пробельный символ. + /// + /// Строка (String) + [ContextProperty("СимволыОтступа", "PaddingSymbols")] + public string PaddingSymbols => _paddingSymbols; + + + /// + /// + /// Определяет способ экранирования символов при записи документа JSON. + /// После создания объекта данное свойство имеет значение СимволыВнеASCII. + /// + /// ЭкранированиеСимволовJSON (JSONCharactersEscapeMode) + [ContextProperty("ЭкранированиеСимволов", "EscapeCharacters")] + public JSONCharactersEscapeModeEnum EscapeCharacters => _escapeCharacters; + + + /// + /// + /// Определяет, будет ли экранироваться знак амперсанда при записи документа JSON. + /// После создания объекта данное свойство имеет значение Ложь. + /// + /// Булево (Boolean) + [ContextProperty("ЭкранироватьАмперсанд", "EscapeAmpersand")] + public bool EscapeAmpersand => _escapeAmpersand; + + + /// + /// + /// Определяет, будет ли экранирован знак одинарной кавычки при записи документа JSON. + /// Имеет значение Истина, если ИспользоватьДвойныеКавычки установлен Ложь. + /// После создания объекта данное свойство имеет значение Ложь + /// + /// Булево (Boolean) + [ContextProperty("ЭкранироватьОдинарныеКавычки", "EscapeSingleQuotes")] + public bool EscapeSingleQuotes => _escapeSingleQuotes; + + + /// + /// + /// Определяет экранирование символов "U+2028" (разделитель строк) и "U+2029" (разделитель абзацев) для совместимости с JavaScript. + /// После создания объекта данное свойство имеет значение Истина. + /// + /// Булево (Boolean) + [ContextProperty("ЭкранироватьРазделителиСтрок", "EscapeLineTerminators")] + public bool EscapeLineTerminators => _escapeLineTerminators; + + + /// + /// + /// Определяет, будет ли экранироваться слеш (косая черта) при записи значения. + /// После создания объекта данное свойство имеет значение Ложь. + /// + /// Булево (Boolean) + [ContextProperty("ЭкранироватьСлеш", "EscapeSlash")] + public bool EscapeSlash => _escapeSlash; + + + /// + /// + /// Определяет, будут ли экранированы знаки угловых скобок при записи документа JSON. + /// После создания объекта данное свойство имеет значение Ложь. + /// + /// Булево (Boolean) + [ContextProperty("ЭкранироватьУгловыеСкобки", "EscapeAngleBrackets")] + public bool EscapeAngleBrackets => _escapeAngleBrackets; + } +} diff --git a/src/OneScript.StandardLibrary/MessageStatusEnum.cs b/src/OneScript.StandardLibrary/MessageStatusEnum.cs new file mode 100644 index 000000000..1d530f830 --- /dev/null +++ b/src/OneScript.StandardLibrary/MessageStatusEnum.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary +{ + [EnumerationType("СтатусСообщения", "MessageStatus")] + public enum MessageStatusEnum + { + [EnumValue("БезСтатуса", "WithoutStatus")] + WithoutStatus, + + [EnumValue("Важное", "Important")] + Important, + + [EnumValue("Внимание", "Attention")] + Attention, + + [EnumValue("Информация", "Information")] + Information, + + [EnumValue("Обычное", "Ordinary")] + Ordinary, + + [EnumValue("ОченьВажное", "VeryImportant")] + VeryImportant + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/MiscGlobalFunctions.cs b/src/OneScript.StandardLibrary/MiscGlobalFunctions.cs similarity index 75% rename from src/ScriptEngine.HostedScript/Library/MiscGlobalFunctions.cs rename to src/OneScript.StandardLibrary/MiscGlobalFunctions.cs index d9ae26b63..ab8c6afa9 100644 --- a/src/ScriptEngine.HostedScript/Library/MiscGlobalFunctions.cs +++ b/src/OneScript.StandardLibrary/MiscGlobalFunctions.cs @@ -4,14 +4,17 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine.Contexts; + using System; using System.Text; - -using ScriptEngine.HostedScript.Library.Binary; +using OneScript.Contexts; +using OneScript.Contexts.Enums; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Text; using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary { [GlobalContext(Category="Прочие функции")] public class MiscGlobalFunctions : GlobalContextBase @@ -36,16 +39,15 @@ public BinaryDataContext Base64Value(string data) /// /// [ContextMethod("КодироватьСтроку", "EncodeString")] - public string EncodeString(string sourceString, SelfAwareEnumValue codeType, IValue encoding = null) + public string EncodeString(string sourceString, StringEncodingMethod encMethod, IValue encoding = null) { - var encMethod = GlobalsManager.GetEnum(); Encoding enc; if (encoding != null) enc = TextEncodingEnum.GetEncoding(encoding); else enc = Encoding.UTF8; - if (codeType == encMethod.URLEncoding) + if (encMethod == StringEncodingMethod.UrlEncoding) return EncodeStringImpl(sourceString, enc, false); else return EncodeStringImpl(sourceString, enc, true); @@ -128,7 +130,7 @@ private bool IsUnreservedSymbol(byte symbolByte) /// /// [ContextMethod("РаскодироватьСтроку", "DecodeString")] - public string DecodeString(string encodedString, SelfAwareEnumValue codeType, IValue encoding = null) + public string DecodeString(string encodedString, StringEncodingMethod codeType, IValue encoding = null) { if (encoding != null) throw new NotSupportedException("Явное указание кодировки в данной версии не поддерживается (только utf-8 согласно RFC 3986)"); @@ -143,38 +145,12 @@ public static MiscGlobalFunctions CreateInstance() } } - [SystemEnum("СпособКодированияСтроки", "StringEncodingMethod")] - public class StringEncodingMethodEnum : EnumerationContext + [EnumerationType("СпособКодированияСтроки", "StringEncodingMethod")] + public enum StringEncodingMethod { - private const string EV_SIMPLE = "КодировкаURL"; - private const string EV_URL = "URLВКодировкеURL"; - - public StringEncodingMethodEnum(TypeDescriptor enumType, TypeDescriptor valuesType) : base(enumType, valuesType) - { - - } - - [EnumValue(EV_SIMPLE, "URLEncoding")] - public EnumerationValue URLEncoding - { - get - { - return this[EV_SIMPLE]; - } - } - - [EnumValue(EV_URL, "URLInURLEncoding")] - public EnumerationValue URLInURLEncoding - { - get - { - return this[EV_URL]; - } - } - - public static StringEncodingMethodEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new StringEncodingMethodEnum(t, v)); - } + [EnumValue("КодировкаURL", "URLEncoding")] + UrlEncoding, + [EnumValue("URLВКодировкеURL", "URLInURLEncoding")] + UrlInUrlEncoding } } diff --git a/src/OneScript.StandardLibrary/Native/CompiledBlock.cs b/src/OneScript.StandardLibrary/Native/CompiledBlock.cs new file mode 100644 index 000000000..52707ec04 --- /dev/null +++ b/src/OneScript.StandardLibrary/Native/CompiledBlock.cs @@ -0,0 +1,244 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using OneScript.Commons; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Native.Compiler; +using OneScript.Native.Runtime; +using OneScript.Sources; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Native +{ + [ContextClass("СкомпилированныйФрагмент", "CompiledCodeBlock")] + public class CompiledBlock : AutoContext + { + private readonly IServiceContainer _services; + private string _codeBlock; + private BslSyntaxNode _ast; + private IErrorSink _errors; + private SourceCode _codeLinesReferences; + + public CompiledBlock(IServiceContainer services) + { + _services = services; + } + + public SymbolTable Symbols { get; set; } + + [ContextProperty("Параметры", "Parameters")] + public StructureImpl Parameters { get; set; } = new StructureImpl(); + + [ContextProperty("ФрагментКода", "CodeFragment")] + public string CodeBlock + { + get => _codeBlock ?? string.Empty; + set + { + _codeBlock = value; + ParseCode(); + } + } + + private void ParseCode() + { + var lexer = new DefaultLexer(); + var source = SourceCodeBuilder.Create() + .FromString(CodeBlock) + .WithName($"Compiled source {CodeBlock.GetHashCode():X8}") + .Build(); + + lexer.Iterator = source.CreateIterator(); + + _codeLinesReferences = source; + _errors = new ListErrorSink(); + var parser = new DefaultBslParser(lexer, _errors, new PreprocessorHandlers()); + + try + { + _ast = parser.ParseCodeBatch(true); + } + catch (ScriptException e) + { + _errors.AddError(new CodeError + { + Description = e.Message, + Position = e.GetPosition() + }); + } + + if (_errors.HasErrors) + { + var prefix = Locale.NStr("ru = 'Ошибка комиляции модуля'; en = 'Module compilation error'"); + var text = string.Join('\n', (new[] {prefix}).Concat(_errors.Errors.Select(x => x.ToString(CodeError.ErrorDetails.Position)))); + throw new RuntimeException(text); + } + } + + [ContextMethod("Скомпилировать", "Compile")] + public DelegateAction Compile() + { + var method = CreateDelegate(); + return new DelegateAction(method); + } + + public Func CreateDelegate() + { + var l = MakeExpression(); + + var arrayOfValuesParam = Expression.Parameter(typeof(BslValue[])); + var convertedAccessList = new List(); + + convertedAccessList.Add(l.Parameters.First()); + int index = 0; + foreach (var parameter in Parameters) + { + var targetType = parameter.Value as BslTypeValue; + var arrayAccess = Expression.ArrayIndex(arrayOfValuesParam, Expression.Constant(index)); + var convertedParam = ExpressionHelpers.ConvertToType(arrayAccess, ConvertTypeToClrType(targetType)); + convertedAccessList.Add(convertedParam); + ++index; + } + + var lambdaInvocation = Expression.Invoke(l, convertedAccessList); + var func = Expression.Lambda>(lambdaInvocation, (ParameterExpression)convertedAccessList[0], arrayOfValuesParam); + + return func.Compile(); + } + + public T CreateDelegate() where T:class + { + var l = MakeExpression(); + + var methodInfo = typeof(T).GetMethod("Invoke") ?? throw new ArgumentException("T must be a delegate type"); + var firstParam = methodInfo.GetParameters().FirstOrDefault(); + var mockProcess = firstParam == null || firstParam.ParameterType != typeof(IBslProcess); + + IEnumerable invocationParameters; + IEnumerable delegateParameters; + + if (mockProcess) + { + var invocationParametersLst = new List(); + + delegateParameters = l.Parameters.Skip(1).ToList(); + invocationParametersLst.Add(Expression.Constant(ForbiddenBslProcess.Instance)); + invocationParametersLst.AddRange(delegateParameters); + invocationParameters = invocationParametersLst; + + } + else + { + invocationParameters = l.Parameters; + delegateParameters = l.Parameters; + } + + var call = Expression.Invoke(l, invocationParameters); + var func = Expression.Lambda(call, delegateParameters); + + return func.Compile(); + } + + public LambdaExpression MakeExpression() + { + if(_ast == default) + ParseCode(); + + var expression = ReduceAst(_ast); + + if (_errors.HasErrors) + { + var prefix = Locale.NStr("ru = 'Ошибка комиляции модуля'; en = 'Module compilation error'"); + var sb = new StringBuilder(); + sb.AppendLine(prefix); + foreach (var error in _errors.Errors) + { + sb.AppendLine(error.ToString(CodeError.ErrorDetails.Position)); + } + + throw new RuntimeException(sb.ToString()); + } + + return expression; + } + + private LambdaExpression ReduceAst(BslSyntaxNode ast) + { + // в параметрах лежат соответствия имени переменной и ее типа + // блок кода надо скомпилировтаь в лямбду с параметрами по количеству в коллекции Parameters и с типами параметров, как там + // пробежать по аст 1С и превратить в BlockExpression + + if (Symbols == null) + Symbols = new SymbolTable(); + + Symbols.PushObject(new StandardGlobalContext()); + + var methodInfo = CreateMethodInfo(); + var methodCompiler = new MethodCompiler(new BslWalkerContext + { + Errors = _errors, + Source = _codeLinesReferences, + Symbols = Symbols, + Services = _services + }, methodInfo); + + methodCompiler.CompileModuleBody(_ast.Children.FirstOrDefault(x => x.Kind == NodeKind.ModuleBody)); + return methodInfo.Implementation; + } + + private BslNativeMethodInfo CreateMethodInfo() + { + var methodInfo = new BslMethodInfoFactory(()=>new BslNativeMethodInfo()) + .NewMethod() + .Name("$__compiled") + .ReturnType(typeof(IValue)); + + foreach (var parameter in Parameters) + { + methodInfo.NewParameter() + .Name(parameter.Key.ToString()) + .ParameterType(ConvertTypeToClrType(parameter.Value as BslTypeValue)); + } + + return methodInfo.Build(); + } + + private static Type ConvertTypeToClrType(BslTypeValue typeVal) + { + var type = typeVal.TypeValue; + return GetClrType(type); + } + + private static Type GetClrType(TypeDescriptor type) + { + return CompilerHelpers.GetClrType(type); + } + + [ScriptConstructor] + public static CompiledBlock Create(TypeActivationContext context) + { + return new CompiledBlock(context.Services); + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/NativeApi/ComponentEventDelegates.cs b/src/OneScript.StandardLibrary/NativeApi/ComponentEventDelegates.cs new file mode 100644 index 000000000..f9e86db31 --- /dev/null +++ b/src/OneScript.StandardLibrary/NativeApi/ComponentEventDelegates.cs @@ -0,0 +1,15 @@ +/*---------------------------------------------------------- +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.NativeApi +{ + public delegate void OnComponentError(MessageStatusEnum status, int errCode, string source, string description); + + public delegate void OnComponentEvent(string source, string eventName, string data); + + public delegate void OnComponentStatusText(string text); +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiComponent.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiComponent.cs new file mode 100644 index 000000000..dc73f7edf --- /dev/null +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiComponent.cs @@ -0,0 +1,307 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.StandardLibrary.NativeApi +{ + /// + /// Экземпляр внешней компоненты Native API + /// + class NativeApiComponent : BslObjectValue, IRuntimeContextInstance, IDisposable + { + private IntPtr _object; + private TypeDescriptor _type; + + public event OnComponentEvent OnComponentEvent; + + public event OnComponentError OnComponentError; + + public event OnComponentStatusText OnComponentStatusText; + + private enum ErrorCodes + { + ADDIN_E_NONE = 1000, + ADDIN_E_ORDINARY = 1001, + ADDIN_E_ATTENTION = 1002, + ADDIN_E_IMPORTANT = 1003, + ADDIN_E_VERY_IMPORTANT = 1004, + ADDIN_E_INFO = 1005, + ADDIN_E_FAIL = 1006, + ADDIN_E_MSGBOX_ATTENTION = 1007, + ADDIN_E_MSGBOX_INFO = 1008, + ADDIN_E_MSGBOX_FAIL = 1009, + } + + private static string S(IntPtr ptr) + { + return NativeApiProxy.Str(ptr); + } + + private MessageStatusEnum Status(ushort wcode) + { + switch ((ErrorCodes)wcode) + { + case ErrorCodes.ADDIN_E_NONE: + return MessageStatusEnum.WithoutStatus; + case ErrorCodes.ADDIN_E_ORDINARY: + return MessageStatusEnum.Ordinary; + case ErrorCodes.ADDIN_E_IMPORTANT: + return MessageStatusEnum.Important; + case ErrorCodes.ADDIN_E_INFO: + return MessageStatusEnum.Information; + case ErrorCodes.ADDIN_E_VERY_IMPORTANT: + case ErrorCodes.ADDIN_E_FAIL: + return MessageStatusEnum.VeryImportant; + case ErrorCodes.ADDIN_E_ATTENTION: + case ErrorCodes.ADDIN_E_MSGBOX_ATTENTION: + case ErrorCodes.ADDIN_E_MSGBOX_INFO: + case ErrorCodes.ADDIN_E_MSGBOX_FAIL: + return MessageStatusEnum.Attention; + default: + return MessageStatusEnum.Ordinary; + } + } + + public NativeApiComponent(object host, NativeApiLibrary library, TypeDescriptor typeDef, string componentName) + { + if (!NativeApiProxy.IsAvailable) + throw new RuntimeException("Native API Proxy DLL is not loaded"); + + _object = NativeApiProxy.GetClassObject(library.Module, componentName, + (wcode, source, descr, scode) => + { + OnComponentError?.Invoke(Status(wcode), scode, S(source), S(descr)); + }, + (source, message, data) => { + OnComponentEvent?.Invoke(S(source), S(message), S(data)); + }, + (status) => { + OnComponentStatusText?.Invoke(S(status)); + } + ); + _type = typeDef; + } + + // ReSharper disable once ConvertToAutoProperty + public override TypeDescriptor SystemType => _type; + + public bool IsIndexed => true; + + public bool DynamicMethodSignatures => false; + + public IValue GetIndexedValue(IValue index) + { + if (index.SystemType != BasicTypes.String) + throw RuntimeException.InvalidArgumentType(); + + var propNum = GetPropertyNumber(index.ToString()); + return GetPropValue(propNum); + } + + public void SetIndexedValue(IValue index, IValue value) + { + if (index.SystemType != BasicTypes.String) + throw RuntimeException.InvalidArgumentType(); + + var propNum = GetPropertyNumber(index.ToString()); + SetPropValue(propNum, value); + } + + public int GetPropertyNumber(string name) + { + var propNumber = NativeApiProxy.FindProp(_object, name); + if (propNumber < 0) + throw PropertyAccessException.PropNotFoundException(name); + return propNumber; + } + + public bool IsPropReadable(int propNum) + { + return NativeApiProxy.IsPropReadable(_object, propNum); + } + + public bool IsPropWritable(int propNum) + { + return NativeApiProxy.IsPropWritable(_object, propNum); + } + + public int GetPropCount() + { + return NativeApiProxy.GetNProps(_object); + } + + public string GetPropName(int propNum) + { + var name = string.Empty; + NativeApiProxy.GetPropName(_object, propNum, 0, + n => name = NativeApiProxy.Str(n) + ); + return name; + } + + public IValue GetPropValue(int propNum) + { + IValue result = ValueFactory.Create(); + NativeApiProxy.GetPropVal(_object, propNum, + variant => result = NativeApiVariant.Value(variant) + ); + return result; + } + + public void SetPropValue(int propNum, IValue value) + { + using (var variant = new NativeApiVariant()) + { + variant.Assign(value); + NativeApiProxy.SetPropVal(_object, propNum, variant.Ptr); + }; + } + + public int GetMethodsCount() + { + return NativeApiProxy.GetNMethods(_object); + } + + public int GetMethodNumber(string name) + { + var methodNumber = NativeApiProxy.FindMethod(_object, name); + if (methodNumber < 0) + throw RuntimeException.MethodNotFoundException(name); + return methodNumber; + } + + public BslMethodInfo GetMethodInfo(int methodNumber) + { + var method = BslMethodBuilder.Create(); + if (methodNumber < 0) + throw new RuntimeException("Метод не найден"); + + NativeApiProxy.GetMethodName(_object, methodNumber, 0, + str => method.Name(NativeApiProxy.Str(str)) + ); + + NativeApiProxy.GetMethodName(_object, methodNumber, 1, + str => method.Alias(NativeApiProxy.Str(str)) + ); + + var paramCount = NativeApiProxy.GetNParams(_object, methodNumber); + for (int i = 0; i < paramCount; i++) + { + var parameter = method.NewParameter() + .Name($"p{i}") + .ByValue(true); + + if (NativeApiProxy.HasParamDefValue(_object, methodNumber, i)) + { + parameter.DefaultValue(BslSkippedParameterValue.Instance); + // кажется что значение не нужно в данном кейсе использования + // если что - раскомментировать + // NativeApiProxy.GetParamDefValue(_object, methodNumber, i, variant => + // { + // var value = (BslPrimitiveValue)NativeApiVariant.Value(variant); + // parameter.DefaultValue(value); + // }); + } + } + + method.ReturnType(NativeApiProxy.HasRetVal(_object, methodNumber) ? typeof(BslValue) : typeof(void)); + method.IsExported(true); + + return method.Build(); + } + + public BslPropertyInfo GetPropertyInfo(int propertyNumber) + { + var propName = GetPropName(propertyNumber); + var isReadable = IsPropReadable(propertyNumber); + var isWritable = IsPropWritable(propertyNumber); + + return BslPropertyBuilder.Create() + .Name(propName) + .CanRead(isReadable) + .CanWrite(isWritable) + .Build(); + } + + private void SetDefValues(int methodNumber, int paramCount, IValue[] arguments) + { + for (int i = 0; i < paramCount; i++) + if (arguments[i] == null) + NativeApiProxy.GetParamDefValue(_object, methodNumber, i, + variant => arguments[i] = NativeApiVariant.Value(variant) + ); + } + + public void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + int paramCount = NativeApiProxy.GetNParams(_object, methodNumber); + using (var variant = new NativeApiVariant(paramCount)) + { + SetDefValues(methodNumber, paramCount, arguments); + for (int i = 0; i < paramCount; i++) + variant.Assign(arguments[i], i); + + NativeApiProxy.CallAsProc(_object, methodNumber, variant.Ptr); + } + } + + public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + var result = ValueFactory.Create(); + int paramCount = NativeApiProxy.GetNParams(_object, methodNumber); + using (var variant = new NativeApiVariant(paramCount)) + { + SetDefValues(methodNumber, paramCount, arguments); + for (int i = 0; i < paramCount; i++) + variant.Assign(arguments[i], i); + + NativeApiProxy.CallAsFunc(_object, methodNumber, variant.Ptr, + res => result = NativeApiVariant.Value(res) + ); + } + retValue = result; + } + + private void ReleaseUnmanagedResources(bool isDisposing) + { + if (_object == IntPtr.Zero) + return; + + try + { + NativeApiProxy.DestroyObject(_object); + } + catch (Exception) + { + if (isDisposing) + throw; + } + finally + { + _object = IntPtr.Zero; + } + } + + public void Dispose() + { + ReleaseUnmanagedResources(true); + GC.SuppressFinalize(this); + } + + ~NativeApiComponent() + { + ReleaseUnmanagedResources(false); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiEnums.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiEnums.cs similarity index 77% rename from src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiEnums.cs rename to src/OneScript.StandardLibrary/NativeApi/NativeApiEnums.cs index f4a156988..6eb895760 100644 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiEnums.cs +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiEnums.cs @@ -5,14 +5,16 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.NativeApi +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.NativeApi { [EnumerationType("ТипВнешнейКомпоненты", "AddInType")] public enum NativeApiEnums { - [EnumItem("OneScript")] + [EnumValue("OneScript")] OneScript, - [EnumItem("Native")] + [EnumValue("Native")] Native, } } diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiFactory.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiFactory.cs new file mode 100644 index 000000000..d33abce8d --- /dev/null +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiFactory.cs @@ -0,0 +1,52 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Types; +using OneScript.Contexts; +using ScriptEngine.Machine; + +namespace OneScript.StandardLibrary.NativeApi +{ + /// + /// Фабрика, осуществляющая регистрацию библиотеки внешних + /// компонент Native API и создания экземпляров компонент. + /// + class NativeApiFactory + { + public static bool Register(string filepath, string identifier, ITypeManager typeManager) + { + if (_libraries.ContainsKey(identifier)) + return false; + var library = new NativeApiLibrary(filepath, identifier, typeManager); + if (library.Loaded) + _libraries.Add(identifier, library); + return library.Loaded; + } + + private static readonly Dictionary _libraries = new Dictionary(); + + internal static void Initialize() + { + foreach (var item in _libraries) + item.Value.Dispose(); + _libraries.Clear(); + } + + [ScriptConstructor] + public static IValue Constructor(TypeActivationContext context) + { + var typeName = context.TypeName; + var separator = new char[] { '.' }; + var names = typeName.Split(separator, StringSplitOptions.RemoveEmptyEntries); + if (names.Length == 3 && _libraries.TryGetValue(names[1], out NativeApiLibrary library)) + return library.CreateComponent(context.TypeManager, default, typeName, names[2]); + throw new NotImplementedException(); + } + } +} diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiKernel.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiKernel.cs new file mode 100644 index 000000000..ddea19030 --- /dev/null +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiKernel.cs @@ -0,0 +1,63 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.InteropServices; + +namespace OneScript.StandardLibrary.NativeApi +{ + /// + /// Подключение DLL-файлов библиотеки внешних компонент Native API + /// + public class NativeApiKernel + { + private const string KernelDll = "kernel32.dll"; + public static bool IsLinux + { + get => System.Environment.OSVersion.Platform == PlatformID.Unix; + } + + private const String KernelWin = "kernel32.dll"; + private const String KernelLin = "libdl.so.2"; + + public static IntPtr LoadLibrary(string filename) + { + return IsLinux ? LinuxLoad(filename, 1) : WindowsLoad(filename); + } + + public static IntPtr GetProcAddress(IntPtr module, string procName) + { + var pointer = IsLinux ? LinuxProc(module, procName) : WindowsProc(module, procName); + return pointer != IntPtr.Zero + ? pointer + : throw new ApplicationException($"Function pointer for {procName} not obtained."); + } + + public static bool FreeLibrary(IntPtr module) + { + return IsLinux ? LinuxFree(module) == 0 : WindowsFree(module); + } + + [DllImport(KernelWin, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LoadLibrary")] + protected static extern IntPtr WindowsLoad(string lpLibFileName); + + [DllImport(KernelWin, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Ansi, EntryPoint = "GetProcAddress")] + protected static extern IntPtr WindowsProc(IntPtr module, string procName); + + [DllImport(KernelWin, SetLastError = true, EntryPoint = "FreeLibrary")] + protected static extern bool WindowsFree(IntPtr module); + + [DllImport(KernelLin, EntryPoint = "dlopen")] + protected static extern IntPtr LinuxLoad(string filename, int flags); + + [DllImport(KernelLin, EntryPoint = "dlsym")] + protected static extern IntPtr LinuxProc(IntPtr handle, string symbol); + + [DllImport(KernelLin, EntryPoint = "dlclose")] + protected static extern int LinuxFree(IntPtr handle); + } +} diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiLibrary.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiLibrary.cs new file mode 100644 index 000000000..f5be8a3ee --- /dev/null +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiLibrary.cs @@ -0,0 +1,118 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using OneScript.Exceptions; +using OneScript.Types; +using ScriptEngine.Machine; + +namespace OneScript.StandardLibrary.NativeApi +{ + /// + /// Класс, ассоциированный с экземпляром библиотеки внешних компонент + /// Native API и осуществляющий непосредственное создание экземпляра компоненты. + /// + class NativeApiLibrary : IDisposable + { + private delegate IntPtr GetClassNames(); + + private readonly List _components = new List(); + + private readonly String _tempfile; + + public NativeApiLibrary(string filepath, string identifier, ITypeManager typeManager) + { + if (!File.Exists(filepath)) + return; + + using (var stream = File.OpenRead(filepath)) + { + if (NativeApiPackage.IsZip(stream)) + { + _tempfile = Path.GetTempFileName(); + NativeApiPackage.Extract(stream, _tempfile); + Module = NativeApiKernel.LoadLibrary(_tempfile); + if (Module == IntPtr.Zero) + { + File.Delete(_tempfile); + } + } + else + Module = NativeApiKernel.LoadLibrary(filepath); + } + if (Loaded) + RegisterComponents(identifier, typeManager); + } + + public IntPtr Module { get; private set; } = IntPtr.Zero; + + public Boolean Loaded + { + get => Module != IntPtr.Zero; + } + + private void RegisterComponents(string identifier, ITypeManager typeManager) + { + var funcPtr = NativeApiKernel.GetProcAddress(Module, "GetClassNames"); + if (funcPtr == IntPtr.Zero) + throw new RuntimeException("В библиотеке внешних компонент не обнаружена функция: GetClassNames()"); + var namesPtr = Marshal.GetDelegateForFunctionPointer(funcPtr)(); + if (namesPtr == IntPtr.Zero) + throw new RuntimeException("Не удалось получить список компонент в составе библиотеки"); + var separator = new char[] { '|' }; + var names = NativeApiProxy.Str(namesPtr).Split(separator, StringSplitOptions.RemoveEmptyEntries); + foreach (String name in names) + typeManager.RegisterType($"AddIn.{identifier}.{name}", default, typeof(NativeApiFactory)); + } + + public IValue CreateComponent(ITypeManager typeManager, object host, String typeName, String componentName) + { + var typeDef = typeManager.GetTypeByName(typeName); + var component = new NativeApiComponent(host, this, typeDef, componentName); + _components.Add(component); + return component; + } + + private void ReleaseUnmanagedResources(bool isDisposing) + { + try + { + foreach (var component in _components) + { + component.Dispose(); + } + + if (Loaded && NativeApiKernel.FreeLibrary(Module)) + { + if (!String.IsNullOrEmpty(_tempfile)) + { + File.Delete(_tempfile); + } + } + } + catch (Exception) + { + if (isDisposing) + throw; + } + } + + public void Dispose() + { + ReleaseUnmanagedResources(true); + GC.SuppressFinalize(this); + } + + ~NativeApiLibrary() + { + ReleaseUnmanagedResources(false); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiPackage.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiPackage.cs similarity index 84% rename from src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiPackage.cs rename to src/OneScript.StandardLibrary/NativeApi/NativeApiPackage.cs index 0b7d54615..8f6c9580f 100644 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiPackage.cs +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiPackage.cs @@ -5,13 +5,13 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using Ionic.Zip; -using ScriptEngine.Machine; using System; using System.IO; using System.Xml; +using Ionic.Zip; +using OneScript.Exceptions; -namespace ScriptEngine.HostedScript.Library.NativeApi +namespace OneScript.StandardLibrary.NativeApi { /// /// Загрузчик для библиотеки внешних компонент, упакованной в zip-файл. @@ -26,6 +26,11 @@ public static bool IsZip(Stream stream) const int ZIP_LEAD_BYTES = 0x04034b50; return (BitConverter.ToInt32(bytes, 0) == ZIP_LEAD_BYTES); } + + private static bool Is64BitProcess() + { + return IntPtr.Size == 8; + } public static void Extract(Stream stream, String tempfile) { @@ -49,16 +54,15 @@ private static String GetEntryName(ZipFile zip) stream.Seek(0, 0); using (var reader = XmlReader.Create(stream)) { + var thisOs = NativeApiProxy.IsLinux ? "Linux" : "Windows"; + var thisArch = Is64BitProcess() ? "x86_64" : "i386"; while (reader.ReadToFollowing("component")) { var attrOs = reader.GetAttribute("os"); var attrArch = reader.GetAttribute("arch"); - var thisArch = System.Environment.Is64BitOperatingSystem ? "x86_64" : "i386"; - if (string.Equals(attrOs, "Windows", StringComparison.OrdinalIgnoreCase) - && string.Equals(attrArch, thisArch, StringComparison.OrdinalIgnoreCase)) - { + if (string.Equals(attrArch, thisArch, StringComparison.OrdinalIgnoreCase) + && string.Equals(attrOs, thisOs, StringComparison.OrdinalIgnoreCase)) return reader.GetAttribute("path"); - } } } } diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiProxy.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiProxy.cs new file mode 100644 index 000000000..ab7cc697b --- /dev/null +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiProxy.cs @@ -0,0 +1,202 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.InteropServices; + +namespace OneScript.StandardLibrary.NativeApi +{ + /// + /// Трансляция вызовов C# для взаимодействия с NativeApi + /// + static class NativeApiProxy + { + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate IntPtr TGetClassObject(IntPtr module, [MarshalAs(UnmanagedType.LPWStr)] string name, OnErrorDelegate onError, OnEventDelegate onEvent, OnStatusDelegate onStatus); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TDestroyObject(IntPtr ptr); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate IntPtr TCreateVariant(Int32 length); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TFreeVariant(IntPtr ptr); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate Int32 TGetNProps(IntPtr ptr); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate Int32 TFindProp(IntPtr ptr, [MarshalAs(UnmanagedType.LPWStr)] string wsPropName); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate bool TIsPropReadable(IntPtr ptr, Int32 lPropNum); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate bool TIsPropWritable(IntPtr ptr, Int32 lPropNum); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TGetPropName(IntPtr ptr, Int32 lPropNum, Int32 lPropAlias, PointerDelegate response); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TGetPropVal(IntPtr ptr, Int32 lPropNum, PointerDelegate response); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetPropVal(IntPtr ptr, Int32 lPropNum, IntPtr variant); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetVariantEmpty(IntPtr ptr, Int32 num); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetVariantBool(IntPtr ptr, Int32 num, Boolean value); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetVariantReal(IntPtr ptr, Int32 num, Double value); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetVariantInt(IntPtr ptr, Int32 num, Int32 value); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetVariantStr(IntPtr ptr, Int32 num, [MarshalAs(UnmanagedType.LPWStr)] string value, Int32 length); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetVariantPtr(IntPtr ptr, Int32 num, IntPtr value, Int32 length); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TSetVariantBlob(IntPtr ptr, Int32 num, byte[] data, Int32 length); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TGetVariant(IntPtr ptr, Int32 num + , TSetVariantEmpty e + , TSetVariantBool b + , TSetVariantInt i + , TSetVariantReal r + , TSetVariantPtr s + , TSetVariantPtr x + ); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate Int32 TGetNMethods(IntPtr ptr); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate Int32 TFindMethod(IntPtr ptr, [MarshalAs(UnmanagedType.LPWStr)] string wsMethodName); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate void TGetMethodName(IntPtr ptr, Int32 lMethodNum, Int32 lMethodAlias, PointerDelegate response); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate Int32 TGetNParams(IntPtr ptr, Int32 lMethodNum); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate bool THasParamDefValue(IntPtr ptr, Int32 lMethodNum, Int32 lParamNum); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate bool TGetParamDefValue(IntPtr ptr, Int32 lMethodNum, Int32 lParamNum, PointerDelegate response); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate bool THasRetVal(IntPtr ptr, Int32 lMethodNum); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate bool TCallAsProc(IntPtr ptr, Int32 lMethodNum, IntPtr value); + + [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)] + public delegate bool TCallAsFunc(IntPtr ptr, Int32 lMethodNum, IntPtr value, PointerDelegate response); + + public static readonly TGetClassObject GetClassObject; + public static readonly TDestroyObject DestroyObject; + public static readonly TCreateVariant CreateVariant; + public static readonly TFreeVariant FreeVariant; + public static readonly TGetNProps GetNProps; + public static readonly TFindProp FindProp; + public static readonly TIsPropReadable IsPropReadable; + public static readonly TIsPropWritable IsPropWritable; + public static readonly TGetPropName GetPropName; + public static readonly TGetPropVal GetPropVal; + public static readonly TSetPropVal SetPropVal; + public static readonly TGetVariant GetVariant; + public static readonly TSetVariantEmpty SetVariantEmpty; + public static readonly TSetVariantBool SetVariantBool; + public static readonly TSetVariantReal SetVariantReal; + public static readonly TSetVariantBlob SetVariantBlob; + public static readonly TSetVariantInt SetVariantInt; + public static readonly TSetVariantStr SetVariantStr; + public static readonly TGetNMethods GetNMethods; + public static readonly TFindMethod FindMethod; + public static readonly TGetMethodName GetMethodName; + public static readonly TGetNParams GetNParams; + public static readonly THasParamDefValue HasParamDefValue; + public static readonly TGetParamDefValue GetParamDefValue; + public static readonly THasRetVal HasRetVal; + public static readonly TCallAsProc CallAsProc; + public static readonly TCallAsFunc CallAsFunc; + + public static bool IsAvailable { get; private set; } + + static NativeApiProxy() + { + string location = System.Reflection.Assembly.GetExecutingAssembly().Location; + string filename = System.IO.Path.GetDirectoryName(location) + + System.IO.Path.DirectorySeparatorChar + "ScriptEngine.NativeApi" + + (IntPtr.Size == 8 ? "64" : "32") + + (NativeApiKernel.IsLinux ? ".so" : ".dll"); + IntPtr module = NativeApiKernel.LoadLibrary(filename); + if (module == IntPtr.Zero) + { + IsAvailable = false; + return; + } + + IsAvailable = true; + + GetClassObject = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetClassObject")); + DestroyObject = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "DestroyObject")); + CreateVariant = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "CreateVariant")); + FreeVariant = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "FreeVariant")); + GetNProps = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetNProps")); + FindProp = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "FindProp")); + IsPropReadable = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "IsPropReadable")); + IsPropWritable = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "IsPropWritable")); + GetPropName = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetPropName")); + GetPropVal = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetPropVal")); + SetPropVal = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "SetPropVal")); + SetVariantEmpty = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "SetVariantEmpty")); + SetVariantBool = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "SetVariantBool")); + SetVariantReal = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "SetVariantReal")); + SetVariantBlob = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "SetVariantBlob")); + SetVariantInt = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "SetVariantInt")); + SetVariantStr = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "SetVariantStr")); + GetVariant = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetVariant")); + GetPropVal = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetPropVal")); + GetNMethods = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetNMethods")); + FindMethod = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "FindMethod")); + GetMethodName = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetMethodName")); + GetNParams = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetNParams")); + HasParamDefValue = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "HasParamDefValue")); + GetParamDefValue = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "GetParamDefValue")); + HasRetVal = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "HasRetVal")); + CallAsProc = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "CallAsProc")); + CallAsFunc = Marshal.GetDelegateForFunctionPointer(NativeApiKernel.GetProcAddress(module, "CallAsFunc")); + } + + public static bool IsLinux + { + get => System.Environment.OSVersion.Platform == PlatformID.Unix; + } + + public delegate void PointerDelegate(IntPtr ptr); + public delegate void ArrayDelegate(IntPtr ptr, Int32 number); + public delegate void OnErrorDelegate(UInt16 wcode, IntPtr source, IntPtr descr, Int32 scode); + public delegate void OnEventDelegate(IntPtr source, IntPtr message, IntPtr data); + public delegate void OnStatusDelegate(IntPtr status); + + public static String Str(IntPtr p) + { + return p != IntPtr.Zero ? Marshal.PtrToStringUni(p) : String.Empty; + } + + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/NativeApi/NativeApiVariant.cs b/src/OneScript.StandardLibrary/NativeApi/NativeApiVariant.cs new file mode 100644 index 000000000..b059638e9 --- /dev/null +++ b/src/OneScript.StandardLibrary/NativeApi/NativeApiVariant.cs @@ -0,0 +1,80 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.InteropServices; +using OneScript.StandardLibrary.Binary; +using ScriptEngine.Machine; + +namespace OneScript.StandardLibrary.NativeApi +{ + + + /// + /// Трансляция значений между IValue и tVariant из состава Native API + /// + class NativeApiVariant: IDisposable + { + private readonly IntPtr variant = IntPtr.Zero; + + public IntPtr Ptr { get { return variant; } } + + public NativeApiVariant(Int32 count = 1) + { + variant = NativeApiProxy.CreateVariant(count); + } + + public void Dispose() + { + if (variant != IntPtr.Zero) + NativeApiProxy.FreeVariant(variant); + } + + public void Assign(IValue value, Int32 number = 0) + { + var clrObject = value.UnwrapToClrObject(); + switch (clrObject) + { + case string str: + NativeApiProxy.SetVariantStr(variant, number, str, str.Length); + break; + case bool v: + NativeApiProxy.SetVariantBool(variant, number, value.AsBoolean()); + break; + case decimal num: + if (num % 1 == 0) + NativeApiProxy.SetVariantInt(variant, number, Convert.ToInt32(value.AsNumber())); + else + NativeApiProxy.SetVariantReal(variant, number, Convert.ToDouble(value.AsNumber())); + break; + case BinaryDataContext binaryData: + NativeApiProxy.SetVariantBlob(variant, number, binaryData.Buffer, binaryData.Buffer.Length); + break; + default: + NativeApiProxy.SetVariantEmpty(variant, number); + break; + } + } + public static IValue Value(IntPtr variant, Int32 number = 0) + { + IValue value = ValueFactory.Create(); + NativeApiProxy.GetVariant(variant, number, + (v, n) => value = ValueFactory.Create(), + (v, n, r) => value = ValueFactory.Create(r), + (v, n, r) => value = ValueFactory.Create((Decimal)r), + (v, n, r) => value = ValueFactory.Create((Decimal)r), + (v, n, r, s) => value = ValueFactory.Create(Marshal.PtrToStringUni(r, s)), + (v, n, r, s) => { + byte[] buffer = new byte[s]; + Marshal.Copy(r, buffer, 0, s); + value = new BinaryDataContext(buffer); + } + ); + return value; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Net/TCPClient.cs b/src/OneScript.StandardLibrary/Net/TCPClient.cs similarity index 90% rename from src/ScriptEngine.HostedScript/Library/Net/TCPClient.cs rename to src/OneScript.StandardLibrary/Net/TCPClient.cs index ea1bb0b2d..c979b64a5 100644 --- a/src/ScriptEngine.HostedScript/Library/Net/TCPClient.cs +++ b/src/OneScript.StandardLibrary/Net/TCPClient.cs @@ -4,16 +4,16 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; +using System.IO; using System.Net.Sockets; using System.Text; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; using ScriptEngine.Machine.Contexts; -using System.IO; -using ScriptEngine.HostedScript.Library.Binary; - -namespace ScriptEngine.HostedScript.Library.Net +namespace OneScript.StandardLibrary.Net { /// /// Соединение по протоколу TCP. Позволяет отправлять и принимать данные с использованием TCP сокета. @@ -80,7 +80,11 @@ private MemoryStream ReadAllData(NetworkStream source, int limit) int numberOfBytesRead = source.Read(readBuffer, 0, portion); ms.Write(readBuffer, 0, numberOfBytesRead); if (useLimit) + { limit -= numberOfBytesRead; + if (limit <= 0) + break; + } } while (source.DataAvailable); if(ms.Length > 0) @@ -114,13 +118,13 @@ public void SendString(string data, string encoding = null) /// /// ДвоичныеДанные которые нужно отправить. [ContextMethod("ОтправитьДвоичныеДанные", "SendBinaryData")] - public void SendString(BinaryDataContext data) + public void SendBinaryData(BinaryDataContext data) { - if (data.Buffer.Length == 0) + if (data.Size() == 0) return; var stream = _client.GetStream(); - stream.Write(data.Buffer, 0, data.Buffer.Length); + data.CopyTo(stream); stream.Flush(); } @@ -162,6 +166,16 @@ public int ReadTimeout set { _client.GetStream().ReadTimeout = value; } } + /// + /// Флаг ожидания наполнения буфера при приеме/отправке данных. По умолчанию - Ложь + /// + [ContextProperty("НеОткладывать", "NoDelay")] + public bool NoDelay + { + get { return _client.NoDelay; } + set { _client.NoDelay = value; } + } + private static Encoding GetEncodingByName(string encoding) { Encoding enc; @@ -204,9 +218,9 @@ public void Dispose() /// адрес машины /// порт сокета [ScriptConstructor] - public static TCPClient Constructor(IValue host, IValue port) + public static TCPClient Constructor(string host, int port) { - var client = new TcpClient(host.AsString(), (int)port.AsNumber()); + var client = new TcpClient(host, port); return new TCPClient(client); } } diff --git a/src/ScriptEngine.HostedScript/Library/Net/TCPServer.cs b/src/OneScript.StandardLibrary/Net/TCPServer.cs similarity index 97% rename from src/ScriptEngine.HostedScript/Library/Net/TCPServer.cs rename to src/OneScript.StandardLibrary/Net/TCPServer.cs index 91473b60f..e0837693b 100644 --- a/src/ScriptEngine.HostedScript/Library/Net/TCPServer.cs +++ b/src/OneScript.StandardLibrary/Net/TCPServer.cs @@ -7,10 +7,11 @@ This Source Code Form is subject to the terms of the using System.Net; using System.Net.Sockets; +using OneScript.Contexts; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Net +namespace OneScript.StandardLibrary.Net { /// /// Простой однопоточный tcp-сокет. Слушает входящие соединения на определенном порту diff --git a/src/OneScript.StandardLibrary/OneScript.StandardLibrary.csproj b/src/OneScript.StandardLibrary/OneScript.StandardLibrary.csproj new file mode 100644 index 000000000..1c2bbf009 --- /dev/null +++ b/src/OneScript.StandardLibrary/OneScript.StandardLibrary.csproj @@ -0,0 +1,35 @@ + + + + + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug + AnyCPU + + + + OneScript.StandardLibrary + OneScript Main Client Libraries + OneScript OpenSource Community + EvilBeaver <ovsiankin.aa@gmail.com> + http://oscript.io + Classes for 1Script processes hosting + OneScript OpenSource Community 2015 + BSL 1C 1Script OneScript + + + + true + false + + + + + + + + + + + + diff --git a/src/OneScript.StandardLibrary/PathHelper.cs b/src/OneScript.StandardLibrary/PathHelper.cs new file mode 100644 index 000000000..444b3cbf7 --- /dev/null +++ b/src/OneScript.StandardLibrary/PathHelper.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +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 +{ + /// + /// Utility methods for working with file paths + /// + internal static class PathHelper + { + /// + /// Strips trailing null characters from a path string. + /// This is needed because Windows WebDAV client can add null characters to the end of paths, + /// which causes ArgumentException in System.IO methods. + /// Only trailing null characters are removed to avoid masking potential security issues + /// with null characters in the middle of paths (e.g., "file.txt\0.exe"). + /// + /// Path that may contain trailing null characters + /// Path with trailing null characters removed, or null if input was null + public static string StripNullCharacters(string path) + { + if (path == null) + return null; + + return path.TrimEnd('\0'); + } + } +} diff --git a/src/OneScript.StandardLibrary/PlatformTypeEnum.cs b/src/OneScript.StandardLibrary/PlatformTypeEnum.cs new file mode 100644 index 000000000..eb086880e --- /dev/null +++ b/src/OneScript.StandardLibrary/PlatformTypeEnum.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary +{ + [EnumerationType("ТипПлатформы", "PlatformType")] + public enum PlatformTypeEnum + { + [EnumValue("Linux_x86")] + Linux_x86, + + [EnumValue("Linux_x86_64")] + Linux_x86_64, + + [EnumValue("MacOS_x86")] + MacOS_x86, + + [EnumValue("MacOS_x86_64")] + MacOS_x86_64, + + [EnumValue("Windows_x86")] + Windows_x86, + + [EnumValue("Windows_x86_64")] + Windows_x86_64, + + [EnumValue("Unknown")] + Unknown, + } +} diff --git a/src/OneScript.StandardLibrary/Processes/ArgumentsParser.cs b/src/OneScript.StandardLibrary/Processes/ArgumentsParser.cs new file mode 100644 index 000000000..6000d85ab --- /dev/null +++ b/src/OneScript.StandardLibrary/Processes/ArgumentsParser.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; +using System.Collections.Generic; +using System.Text; + +namespace OneScript.StandardLibrary.Processes +{ + public class ArgumentsParser + { + private readonly string _input; + private readonly List _quotes; + private readonly List _arguments; + private readonly StringBuilder _currentArgument; + + private char _currentQuote; + private bool _isInQuotes; + + public ArgumentsParser(string input) + { + _input = input; + _currentArgument = new StringBuilder(); + _arguments = new List(); + _quotes = new List { '\'', '"' }; + } + + public string[] GetArguments() + { + if (string.IsNullOrWhiteSpace(_input)) + { + return Array.Empty(); + } + + if (_arguments.Count > 0) + { + return _arguments.ToArray(); + } + + _currentArgument.Clear(); + + foreach (var currentChar in _input) + { + if (char.IsWhiteSpace(currentChar) && !_isInQuotes) + { + PushArgument(); + } + else if (_quotes.Contains(currentChar)) + { + HandleQuotes(currentChar); + } + else + { + PushChar(currentChar); + } + } + + PushArgument(); + + return _arguments.ToArray(); + } + + private void HandleQuotes(char quoteChar) + { + if (!_isInQuotes) + { + _isInQuotes = true; + _currentQuote = quoteChar; + } + else if (quoteChar == _currentQuote) + { + _isInQuotes = false; + _currentQuote = default; + } + else + { + PushChar(quoteChar); + } + } + + private void PushChar(char ch) + { + _currentArgument.Append(ch); + } + + private void PushArgument() + { + if (_currentArgument.Length == 0) + { + return; + } + + _arguments.Add(_currentArgument.ToString()); + _currentArgument.Clear(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Processes/GlobalProcessesFunctions.cs b/src/OneScript.StandardLibrary/Processes/GlobalProcessesFunctions.cs new file mode 100644 index 000000000..dcf4873cc --- /dev/null +++ b/src/OneScript.StandardLibrary/Processes/GlobalProcessesFunctions.cs @@ -0,0 +1,113 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Processes +{ + [GlobalContext(Category = "Работа с процессами операционной системы")] + public class GlobalProcessesFunctions : GlobalContextBase + { + /// + /// Запуск приложения в операционной системе + /// + /// Командная строка запуска + /// Текущая директория запускаемого процесса (необязательно) + /// Ожидать завершения (необязательно) по умолчанию Ложь + /// Выходной параметр. Код возврата процесса. Имеет смысл только если указан параметр wait=true + [ContextMethod("ЗапуститьПриложение", "RunApp")] + public void RunApp(string cmdLine, string currentDir = null, bool wait = false, [ByRef] IVariable retCode = null) + { + var sInfo = ProcessContext.PrepareProcessStartupInfo(cmdLine, currentDir); + + var p = new System.Diagnostics.Process(); + p.StartInfo = sInfo; + p.Start(); + + if(wait) + { + p.WaitForExit(); + if(retCode != null) + retCode.Value = ValueFactory.Create(p.ExitCode); + } + + } + + /// + /// Получает текущий процесс + /// + [ContextMethod("ТекущийПроцесс", "CurrentProcess")] + public ProcessContext CurrentProcess() + { + var p = System.Diagnostics.Process.GetCurrentProcess(); + + return new ProcessContext(p); + } + + /// + /// Создает процесс, которым можно манипулировать из скрипта + /// + /// Командная строка запуска + /// Текущая директория запускаемого процесса (необязательно) + /// Перехватывать стандартные потоки stdout и stderr + /// Перехватывать стандартный поток stdin + /// Кодировка стандартных потоков вывода и ошибок + /// Соответствие, где установлены значения переменных среды + [ContextMethod("СоздатьПроцесс", "CreateProcess")] + public ProcessContext CreateProcess(string cmdLine, string currentDir = null, bool redirectOutput = false, bool redirectInput = false, IValue encoding = null, MapImpl env = null) + { + return ProcessContext.Create(cmdLine, currentDir, redirectOutput, redirectInput, encoding, env); + } + + /// + /// Выполняет поиск процесса по PID среди запущенных в операционной системе + /// + /// Идентификатор процесса + /// Процесс. Если не найден - Неопределено + [ContextMethod("НайтиПроцессПоИдентификатору", "FindProcessById")] + public IValue FindProcessById(int PID) + { + System.Diagnostics.Process process; + try + { + process = System.Diagnostics.Process.GetProcessById(PID); + } + catch (ArgumentException) + { + return ValueFactory.Create(); + } + + return new ProcessContext(process); + + } + + /// + /// Выполняет поиск процессов с определенным именем + /// + /// Имя процесса + /// Массив объектов Процесс. + [ContextMethod("НайтиПроцессыПоИмени", "FindProcessesByName")] + public IValue FindProcessesByName(string name) + { + var processes = System.Diagnostics.Process.GetProcessesByName(name); + var contextWrappers = processes.Select(x => new ProcessContext(x)); + + return new ArrayImpl(contextWrappers); + + } + + public static GlobalProcessesFunctions CreateInstance() + { + return new GlobalProcessesFunctions(); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/ProcessContext.cs b/src/OneScript.StandardLibrary/Processes/ProcessContext.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/ProcessContext.cs rename to src/OneScript.StandardLibrary/Processes/ProcessContext.cs index 4edc05f06..e4d2b6267 100644 --- a/src/ScriptEngine.HostedScript/Library/ProcessContext.cs +++ b/src/OneScript.StandardLibrary/Processes/ProcessContext.cs @@ -4,13 +4,19 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Text; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.Processes { /// /// Позволяет управлять процессом операционной системы. Получать текст из стандартных потоков, @@ -188,6 +194,8 @@ public int ProcessId } } + [ContextProperty("Имя", "Name")] public string Name => _p.ProcessName; + [ContextMethod("Завершить","Stop")] public void Stop() { @@ -242,8 +250,8 @@ public static ProcessContext Create(string cmdLine, string currentDir = null, bo { var source = env.Select(x => new { - Key = x.Key.AsString(), - Value = x.Value.AsString() + Key = ContextValuesMarshaller.ConvertValueStrict(x.Key), + Value = ContextValuesMarshaller.ConvertValueStrict(x.Value) }).Where(x => !string.IsNullOrWhiteSpace(x.Key)); foreach (var kv in source) @@ -263,13 +271,33 @@ public static ProcessStartInfo PrepareProcessStartupInfo(string cmdLine, string var sInfo = new ProcessStartInfo(); int argsPosition; + sInfo.UseShellExecute = true; sInfo.FileName = ExtractExecutableName(cmdLine, out argsPosition); - sInfo.Arguments = argsPosition >= cmdLine.Length ? "" : cmdLine.Substring(argsPosition); if (currentDir != null) sInfo.WorkingDirectory = currentDir; + + // Поведение под MacOS и остальные системы различается + // Страдает обратная совместимость и неясны до конца синтаксисы. + // См. https://github.com/EvilBeaver/OneScript/issues/1299 + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + sInfo.Arguments = argsPosition >= cmdLine.Length ? "" : cmdLine.Substring(argsPosition); + } + else + { + var arguments = argsPosition >= cmdLine.Length + ? Array.Empty() + : new ArgumentsParser(cmdLine[argsPosition..]).GetArguments(); + + foreach (var argument in arguments) + { + sInfo.ArgumentList.Add(argument); + } + } + return sInfo; } - + private static string ExtractExecutableName(string cmdLine, out int argsPosition) { bool inQuotes = false; diff --git a/src/ScriptEngine.HostedScript/Library/ProcessOutputWrapper.cs b/src/OneScript.StandardLibrary/Processes/ProcessOutputWrapper.cs similarity index 93% rename from src/ScriptEngine.HostedScript/Library/ProcessOutputWrapper.cs rename to src/OneScript.StandardLibrary/Processes/ProcessOutputWrapper.cs index 690f257e2..afcd9800a 100644 --- a/src/ScriptEngine.HostedScript/Library/ProcessOutputWrapper.cs +++ b/src/OneScript.StandardLibrary/Processes/ProcessOutputWrapper.cs @@ -4,22 +4,20 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.IO; using System.Text; -using System.Threading; - using sys = System.Diagnostics; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.Processes { class ProcessOutputWrapper : TextReader { - private sys.Process _process; - private OutputVariant _variant; - private StringBuilder _buffer = new StringBuilder(4096); - private ReaderWriterLockSlim _locker; - + private readonly sys.Process _process; + private readonly OutputVariant _variant; + private readonly StringBuilder _buffer = new StringBuilder(4096); + private int _bufferIndex = 0; private bool AlreadyReading { get; set; } @@ -36,7 +34,6 @@ public ProcessOutputWrapper(sys.Process process, OutputVariant variant) { _process = process; _variant = variant; - _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); } public void StartReading() diff --git a/src/ScriptEngine.HostedScript/Library/StdTextReadStream.cs b/src/OneScript.StandardLibrary/Processes/StdTextReadStream.cs similarity index 97% rename from src/ScriptEngine.HostedScript/Library/StdTextReadStream.cs rename to src/OneScript.StandardLibrary/Processes/StdTextReadStream.cs index e0e013d44..18e17b6b5 100644 --- a/src/ScriptEngine.HostedScript/Library/StdTextReadStream.cs +++ b/src/OneScript.StandardLibrary/Processes/StdTextReadStream.cs @@ -4,12 +4,14 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; + using System; using System.IO; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.Processes { /// /// Стандартный поток вывода текста. diff --git a/src/ScriptEngine.HostedScript/Library/StdTextWriteStream.cs b/src/OneScript.StandardLibrary/Processes/StdTextWriteStream.cs similarity index 95% rename from src/ScriptEngine.HostedScript/Library/StdTextWriteStream.cs rename to src/OneScript.StandardLibrary/Processes/StdTextWriteStream.cs index 5d2fb3de3..cf209d926 100644 --- a/src/ScriptEngine.HostedScript/Library/StdTextWriteStream.cs +++ b/src/OneScript.StandardLibrary/Processes/StdTextWriteStream.cs @@ -5,11 +5,12 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine.Contexts; using System; using System.IO; +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.Processes { /// /// Стандартный поток ввода текста. Используется для взаимодействия с работающими процессами. diff --git a/src/OneScript.StandardLibrary/RandomNumberGenerator.cs b/src/OneScript.StandardLibrary/RandomNumberGenerator.cs new file mode 100644 index 000000000..b65af57b4 --- /dev/null +++ b/src/OneScript.StandardLibrary/RandomNumberGenerator.cs @@ -0,0 +1,92 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Exceptions; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + [ContextClass("ГенераторСлучайныхЧисел", "RandomNumberGenerator")] + public class RandomNumberGenerator : AutoContext + { + private readonly Random _random; + + public RandomNumberGenerator() + { + _random = new Random(); + } + + public RandomNumberGenerator(int seed) + { + _random = new Random(seed); + } + + [ContextMethod("СлучайноеЧисло", "RandomNumber")] + public IValue RandomNumber(uint? low = null, uint? high = null) + { + uint lo = low !=null ? (uint)low : 0; + uint hi = high != null ? (uint)high : uint.MaxValue; + + if (hi < lo) + throw RuntimeException.InvalidArgumentValue(); + + uint range = hi - lo; + if (range == uint.MaxValue) + return ValueFactory.Create( Random32() ); + + // Приводим к рабочему диапазону + long maxValue = int.MinValue + range + 1; + + long v = _random.Next(int.MinValue, (int)maxValue ); + v -= int.MinValue - lo; + + return ValueFactory.Create( v ); + } + + private uint Random32() + { + byte[] bytes = new byte[4]; + _random.NextBytes(bytes); + return BitConverter.ToUInt32(bytes, 0); + } + + /// + /// Формирует ГСЧ с возможностью указания начального числа. + /// + /// Начальное число. Последовательность случайных чисел для одного и того же начального числа будет одинакова + /// + [ScriptConstructor(Name = "Конструктор по умолчанию")] + public static RandomNumberGenerator Constructor(decimal seed) + { + if (seed == 0) + return new RandomNumberGenerator(); + + int seedInt; + if (seed < int.MinValue || seed > int.MaxValue) + { + var bits = decimal.GetBits(seed); + seedInt = bits[0]; + } + else + { + seedInt = (int)seed; + } + + return new RandomNumberGenerator(seedInt); + } + + [ScriptConstructor(Name = "Формирование неинициализированного объекта")] + public static RandomNumberGenerator Constructor() + { + return new RandomNumberGenerator(); + } + + } +} diff --git a/src/OneScript.StandardLibrary/Reflector.cs b/src/OneScript.StandardLibrary/Reflector.cs new file mode 100644 index 000000000..1074b364c --- /dev/null +++ b/src/OneScript.StandardLibrary/Reflector.cs @@ -0,0 +1,571 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Collections.ValueTable; +using OneScript.StandardLibrary.TypeDescriptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + /// + /// Рефлектор предназначен для получения метаданных объектов во время выполнения. + /// Как правило, рефлексия используется для проверки наличия у объекта определенных свойств/методов. + /// В OneScript рефлексию можно применять для вызова методов объектов по именам методов. + /// + [ContextClass("Рефлектор", "Reflector")] + public class ReflectorContext : AutoContext + { + private readonly ITypeManager _typeManager; + + private ReflectorContext(ITypeManager typeManager) + { + _typeManager = typeManager; + } + + /// + /// Вызывает метод по его имени. + /// + /// Объект, метод которого нужно вызвать. + /// Имя метода для вызова + /// Массив аргументов, передаваемых методу. Следует учесть, что все параметры нужно передавать явно, в том числе необязательные. + /// Если вызывается функция, то возвращается ее результат. В противном случае возвращается Неопределено. + [ContextMethod("ВызватьМетод", "CallMethod")] + public IValue CallMethod(IBslProcess process, IRuntimeContextInstance target, string methodName, ArrayImpl arguments = null) + { + var methodIdx = target.GetMethodNumber(methodName); + var methInfo = target.GetMethodInfo(methodIdx); + + IValue[] argsToPass; + if (target.DynamicMethodSignatures) + argsToPass = arguments?.ToArray() ?? Array.Empty(); + else + argsToPass = GetArgsToPass(arguments, methInfo.GetBslParameters()); + + IValue retValue = ValueFactory.Create(); + if (methInfo.IsFunction()) + { + target.CallAsFunction(methodIdx, argsToPass, out retValue, process); + } + else + { + target.CallAsProcedure(methodIdx, argsToPass, process); + } + + if (arguments != null) + { + for (int i = 0; i < argsToPass.Length; i++) + { + if (i < arguments.Count()) + { + arguments.Set(i, argsToPass[i] is IValueReference r ? r.Value : argsToPass[i]); + } + } + } + + return retValue; + } + + private static IValue[] GetArgsToPass(ArrayImpl arguments, ParameterInfo[] parameters) + { + var argValues = arguments?.ToArray() ?? Array.Empty(); + // ArrayImpl не может (не должен!) содержать null или NotAValidValue + + if (argValues.Length > parameters.Length) + throw RuntimeException.TooManyArgumentsPassed(); + + var argsToPass = new IValue[parameters.Length]; + + int i = 0; + for (; i < argValues.Length; i++) + { + if (parameters[i].IsByRef()) + argsToPass[i] = Variable.Create(argValues[i], ""); + else + argsToPass[i] = argValues[i]; + } + for (; i < parameters.Length; i++) + { + if (!parameters[i].HasDefaultValue) + throw RuntimeException.TooFewArgumentsPassed(); + + // else keep null as a default value + } + + return argsToPass; + } + + /// + /// Проверяет существование указанного метода у переданного объекта. + /// + /// Объект, из которого получаем таблицу методов. + /// Имя метода для вызова + /// Истина, если метод существует, и Ложь в обратном случае. + [ContextMethod("МетодСуществует", "MethodExists")] + public bool MethodExists(BslValue target, string methodName) + { + if (target is BslObjectValue) + return MethodExistsForObject(target.AsObject(), methodName); + + if (target.SystemType == BasicTypes.Type) + return MethodExistsForType(target as BslTypeValue, methodName); + + throw RuntimeException.InvalidArgumentType("target"); + } + + private static bool MethodExistsForObject(IRuntimeContextInstance target, string methodName) + { + try + { + var idx = target.GetMethodNumber(methodName); + return idx >= 0; + } + catch (RuntimeException) + { + return false; + } + } + + + private const int annotNameColumnIndex = 0; + private const int annotParamsColumnIndex = 1; + private static ValueTable EmptyAnnotationsTable() + { + var annotationsTable = new ValueTable(); + annotationsTable.Columns.AddUnchecked("Имя"); + annotationsTable.Columns.AddUnchecked("Параметры"); + + return annotationsTable; + } + + private static ValueTable CreateAnnotationTable(BslAnnotationAttribute[] annotations) + { + var annotationsTable = EmptyAnnotationsTable(); + var annotationNameColumn = annotationsTable.Columns.FindColumnByIndex(annotNameColumnIndex); + var annotationParamsColumn = annotationsTable.Columns.FindColumnByIndex(annotParamsColumnIndex); + + foreach (var annotation in annotations) + { + var annotationRow = annotationsTable.Add(); + if (annotation.Name != null) + { + annotationRow.Set(annotationNameColumn, ValueFactory.Create(annotation.Name)); + } + var parametersTable = FillAnnotationParameters(annotation.Parameters); + annotationRow.Set(annotationParamsColumn, parametersTable); + } + + return annotationsTable; + } + + private static ValueTable FillAnnotationParameters(IEnumerable parameters) + { + var parametersTable = new ValueTable(); + var parameterNameColumn = parametersTable.Columns.Add("Имя"); + var parameterValueColumn = parametersTable.Columns.Add("Значение"); + + foreach (var annotationParameter in parameters) + { + var parameterRow = parametersTable.Add(); + if (annotationParameter.Name != null) + { + parameterRow.Set(parameterNameColumn, ValueFactory.Create(annotationParameter.Name)); + } + if (annotationParameter.Value is BslAnnotationValue annotationValue) + { + var expandedValue = EmptyAnnotationsTable(); + var expandedValueColumns = expandedValue.Columns; + var row = expandedValue.Add(); + row.Set(expandedValueColumns.FindColumnByIndex(annotNameColumnIndex), ValueFactory.Create(annotationValue.Name)); + row.Set(expandedValueColumns.FindColumnByIndex(annotParamsColumnIndex), FillAnnotationParameters(annotationValue.Parameters)); + parameterRow.Set(parameterValueColumn, row); + } + else + { + parameterRow.Set(parameterValueColumn, annotationParameter.Value); + } + } + + return parametersTable; + } + + private static bool MethodExistsForType(BslTypeValue type, string methodName) + { + var clrType = GetReflectableClrType(type); + return clrType.GetMethod(methodName) != null; + } + + private static Type GetReflectableClrType(BslTypeValue type) + { + var clrType = type.TypeValue.ImplementingClass; + if (clrType != typeof(AttachedScriptsFactory) && !typeof(IRuntimeContextInstance).IsAssignableFrom(clrType)) + { + throw NonReflectableType(); + } + + Type reflectableType; + if (clrType == typeof(AttachedScriptsFactory)) + reflectableType = ReflectUserType(type.TypeValue.Name); + else + reflectableType = ReflectContext(clrType); + + return reflectableType; + } + + private static RuntimeException NonReflectableType() + { + return RuntimeException.InvalidArgumentValue("Тип не может быть отражен."); + } + + /// + /// Получает таблицу методов для переданного объекта. + /// + /// Объект, из которого получаем таблицу методов. + /// Таблица значений с колонками: Имя, Количество, ЭтоФункция, Аннотации, Параметры, Экспорт + [ContextMethod("ПолучитьТаблицуМетодов", "GetMethodsTable")] + public ValueTable GetMethodsTable(BslValue target) + { + var result = new ValueTable(); + if (target is BslObjectValue) + FillMethodsTableForObject(target.AsObject(), result); + else if (target.SystemType == BasicTypes.Type) + FillMethodsTableForType(target as BslTypeValue, result); + else + throw RuntimeException.InvalidArgumentType(); + + return result; + } + + private static void FillMethodsTableForObject(IRuntimeContextInstance target, ValueTable result) + { + FillMethodsTable(result, target.GetMethods()); + } + + private static void FillMethodsTableForType(BslTypeValue type, ValueTable result) + { + var clrType = GetReflectableClrType(type); + var clrMethods = clrType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + FillMethodsTable(result, clrMethods.Cast()); + } + + private void FillPropertiesTableForObject(ValueTable result, IValue target, bool withPrivate) + { + if (target is ScriptDrivenObject scriptObject) + { + var fieldsQuery = scriptObject.Module.Fields.Cast(); + + if (!withPrivate) + { + fieldsQuery = fieldsQuery.Where(x => x.IsPublic); + } + + var fields = fieldsQuery.Select(field => BslPropertyBuilder.Create() + .Name(field.Name) + .IsExported(field.IsPublic) + .SetAnnotations(field.GetAnnotations()) + .SetDispatchingIndex(field.DispatchId) + .Build() + ) + .OrderBy(p => p.DispatchId) + .ToArray(); + + var fieldNames = fields.Select(x => x.Name) + .ToHashSet(); + + var properties = scriptObject.GetProperties() + .Where(prop => !fieldNames.Contains(prop.Name)); + + if (!withPrivate) + { + properties = properties.OfType() + .Where(p => p.IsExported); + } + + FillPropertiesTable(result, properties.Concat(fields)); + } + else + { + var objectProperties = target.AsObject().GetProperties(); + FillPropertiesTable(result, objectProperties); + } + } + + private static void FillPropertiesTableForType(BslTypeValue type, ValueTable result, bool withPrivate) + { + var clrType = GetReflectableClrType(type); + var nativeProps = clrType.GetProperties() + .Select(x => new + { + PropDef = x.GetCustomAttribute(), + Prop = x + }) + .Where(x => x.PropDef != null) + .Select(x => new ContextPropertyInfo(x.Prop)); + + var infos = new List(); + + infos.AddRange(nativeProps); + int indices = infos.Count; + + if (typeof(ScriptDrivenObject).IsAssignableFrom(clrType.BaseType)) + { + var flags = BindingFlags.Instance | BindingFlags.Public; + if (withPrivate) + flags |= BindingFlags.NonPublic; + + var nativeFields = clrType.GetFields(flags); + foreach (var field in nativeFields) + { + var prop = BslPropertyBuilder.Create() + .Name(field.Name) + .IsExported(field.IsPublic) + .SetDispatchingIndex(indices++) + .SetAnnotations(field.GetAnnotations()) + .Build(); + + infos.Add(prop); + } + } + + FillPropertiesTable(result, infos); + + } + + private static void FillMethodsTable(ValueTable result, IEnumerable methods) + { + var nameColumn = result.Columns.AddUnchecked("Имя"); + var countColumn = result.Columns.AddUnchecked("КоличествоПараметров", "Количество параметров"); + var isFunctionColumn = result.Columns.AddUnchecked("ЭтоФункция", "Это функция"); + var annotationsColumn = result.Columns.AddUnchecked("Аннотации"); + var paramsColumn = result.Columns.AddUnchecked("Параметры"); + var isExportlColumn = result.Columns.AddUnchecked("Экспорт"); + + foreach (var methInfo in methods) + { + var annotations = methInfo.GetAnnotations(); + var parameters = methInfo.GetBslParameters(); + + ValueTableRow new_row = result.Add(); + new_row.Set(nameColumn, ValueFactory.Create(methInfo.Name)); + new_row.Set(countColumn, ValueFactory.Create(parameters.Length)); + new_row.Set(isFunctionColumn, ValueFactory.Create(methInfo.IsFunction())); + new_row.Set(isExportlColumn, ValueFactory.Create(methInfo.IsPublic)); + + new_row.Set(annotationsColumn, CreateAnnotationTable(annotations)); + + var paramTable = new ValueTable(); + var paramNameColumn = paramTable.Columns.AddUnchecked("Имя"); + var paramByValue = paramTable.Columns.AddUnchecked("ПоЗначению", "По значению"); + var paramHasDefaultValue = paramTable.Columns.AddUnchecked("ЕстьЗначениеПоУмолчанию", "Есть значение по-умолчанию"); + var paramDefaultValue = paramTable.Columns.AddUnchecked("ЗначениеПоУмолчанию", "Значение по умолчанию"); + var paramAnnotationsColumn = paramTable.Columns.AddUnchecked("Аннотации"); + new_row.Set(paramsColumn, paramTable); + + if (parameters.Length != 0) + { + var index = 0; + foreach (var param in parameters) + { + var name = param.Name ?? $"param{++index}"; + var paramRow = paramTable.Add(); + paramRow.Set(paramNameColumn, ValueFactory.Create(name)); + paramRow.Set(paramByValue, ValueFactory.Create(!param.IsByRef())); + paramRow.Set(paramHasDefaultValue, ValueFactory.Create(param.HasDefaultValue)); + paramRow.Set(paramDefaultValue, param.DefaultValue as IValue); + paramRow.Set(paramAnnotationsColumn, CreateAnnotationTable(param.GetAnnotations())); + } + } + } + } + + /// + /// Получает таблицу свойств для переданного объекта. + /// + /// Объект, из которого получаем таблицу свойств. + /// Включить в результат приватные поля + /// Таблица значений с колонками - Имя, Аннотации, Экспорт + [ContextMethod("ПолучитьТаблицуСвойств", "GetPropertiesTable")] + public ValueTable GetPropertiesTable(BslValue target, bool withPrivate = false) + { + var result = new ValueTable(); + + if (target is BslObjectValue) + FillPropertiesTableForObject(result, target, withPrivate); + else if (target.SystemType == BasicTypes.Type) + { + var type = target as BslTypeValue; + FillPropertiesTableForType(type, result, withPrivate); + } + else + throw RuntimeException.InvalidArgumentType(); + + return result; + } + + /// + /// Получает свойство по его имени. + /// + /// Объект, свойство которого необходимо установить. + /// Имя свойства + /// Значение свойства + [ContextMethod("ПолучитьСвойство", "GetProperty")] + public IValue GetProperty(IRuntimeContextInstance target, string prop) + { + int propIdx; + if (target is ScriptDrivenObject script) + propIdx = script.FindAnyProperty(prop); + else + propIdx = target.GetPropertyNumber(prop); + return target.GetPropValue(propIdx); + } + + /// + /// Устанавливает свойство по его имени. + /// + /// Объект, свойство которого необходимо установить. + /// Имя свойства + /// Значение свойства. + [ContextMethod("УстановитьСвойство", "SetProperty")] + public void SetProperty(IRuntimeContextInstance target, string prop, IValue value) + { + int propIdx; + if (target is ScriptDrivenObject script) + propIdx = script.FindAnyProperty(prop); + else + propIdx = target.GetPropertyNumber(prop); + + if (target.IsPropWritable(propIdx)) + target.SetPropValue(propIdx, value); + else + throw PropertyAccessException.PropIsNotWritableException(prop); + } + + private static void FillPropertiesTable(ValueTable result, IEnumerable properties) + { + var nameColumn = result.Columns.AddUnchecked("Имя"); + var annotationsColumn = result.Columns.AddUnchecked("Аннотации"); + var isExportedColumn = result.Columns.AddUnchecked("Экспорт"); + + var systemVarNames = new string[] { "этотобъект", "thisobject" }; + + foreach (var propInfo in properties) + { + if (systemVarNames.Contains(propInfo.Name.ToLower())) continue; + + ValueTableRow new_row = result.Add(); + new_row.Set(nameColumn, ValueFactory.Create(propInfo.Name)); + + var annotations = propInfo.GetAnnotations(); + new_row.Set(annotationsColumn, annotations.Length != 0 ? CreateAnnotationTable(annotations) : EmptyAnnotationsTable()); + + if (propInfo is BslScriptPropertyInfo scriptProp) + { + new_row.Set(isExportedColumn, BslBooleanValue.Create(scriptProp.IsExported)); + } + else + { + new_row.Set(isExportedColumn, BslBooleanValue.Create(propInfo.CanRead)); + } + } + } + + public static Type ReflectUserType(string typeName) + { + IExecutableModule module; + try + { + module = AttachedScriptsFactory.GetModuleOfType(typeName); + } + catch (KeyNotFoundException) + { + throw NonReflectableType(); + } + + var builder = new ClassBuilder(typeof(UserScriptContextInstance)); + + return builder + .SetTypeName(typeName) + .SetModule(module) + .ExportDefaults() + .Build(); + } + + public static Type ReflectContext(Type clrType) + { + var attrib = clrType.GetCustomAttribute(); + if (attrib == null || !typeof(ContextIValueImpl).IsAssignableFrom(clrType)) + throw NonReflectableType(); + + var builder = new ClassBuilder(clrType); + + return builder.SetTypeName(attrib.Name) + .ExportDefaults() + .Build(); + } + + /// + /// Возвращает все известные типы + /// + /// Структура - Условия поиска. Ключ - имя колонки, значение - искомое значение + /// + /// ТаблицаЗначений: + /// * Имя - Строка - Имя типа + /// * Значение - Тип - Тип + /// * Примитивный - Булево - Это примитивный тип + /// * Пользовательский - Булево - Это пользовательский типа + /// * Коллекция - Булево - Это коллекция + /// + [ContextMethod("ИзвестныеТипы", "KnownTypes")] + public ValueTable KnownTypes(StructureImpl filter = default) + { + var result = new ValueTable(); + + var nameColumn = result.Columns.AddUnchecked("Имя"); + var valueColumn = result.Columns.AddUnchecked("Значение"); + var primitiveColumn = result.Columns.AddUnchecked("Примитивный"); + var userColumn = result.Columns.AddUnchecked("Пользовательский"); + var collectionColumn = result.Columns.AddUnchecked("Коллекция"); + + _typeManager.RegisteredTypes().ForEach(descriptor => + { + var row = result.Add(); + + row.Set(nameColumn, ValueFactory.Create(descriptor.ToString())); + row.Set(valueColumn, new BslTypeValue(descriptor)); + row.Set(primitiveColumn, ValueFactory.Create(descriptor.ImplementingClass.IsSubclassOf(typeof(BslPrimitiveValue)))); + row.Set(userColumn, ValueFactory.Create(descriptor.ImplementingClass == typeof(AttachedScriptsFactory))); + row.Set(collectionColumn, ValueFactory.Create( + descriptor.ImplementingClass.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollectionContext<>)) + )); + }); + + if (filter != default) + { + result = result.Copy(filter); + } + + return result; + } + + [ScriptConstructor] + public static ReflectorContext CreateNew(TypeActivationContext context) + { + return new ReflectorContext(context.TypeManager); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Regex/MatchCollectionImpl.cs b/src/OneScript.StandardLibrary/Regex/MatchCollectionImpl.cs similarity index 75% rename from src/ScriptEngine.HostedScript/Library/Regex/MatchCollectionImpl.cs rename to src/OneScript.StandardLibrary/Regex/MatchCollectionImpl.cs index 101c10575..35172dae2 100644 --- a/src/ScriptEngine.HostedScript/Library/Regex/MatchCollectionImpl.cs +++ b/src/OneScript.StandardLibrary/Regex/MatchCollectionImpl.cs @@ -6,15 +6,15 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Collections.Generic; +using OneScript.Contexts; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; - using RegExp = System.Text.RegularExpressions; -namespace ScriptEngine.HostedScript.Library.Regex +namespace OneScript.StandardLibrary.Regex { [ContextClass("КоллекцияСовпаденийРегулярногоВыражения", "RegExMatchCollection")] - public class MatchCollection : AutoContext, ICollectionContext, IEnumerable + public class MatchCollection : AutoCollectionContext { private readonly RegExp.MatchCollection _matches; private readonly RegExp.Regex _regex; @@ -32,21 +32,16 @@ public MatchCollection(RegExp.MatchCollection matches, RegExp.Regex regex) /// /// Количество полученных совпадений. [ContextMethod("Количество", "Count")] - public int Count() + public override int Count() { return _matches.Count; } - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - #endregion #region IEnumerable Members - public IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { foreach (RegExp.Match item in _matches) { @@ -59,20 +54,10 @@ public override IValue GetIndexedValue(IValue index) } #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } [ContextClass("КоллекцияГруппРегулярногоВыражения", "RegExGroupCollection")] - public class GroupCollection : AutoContext, ICollectionContext, IEnumerable + public class GroupCollection : AutoCollectionContext { private readonly RegExp.GroupCollection _groups; private readonly RegExp.Regex _regex; @@ -90,7 +75,7 @@ public GroupCollection(RegExp.GroupCollection groups, RegExp.Regex regex) /// /// Количество полученных групп. [ContextMethod("Количество", "Count")] - public int Count() + public override int Count() { return _groups.Count; } @@ -107,16 +92,11 @@ public GroupImpl FromName(string inputName) return new GroupImpl(_groups[(int)index], (int)index, _regex); } - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - #endregion #region IEnumerable Members - public IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { int i = 0; foreach (RegExp.Group item in _groups) @@ -131,15 +111,5 @@ public override IValue GetIndexedValue(IValue index) } #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - } } diff --git a/src/ScriptEngine.HostedScript/Library/Regex/MatchImpl.cs b/src/OneScript.StandardLibrary/Regex/MatchImpl.cs similarity index 98% rename from src/ScriptEngine.HostedScript/Library/Regex/MatchImpl.cs rename to src/OneScript.StandardLibrary/Regex/MatchImpl.cs index 28b4f039f..00484cf60 100644 --- a/src/ScriptEngine.HostedScript/Library/Regex/MatchImpl.cs +++ b/src/OneScript.StandardLibrary/Regex/MatchImpl.cs @@ -5,10 +5,11 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Contexts; using ScriptEngine.Machine.Contexts; using RegExp = System.Text.RegularExpressions; -namespace ScriptEngine.HostedScript.Library.Regex +namespace OneScript.StandardLibrary.Regex { [ContextClass("СовпадениеРегулярногоВыражения", "RegExMatch")] public class MatchImpl : AutoContext diff --git a/src/ScriptEngine.HostedScript/Library/Regex/Regex.cs b/src/OneScript.StandardLibrary/Regex/Regex.cs similarity index 82% rename from src/ScriptEngine.HostedScript/Library/Regex/Regex.cs rename to src/OneScript.StandardLibrary/Regex/Regex.cs index 6c1dfd6ce..4b0496966 100644 --- a/src/ScriptEngine.HostedScript/Library/Regex/Regex.cs +++ b/src/OneScript.StandardLibrary/Regex/Regex.cs @@ -1,123 +1,126 @@ -/*---------------------------------------------------------- -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.Linq; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -using RegExp = System.Text.RegularExpressions; - -namespace ScriptEngine.HostedScript.Library.Regex -{ - [ContextClass("РегулярноеВыражение", "Regex")] - public class RegExpImpl : AutoContext - { - private RegExp.Regex _regex; - private readonly string _pattern; - - public RegExpImpl(string pattern) - { - _pattern = pattern; - _regex = new RegExp.Regex(_pattern, RegExp.RegexOptions.IgnoreCase | RegExp.RegexOptions.Multiline ); - } - - /// - /// Проверяет, что строка совпадает с шаблоном регулярного выражения. - /// - /// Строка, которая проверяется. - /// Число. Необязательный параметр. По-умолчанию 0. Содержит стартовую позицию, начиная с которой надо анализировать текст. Нумерация позиций в отличии от 1С начинается с 0 - /// Признак совпадения. Булево. - [ContextMethod("Совпадает", "IsMatch")] - public IValue IsMatch(string inputString, int startAt = 0) - { - return ValueFactory.Create(_regex.IsMatch(inputString, startAt)); - } - - /// - /// Находит все совпадения в строке по шаблону регулярного выражения. - /// - /// Строка, которая проверяется. - /// Число. Необязательный параметр. По-умолчанию 0. Содержит стартовую позицию, начиная с которой надо анализировать текст. Нумерация позиций в отличии от 1С начинается с 0 - /// Коллекция совпадения (тип КоллекцияСовпаденийРегулярногоВыражения). - [ContextMethod("НайтиСовпадения", "Matches")] - public MatchCollection Matches(string inputString, int startAt = 0) - { - return new MatchCollection(_regex.Matches(inputString, startAt), _regex); - } - - /// - /// Разделяет исходную строку на части, используя как разделитель заданное регулярное выражение. - /// - /// Строка, которая проверяется. - /// Число. Необязательный параметр. По-умолчанию 0 (искать все). Содержит количество искомых элементов. - /// Число. Необязательный параметр. По-умолчанию 0. Содержит стартовую позицию, начиная с которой надо анализировать текст. Нумерация позиций в отличии от 1С начинается с 0 - /// Массив полученных строк. - [ContextMethod("Разделить", "Split")] - public ArrayImpl Split(string inputString, int count = 0, int startAt = 0) - { - string[] arrParsed = _regex.Split(inputString, count, startAt); - return new ArrayImpl(arrParsed.Select(x => ValueFactory.Create(x))); - } - - /// - /// Заменяет в исходной строке все вхождения регулярного выражения на СтрокаЗамены. - /// В строке замены можно использовать ссылки на захваченные группы, как $n, где n - номер захваченной группы ($0 - все захваченное выражение). - /// - /// Строка. Текст, в котором необходимо выполнить замены. - /// Строка. Текст, который будет вставляться в места замены. - /// Строку-результат замены. - [ContextMethod("Заменить", "Replace")] - public string Replace(string inputString, string replacement) - { - return _regex.Replace(inputString, replacement); - } - - /// - /// Признак Не учитывать регистр символов. Булево - /// - [ContextProperty("ИгнорироватьРегистр", "IgnoreCase")] - public bool IgnoreCase - { - get { return _regex.Options.HasFlag( RegExp.RegexOptions.IgnoreCase ); } - set { SetOption(value, RegExp.RegexOptions.IgnoreCase); } - } - - /// - /// Признак выполнения многострочного поиска. Булево - /// - [ContextProperty("Многострочный", "Multiline")] - public bool Multiline - { - get { return _regex.Options.HasFlag(RegExp.RegexOptions.Multiline); } - set { SetOption(value, RegExp.RegexOptions.Multiline); } - } - - /// - /// Конструктор создания регулярного выражения по заданному шаблону. - /// - /// Строка-шаблон регулярного выражения. - [ScriptConstructor(Name = "По регулярному выражению")] - public static RegExpImpl Constructor(IValue pattern) - { - var regex = new RegExpImpl(pattern.AsString()); - return regex; - } - - private void SetOption(bool value, RegExp.RegexOptions option) - { - var options = _regex.Options; - if (value) - options |= option; - else - options &= ~option; - - //приходится пересоздавать объект, т.к. опции объекта по умолчанию только read-only - _regex = new RegExp.Regex(_pattern, options); - } - } -} +/*---------------------------------------------------------- +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.Linq; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using RegExp = System.Text.RegularExpressions; + +namespace OneScript.StandardLibrary.Regex +{ + [ContextClass("РегулярноеВыражение", "Regex")] + public class RegExpImpl : AutoContext + { + private RegExp.Regex _regex; + private readonly string _pattern; + + public RegExpImpl(string pattern) + { + _pattern = pattern; + _regex = new RegExp.Regex(_pattern, RegExp.RegexOptions.IgnoreCase | RegExp.RegexOptions.Multiline ); + } + + /// + /// Проверяет, что в строке есть совпадение с шаблоном регулярного выражения. + /// + /// Строка, которая проверяется. + /// Число. Необязательный параметр. По умолчанию 0. Содержит стартовую позицию, начиная с которой надо анализировать текст. + /// Нумерация позиций, в отличие от 1С, начинается с 0 + /// Признак совпадения. Булево. + [ContextMethod("Совпадает", "IsMatch")] + public IValue IsMatch(string inputString, int startAt = 0) + { + return ValueFactory.Create(_regex.IsMatch(inputString, startAt)); + } + + /// + /// Находит все совпадения в строке по шаблону регулярного выражения. + /// + /// Строка, которая проверяется. + /// Число. Необязательный параметр. По умолчанию 0. Содержит стартовую позицию, начиная с которой надо анализировать текст. + /// Нумерация позиций, в отличие от 1С, начинается с 0 + /// Коллекция совпадения (тип КоллекцияСовпаденийРегулярногоВыражения). + [ContextMethod("НайтиСовпадения", "Matches")] + public MatchCollection Matches(string inputString, int startAt = 0) + { + return new MatchCollection(_regex.Matches(inputString, startAt), _regex); + } + + /// + /// Разделяет исходную строку на части, используя как разделитель заданное регулярное выражение. + /// + /// Строка, которая проверяется. + /// Число. Необязательный параметр. По умолчанию 0 (искать все). Содержит количество искомых элементов. + /// Число. Необязательный параметр. По умолчанию 0. Содержит стартовую позицию, начиная с которой надо анализировать текст. + /// Нумерация позиций, в отличие от 1С, начинается с 0 + /// Массив полученных строк. + [ContextMethod("Разделить", "Split")] + public ArrayImpl Split(string inputString, int count = 0, int startAt = 0) + { + string[] arrParsed = _regex.Split(inputString, count, startAt); + return new ArrayImpl(arrParsed.Select(x => ValueFactory.Create(x))); + } + + /// + /// Заменяет в исходной строке все вхождения регулярного выражения на СтрокаЗамены. + /// В строке замены можно использовать ссылки на захваченные группы как $n, где n - номер захваченной группы ($0 - все захваченное выражение). + /// + /// Строка. Текст, в котором необходимо выполнить замены. + /// Строка. Текст, который будет вставляться в места замены. + /// Строку-результат замены. + [ContextMethod("Заменить", "Replace")] + public string Replace(string inputString, string replacement) + { + return _regex.Replace(inputString, replacement); + } + + /// + /// Признак Не учитывать регистр символов. Булево + /// + [ContextProperty("ИгнорироватьРегистр", "IgnoreCase")] + public bool IgnoreCase + { + get { return _regex.Options.HasFlag( RegExp.RegexOptions.IgnoreCase ); } + set { SetOption(value, RegExp.RegexOptions.IgnoreCase); } + } + + /// + /// Признак выполнения многострочного поиска. Булево + /// + [ContextProperty("Многострочный", "Multiline")] + public bool Multiline + { + get { return _regex.Options.HasFlag(RegExp.RegexOptions.Multiline); } + set { SetOption(value, RegExp.RegexOptions.Multiline); } + } + + /// + /// Конструктор создания регулярного выражения по заданному шаблону. + /// + /// Строка-шаблон регулярного выражения. + [ScriptConstructor(Name = "По регулярному выражению")] + public static RegExpImpl Constructor(string pattern) + { + var regex = new RegExpImpl(pattern); + return regex; + } + + private void SetOption(bool value, RegExp.RegexOptions option) + { + var options = _regex.Options; + if (value) + options |= option; + else + options &= ~option; + + //приходится пересоздавать объект, т.к. опции объекта по умолчанию только read-only + _regex = new RegExp.Regex(_pattern, options); + } + } +} diff --git a/src/OneScript.StandardLibrary/SpecialFolderEnum.cs b/src/OneScript.StandardLibrary/SpecialFolderEnum.cs new file mode 100644 index 000000000..99b58fa73 --- /dev/null +++ b/src/OneScript.StandardLibrary/SpecialFolderEnum.cs @@ -0,0 +1,50 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; +using sysFolder = System.Environment.SpecialFolder; + +namespace OneScript.StandardLibrary +{ + /// + /// Системное перечисление для специальных папок. + /// + [SystemEnum("СпециальнаяПапка", "SpecialFolder")] + public class SpecialFolderEnum : EnumerationContext + { + private SpecialFolderEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + + } + + public static SpecialFolderEnum CreateInstance(ITypeManager typeManager) + { + var instance = EnumContextHelper.CreateClrEnumInstance( + typeManager, + (t,v) => new SpecialFolderEnum(t,v)); + + instance.WrapClrValue("МоиДокументы", "MyDocuments", sysFolder.Personal); + instance.WrapClrValue("ДанныеПриложений", "ApplicationData", sysFolder.ApplicationData); + instance.WrapClrValue("ЛокальныйКаталогДанныхПриложений", "LocalApplicationData", sysFolder.LocalApplicationData); + instance.WrapClrValue("РабочийСтол", "Desktop", sysFolder.Desktop); + instance.WrapClrValue("КаталогРабочийСтол", "DesktopDirectory", sysFolder.DesktopDirectory); + instance.WrapClrValue("МояМузыка", "MyMusic", sysFolder.MyMusic); + instance.WrapClrValue("МоиРисунки", "MyPictures", sysFolder.MyPictures); + instance.WrapClrValue("Шаблоны", "Templates", sysFolder.Templates); + instance.WrapClrValue("МоиВидеозаписи", "MyVideos", sysFolder.MyVideos); + instance.WrapClrValue("ОбщиеШаблоны", "CommonTemplates", sysFolder.CommonTemplates); + instance.WrapClrValue("ПрофильПользователя", "UserProfile", sysFolder.UserProfile); + instance.WrapClrValue("ОбщийКаталогДанныхПриложения", "CommonApplicationData", sysFolder.CommonApplicationData); + + return instance; + } + } + +} diff --git a/src/OneScript.StandardLibrary/StandardGlobalContext.cs b/src/OneScript.StandardLibrary/StandardGlobalContext.cs new file mode 100644 index 000000000..f12754f12 --- /dev/null +++ b/src/OneScript.StandardLibrary/StandardGlobalContext.cs @@ -0,0 +1,303 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + /// + /// Общие встроенные свойства и методы, присутствующие во всех контекстах исполнения + /// + [GlobalContext(Category = "Общие функции глобального контекста")] + public class StandardGlobalContext : GlobalContextBase + { + public StandardGlobalContext() + { + Chars = new SymbolsContext(); + } + + /// + /// Содержит набор системных символов. + /// + /// Набор системных символов. + [ContextProperty("Символы", "Chars", CanWrite = false)] + public SymbolsContext Chars { get; set; } + + /// + /// Явное освобождение ресурса через интерфейс IDisposable среды CLR. + /// + /// OneScript не выполняет подсчет ссылок на объекты, а полагается на сборщик мусора CLR. + /// Это значит, что объекты автоматически не освобождаются при выходе из области видимости. + /// + /// Метод ОсвободитьОбъект можно использовать для детерминированного освобождения ресурсов. Если объект поддерживает интерфейс IDisposable, то данный метод вызовет Dispose у данного объекта. + /// + /// Как правило, интерфейс IDisposable реализуется различными ресурсами (файлами, соединениями с ИБ и т.п.) + /// + /// Объект, ресурсы которого требуется освободить. + [ContextMethod("ОсвободитьОбъект", "FreeObject")] + public void DisposeObject(IRuntimeContextInstance obj) + { + var disposable = obj as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + } + + /// + /// OneScript не выполняет подсчет ссылок на объекты, а полагается на сборщик мусора CLR. + /// Это значит, что объекты автоматически не освобождаются при выходе из области видимости. + /// + /// С помощью данного метода можно запустить принудительную сборку мусора среды CLR. + /// Данные метод следует использовать обдуманно, поскольку вызов данного метода не гарантирует освобождение всех объектов. + /// Локальные переменные, например, до завершения текущего метода очищены не будут, + /// поскольку до завершения текущего метода CLR будет видеть, что они используются движком 1Script. + /// + /// + [ContextMethod("ВыполнитьСборкуМусора", "RunGarbageCollection")] + public void RunGarbageCollection() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + /// + /// Приостанавливает выполнение скрипта. + /// + /// Время приостановки в миллисекундах + [ContextMethod("Приостановить", "Sleep")] + public void Sleep(int delay) + { + System.Threading.Thread.Sleep(delay); + } + + [ContextMethod("КраткоеПредставлениеОшибки", "BriefErrorDescription")] + public string BriefErrorDescription(ExceptionInfoContext errInfo) + { + return errInfo.Description; + } + + [ContextMethod("ПодробноеПредставлениеОшибки", "DetailErrorDescription")] + public string DetailErrorDescription(ExceptionInfoContext errInfo) + { + return errInfo.GetDetailedDescription(); + } + + [ContextMethod("ТекущаяУниверсальнаяДата", "CurrentUniversalDate")] + public IValue CurrentUniversalDate() + { + return ValueFactory.Create(DateTime.UtcNow); + } + + [ContextMethod("ТекущаяУниверсальнаяДатаВМиллисекундах", "CurrentUniversalDateInMilliseconds")] + public long CurrentUniversalDateInMilliseconds() + { + return DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond; + } + + /// + /// Проверяет заполненность значения по принципу, заложенному в 1С:Предприятии + /// + /// + /// Булево. Истина, если значение считается заполненным. + [ContextMethod("ЗначениеЗаполнено","ValueIsFilled")] + public bool ValueIsFilled(IBslProcess process, BslValue value) + { + if (value == null) + { + return false; + } + + switch (value) + { + case IEmptyValueCheck emptyHandler: + return !emptyHandler.IsEmpty; + case BslStringValue v: + return !string.IsNullOrWhiteSpace(v.ToString()); + case ICollectionContext collection: + return collection.Count(process) != 0; + case BslNumericValue v: + return v != 0; + case BslUndefinedValue _: + return false; + case BslNullValue _: + return false; + case BslDateValue v: + return !((DateTime)v).Equals(DateTime.MinValue); + default: + return true; + } + } + + /// + /// Заполняет одноименные значения свойств одного объекта из другого + /// + /// Объект-приемник + /// Объект-источник + /// Заполняемые свойства (строка, через запятую) + /// Игнорируемые свойства (строка, через запятую) + [ContextMethod("ЗаполнитьЗначенияСвойств","FillPropertyValues")] + public void FillPropertyValues(IRuntimeContextInstance acceptor, IRuntimeContextInstance source, IValue filledProperties = null, IValue ignoredProperties = null) + { + string strFilled; + string strIgnored; + + if (filledProperties == null || filledProperties.SystemType == BasicTypes.Undefined) + { + strFilled = null; + } + else if (filledProperties.SystemType == BasicTypes.String) + { + strFilled = filledProperties.ToString(); + } + else + { + throw RuntimeException.InvalidArgumentType(3, nameof(filledProperties)); + } + + if (ignoredProperties == null || ignoredProperties.SystemType == BasicTypes.Undefined) + { + strIgnored = null; + } + else if (ignoredProperties.SystemType == BasicTypes.String) + { + strIgnored = ignoredProperties.ToString(); + } + else + { + throw RuntimeException.InvalidArgumentType(4, nameof(ignoredProperties)); + } + + FillPropertyValuesStr(acceptor, source, strFilled, strIgnored); + } + + private static void FillPropertyValuesStr(IRuntimeContextInstance acceptor, IRuntimeContextInstance source, string filledProperties = null, string ignoredProperties = null) + { + IEnumerable sourceProperties; + + if (filledProperties == null) + { + string[] names = new string[source.GetPropCount()]; + for (int i = 0; i < names.Length; i++) + { + names[i] = source.GetPropName(i); + } + + if (ignoredProperties == null) + { + sourceProperties = names; + } + else + { + IEnumerable ignoredPropCollection = ignoredProperties.Split(',') + .Select(x => x.Trim()) + .Where(x => x.Length > 0); + + sourceProperties = names.Where(x => !ignoredPropCollection.Contains(x)); + } + } + else + { + sourceProperties = filledProperties.Split(',') + .Select(x => x.Trim()) + .Where(x => x.Length > 0); + + // Проверка существования заявленных свойств + foreach (var item in sourceProperties) + { + acceptor.GetPropertyNumber(item); // бросает PropertyAccessException если свойства нет + } + } + + + foreach (var srcProperty in sourceProperties) + { + try + { + var srcPropIdx = source.GetPropertyNumber(srcProperty); + var accPropIdx = acceptor.GetPropertyNumber(srcProperty); // бросает PropertyAccessException если свойства нет + + if (source.IsPropReadable(srcPropIdx) && acceptor.IsPropWritable(accPropIdx)) + acceptor.SetPropValue(accPropIdx, source.GetPropValue(srcPropIdx)); + + } + catch (PropertyAccessException) + { + // игнорировать свойства Источника, которых нет в Приемнике + } + } + } + + /// + /// Получает объект класса COM по его имени или пути. Подробнее см. синтакс-помощник от 1С. + /// + /// Путь к библиотеке + /// Имя класса + /// COMОбъект + [ContextMethod("ПолучитьCOMОбъект", "GetCOMObject")] + public IValue GetCOMObject(string pathName = null, string className = null) + { + var comObject = GetCOMObjectInternal(pathName, className); + + return COMWrapperContext.Create(comObject); + } + + /// + /// Ported from Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + /// By JetBrains dotPeek decompiler + /// + private object GetCOMObjectInternal(string pathName = null, string className = null) + { + if (String.IsNullOrEmpty(className)) + { + return Marshal.BindToMoniker(pathName); + } + else if (pathName == null) + { +#if NET6_0_OR_GREATER + throw new NotSupportedException("Getting object by classname not supported on net6+"); +#else + return Marshal.GetActiveObject(className); +#endif + } + else if (pathName.Length == 0) + { + return Activator.CreateInstance(System.Type.GetTypeFromProgID(className)); + } + else + { +#if NET6_0_OR_GREATER + throw new NotSupportedException("Getting object by classname not supported on net6+"); +#else + var persistFile = (IPersistFile)Marshal.GetActiveObject(className); + persistFile.Load(pathName, 0); + + return (object)persistFile; +#endif + } + } + + #region Static infrastructure + + public static IAttachableContext CreateInstance() + { + return new StandardGlobalContext(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/StringOperations.cs b/src/OneScript.StandardLibrary/StringOperations.cs new file mode 100644 index 000000000..c68d50216 --- /dev/null +++ b/src/OneScript.StandardLibrary/StringOperations.cs @@ -0,0 +1,314 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Contexts.Enums; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System; +using System.Linq; + +namespace OneScript.StandardLibrary +{ + [GlobalContext(Category = "Операции со строками")] + public class StringOperations : GlobalContextBase + { + private static readonly System.Text.RegularExpressions.Regex _templateRe + = new System.Text.RegularExpressions.Regex(@"(%%)|%(\d+)|%\((\d+)\)|%", + System.Text.RegularExpressions.RegexOptions.Compiled); + + /// + /// Получает строку на языке, заданном во втором параметре (коды языков в соответствии с ISO 639-1) + /// или на текущем языке системы. + /// + /// Строка на нескольких языках + /// Код языка (если не указан, возвращает вариант для текущего языка системы, + /// если вариант не найден, то возвращает вариант для английского языка, + /// если не задан вариант для английского языка, то возвращает первый вариант из списка) + [ContextMethod("НСтр", "NStr")] + public string NStr(string src, string lang = null) + { + return Locale.NStr(src, lang); + } + + /// + /// Определяет, что строка начинается с указанной подстроки. + /// + /// Строка, начало которой проверяется на совпадение с подстрокой поиска. + /// Строка, содержащая предполагаемое начало строки. + /// В случае, если переданное значение является пустой строкой, генерируется исключительная ситуация. + [ContextMethod("СтрНачинаетсяС", "StrStartsWith")] + public bool StrStartsWith(string inputString, string searchString) + { + bool result = false; + + if (!string.IsNullOrEmpty(inputString)) + { + if (!string.IsNullOrEmpty(searchString)) + { + result = inputString.StartsWith(searchString); + } + else throw StringOpException.StrStartsWith(); + } + + return result; + } + + /// + /// Определяет, заканчивается ли строка указанной подстрокой. + /// + /// Строка, окончание которой проверяется на совпадение с подстрокой поиска. + /// Строка, содержащая предполагаемое окончание строки. + /// В случае, если переданное значение является пустой строкой, генерируется исключительная ситуация. + [ContextMethod("СтрЗаканчиваетсяНа", "StrEndsWith")] + public bool StrEndsWith(string inputString, string searchString) + { + bool result = false; + + if (!string.IsNullOrEmpty(inputString)) + { + if (!string.IsNullOrEmpty(searchString)) + { + result = inputString.EndsWith(searchString); + } + else throw StringOpException.StrEndsWith(); + } + + return result; + } + + /// + /// Разделяет строку на части по указанным символам-разделителям. + /// + /// Разделяемая строка. + /// Строка символов, каждый из которых является индивидуальным разделителем. + /// Указывает необходимость включать в результат пустые строки, + /// которые могут образоваться в результате разделения исходной строки. Значение по умолчанию: Истина. + [ContextMethod("СтрРазделить", "StrSplit")] + public ArrayImpl StrSplit(string inputString, string stringDelimiter, bool? includeEmpty = true) + { + string[] arrParsed; + if (includeEmpty == null) + includeEmpty = true; + + if (!string.IsNullOrEmpty(inputString)) + { + arrParsed = inputString.Split(stringDelimiter?.ToCharArray(), + (bool)includeEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); + } + else + { + arrParsed = (bool)includeEmpty ? new string[] { string.Empty } : Array.Empty(); + } + + return new ArrayImpl(arrParsed.Select(x => ValueFactory.Create(x))); + } + + /// + /// Соединяет массив переданных строк в одну строку с указанным разделителем + /// + /// Массив - соединяемые строки + /// Разделитель. Если не указан, строки объединяются слитно + [ContextMethod("СтрСоединить", "StrConcat")] + public string StrConcat(IBslProcess process, ArrayImpl input, string delimiter = null) + { + var strings = input.Select(x => x.AsString(process)); + + return String.Join(delimiter, strings); + } + + /// + /// Сравнивает строки без учета регистра. + /// + /// + /// + /// -1 первая строка больше, 1 - вторая строка больше. 0 - строки равны + [ContextMethod("СтрСравнить", "StrCompare")] + public int StrCompare(string first, string second) + { + return String.Compare(first, second, true); + } + + /// + /// Находит вхождение искомой строки как подстроки в исходной строке + /// + /// Строка, в которой ищем + /// Строка, которую надо найти + /// значение перечисления НаправлениеПоиска (с конца/с начала) + /// Начальная позиция, с которой начинать поиск + /// Указывает номер вхождения искомой подстроки в исходной строке + /// Позицию искомой строки в исходной строке. Возвращает 0, если подстрока не найдена. + [ContextMethod("СтрНайти", "StrFind")] + public int StrFind(string haystack, string needle, SearchDirection direction = SearchDirection.FromBegin, int startPos = 0, int occurance = 1) + { + if (needle == null || needle.Length == 0) + return 1; + + int len = haystack?.Length ?? 0; + if (len == 0) + return 0; + + bool fromBegin = direction == SearchDirection.FromBegin; + + if (startPos == 0) + { + startPos = fromBegin ? 1 : len; + } + else if (startPos < 1 || startPos > len) + throw RuntimeException.InvalidNthArgumentValue(4); + + if (occurance < 0) + return 0; + else if (occurance == 0) + throw RuntimeException.InvalidNthArgumentValue(5); + + int startIndex = startPos - 1; + int foundTimes = 0; + int index = len + 1; + + if(fromBegin) + { + while(foundTimes < occurance && index >= 0) + { + index = haystack.IndexOf(needle, startIndex, StringComparison.Ordinal); + if (index >= 0) + { + startIndex = index + 1; + foundTimes++; + } + if (startIndex >= len) + break; + } + } + else + { + while(foundTimes < occurance && index >= 0) + { + index = haystack.LastIndexOf(needle, startIndex, StringComparison.Ordinal); + if (index >= 0) + { + startIndex = index - 1; + foundTimes++; + } + if (startIndex < 0) + break; + } + } + + return foundTimes == occurance ? index + 1 : 0; + } + + /// + /// Подставляет параметры в строку по номеру + /// + /// Шаблон: строка, содержащая маркеры подстановки вида %N + /// Параметры, строковые представления которых должны быть подставлены в шаблон + /// Строка шаблона с подставленными параметрами + [ContextMethod("СтрШаблон", "StrTemplate")] + public string StrTemplate(string template, + string p1=null, string p2=null, string p3=null, string p4=null, string p5=null, + string p6=null, string p7=null, string p8=null, string p9=null, string p10=null) + { + var srcFormat = template ?? ""; + + var arguments = new [] { p10,p9,p8,p7,p6,p5,p4,p3,p2,p1 }; + int passedArgsCount = arguments + .SkipWhile(x => x == null) + .Count(); + + int maxNumber = 0; + var result = _templateRe.Replace(srcFormat, (m) => + { + if (m.Groups[1].Success) + return "%"; + + if(m.Groups[2].Success || m.Groups[3].Success) + { + var number = int.Parse(m.Groups[2].Success ? m.Groups[2].Value : m.Groups[3].Value); + + if (number < 1 || number > 10) + throw StringOpException.TemplateSubst(m.Index + 2, number); + + //FIXME: отключено, т.к. платформа игнорирует ошибку с недостаточным числом параметров + //if (number > passedArgsCount) + // throw RuntimeException.TooFewArgumentsPassed(); + + if (number > maxNumber) + maxNumber = number; + + return arguments[10-number] ?? ""; + } + + throw StringOpException.TemplateSyntax(m.Index + 2); + }); + + if (passedArgsCount > maxNumber) + throw RuntimeException.TooManyArgumentsPassed(); + + return result; + } + + public static IAttachableContext CreateInstance() + { + return new StringOperations(); + } + } + + + [EnumerationType("НаправлениеПоиска", "SearchDirection")] + public enum SearchDirection + { + [EnumValue("СНачала", "FromBegin")] + FromBegin, + [EnumValue("СКонца", "FromEnd")] + FromEnd + } + + + public class StringOpException : RuntimeException + { + public StringOpException(BilingualString message, Exception innerException) : base(message, + innerException) + {} + + public StringOpException(BilingualString message) : base(message) + {} + + public static StringOpException StrStartsWith() + { + return new StringOpException(new BilingualString( + "Ошибка при вызове метода контекста (СтрНачинаетсяС): Недопустимое значение параметра номер '2'", + "Error calling context method (StrStartsWith): Invalid parameter number '2' value")); + } + public static StringOpException StrEndsWith() + { + return new StringOpException(new BilingualString( + "Ошибка при вызове метода контекста (СтрЗаканчиваетсяНа): Недопустимое значение параметра номер '2'", + "Error calling context method (StrEndsWith): Invalid parameter number '2' value")); + } + + public static StringOpException TemplateSyntax(int pos) + { + return new StringOpException(new BilingualString( + $"Ошибка синтаксиса шаблона в позиции {pos}", + $"Template syntax error at position {pos}")); + } + + public static StringOpException TemplateSubst(int pos, int num) + { + return new StringOpException(new BilingualString( + $"Ошибка синтаксиса шаблона в позиции {pos}. Недопустимый номер подстановки: '{num}'", + $"Template syntax error at position {pos}. Invalid substitution number: '{num}'")); + } + } + +} diff --git a/src/ScriptEngine.HostedScript/Library/SymbolsContext.cs b/src/OneScript.StandardLibrary/SymbolsContext.cs similarity index 96% rename from src/ScriptEngine.HostedScript/Library/SymbolsContext.cs rename to src/OneScript.StandardLibrary/SymbolsContext.cs index 6c156487d..81c272301 100644 --- a/src/ScriptEngine.HostedScript/Library/SymbolsContext.cs +++ b/src/OneScript.StandardLibrary/SymbolsContext.cs @@ -5,9 +5,10 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Contexts; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary { [ContextClass("Символы", "Chars")] public sealed class SymbolsContext : AutoContext diff --git a/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs new file mode 100644 index 000000000..57b74b6b2 --- /dev/null +++ b/src/OneScript.StandardLibrary/SystemEnvironmentContext.cs @@ -0,0 +1,272 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Reflection; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.HostedScript.Library; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary +{ + /// + /// Класс предоставляет информацию о системе + /// + [ContextClass("СистемнаяИнформация", "SystemInfo")] + public class SystemEnvironmentContext : AutoContext + { + + private static readonly string _osKernelName; + private static readonly PlatformID _platformId; + + /// + /// Имя машины, на которой выполняется сценарий + /// + [ContextProperty("ИмяКомпьютера", "MachineName")] + public string MachineName => Environment.MachineName; + + /// + /// Версия операционной системы, на которой выполняется сценарий + /// + [ContextProperty("ВерсияОС", "OSVersion")] + public string OSVersion => Environment.OSVersion.VersionString; + + /// + /// Имя ядра ОС/ + /// + [ContextProperty("ИмяЯдра", "KernelName")] + public string KernelName => _osKernelName; // позволит различать linux/mac/hp-ux/sunos/... + + /// + /// Версия OneScript, выполняющая данный сценарий + /// + [ContextProperty("Версия", "Version")] + public string Version + { + get + { + var assembly = Assembly.GetExecutingAssembly(); + var informationVersion = assembly.GetCustomAttribute()? + .InformationalVersion ?? assembly.GetName().Version?.ToString() ?? ""; + + return informationVersion; + } + } + + /// + /// Тип операционной системы, на которой выполняется сценарий + /// + [ContextProperty("ТипПлатформы", "PlatformType")] + public PlatformTypeEnum PlatformType + { + get + { + switch (_platformId) { + case PlatformID.Win32NT: return Is64BitOperatingSystem ? PlatformTypeEnum.Windows_x86_64 : PlatformTypeEnum.Windows_x86; + case PlatformID.MacOSX: return Is64BitOperatingSystem ? PlatformTypeEnum.MacOS_x86_64 : PlatformTypeEnum.MacOS_x86; + case PlatformID.Unix: return Is64BitOperatingSystem ? PlatformTypeEnum.Linux_x86_64 : PlatformTypeEnum.Linux_x86; + default: return PlatformTypeEnum.Unknown; + } + } + } + + /// + /// Имя пользователя ОС с учетом домена + /// Формат строки: \\ИмяДомена\ИмяПользователя. + /// + [ContextProperty("ПользовательОС", "OSUser")] + public string OSUser + { + get + { + string DomainName = Environment.UserDomainName; + + if (DomainName != "") + { + return @"\\" + DomainName + @"\" + Environment.UserName; + } + + return Environment.UserName; + } + } + + /// + /// Определяет, является ли текущая операционная система 64-разрядной. + /// + [ContextProperty("Это64БитнаяОперационнаяСистема")] + public bool Is64BitOperatingSystem => Environment.Is64BitOperatingSystem; + + /// + /// Возвращает число процессоров. + /// 32-битовое целое число со знаком, которое возвращает количество процессоров на текущем компьютере. + /// Значение по умолчанию отсутствует. Если текущий компьютер содержит несколько групп процессоров, + /// данное свойство возвращает число логических процессоров, доступных для использования средой CLR + /// + [ContextProperty("КоличествоПроцессоров")] + public int ProcessorCount => Environment.ProcessorCount; + + /// + /// Возвращает количество байтов на странице памяти операционной системы + /// + [ContextProperty("РазмерСистемнойСтраницы")] + public int SystemPageSize => Environment.SystemPageSize; + + /// + /// Возвращает время, истекшее с момента загрузки системы (в миллисекундах). + /// + [ContextProperty("ВремяРаботыСМоментаЗагрузки")] + public long TickCount + { + get + { + var unsig = (uint)Environment.TickCount; + return unsig; + } + } + + /// + /// Возвращает путь для специальной папки. Поддерживаемые значения: + /// + /// * МоиДокументы / MyDocuments + /// * ДанныеПриложений / ApplicationData + /// * ЛокальныйКаталогДанныхПриложений / LocalApplicationData + /// * РабочийСтол / Desktop + /// * КаталогРабочийСтол / DesktopDirectory + /// * МояМузыка / MyMusic + /// * МоиРисунки / MyPictures + /// * Шаблоны / Templates + /// * МоиВидеозаписи / MyVideos + /// * ОбщиеШаблоны / CommonTemplates + /// * ПрофильПользователя / UserProfile + /// * ОбщийКаталогДанныхПриложения / CommonApplicationData + /// + /// Тип: СпециальнаяПапка + /// Строка + [ContextMethod("ПолучитьПутьПапки")] + public string GetFolderPath(IValue folder) + { + var typedValue = folder as ClrEnumValueWrapper; + if (typedValue == null) + throw RuntimeException.InvalidArgumentType(); + + return Environment.GetFolderPath(typedValue.UnderlyingValue); + + } + + /// + /// Возвращает массив строк, содержащий имена логических дисков текущего компьютера. + /// + [ContextProperty("ИменаЛогическихДисков")] + public FixedArrayImpl GetLogicalDrives + { + get + { + var arr = new ArrayImpl(); + var data = Environment.GetLogicalDrives(); + foreach (var itm in data) + { + arr.Add(ValueFactory.Create(itm)); + } + return new FixedArrayImpl(arr); + } + } + + + /// + /// Возвращает соответствие переменных среды. Ключом является имя переменной, а значением - значение переменной + /// + /// + /// СИ = Новый СистемнаяИнформация(); + /// Для Каждого Переменная Из СИ.ПеременныеСреды() Цикл + /// Сообщить(Переменная.Ключ + " = " + Переменная.Значение); + /// КонецЦикла; + /// + /// Соответствие + [ContextMethod("ПеременныеСреды", "EnvironmentVariables")] + public MapImpl EnvironmentVariables() + { + SystemLogger.Write("WARNING! Deprecated method: 'SystemInfo.EnvironmentVariables' is deprecated, use 'EnvironmentVariables' from global context"); + var varsMap = new MapImpl(); + var allVars = Environment.GetEnvironmentVariables(); + foreach (DictionaryEntry item in allVars) + { + varsMap.Insert( + ValueFactory.Create((string)item.Key), + ValueFactory.Create((string)item.Value)); + } + + return varsMap; + } + + /// + /// Позволяет установить переменную среды. + /// Переменная устанавливается в области видимости процесса и очищается после его завершения. + /// + /// Имя переменной + /// Значение переменной + [ContextMethod("УстановитьПеременнуюСреды","SetEnvironmentVariable")] + public void SetEnvironmentVariable(string varName, string value) + { + SystemLogger.Write(string.Format(Locale.NStr("en='{0}';ru='{1}'"), + "WARNING! Deprecated method: \"SystemInfo.SetEnvironmentVariable\" is deprecated, use \"SetEnvironmentVariable\" from global context", + "Предупреждение! Устаревший метод: \"СистемнаяИнформация.УстановитьПеременнуюСреды\" устарел, используйте метод глобального контекста \"УстановитьПеременнуюСреды\"")); + Environment.SetEnvironmentVariable(varName, value); + } + + /// + /// Получить значение переменной среды. + /// + /// Имя переменной + /// Строка. Значение переменной + [ContextMethod("ПолучитьПеременнуюСреды", "GetEnvironmentVariable")] + public IValue GetEnvironmentVariable(string varName) + { + SystemLogger.Write(string.Format(Locale.NStr("en='{0}';ru='{1}'"), + "WARNING! Deprecated method: \"SystemInfo.GetEnvironmentVariable\" is deprecated, use \"GetEnvironmentVariable\" from global context", + "Предупреждение! Устаревший метод: \"СистемнаяИнформация.ПолучитьПеременнуюСреды\" устарел, используйте метод глобального контекста \"ПолучитьПеременнуюСреды\"")); + string value = Environment.GetEnvironmentVariable(varName); + if (value == null) + return ValueFactory.Create(); + return ValueFactory.Create(value); + + } + + [ScriptConstructor] + public static SystemEnvironmentContext Create() + { + return new SystemEnvironmentContext(); + } + + static SystemEnvironmentContext() + { + _platformId = Environment.OSVersion.Platform; + switch (_platformId) + { + case PlatformID.Unix: + _osKernelName = SystemHelper.UnixKernelName(); + if (_osKernelName == "Darwin") + { + _platformId = PlatformID.MacOSX; + } + break; + case PlatformID.Win32NT: + _osKernelName = "WindowsNT"; + break; + case PlatformID.MacOSX: + _osKernelName = "Darwin"; + break; + } + } + } +} diff --git a/src/OneScript.StandardLibrary/SystemHelper.cs b/src/OneScript.StandardLibrary/SystemHelper.cs new file mode 100644 index 000000000..71bf18f22 --- /dev/null +++ b/src/OneScript.StandardLibrary/SystemHelper.cs @@ -0,0 +1,48 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.InteropServices; + +namespace ScriptEngine.HostedScript.Library +{ + internal static class SystemHelper + { + public static string UnixKernelName() + { + return KernelNameInternal(); + } + + [DllImport("libc")] + private static extern int uname(IntPtr buf); + + private static string KernelNameInternal() + { + IntPtr buf = IntPtr.Zero; + try + { + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_utsname.h.html + // стандартом размер структуры не определён, поэтому считаем, что 8K хватит всем + buf = Marshal.AllocHGlobal(8192); + if (uname(buf) == 0) + { + var osKernelName = Marshal.PtrToStringAnsi(buf); + return osKernelName; + } + } + finally + { + if (buf != IntPtr.Zero) + { + Marshal.FreeHGlobal(buf); + } + } + + return null; + } + } +} diff --git a/src/OneScript.StandardLibrary/Tasks/BackgroundTask.cs b/src/OneScript.StandardLibrary/Tasks/BackgroundTask.cs new file mode 100644 index 000000000..d3dd251e3 --- /dev/null +++ b/src/OneScript.StandardLibrary/Tasks/BackgroundTask.cs @@ -0,0 +1,122 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Threading.Tasks; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Tasks +{ + [ContextClass("ФоновоеЗадание", "BackgroundTask")] + public class BackgroundTask : AutoContext + { + private readonly BslMethodInfo _method; + private readonly int _methIndex; + private Task _workerTask; + private int _taskId; + + public BackgroundTask(IRuntimeContextInstance target, string methodName, ArrayImpl parameters = default) + { + Target = target; + MethodName = methodName; + if(parameters != default) + Parameters = new ArrayImpl(parameters); + + Identifier = new GuidWrapper(); + + _methIndex = Target.GetMethodNumber(MethodName); + _method = Target.GetMethodInfo(_methIndex); + } + + public Task WorkerTask + { + get => _workerTask; + set + { + _workerTask = value; + _taskId = _workerTask.Id; + } + } + + public int TaskId => _taskId; + + [ContextProperty("УникальныйИдентификатор","UUID")] + public GuidWrapper Identifier { get; private set; } + + [ContextProperty("ИмяМетода","MethodName")] + public string MethodName { get; private set; } + + [ContextProperty("Объект","Object")] + public IRuntimeContextInstance Target { get; private set; } + + [ContextProperty("Состояние", "State")] + public TaskStateEnum State { get; private set; } + + [ContextProperty("Параметры", "Parameters")] + public IValue Parameters { get; private set; } = ValueFactory.Create(); + + [ContextProperty("Результат", "Result")] + public IValue Result { get; private set; } = ValueFactory.Create(); + + [ContextProperty("ИнформацияОбОшибке", "ExceptionInfo")] + public ExceptionInfoContext ExceptionInfo { get; private set; } + + /// + /// Ждать завершения задания указанное число миллисекунд + /// + /// Таймаут в миллисекундах. Если ноль - ждать вечно + /// Истина - дождались завершения. Ложь - сработал таймаут + [ContextMethod("ОжидатьЗавершения", "Wait")] + public bool Wait(int timeout = 0) + { + timeout = BackgroundTasksManager.ConvertTimeout(timeout); + + return WorkerTask.Wait(timeout); + } + + public void ExecuteOnCurrentThread(IBslProcess process) + { + if (State != TaskStateEnum.NotRunned) + throw new RuntimeException(Locale.NStr("ru = 'Неверное состояние задачи';en = 'Incorrect task status'")); + + var parameters = Parameters is ArrayImpl array ? + array.ToArray() : Array.Empty(); + + try + { + State = TaskStateEnum.Running; + if (_method.IsFunction()) + { + Target.CallAsFunction(_methIndex, parameters, out var result, process); + Result = result; + } + else + { + Target.CallAsProcedure(_methIndex, parameters, process); + } + + State = TaskStateEnum.Completed; + } + catch (ScriptException exception) + { + State = TaskStateEnum.CompletedWithErrors; + exception.RuntimeSpecificInfo = process.Services + .TryResolve()?.Machine?.GetExecutionFrames(); + + ExceptionInfo = new ExceptionInfoContext(exception); + } + } + } +} diff --git a/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs b/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs new file mode 100644 index 000000000..9999dd86c --- /dev/null +++ b/src/OneScript.StandardLibrary/Tasks/BackgroundTasksManager.cs @@ -0,0 +1,230 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ExecutionContext = ScriptEngine.Machine.ExecutionContext; + +namespace OneScript.StandardLibrary.Tasks +{ + [ContextClass("МенеджерФоновыхЗаданий", "BackgroundTasksManager")] + public class BackgroundTasksManager : AutoContext, IDisposable + { + private readonly ExecutionContext _runtimeContext; + private List _tasks = new List(); + + public BackgroundTasksManager(ExecutionContext runtimeContext) + { + _runtimeContext = runtimeContext; + } + + /// + /// Создать и стартовать задание + /// + /// Объект, метод которого нужно выполнить + /// Имя экспортного метода в объекте + /// Массив параметров метода + /// Задание выполняется длительное время и требует себе отдельный поток (не использует пул потоков) + /// ФоновоеЗадание + [ContextMethod("Выполнить", "Execute")] + public BackgroundTask Execute(IRuntimeContextInstance target, string methodName, ArrayImpl parameters = null, bool longRunning = false) + { + var task = new BackgroundTask(target, methodName, parameters); + _tasks.Add(task); + + var taskCreationOptions = longRunning ? TaskCreationOptions.LongRunning : TaskCreationOptions.None; + var worker = new Task(() => + { + var process = _runtimeContext.Services.Resolve().NewProcess(); + task.ExecuteOnCurrentThread(process); + + }, taskCreationOptions); + + task.WorkerTask = worker; + worker.Start(); + + return task; + } + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _tasks.Clear(); + } + + /// + /// Ожидает завершения всех переданных заданий + /// + /// Массив заданий + /// Таймаут ожидания. 0 = ожидать бесконечно + /// Истина - дождались все задания, Ложь - истек таймаут + [ContextMethod("ОжидатьВсе", "WaitAll")] + public bool WaitAll(ArrayImpl tasks, int timeout = 0) + { + var workers = GetWorkerTasks(tasks); + timeout = ConvertTimeout(timeout); + + // Фоновые задания перехватывают исключения внутри себя + // и выставляют свойство ИнформацияОбОшибке + // если WaitAll выбросит исключение, значит действительно что-то пошло не так на уровне самого Task + return Task.WaitAll(workers, timeout); + } + + /// + /// Ожидать хотя бы одно из переданных заданий. + /// + /// Массив заданий + /// Таймаут ожидания. 0 = ожидать бесконечно + /// Число. Индекс в массиве заданий, указывающий на элемент-задание, которое завершилось. -1 = сработал таймаут + [ContextMethod("ОжидатьЛюбое", "WaitAny")] + public int WaitAny(ArrayImpl tasks, int timeout = 0) + { + var workers = GetWorkerTasks(tasks); + timeout = ConvertTimeout(timeout); + + // Фоновые задания перехватывают исключения внутри себя + // и выставляют свойство ИнформацияОбОшибке + // если WaitAny выбросит исключение, значит действительно что-то пошло не так на уровне самого Task + return Task.WaitAny(workers, timeout); + } + + /// + /// Блокирует поток до завершения всех заданий. + /// Выбрасывает исключение, если какие-то задания завершились аварийно. + /// Выброшенное исключение в свойстве Параметры содержит массив аварийных заданий. + /// + [ContextMethod("ОжидатьЗавершенияЗадач", "WaitCompletionOfTasks")] + public void WaitCompletionOfTasks() + { + lock (_tasks) + { + var workers = GetWorkerTasks(); + Task.WaitAll(workers); + + var failedTasks = _tasks.Where(x => x.State == TaskStateEnum.CompletedWithErrors) + .ToList(); + + if (failedTasks.Count != 0) + { + throw new ParametrizedRuntimeException( + Locale.NStr("ru = 'Задания завершились с ошибками';en = 'Tasks are completed with errors'"), + new ArrayImpl(failedTasks)); + } + + _tasks.Clear(); + } + } + + [ContextMethod("ПолучитьФоновыеЗадания", "GetBackgroundJobs")] + public ArrayImpl GetBackgroundJobs(StructureImpl filter = default) + { + if(filter == default) + return new ArrayImpl(_tasks.ToArray()); + + var arr = new ArrayImpl(); + foreach (var task in _tasks) + { + var result = true; + foreach (var filterItem in filter) + { + switch (filterItem.Key.ToString()!.ToLower()) + { + case "состояние": + case "state": + var enumval = filterItem.Value as ClrEnumValueWrapper; + if(enumval == default) + continue; + + result = result && task.State == enumval.UnderlyingValue; + break; + + case "имяметода": + case "methodname": + result = result && task.MethodName.ToLower() == filterItem.Value.ToString(); + break; + + case "объект": + case "object": + result = result && task.Target.Equals(filterItem.Value); + break; + + case "уникальныйидентификатор": + case "uuid": + result = result && task.Identifier.Equals(filterItem.Value); + break; + } + } + + if(result) + arr.Add(task); + } + + return arr; + } + + [ContextMethod("ПолучитьТекущее", "GetCurrent")] + public IValue GetCurrent() + { + var currentId = Task.CurrentId; + if (currentId == null) + return ValueFactory.Create(); + + var task = _tasks.FirstOrDefault(x => x.TaskId == (int) currentId && x.State == TaskStateEnum.Running); + return task ?? ValueFactory.Create(); + } + + internal static int ConvertTimeout(int timeout) + { + if(timeout < 0) + throw RuntimeException.InvalidArgumentValue(); + + return timeout == 0 ? Timeout.Infinite : timeout; + } + + private static Task[] GetWorkerTasks(ArrayImpl tasks) + { + return tasks + .Cast() + .Select(x => x.WorkerTask) + .ToArray(); + } + + private static Task[] GetWorkerTasks(IEnumerable tasks) + { + return tasks.Select(x => x.WorkerTask).ToArray(); + } + + private Task[] GetWorkerTasks() + { + return GetWorkerTasks(_tasks); + } + + public void Dispose() + { + Task.WaitAll(GetWorkerTasks()); + _tasks.Clear(); + } + + [ScriptConstructor] + public static BackgroundTasksManager Create(TypeActivationContext context) + { + return new BackgroundTasksManager(context.Services.Resolve()); + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Tasks/TaskStateEnum.cs b/src/OneScript.StandardLibrary/Tasks/TaskStateEnum.cs new file mode 100644 index 000000000..970dd14e6 --- /dev/null +++ b/src/OneScript.StandardLibrary/Tasks/TaskStateEnum.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Tasks +{ + [EnumerationType("СостояниеФоновогоЗадания", "BackgroundJobState")] + public enum TaskStateEnum + { + [EnumValue("НеВыполнялось", "NotRunned")] + NotRunned, + [EnumValue("Активно", "Active")] + Running, + [EnumValue("Завершено", "Completed")] + Completed, + [EnumValue("ЗавершеноАварийно", "Failed")] + CompletedWithErrors + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Text/ByteOrderMarkUsageEnum.cs b/src/OneScript.StandardLibrary/Text/ByteOrderMarkUsageEnum.cs new file mode 100644 index 000000000..8ad031141 --- /dev/null +++ b/src/OneScript.StandardLibrary/Text/ByteOrderMarkUsageEnum.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Text +{ + [EnumerationType("ИспользованиеByteOrderMark", "ByteOrderMarkUsage")] + public enum ByteOrderMarkUsageEnum + { + [EnumValue("Авто", "Auto")] + Auto, + + [EnumValue("Использовать", "Use")] + Use, + + [EnumValue("НеИспользовать", "DontUse")] + DontUse + } +} diff --git a/src/OneScript.StandardLibrary/Text/ConsoleColorEnum.cs b/src/OneScript.StandardLibrary/Text/ConsoleColorEnum.cs new file mode 100644 index 000000000..5a6101f00 --- /dev/null +++ b/src/OneScript.StandardLibrary/Text/ConsoleColorEnum.cs @@ -0,0 +1,45 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Text +{ + [SystemEnum("ЦветКонсоли", "ConsoleColor")] + public class ConsoleColorEnum : ClrEnumWrapper + { + private ConsoleColorEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + this.WrapClrValue("Черный", "Black", ConsoleColor.Black); + this.WrapClrValue("ТемноСиний", "DarkBlue", ConsoleColor.DarkBlue); + this.WrapClrValue("ТемноЗеленый", "DarkGreen", ConsoleColor.DarkGreen); + this.WrapClrValue("ТемноБирюзовый", "DarkCyan", ConsoleColor.DarkCyan); + this.WrapClrValue("ТемноКрасный", "DarkRed", ConsoleColor.DarkRed); + this.WrapClrValue("ТемноМалиновый", "DarkMagenta", ConsoleColor.DarkMagenta); + this.WrapClrValue("ТемноЖелтый", "DarkYellow", ConsoleColor.DarkYellow); + this.WrapClrValue("Серый", "Gray", ConsoleColor.Gray); + + this.WrapClrValue("ТемноСерый", "DarkGray", ConsoleColor.DarkGray); + this.WrapClrValue("Синий", "Blue", ConsoleColor.Blue); + this.WrapClrValue("Зеленый", "Green", ConsoleColor.Green); + this.WrapClrValue("Бирюза", "Cyan", ConsoleColor.Cyan); + this.WrapClrValue("Красный", "Red", ConsoleColor.Red); + this.WrapClrValue("Малиновый", "Magenta", ConsoleColor.Magenta); + this.WrapClrValue("Желтый", "Yellow", ConsoleColor.Yellow); + this.WrapClrValue("Белый", "White", ConsoleColor.White); + } + + public static ConsoleColorEnum CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new ConsoleColorEnum(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/Text/ConsoleContext.cs b/src/OneScript.StandardLibrary/Text/ConsoleContext.cs new file mode 100644 index 000000000..ccc2dca56 --- /dev/null +++ b/src/OneScript.StandardLibrary/Text/ConsoleContext.cs @@ -0,0 +1,284 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary.Binary; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Text +{ + /// + /// Класс представляет собой инструмент доступа к системной консоли. + /// Предназначен для низкоуровнего манипулирования выводом в консоль. + /// + /// Поддерживается регистрация обработчика для нажатия Ctrl+C. + /// Обработчик регистрируется для события с именем CancelKeyPressed. Стоит учитывать, что обработчик вызывается + /// в отдельном потоке и необходимо помнить о потокобезопасности общих данных. + /// + /// + /// + /// Перем Хватит; + /// + /// Процедура Обработчик(Отказ) + /// Сообщить("Обработчик остановки вызван", СтатусСообщения.Важное); + /// Отказ = Истина; // не даем системе убить процесс + /// Хватит = Истина; // выставим свой флаг завершения цикла + /// КонецПроцедуры + /// + /// Хватит = Ложь; + /// ДобавитьОбработчик Консоль.CancelKeyPressed, Обработчик; + /// + /// Пока Не Хватит Цикл + /// Сообщить("Пинг", СтатусСообщения.Информация); + /// Приостановить(1000); + /// Сообщить("Понг", СтатусСообщения.Информация); + /// Приостановить(1000); + /// КонецЦикла; + /// + /// Сообщить("Мягкая остановка процесса", СтатусСообщения.Информация); + /// + [ContextClass("Консоль", "Console")] + public class ConsoleContext : AutoContext + { + private readonly ExecutionContext _executionContext; + + public ConsoleContext(ExecutionContext executionContext) + { + _executionContext = executionContext; + Console.CancelKeyPress += ConsoleOnCancelKeyPress; + } + + [ContextProperty("НажатаКлавиша", "KeyPressed")] + public bool HasKey => Console.KeyAvailable; + + [ContextProperty("КурсорЛево", "CursorLeft")] + public int XPos + { + get => Console.CursorLeft; + set => Console.CursorLeft = Math.Min(value, Console.WindowWidth-1); + } + + [ContextProperty("КурсорВерх", "CursorTop")] + public int YPos + { + get => Console.CursorTop; + set => Console.CursorTop = Math.Min(value, Console.WindowHeight-1); + } + + [ContextMethod("ПрочитатьСтроку", "ReadLine")] + public string ReadLine() + { + return Console.ReadLine(); + } + + [ContextMethod("Прочитать", "Read")] + public int ReadKey() + { + var kki = Console.ReadKey(true); + return (int)kki.Key; + } + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + Console.Clear(); + } + + [ContextMethod("ВывестиСтроку", "WriteLine")] + public void WriteLine(string text) + { + Console.WriteLine(text); + } + + [ContextMethod("Вывести", "Write")] + public void Write(string text) + { + Console.Write(text); + } + + [ContextProperty("Ширина", "Width")] + public int Width => Console.WindowWidth; + + [ContextProperty("Высота", "Height")] + public int Height => Console.WindowHeight; + + [ContextMethod("ВидимостьКурсора", "CursorVisible")] + public bool CursorVisible(bool visible) + { + bool oldVal = Console.CursorVisible; + Console.CursorVisible = visible; + return oldVal; + } + + [ContextProperty("ЦветТекста", "TextColor")] + public ClrEnumValueWrapper TextColor + { + get => + ConsoleColorEnum.Instance.TryGetFromNativeValue(Console.ForegroundColor) + ?? ConsoleColorEnum.Instance.FromNativeValue(ConsoleColor.Gray); + + set => Console.ForegroundColor = value.UnderlyingValue; + } + + [ContextProperty("ЦветФона", "BackgroundColor")] + public ClrEnumValueWrapper BackgroundColor + { + get => + ConsoleColorEnum.Instance.TryGetFromNativeValue(Console.BackgroundColor) + ?? ConsoleColorEnum.Instance.FromNativeValue(ConsoleColor.Black); + + set => Console.BackgroundColor = value.UnderlyingValue; + } + + /// + /// Возвращает или задает кодировку консоли, используемую при чтении входных данных. + /// + /// КодировкаТекста + [ContextProperty("КодировкаВходногоПотока", "InputEncoding")] + public IValue InputEncoding + { + get + { + var encodingEnum = _executionContext.GlobalInstances.GetInstance(); + return encodingEnum.GetValue(Console.InputEncoding); + } + set + { + Console.InputEncoding = TextEncodingEnum.GetEncoding(value); + } + } + + /// + /// Сбрасывает цвета консоли к их исходному состоянию + /// + [ContextMethod("СброситьЦвет", "ResetColor")] + public void ResetColor() + { + Console.ResetColor(); + } + + /// + /// Возвращает или задает кодировку консоли, используемую при выводе данных. + /// + /// КодировкаТекста + [ContextProperty("КодировкаВыходногоПотока", "OutputEncoding")] + public IValue OutputEncoding + { + get + { + var encodingEnum = _executionContext.GlobalInstances.GetInstance(); + return encodingEnum.GetValue(Console.OutputEncoding); + } + set + { + Console.OutputEncoding = TextEncodingEnum.GetEncoding(value); + } + } + + /// + /// Воспроизводит звуковой сигнал. + /// + [ContextMethod("Сигнал", "Beep")] + public void Beep() + { + Console.Beep(); + } + + /// + /// Получает системный поток ввода stdin + /// + /// Поток + [ContextMethod("ОткрытьСтандартныйПотокВвода", "OpenStandardInput")] + public GenericStream OpenStandardInput() + { + var stream = Console.OpenStandardInput(); + var streamWithTimeout = new StreamWithTimeout(stream); + return new GenericStream(streamWithTimeout, true); + } + + /// + /// Получает системный поток вывода ошибок stderr + /// + /// Поток + [ContextMethod("ОткрытьСтандартныйПотокОшибок", "OpenStandardError")] + public GenericStream OpenStandardError() + { + var stream = Console.OpenStandardError(); + return new GenericStream(stream); + } + + /// + /// Получает системный поток вывода stdout + /// + /// Поток + [ContextMethod("ОткрытьСтандартныйПотокВывода", "OpenStandardOutput")] + public GenericStream OpenStandardOutput() + { + var stream = Console.OpenStandardOutput(); + return new GenericStream(stream); + } + + /// + /// Глобально переопределяет стандартный вывод и направляет в другой поток + /// + /// Поток назначения + [ContextMethod("УстановитьПотокВывода", "SetOutput")] + public void SetOutput(IValue target) + { + if (!(target.AsObject() is IStreamWrapper stream)) + throw RuntimeException.InvalidArgumentType(nameof(target)); + + var writer = new StreamWriter(stream.GetUnderlyingStream(), Console.OutputEncoding) + { + AutoFlush = true, + }; + Console.SetOut(writer); + } + + /// + /// Глобально переопределяет стандартный поток ошибок и направляет в другой поток + /// + /// Поток назначения + [ContextMethod("УстановитьПотокОшибок", "SetError")] + public void SetError(IValue target) + { + if (!(target.AsObject() is IStreamWrapper stream)) + throw RuntimeException.InvalidArgumentType(nameof(target)); + + var writer = new StreamWriter(stream.GetUnderlyingStream()); + Console.SetError(writer); + } + + public const string ConsoleCancelKeyEvent = "CancelKeyPressed"; + + private void ConsoleOnCancelKeyPress(object sender, ConsoleCancelEventArgs e) + { + if (e.SpecialKey != ConsoleSpecialKey.ControlC) + return; + + // Сначала проверим, что события вообще включены + var eventProcessor = _executionContext.Services.TryResolve(); + if (eventProcessor == null) + return; + + var process = _executionContext.Services.Resolve().NewProcess(); + + var cancelVar = Variable.Create(BslBooleanValue.False, "Cancel"); + var reference = Variable.CreateReference(cancelVar, "Cancel"); + var args = new IValue[] { reference }; + + eventProcessor.HandleEvent(this, ConsoleCancelKeyEvent, args, process); + e.Cancel = reference.Value.AsBoolean(); + } + } +} diff --git a/src/OneScript.StandardLibrary/Text/ConsoleProvider.cs b/src/OneScript.StandardLibrary/Text/ConsoleProvider.cs new file mode 100644 index 000000000..7dd8ec664 --- /dev/null +++ b/src/OneScript.StandardLibrary/Text/ConsoleProvider.cs @@ -0,0 +1,48 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Text +{ + [GlobalContext(Category = "Работа с консолью")] + public class ConsoleProvider : GlobalContextBase + { + private readonly ConsoleContext _console; + + private ConsoleProvider(ExecutionContext executionContext) + { + _console = new ConsoleContext(executionContext); + } + + [ContextProperty("Консоль", "Console")] + public ConsoleContext Console => _console; + + public override bool IsPropWritable(int propNum) + { + // обратная совместимость. Присваивание Консоль = Новый Консоль не должно ругаться на недоступность записи + return true; + } + + public override void SetPropValue(int propNum, IValue newVal) + { + // обратная совместимость. Присваивание Консоль = Новый Консоль не должно ничего делать + if (!ReferenceEquals(newVal, _console)) + { + throw new InvalidOperationException("Can't assign to global property Console"); + } + } + + public static ConsoleProvider CreateInstance(ExecutionContext executionContext) + { + return new ConsoleProvider(executionContext); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/TextDocumentContext.cs b/src/OneScript.StandardLibrary/Text/TextDocumentContext.cs similarity index 96% rename from src/ScriptEngine.HostedScript/Library/TextDocumentContext.cs rename to src/OneScript.StandardLibrary/Text/TextDocumentContext.cs index cfb3ab8cf..21ca9b1e1 100644 --- a/src/ScriptEngine.HostedScript/Library/TextDocumentContext.cs +++ b/src/OneScript.StandardLibrary/Text/TextDocumentContext.cs @@ -5,16 +5,18 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.Text { [ContextClass("ТекстовыйДокумент", "TextDocument")] public class TextDocumentContext : AutoContext @@ -291,9 +293,9 @@ private StreamReader GetDefaultReader(string path, IValue encoding) { StreamReader reader; if (encoding == null) - reader = Environment.FileOpener.OpenReader(path); + reader = FileOpener.OpenReader(path); else - reader = Environment.FileOpener.OpenReader(path, TextEncodingEnum.GetEncoding(encoding)); + reader = FileOpener.OpenReader(path, TextEncodingEnum.GetEncoding(encoding)); return reader; } @@ -302,9 +304,9 @@ private StreamWriter GetDefaultWriter(string path, IValue encoding) { StreamWriter writer; if (encoding == null) - writer = Environment.FileOpener.OpenWriter(path, new UTF8Encoding(true)); + writer = FileOpener.OpenWriter(path, new UTF8Encoding(true)); else - writer = Environment.FileOpener.OpenWriter(path, TextEncodingEnum.GetEncoding(encoding)); + writer = FileOpener.OpenWriter(path, TextEncodingEnum.GetEncoding(encoding)); return writer; } diff --git a/src/OneScript.StandardLibrary/Text/TextEncodingEnum.cs b/src/OneScript.StandardLibrary/Text/TextEncodingEnum.cs new file mode 100644 index 000000000..aa3839130 --- /dev/null +++ b/src/OneScript.StandardLibrary/Text/TextEncodingEnum.cs @@ -0,0 +1,161 @@ +/*---------------------------------------------------------- +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.Diagnostics; +using System.Text; +using OneScript.Contexts.Enums; +using OneScript.Exceptions; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using OneScript.Values; + +namespace OneScript.StandardLibrary.Text +{ + [SystemEnum("КодировкаТекста", "TextEncoding")] + public class TextEncodingEnum : EnumerationContext + { + private enum TextEncodingValues + { + System, + ANSI, + OEM, + UTF16, + UTF8, + UTF8NoBOM + } + + private TextEncodingEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + System = this.WrapClrValue("Системная", "System", TextEncodingValues.System); + Ansi = this.WrapClrValue("ANSI", default, TextEncodingValues.ANSI); + Oem = this.WrapClrValue("OEM", default, TextEncodingValues.OEM); + Utf16 = this.WrapClrValue("UTF16", default, TextEncodingValues.UTF16); + Utf8 = this.WrapClrValue("UTF8", default, TextEncodingValues.UTF8); + Utf8NoBOM = this.WrapClrValue("UTF8БезBOM", "UTF8NoBOM", TextEncodingValues.UTF8NoBOM); + } + + public EnumerationValue Ansi { get; } + + public EnumerationValue Oem { get; } + + public EnumerationValue Utf16 { get; } + + public EnumerationValue Utf8 { get; } + + public EnumerationValue Utf8NoBOM { get; } + + public EnumerationValue System { get; } + + public EnumerationValue GetValue(Encoding encoding) + { + if (encoding.Equals(Encoding.GetEncoding(866)) || encoding.CodePage == 866) + return Oem; + + if (encoding.Equals(Encoding.GetEncoding(1251)) || encoding.CodePage == 1251) + return Ansi; + + if (encoding.Equals(new UnicodeEncoding(false, true))) + return Utf16; + + if (encoding.Equals(new UTF8Encoding(true))) + return Utf8; + + if (encoding.Equals(new UTF8Encoding(false)) || encoding.CodePage == 65001) + return Utf8NoBOM; + + if (encoding.Equals(Encoding.Default)) + return System; + + throw RuntimeException.InvalidArgumentValue(encoding); + } + + public static TextEncodingEnum CreateInstance(ITypeManager typeManager) + { + var instance = EnumContextHelper.CreateClrEnumInstance( + typeManager, + (t, v) => new TextEncodingEnum(t, v)); + + return instance; + } + + public static Encoding GetEncodingByName(string encoding, bool addBOM = true) + { + Encoding enc; + if (string.IsNullOrEmpty(encoding)) + enc = new UTF8Encoding(addBOM); + else + { + switch (encoding.Trim().ToUpper()) + { + case "UTF-8": + enc = new UTF8Encoding(addBOM); + break; + case "UTF-16": + case "UTF-16LE": + // предположительно, варианты UTF16_PlatformEndian\UTF16_OppositeEndian + // зависят от платформы x86\m68k\SPARC. Пока нет понимания как корректно это обработать. + // Сейчас сделано исходя из предположения что PlatformEndian должен быть LE поскольку + // платформа x86 более широко распространена + case "UTF16_PLATFORMENDIAN": + enc = new UnicodeEncoding(false, addBOM); + break; + case "UTF-16BE": + case "UTF16_OPPOSITEENDIAN": + enc = new UnicodeEncoding(true, addBOM); + break; + case "UTF-32": + case "UTF-32LE": + case "UTF32_PLATFORMENDIAN": + enc = new UTF32Encoding(false, addBOM); + break; + case "UTF-32BE": + case "UTF32_OPPOSITEENDIAN": + enc = new UTF32Encoding(true, addBOM); + break; + default: + try + { + enc = Encoding.GetEncoding(encoding); + break; + } + catch + { + throw RuntimeException.InvalidEncoding(encoding); + } + } + } + + return enc; + } + + public static Encoding GetEncoding(IValue encoding, bool addBOM = true) + { + if (encoding.SystemType == BasicTypes.String) + return GetEncodingByName(encoding.ToString(), addBOM); + else + { + Debug.Assert(!(encoding is IValueReference)); + + if (!(encoding is ClrEnumValueWrapper encValue)) + throw RuntimeException.InvalidArgumentType(); + + return encValue.UnderlyingValue switch + { + TextEncodingValues.System => Encoding.Default, + TextEncodingValues.ANSI => Encoding.GetEncoding(1251), + TextEncodingValues.OEM => Encoding.GetEncoding(866), + TextEncodingValues.UTF16 => new UnicodeEncoding(false, addBOM), + TextEncodingValues.UTF8 => new UTF8Encoding(addBOM), + TextEncodingValues.UTF8NoBOM => new UTF8Encoding(false), + _ => throw RuntimeException.InvalidArgumentValue() + }; + } + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/TextReadImpl.cs b/src/OneScript.StandardLibrary/Text/TextReadImpl.cs similarity index 84% rename from src/ScriptEngine.HostedScript/Library/TextReadImpl.cs rename to src/OneScript.StandardLibrary/Text/TextReadImpl.cs index fbd6d4719..e0d3a5c4e 100644 --- a/src/ScriptEngine.HostedScript/Library/TextReadImpl.cs +++ b/src/OneScript.StandardLibrary/Text/TextReadImpl.cs @@ -1,253 +1,266 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.IO; -using System.Text; -using ScriptEngine.HostedScript.Library.Binary; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Предназначен для последовательного чтения файлов, в том числе большого размера. - /// - [ContextClass("ЧтениеТекста", "TextReader")] - public class TextReadImpl : AutoContext, IDisposable - { - // TextReader _reader; - CustomLineFeedStreamReader _reader; - string _lineDelimiter = "\n"; - - public TextReadImpl () - { - AnalyzeDefaultLineFeed = true; - } - - /// - /// Открывает текстовый файл для чтения. Ранее открытый файл закрывается. - /// - /// Путь к файлу или поток - /// Кодировка - /// Раздедитель строк - /// Разделитель строк в файле - /// Открывать монопольно - [ContextMethod("Открыть", "Open")] - public void Open(IValue input, IValue encoding = null, string lineDelimiter = "\n", string eolDelimiter = null, - bool? monopoly = null) - { - Close(); - if(IsStream(input, out var wrapper)) - { - OpenStream(wrapper, encoding, lineDelimiter, eolDelimiter); - } - else - { - OpenFile(input.AsString(), encoding, lineDelimiter, eolDelimiter, monopoly); - } - } - - private void OpenStream(IStreamWrapper streamObj, IValue encoding = null, string lineDelimiter = "\n", string eolDelimiter = null) - { - TextReader imReader; - if (encoding == null) - { - imReader = Environment.FileOpener.OpenReader(streamObj.GetUnderlyingStream()); - } - else - { - var enc = TextEncodingEnum.GetEncoding(encoding); - imReader = Environment.FileOpener.OpenReader(streamObj.GetUnderlyingStream(), enc); - } - _reader = GetCustomLineFeedReader(imReader, lineDelimiter, eolDelimiter, AnalyzeDefaultLineFeed); - } - - private void OpenFile(string path, IValue encoding = null, string lineDelimiter = "\n", string eolDelimiter = null, - bool? monopoly = null) - { - TextReader imReader; - var shareMode = (monopoly ?? true) ? FileShare.None : FileShare.ReadWrite; - if (encoding == null) - { - imReader = Environment.FileOpener.OpenReader(path, shareMode); - } - else - { - var enc = TextEncodingEnum.GetEncoding(encoding); - imReader = Environment.FileOpener.OpenReader(path, shareMode, enc); - } - _reader = GetCustomLineFeedReader(imReader, lineDelimiter, eolDelimiter, AnalyzeDefaultLineFeed); - } - - private bool AnalyzeDefaultLineFeed { get; set; } - - private int ReadNext() - { - return _reader.Read (); - } - - /// - /// Считывает строку указанной длины или до конца файла. - /// - /// Размер строки. Если не задан, текст считывается до конца файла - /// Строка - считанная строка, Неопределено - в файле больше нет данных - [ContextMethod("Прочитать", "Read")] - public IValue ReadAll(int size = 0) - { - RequireOpen(); - - var sb = new StringBuilder(); - var read = 0; - do { - var ic = ReadNext(); - if (ic == -1) - break; - sb.Append((char)ic); - ++read; - } while (size == 0 || read < size); - - if (sb.Length == 0) - return ValueFactory.Create (); - - return ValueFactory.Create(sb.ToString()); - } - - /// - /// Считывает очередную строку текстового файла. - /// - /// Подстрока, считающаяся концом строки. Переопределяет РазделительСтрок, - /// переданный в конструктор или в метод Открыть - /// Строка - в случае успешного чтения, Неопределено - больше нет данных - [ContextMethod("ПрочитатьСтроку", "ReadLine")] - public IValue ReadLine(string overridenLineDelimiter = null) - { - RequireOpen(); - string l = _reader.ReadLine (overridenLineDelimiter ?? _lineDelimiter); - - if (l == null) - return ValueFactory.Create(); - - return ValueFactory.Create(l); - } - - /// - /// Закрывает открытый текстовый файл. Если файл был открыт монопольно, то после закрытия он становится доступен. - /// - [ContextMethod("Закрыть", "Close")] - public void Close() - { - Dispose(); - } - - private void RequireOpen() - { - if (_reader == null) - { - throw new RuntimeException("Файл не открыт"); - } - } - - /// - /// Открывает текстовый файл для чтения. - /// - /// Путь к файлу или поток - /// ЧтениеТекста - [ScriptConstructor(Name = "На основании файла или потока без кодировки")] - public static TextReadImpl Constructor (IValue input) - { - var reader = new TextReadImpl (); - reader.AnalyzeDefaultLineFeed = false; - reader.Open (input, null, "\n", "\r\n"); - return reader; - } - - /// - /// Открывает текстовый файл или поток для чтения. Работает аналогично методу Открыть. - /// - /// Путь к файлу или поток - /// Кодировка - /// Разделитель строк - /// Разделитель строк в файле - /// Открывать файл монопольно - /// ЧтениеТекста - [ScriptConstructor(Name = "На основании потока или файла")] - public static TextReadImpl Constructor(IValue input, IValue encoding = null, - IValue lineDelimiter = null, IValue eolDelimiter = null, IValue monopoly = null) - { - var reader = new TextReadImpl(); - if (lineDelimiter != null) - reader.AnalyzeDefaultLineFeed = false; - - if(IsStream(input, out var wrapper)) - { - reader.OpenStream(wrapper, encoding, - lineDelimiter?.GetRawValue().AsString() ?? "\n", - eolDelimiter?.GetRawValue().AsString()); - } - else - { - reader.OpenFile(input.AsString(), encoding, - lineDelimiter?.GetRawValue().AsString() ?? "\n", - eolDelimiter?.GetRawValue().AsString(), - monopoly?.AsBoolean() ?? true); - } - - return reader; - } - - /// - /// Создаёт неинициализированный объект. Для инициализации необходимо открыть файл методом Открыть. - /// - /// ЧтениеТекста - [ScriptConstructor(Name = "Формирование неинициализированного объекта")] - public static TextReadImpl Constructor() - { - var reader = new TextReadImpl(); - reader.AnalyzeDefaultLineFeed = false; - return reader; - } - - private static bool IsStream(IValue input, out IStreamWrapper wrapper) - { - wrapper = null; - if (input.DataType == DataType.Object) - { - var obj = input.AsObject(); - if (obj is IStreamWrapper wrap) - { - wrapper = wrap; - return true; - } - } - return false; - } - - private CustomLineFeedStreamReader GetCustomLineFeedReader(TextReader imReader, string lineDelimiter, - string eolDelimiter, bool AnalyzeDefaultLineFeed) - { - _lineDelimiter = lineDelimiter ?? "\n"; - if (eolDelimiter != null) - return new CustomLineFeedStreamReader(imReader, eolDelimiter, AnalyzeDefaultLineFeed); - else - return new CustomLineFeedStreamReader(imReader, "\r\n", AnalyzeDefaultLineFeed); - } - - - #region IDisposable Members - - public void Dispose() - { - if (_reader != null) - { - _reader.Dispose(); - _reader = null; - } - } - - #endregion - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Binary; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Text +{ + /// + /// Предназначен для последовательного чтения файлов, в том числе большого размера. + /// + [ContextClass("ЧтениеТекста", "TextReader")] + public class TextReadImpl : AutoContext, IDisposable + { + // TextReader _reader; + CustomLineFeedStreamReader _reader; + string _lineDelimiter = "\n"; + + public TextReadImpl () + { + AnalyzeDefaultLineFeed = true; + } + + /// + /// Открывает текстовый файл для чтения. Ранее открытый файл закрывается. + /// + /// Путь к файлу или поток + /// Кодировка + /// Раздедитель строк + /// Разделитель строк в файле + /// Открывать монопольно + [ContextMethod("Открыть", "Open")] + public void Open(IValue input, IValue encoding = null, string lineDelimiter = "\n", string eolDelimiter = null, + bool? monopoly = null) + { + Close(); + if(IsStream(input, out var wrapper)) + { + OpenStream(wrapper, encoding, lineDelimiter, eolDelimiter); + } + else + { + OpenFile( + ContextValuesMarshaller.ConvertValueStrict(input), + encoding, + lineDelimiter, + eolDelimiter, + monopoly); + } + } + + private void OpenStream(IStreamWrapper streamObj, IValue encoding = null, string lineDelimiter = "\n", string eolDelimiter = null) + { + TextReader imReader; + if (encoding == null) + { + imReader = FileOpener.OpenReader(streamObj.GetUnderlyingStream(), Encoding.Default); + } + else + { + var enc = TextEncodingEnum.GetEncoding(encoding); + imReader = FileOpener.OpenReader(streamObj.GetUnderlyingStream(), enc); + } + _reader = GetCustomLineFeedReader(imReader, lineDelimiter, eolDelimiter, AnalyzeDefaultLineFeed); + } + + private void OpenFile(string path, IValue encoding = null, string lineDelimiter = "\n", string eolDelimiter = null, + bool? monopoly = null) + { + TextReader imReader; + var shareMode = (monopoly ?? true) ? FileShare.None : FileShare.ReadWrite; + if (encoding == null) + { + imReader = FileOpener.OpenReader(path, shareMode); + } + else + { + var enc = TextEncodingEnum.GetEncoding(encoding); + imReader = FileOpener.OpenReader(path, shareMode, enc); + } + _reader = GetCustomLineFeedReader(imReader, lineDelimiter, eolDelimiter, AnalyzeDefaultLineFeed); + } + + private bool AnalyzeDefaultLineFeed { get; set; } + + private int ReadNext() + { + return _reader.Read (); + } + + /// + /// Считывает строку указанной длины или до конца файла. + /// + /// Размер строки. Если не задан, текст считывается до конца файла + /// Строка - считанная строка, Неопределено - в файле больше нет данных + [ContextMethod("Прочитать", "Read")] + public IValue ReadAll(int size = 0) + { + RequireOpen(); + + var sb = new StringBuilder(); + var read = 0; + do { + var ic = ReadNext(); + if (ic == -1) + break; + sb.Append((char)ic); + ++read; + } while (size == 0 || read < size); + + if (sb.Length == 0) + return ValueFactory.Create (); + + return ValueFactory.Create(sb.ToString()); + } + + /// + /// Считывает очередную строку текстового файла. + /// + /// Подстрока, считающаяся концом строки. Переопределяет РазделительСтрок, + /// переданный в конструктор или в метод Открыть + /// Строка - в случае успешного чтения, Неопределено - больше нет данных + [ContextMethod("ПрочитатьСтроку", "ReadLine")] + public IValue ReadLine(string overridenLineDelimiter = null) + { + RequireOpen(); + string l = _reader.ReadLine (overridenLineDelimiter ?? _lineDelimiter); + + if (l == null) + return ValueFactory.Create(); + + return ValueFactory.Create(l); + } + + /// + /// Закрывает открытый текстовый файл. Если файл был открыт монопольно, то после закрытия он становится доступен. + /// + [ContextMethod("Закрыть", "Close")] + public void Close() + { + Dispose(); + } + + private void RequireOpen() + { + if (_reader == null) + { + throw new RuntimeException("Файл не открыт"); + } + } + + /// + /// Открывает текстовый файл для чтения. + /// + /// Путь к файлу или поток + /// ЧтениеТекста + [ScriptConstructor(Name = "На основании файла или потока без кодировки")] + public static TextReadImpl Constructor (IValue input) + { + var reader = new TextReadImpl (); + reader.AnalyzeDefaultLineFeed = false; + reader.Open (input, null, "\n", "\r\n"); + return reader; + } + + /// + /// Создаёт неинициализированный объект. Для инициализации необходимо открыть файл методом Открыть. + /// + /// ЧтениеТекста + [ScriptConstructor(Name = "Формирование неинициализированного объекта")] + public static TextReadImpl Constructor() + { + var reader = new TextReadImpl(); + reader.AnalyzeDefaultLineFeed = false; + return reader; + } + + /// + /// Открывает текстовый файл или поток для чтения. Работает аналогично методу Открыть. + /// + /// Путь к файлу или поток + /// Кодировка + /// Разделитель строк + /// Разделитель строк в файле + /// Открывать файл монопольно + /// ЧтениеТекста + [ScriptConstructor(Name = "На основании потока или файла")] + public static TextReadImpl ConstructorWithEncoding(IValue input, IValue encoding = null, + string lineDelimiter = null, string eolDelimiter = null, bool monopoly = true) + { + var reader = new TextReadImpl(); + if (lineDelimiter != null) + reader.AnalyzeDefaultLineFeed = false; + + if(IsStream(input, out var wrapper)) + { + reader.OpenStream( + wrapper, + encoding, + lineDelimiter ?? "\n", + eolDelimiter); + } + else + { + reader.OpenFile( + ContextValuesMarshaller.ConvertValueStrict(input), + encoding, + lineDelimiter ?? "\n", + eolDelimiter, + monopoly); + } + + return reader; + } + + private static bool IsStream(IValue input, out IStreamWrapper wrapper) + { + Debug.Assert(!(input is IValueReference)); + + wrapper = null; + if (input is IStreamWrapper wrap) + { + wrapper = wrap; + return true; + } + return false; + } + + private CustomLineFeedStreamReader GetCustomLineFeedReader(TextReader imReader, string lineDelimiter, + string eolDelimiter, bool AnalyzeDefaultLineFeed) + { + _lineDelimiter = lineDelimiter ?? "\n"; + if (eolDelimiter != null) + return new CustomLineFeedStreamReader(imReader, eolDelimiter, AnalyzeDefaultLineFeed); + else + return new CustomLineFeedStreamReader(imReader, "\r\n", AnalyzeDefaultLineFeed); + } + + + #region IDisposable Members + + public void Dispose() + { + if (_reader != null) + { + _reader.Dispose(); + _reader = null; + } + } + + #endregion + } +} diff --git a/src/OneScript.StandardLibrary/Text/TextWriteImpl.cs b/src/OneScript.StandardLibrary/Text/TextWriteImpl.cs new file mode 100644 index 000000000..cacfa1152 --- /dev/null +++ b/src/OneScript.StandardLibrary/Text/TextWriteImpl.cs @@ -0,0 +1,270 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Text; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; +using OneScript.StandardLibrary.Binary; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Text +{ + [ContextClass("ЗаписьТекста", "TextWriter")] + public class TextWriteImpl : AutoContext, IDisposable + { + StreamWriter _writer; + IStreamWrapper _streamWrapper; + Encoding _encoding; + string _lineDelimiter = ""; + string _eolReplacement = ""; + + public TextWriteImpl() + { + + } + + public TextWriteImpl(string path, IValue encoding) + { + Open(ValueFactory.Create(path), encoding); + } + + public TextWriteImpl(string path, IValue encoding, bool append) + { + Open(ValueFactory.Create(path), encoding, null, ValueFactory.Create(append)); + } + + public TextWriteImpl(IValue stream, IValue encoding, IValue writeBom) + { + Open(stream, encoding, null, null, writeBom); + } + + /// + /// Открывает файл или устанавливает поток для записи. + /// + /// Имя файла или поток, в который будет выполнена запись. + /// Кодировка (необязательный). По умолчанию используется utf-8 + /// + /// Определяет строку, разделяющую строки в файле/потоке (необязательный). + /// Значение по умолчанию: ПС. + /// + /// Для файла: + /// Признак добавления в конец файла (необязательный). + /// Значение по умолчанию: Ложь. + /// Для потока: + /// Определяет разделение строк в потоке для конвертации в стандартный перевод строк ПС (необязательный). + /// Значение по умолчанию: ВК + ПС. + /// + /// Для файла: + /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС (необязательный). + /// Значение по умолчанию: ВК + ПС. + /// Для потока: + /// Если в начало потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста, + /// то данный параметр должен иметь значение Истина. + /// Значение по умолчанию: Ложь. + [ContextMethod("Открыть", "Open")] + public void Open(IValue fileOrStream, IValue encoding = null, string lineDelimiter = null, IValue param4 = null, IValue param5 = null) + { + if (fileOrStream is IStreamWrapper streamWrapper) + { + OpenStream( + streamWrapper, + encoding, + lineDelimiter, + ContextValuesMarshaller.ConvertValueStrict(param4), + ContextValuesMarshaller.ConvertValueStrict(param5)); + } + else + { + OpenFile( + fileOrStream.ToString(), + encoding, + lineDelimiter, + ContextValuesMarshaller.ConvertValueStrict(param4), + ContextValuesMarshaller.ConvertValueStrict(param5)); + } + } + + [ContextMethod("Закрыть","Close")] + public void Close() + { + Dispose(); + } + + /// + /// Записывает текст "как есть" + /// + /// Текст для записи + [ContextMethod("Записать", "Write")] + public void Write(string what) + { + PrepareForWriting(); + + var stringToOutput = what.Replace ("\n", _eolReplacement); + + _writer.Write(stringToOutput); + } + + /// + /// Записывает текст и добавляет перевод строки + /// + /// Текст для записи + /// Разделитель строк + [ContextMethod("ЗаписатьСтроку", "WriteLine")] + public void WriteLine(IBslProcess process, string what, BslValue delimiter = null) + { + Write(what); + + var sDelimiter = _lineDelimiter; + if (delimiter != null && delimiter.SystemType != BasicTypes.Undefined) + sDelimiter = delimiter.ToString(process); + + Write(sDelimiter); + } + + public void ThrowIfNotOpened() + { + if (_writer == null) + throw new RuntimeException("Файл не открыт"); + } + + public void Dispose() + { + if (_writer != null) + { + _writer.Dispose(); + _writer = null; + } + + _streamWrapper = null; + } + + private void PrepareForWriting() + { + ThrowIfClosedStream(); + ThrowIfNonWritableStream(); + + if (_writer == null && _streamWrapper != null) + { + _writer = new StreamWriter(_streamWrapper.GetUnderlyingStream(), _encoding, -1, true); + _writer.AutoFlush = true; + } + + ThrowIfNotOpened(); + } + + private void OpenFile(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null) + { + Dispose(); + + _lineDelimiter = lineDelimiter ?? "\n"; + _eolReplacement = eolReplacement ?? "\r\n"; + _encoding = ResolveEncodingForFile(encoding, append); + _streamWrapper = null; + + _writer = new StreamWriter(path, append, _encoding); + _writer.AutoFlush = true; + } + + private void OpenStream(IStreamWrapper streamWrapper, IValue encoding = null, string lineDelimiter = null, string eolReplacement = null, bool writeBom = false) + { + Dispose(); + + _lineDelimiter = lineDelimiter ?? "\n"; + _eolReplacement = eolReplacement ?? "\r\n"; + _encoding = ResolveEncodingForStream(encoding, writeBom); + _streamWrapper = streamWrapper; + } + + private Encoding ResolveEncodingForFile(IValue encoding, bool append) + { + Encoding enc; + if (encoding == null) + { + enc = new UTF8Encoding(true); + } + else + { + enc = TextEncodingEnum.GetEncoding(encoding); + if (enc.WebName == "utf-8" && append == true) + enc = new UTF8Encoding(false); + } + return enc; + } + + private Encoding ResolveEncodingForStream(IValue encoding, bool writeBom) + { + if (encoding == null) + { + return new UTF8Encoding(writeBom); + } + else + { + return TextEncodingEnum.GetEncoding(encoding, writeBom); + } + } + + private void ThrowIfClosedStream() + { + if (_streamWrapper != null) + { + var stream = _streamWrapper.GetUnderlyingStream(); + if (stream is { CanWrite: false, CanRead: false }) + throw RuntimeException.ClosedStream(); + } + } + + private void ThrowIfNonWritableStream() + { + if (_streamWrapper != null && _streamWrapper.IsReadOnly) + throw RuntimeException.NonWritableStream(); + } + + /// + /// Создает объект для записи текста в файл или поток. + /// + /// Имя файла или поток, в который будет выполнена запись. + /// Кодировка (необязательный). По умолчанию используется utf-8 + /// + /// Определяет строку, разделяющую строки в файле/потоке (необязательный). + /// Значение по умолчанию: ПС. + /// + /// Для файла: + /// Признак добавления в конец файла (необязательный). + /// Значение по умолчанию: Ложь. + /// Для потока: + /// Определяет разделение строк в потоке для конвертации в стандартный перевод строк ПС (необязательный). + /// Значение по умолчанию: ВК + ПС. + /// + /// Для файла: + /// Определяет разделение строк в файле для конвертации в стандартный перевод строк ПС (необязательный). + /// Значение по умолчанию: ВК + ПС. + /// Для потока: + /// Если в начало потока требуется записать метку порядка байтов (BOM) для используемой кодировки текста, + /// то данный параметр должен иметь значение Истина. + /// Значение по умолчанию: Ложь. + [ScriptConstructor(Name = "По имени файла")] + public static TextWriteImpl Constructor(IValue fileOrStream, IValue encoding = null, string lineDelimiter = null, IValue param4 = null, IValue param5 = null) + { + var result = new TextWriteImpl(); + result.Open(fileOrStream, encoding, lineDelimiter, param4, param5); + return result; + } + + [ScriptConstructor(Name = "Формирование неинициализированного объекта")] + public static TextWriteImpl Constructor() + { + return new TextWriteImpl(); + } + + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Timezones/GlobalTimezoneFunctions.cs b/src/OneScript.StandardLibrary/Timezones/GlobalTimezoneFunctions.cs similarity index 97% rename from src/ScriptEngine.HostedScript/Library/Timezones/GlobalTimezoneFunctions.cs rename to src/OneScript.StandardLibrary/Timezones/GlobalTimezoneFunctions.cs index b19eab7d3..5e7cbee32 100644 --- a/src/ScriptEngine.HostedScript/Library/Timezones/GlobalTimezoneFunctions.cs +++ b/src/OneScript.StandardLibrary/Timezones/GlobalTimezoneFunctions.cs @@ -4,12 +4,16 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Timezones +namespace OneScript.StandardLibrary.Timezones { /// /// Глобальный контекст. Операции с временными зонами и часовыми поясами. diff --git a/src/OneScript.StandardLibrary/Timezones/TimeZoneConverter.cs b/src/OneScript.StandardLibrary/Timezones/TimeZoneConverter.cs new file mode 100644 index 000000000..f5fd0bb58 --- /dev/null +++ b/src/OneScript.StandardLibrary/Timezones/TimeZoneConverter.cs @@ -0,0 +1,154 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OneScript.StandardLibrary.Timezones +{ + public class TimeZoneConverter + { + private const int MAX_HOURS = 23; + private const int MAX_MINUTES = 59; + + public static TimeSpan GetTimespan(string timezone) + { + var tz = ResolveTimeZone(timezone); + return tz.BaseUtcOffset; + } + + public static TimeZoneInfo TimeZoneById(string id) + { + return ResolveTimeZone(id); + } + + public static DateTime ToUniversalTime(DateTime dt, string timeZone = null) + { + var tz = ResolveTimeZone(timeZone); + var unspecified = DateTime.SpecifyKind(dt, DateTimeKind.Unspecified); + return TimeZoneInfo.ConvertTimeToUtc(unspecified, tz); + } + + public static DateTime ToLocalTime(DateTime dt, string timeZone = null) + { + var tz = ResolveTimeZone(timeZone); + var utc = DateTime.SpecifyKind(dt, DateTimeKind.Utc); + return TimeZoneInfo.ConvertTimeFromUtc(utc, tz); + } + + public static int StandardTimeOffset(string timeZone = null, DateTime? dt = null) + { + var tz = ResolveTimeZone(timeZone); + return (int)tz.BaseUtcOffset.TotalSeconds; + } + + public static IEnumerable GetAvailableTimeZones() + { + return TimeZoneInfo.GetSystemTimeZones() + .Select(x => x.Id); + } + + public static string TimeZone() + { + return TimeZoneInfo.Local.Id; + } + + public static string TimeZonePresentation(string timeZone) + { + TimeSpan offset; + if (IsGmtString(timeZone)) + { + offset = TimeSpanByGMTString(timeZone); + } + else + { + var tz = ResolveTimeZone(timeZone); + offset = tz.BaseUtcOffset; + } + return FormatGmtOffset(offset); + } + + private static TimeSpan TimeSpanByGMTString(string gmtString) + { + + gmtString = gmtString.ToLower(); + + var positiveOffset = gmtString.StartsWith("gmt+"); + + var arr_id = gmtString.Split( + new string[] { "+", ":", "-" }, + StringSplitOptions.None); + + int hours = 0; + int minutes = 0; + + if (arr_id.Length < 2 || arr_id.Length > 3) + throw new TimeZoneNotFoundException(); + else if (arr_id.Length == 3) + minutes = int.Parse(arr_id[2]); + + hours = int.Parse(arr_id[1]); + + if (hours > MAX_HOURS || minutes > MAX_MINUTES) + throw new TimeZoneNotFoundException(); + + if (!positiveOffset) + hours = -hours; + + var span = new TimeSpan(hours, minutes, 0); + + return span; + } + + private static bool IsGmtString(string zone) + { + return !string.IsNullOrEmpty(zone) && zone.StartsWith("GMT", StringComparison.InvariantCultureIgnoreCase); + } + + private static string FormatGmtOffset(TimeSpan offset) + { + var sign = offset >= TimeSpan.Zero ? "+" : "-"; + var d = offset.Duration(); + return $"GMT{sign}{d.ToString(@"hh\:mm")}"; + } + + private static TimeZoneInfo ResolveTimeZone(string id) + { + if (string.IsNullOrWhiteSpace(id)) + return TimeZoneInfo.Local; + + if (IsGmtString(id)) + { + var offset = TimeSpanByGMTString(id); + var display = FormatGmtOffset(offset); + return TimeZoneInfo.CreateCustomTimeZone(id, offset, display, display); + } + + // Имена таймзон в Windows отличаются от IANA и будут не совпадать + // с поведением 1С, работающей с ICU + // Надо заводить отдельную карту для коротких имен таймзон. + // Как минимум, для EET, CET, WET и может еще чего-то. + // Например, зона "E. Europe Standard Time" работает в .net только на Windows. + // База данных IANA (https://www.iana.org/time-zones) в принципе не содержит этих таймзон, но вероятно + // имеет смысл их поддержать, т.к. в 1С они используются, и GetAvailableTimeZones() их возвращает. + + // TODO: Переписать на нормальное решение + var tzFixes = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + tzFixes.Add("EET", "GMT+02:00"); + tzFixes.Add("CET", "GMT+01:00"); + + if (tzFixes.TryGetValue(id, out var gmtString)) + { + return ResolveTimeZone(gmtString); + } + + return TimeZoneInfo.FindSystemTimeZoneById(id); + } + } + +} diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/AllowedLengthEnum.cs b/src/OneScript.StandardLibrary/TypeDescriptions/AllowedLengthEnum.cs new file mode 100644 index 000000000..77a5530f1 --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/AllowedLengthEnum.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + [EnumerationType("ДопустимаяДлина", "AllowedLength")] + public enum AllowedLengthEnum + { + [EnumValue("Переменная", "Variable")] + Variable, + + [EnumValue("Фиксированная", "Fixed")] + Fixed + } +} diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/AllowedSignEnum.cs b/src/OneScript.StandardLibrary/TypeDescriptions/AllowedSignEnum.cs new file mode 100644 index 000000000..3fc028fe8 --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/AllowedSignEnum.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + [EnumerationType("ДопустимыйЗнак", "AllowedSign")] + public enum AllowedSignEnum + { + [EnumValue("Любой", "Any")] + Any, + + [EnumValue("Неотрицательный", "Nonnegative")] + Nonnegative + } +} diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/BinaryDataQualifiers.cs b/src/OneScript.StandardLibrary/TypeDescriptions/BinaryDataQualifiers.cs new file mode 100644 index 000000000..a426972f2 --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/BinaryDataQualifiers.cs @@ -0,0 +1,56 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + [ContextClass("КвалификаторыДвоичныхДанных", "BinaryDataQualifiers")] + public sealed class BinaryDataQualifiers : AutoContext + { + public BinaryDataQualifiers(int length = 0, + AllowedLengthEnum allowedLength = AllowedLengthEnum.Variable) + { + Length = length; + AllowedLength = allowedLength; + } + + [ContextProperty("Длина", "Length")] + public int Length { get; } + + [ContextProperty("ДопустимаяДлина", "AllowedLength")] + public AllowedLengthEnum AllowedLength { get; } + + public override bool Equals(object obj) + { + if (!(obj is BinaryDataQualifiers asThis)) + return false; + + return Length == asThis.Length + && AllowedLength == asThis.AllowedLength; + } + + public override bool Equals(BslValue other) + { + return Equals((object)other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Length, AllowedLength); + } + + [ScriptConstructor] + public static BinaryDataQualifiers Constructor(int length = default, AllowedLengthEnum allowedLength = default) + { + return new BinaryDataQualifiers(length, allowedLength); + } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/BooleanTypeAdjuster.cs b/src/OneScript.StandardLibrary/TypeDescriptions/BooleanTypeAdjuster.cs similarity index 82% rename from src/ScriptEngine.HostedScript/Library/TypeDescription/BooleanTypeAdjuster.cs rename to src/OneScript.StandardLibrary/TypeDescriptions/BooleanTypeAdjuster.cs index a3de30966..4ca274a7c 100644 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/BooleanTypeAdjuster.cs +++ b/src/OneScript.StandardLibrary/TypeDescriptions/BooleanTypeAdjuster.cs @@ -5,15 +5,17 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Exceptions; +using OneScript.Types; using ScriptEngine.Machine; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.TypeDescriptions { public sealed class BooleanTypeAdjuster : IValueAdjuster { public IValue Adjust(IValue value) { - if (value?.DataType == DataType.Boolean) + if (value?.SystemType == BasicTypes.Boolean) return value; try diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/DateFractionsEnum.cs b/src/OneScript.StandardLibrary/TypeDescriptions/DateFractionsEnum.cs new file mode 100644 index 000000000..aa5b96952 --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/DateFractionsEnum.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + [EnumerationType("ЧастиДаты", "DateFractions")] + public enum DateFractionsEnum + { + [EnumValue("Дата", "Date")] + Date, + + [EnumValue("ДатаВремя", "DateTime")] + DateTime, + + [EnumValue("Время", "Time")] + Time + } +} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/DateQualifiers.cs b/src/OneScript.StandardLibrary/TypeDescriptions/DateQualifiers.cs similarity index 76% rename from src/ScriptEngine.HostedScript/Library/TypeDescription/DateQualifiers.cs rename to src/OneScript.StandardLibrary/TypeDescriptions/DateQualifiers.cs index 45600788e..822915034 100644 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/DateQualifiers.cs +++ b/src/OneScript.StandardLibrary/TypeDescriptions/DateQualifiers.cs @@ -4,16 +4,20 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; +using OneScript.Contexts; +using OneScript.Types; +using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.TypeDescriptions { [ContextClass("КвалификаторыДаты", "DateQualifiers")] public sealed class DateQualifiers : AutoContext, IValueAdjuster { - public DateQualifiers(DateFractionsEnum dateFractions = DateFractionsEnum.Date) + public DateQualifiers(DateFractionsEnum dateFractions = DateFractionsEnum.DateTime) { DateFractions = dateFractions; } @@ -23,16 +27,15 @@ public DateQualifiers(DateFractionsEnum dateFractions = DateFractionsEnum.Date) public override bool Equals(object obj) { - var asThis = obj as DateQualifiers; - if (asThis == null) + if (!(obj is DateQualifiers asThis)) return false; return DateFractions == asThis.DateFractions; } - public override bool Equals(IValue other) + public override bool Equals(BslValue other) { - return object.Equals(this, other?.GetRawValue()); + return Equals((object)other); } public override int GetHashCode() @@ -42,7 +45,7 @@ public override int GetHashCode() public IValue Adjust(IValue value) { - if (value == null || value.DataType == DataType.Undefined) + if (value == null || value.SystemType == BasicTypes.Undefined) return ValueFactory.Create(new DateTime(1, 1, 1)); try @@ -65,10 +68,9 @@ public IValue Adjust(IValue value) } [ScriptConstructor(Name = "На основании описания даты")] - public static DateQualifiers Constructor(IValue dateFractions = null) + public static DateQualifiers Constructor(DateFractionsEnum dateFractions = DateFractionsEnum.DateTime) { - var paramDateFractions = ContextValuesMarshaller.ConvertParam(dateFractions); - return new DateQualifiers(paramDateFractions); + return new DateQualifiers(dateFractions); } } } diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/IValueAdjuster.cs b/src/OneScript.StandardLibrary/TypeDescriptions/IValueAdjuster.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/TypeDescription/IValueAdjuster.cs rename to src/OneScript.StandardLibrary/TypeDescriptions/IValueAdjuster.cs index fa767ec95..488eb69df 100644 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/IValueAdjuster.cs +++ b/src/OneScript.StandardLibrary/TypeDescriptions/IValueAdjuster.cs @@ -7,7 +7,7 @@ This Source Code Form is subject to the terms of the using ScriptEngine.Machine; -namespace ScriptEngine.HostedScript.Library +namespace OneScript.StandardLibrary.TypeDescriptions { public interface IValueAdjuster { diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/NumberQualifiers.cs b/src/OneScript.StandardLibrary/TypeDescriptions/NumberQualifiers.cs new file mode 100644 index 000000000..1d0dacaa1 --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/NumberQualifiers.cs @@ -0,0 +1,106 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + [ContextClass("КвалификаторыЧисла", "NumberQualifiers")] + public sealed class NumberQualifiers : AutoContext, IValueAdjuster + { + public NumberQualifiers(int digits = 0, + int fractionDigits = 0, + AllowedSignEnum allowedSign = AllowedSignEnum.Any) + { + Digits = digits; + FractionDigits = fractionDigits; + AllowedSign = allowedSign; + } + + [ContextProperty("ДопустимыйЗнак", "AllowedSign")] + public AllowedSignEnum AllowedSign { get; } + + [ContextProperty("Разрядность", "Digits")] + public int Digits { get; } + + [ContextProperty("РазрядностьДробнойЧасти", "FractionDigits")] + public int FractionDigits { get; } + + public override bool Equals(object obj) + { + var asThis = obj as NumberQualifiers; + if (asThis == null) + return false; + + return Digits == asThis.Digits + && FractionDigits == asThis.FractionDigits + && AllowedSign == asThis.AllowedSign; + } + + public override bool Equals(BslValue other) + { + return Equals((object)other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Digits, FractionDigits, AllowedSign); + } + + public IValue Adjust(IValue value) + { + if (value == null) + { + // Значение по-умолчанию + return ValueFactory.Create(0); + } + // TODO: обрезать по количеству знаков + + try + { + // TODO: Вменяемое преобразование без Попытки + var numericValue = value.AsNumber(); + + if (AllowedSign == AllowedSignEnum.Nonnegative && numericValue < 0) + { + numericValue = 0; + } + + if (Digits > 0) + { + ValueFormatter.ApplyNumericSizeRestrictions(ref numericValue, Digits, FractionDigits); + } + return ValueFactory.Create(numericValue); + + } catch + { + } + + return ValueFactory.Create(0); + } + + [ScriptConstructor(Name = "На основании описания числа")] + public static NumberQualifiers Constructor( + int digits = default, + int fractionDigits = default, + AllowedSignEnum allowedSign = default) + { + if (digits < 0 || fractionDigits < 0) + { + throw RuntimeException.InvalidArgumentValue(); + } + + return new NumberQualifiers(digits, fractionDigits, allowedSign); + } + } +} diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/StringQualifiers.cs b/src/OneScript.StandardLibrary/TypeDescriptions/StringQualifiers.cs new file mode 100644 index 000000000..d7775172a --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/StringQualifiers.cs @@ -0,0 +1,91 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + [ContextClass("КвалификаторыСтроки", "StringQualifiers")] + public sealed class StringQualifiers : AutoContext, IValueAdjuster + { + public StringQualifiers(int length = 0, + AllowedLengthEnum allowedLength = AllowedLengthEnum.Variable) + { + Length = length; + AllowedLength = allowedLength; + } + + [ContextProperty("Длина", "Length")] + public int Length { get; } + + [ContextProperty("ДопустимаяДлина", "AllowedLength")] + public AllowedLengthEnum AllowedLength { get; } + + public override bool Equals(object obj) + { + var asThis = obj as StringQualifiers; + if (asThis == null) + return false; + + return Length == asThis.Length + && AllowedLength == asThis.AllowedLength; + } + + public override bool Equals(BslValue other) + { + return Equals((object)other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Length, AllowedLength); + } + + public string DefaultString() + { + if (AllowedLength == AllowedLengthEnum.Variable) + return ""; + + if (Length == 0) + return ""; + + return new string(' ', Length); + } + + public IValue Adjust(IValue value) + { + // FIXME: не пробрасывается процесс в приведение значения + // Кастомизированные представления из UserScriptContextInstance при присваивании в строковые + // колонки ТаблицыЗначений будут получать стандартное приведение, а не кастомное. + var stringValue = value?.ToString() ?? ""; + + if (Length != 0 && stringValue.Length > Length) + { + stringValue = stringValue.Substring(0, Length); + } + + if (AllowedLength == AllowedLengthEnum.Fixed && stringValue.Length < Length) + { + var tail = new string(' ', Length - stringValue.Length); + stringValue += tail; + } + + return ValueFactory.Create(stringValue); + } + + [ScriptConstructor(Name = "На основании описания строки")] + public static StringQualifiers Constructor(int length = default, + AllowedLengthEnum allowedLength = default) + { + return new StringQualifiers(length, allowedLength); + } + } +} diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/TypeComparer.cs b/src/OneScript.StandardLibrary/TypeDescriptions/TypeComparer.cs new file mode 100644 index 000000000..b0877c8cd --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/TypeComparer.cs @@ -0,0 +1,66 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using OneScript.Types; +using OneScript.Values; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + internal class TypeComparer : IComparer + { + private const string TYPE_BINARYDATA_NAME = "ДвоичныеДанные"; + private static readonly Dictionary primitives = new Dictionary(); + + public int Compare(BslTypeValue x, BslTypeValue y) + { + if (x.TypeValue.Equals(y)) return 0; + + var primitiveX = PrimitiveIndex(x); + var primitiveY = PrimitiveIndex(y); + + if (primitiveX != -1) + { + if (primitiveY != -1) + return primitiveX - primitiveY; + + return -1; + } + + if (primitiveY != -1) + return 1; + + return x.TypeValue.Id.CompareTo(y.TypeValue.Id); + } + + private static int PrimitiveIndex(BslTypeValue type) + { + if (StringComparer.CurrentCultureIgnoreCase.Equals(type.TypeValue.Name, TYPE_BINARYDATA_NAME)) + { + // Пора двоичным данным стать примитивом + return 1; + } + + if (primitives.TryGetValue(type.TypeValue, out var index)) + return index; + + return -1; + } + + static TypeComparer() + { + primitives.Add(BasicTypes.Boolean, 0); + primitives.Add(BasicTypes.String, 2); + primitives.Add(BasicTypes.Date, 3); + primitives.Add(BasicTypes.Null, 4); + primitives.Add(BasicTypes.Number, 5); + primitives.Add(BasicTypes.Type, 6); + } + + } +} + \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/TypeDescription.cs b/src/OneScript.StandardLibrary/TypeDescriptions/TypeDescription.cs new file mode 100644 index 000000000..9e91465bb --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/TypeDescription.cs @@ -0,0 +1,342 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + [ContextClass("ОписаниеТипов", "TypeDescription")] + public class TypeDescription : AutoContext + { + private readonly List _types = new List(); + + public TypeDescription(IEnumerable types = null) + { + if (types != null) + { + _types.AddRange(types); + } + + NumberQualifiers = new NumberQualifiers(); + StringQualifiers = new StringQualifiers(); + DateQualifiers = new DateQualifiers(); + BinaryDataQualifiers = new BinaryDataQualifiers(); + } + + internal TypeDescription(IEnumerable types, + NumberQualifiers numberQualifiers, + StringQualifiers stringQualifiers, + DateQualifiers dateQualifiers, + BinaryDataQualifiers binaryDataQualifiers) + { + if (types != null) + { + _types.AddRange(types); + } + + NumberQualifiers = numberQualifiers; + StringQualifiers = stringQualifiers; + DateQualifiers = dateQualifiers; + BinaryDataQualifiers = binaryDataQualifiers; + } + + [ContextProperty("КвалификаторыЧисла", "NumberQualifiers")] + public NumberQualifiers NumberQualifiers { get; } + + [ContextProperty("КвалификаторыСтроки", "StringQualifiers")] + public StringQualifiers StringQualifiers { get; } + + [ContextProperty("КвалификаторыДаты", "DateQualifiers")] + public DateQualifiers DateQualifiers { get; } + + [ContextProperty("КвалификаторыДвоичныхДанных", "BinaryDataQualifiers")] + public BinaryDataQualifiers BinaryDataQualifiers { get; } + + [ContextMethod("Типы", "Types")] + public ArrayImpl Types() + { + var result = ArrayImpl.Constructor(); + + foreach (var type in _types) + { + result.Add(type); + } + + return result; + } + + internal IEnumerable TypesInternal() + { + return _types; + } + + [ContextMethod("СодержитТип", "ContainsType")] + public bool ContainsType(IValue type) + { + if (type is BslTypeValue typeVal) + { + if (typeVal.TypeValue.Equals(BasicTypes.Undefined)) + { + // тип "Неопределено" содержится в любом явно определенном составном типе + // и не содержится в типе Произвольный (когда явно не указан состав типов) + // или когда указан один конкретный тип + return (_types.Count > 1); + } + return _types.Contains(typeVal); + } + + throw RuntimeException.InvalidArgumentType(nameof(type)); + } + + private IValueAdjuster GetAdjusterForType(BslTypeValue type) + { + var value = type.TypeValue; + + if (value.Equals(BasicTypes.Number)) + return NumberQualifiers; + + if (value.Equals(BasicTypes.String)) + return StringQualifiers; + + if (value.Equals(BasicTypes.Date)) + return DateQualifiers; + + if (value.Equals(BasicTypes.Boolean)) + return new BooleanTypeAdjuster(); + + if (value.Equals(BasicTypes.Undefined)) + return new UndefinedTypeAdjuster(); + + return null; + } + + [ContextMethod("ПривестиЗначение", "AdjustValue")] + public IValue AdjustValue(IValue value = null) + { + if (_types.Count == 0) + { + return value ?? ValueFactory.Create(); + } + + if (value != null) + { + var valueType = new BslTypeValue(value.SystemType); + if (ContainsType(valueType)) + { + // Если такой тип у нас есть + var adjuster = GetAdjusterForType(valueType); + var adjustedValue = adjuster?.Adjust(value) ?? value; + return adjustedValue; + } + } + + foreach (var type in _types) + { + var adjuster = GetAdjusterForType(type); + var adjustedValue = adjuster?.Adjust(value); + if (adjustedValue != null) + return adjustedValue; + } + + return ValueFactory.Create(); + } + + public static TypeDescription StringType(int length = 0, + AllowedLengthEnum allowedLength = AllowedLengthEnum.Variable) + { + return new TypeDescription(new[] { new BslTypeValue(BasicTypes.String) }, + new NumberQualifiers(), + new StringQualifiers(length, allowedLength), + new DateQualifiers(), + new BinaryDataQualifiers()); + } + + public static TypeDescription IntegerType(int length = 10, + AllowedSignEnum allowedSign = AllowedSignEnum.Any) + { + return new TypeDescription(new[] { new BslTypeValue(BasicTypes.Number) }, + new NumberQualifiers(length, 0, allowedSign), + new StringQualifiers(), + new DateQualifiers(), + new BinaryDataQualifiers()); + } + + public static TypeDescription BooleanType() + { + return new TypeDescription(new[] { new BslTypeValue(BasicTypes.Boolean) }, + new NumberQualifiers(), + new StringQualifiers(), + new DateQualifiers(), + new BinaryDataQualifiers()); + } + + [ScriptConstructor] + public static TypeDescription Constructor( + TypeActivationContext context, + BslValue source = null, + BslValue p2 = null, + BslValue p3 = null, + BslValue p4 = null, + BslValue p5 = null, + BslValue p6 = null, + BslValue p7 = null) + { + var builder = new TypeDescriptionBuilder(); + + // параметры, которые заведомо не квалификаторы, заменяем на null, но оставляем, + // чтобы указать номер параметра при выводе ошибки несоответствия типа + var qualifiers = new[] { null, p2, p3, p4, p5, p6, p7 }; + + if (source != null && source.SystemType != BasicTypes.Undefined) + { + if (source is TypeDescription typeDesc) + { + // Если 1 парарметр - ОписаниеТипов, то 2 - добавляемые типы, 3 - убираемые типы, + builder.SourceDescription(typeDesc); + + var typesToAdd = CheckAndParseTypeList(context.TypeManager, p2, 2); + var typesToRemove = CheckAndParseTypeList(context.TypeManager, p3, 3); + + builder.RemoveTypes(typesToRemove); + builder.AddTypes(typesToAdd); + + qualifiers[1] = null; // эти параметры не квалификаторы + qualifiers[2] = null; // эти параметры не квалификаторы + } + else if (source.SystemType == BasicTypes.String || source is ArrayImpl) + { + // Если 1 парарметр - Массив или строка, то это набор конкретных типов + // остальные параметры (2 и далее) - клвалификаторы в произвольном порядке + var typesList = CheckAndParseTypeList(context.TypeManager, source, 1); + builder.AddTypes(typesList); + } + else + throw RuntimeException.InvalidArgumentValue(); + } /* else + пустой первый параметр - нет объекта-основания + добавляемые/вычитаемые типы не допускаются, квалификаторы игнорируются + квалификакторы передаются только для контроля типов + */ + CheckAndAddQualifiers(builder, qualifiers); + return builder.Build(); + } + + /// + /// Преобразует входящий параметр в список типов. + /// + /// В качестве типов могут быть переданы Строка или Массив Типов + /// Номер параметра, который будет указан в исключении, если параметр typeList задан неверно + /// Если typeList не может быть разобран как набор типов + /// Список переданных типов, приведенный к конкретным TypeTypeValue + private static List CheckAndParseTypeList(ITypeManager typeManager, IValue types, int nParam) + { + if (types == null || types.SystemType == BasicTypes.Undefined) + return new List(); + + if (types.SystemType == BasicTypes.String) + { + return FromTypeNames(typeManager, types.ToString()); + } + if (types is ArrayImpl arrayOfTypes) + { + return FromArrayOfTypes(arrayOfTypes); + } + + throw RuntimeException.InvalidNthArgumentType(nParam); + } + + private static List FromTypeNames(ITypeManager typeManager, string types) + { + var typeNames = types.Split(','); + var typesList = new List(); + foreach (var typeName in typeNames) + { + if (string.IsNullOrWhiteSpace(typeName)) + continue; + + var typeValue = new BslTypeValue(typeManager.GetTypeByName(typeName.Trim())); + if (!typesList.Contains(typeValue)) + typesList.Add(typeValue); + } + + return typesList; + } + + private static List FromArrayOfTypes(ArrayImpl arrayOfTypes) + { + var typesList = new List(); + foreach (var type in arrayOfTypes) + { + if (type is BslTypeValue rawType) + { + typesList.Add(rawType); + } + } + return typesList; + } + + private static void CheckAndAddQualifiers(TypeDescriptionBuilder builder, BslValue[] parameters) + { + for (var i = 0; i < parameters.Length; i++) + { + var rawQualifier = parameters[i]; + if (rawQualifier != null && !rawQualifier.Equals(ValueFactory.Create())) + { + CheckAndAddOneQualifier(builder, rawQualifier, i + 1); + } + } + } + + /// + /// Проверяет, что переданный параметр является квалификатором типа. + /// Если тип параметра не является квалификатором, бросает исключение с указанием номера параметра. + /// + /// Построитель описания типов, которому будет присвоен квалификатор + /// Проверяемый входящий параметр + /// Порядковый номер параметра для выброса исключения + /// Если qualifier не является квалификатором типа + private static void CheckAndAddOneQualifier(TypeDescriptionBuilder builder, IValue qualifier, int nParam) + { + switch (qualifier) + { + case NumberQualifiers nq: + builder.SetNumberQualifiers(nq); + break; + + case StringQualifiers sq: + builder.SetStringQualifiers(sq); + break; + + case DateQualifiers dq: + builder.SetDateQualifiers(dq); + break; + + case BinaryDataQualifiers bdq: + builder.SetBinaryDataQualifiers(bdq); + break; + + default: + throw RuntimeException.InvalidNthArgumentType(nParam); + } + } + } + + internal class UndefinedTypeAdjuster : IValueAdjuster + { + public IValue Adjust(IValue value) + { + return ValueFactory.Create(); + } + } +} diff --git a/src/OneScript.StandardLibrary/TypeDescriptions/TypeDescriptionBuilder.cs b/src/OneScript.StandardLibrary/TypeDescriptions/TypeDescriptionBuilder.cs new file mode 100644 index 000000000..9f29de509 --- /dev/null +++ b/src/OneScript.StandardLibrary/TypeDescriptions/TypeDescriptionBuilder.cs @@ -0,0 +1,111 @@ +/*---------------------------------------------------------- +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 OneScript.Types; +using OneScript.Values; + +namespace OneScript.StandardLibrary.TypeDescriptions +{ + internal class TypeDescriptionBuilder + { + private NumberQualifiers _numberQualifiers; + private StringQualifiers _stringQualifiers; + private DateQualifiers _dateQualifiers; + private BinaryDataQualifiers _binaryDataQualifiers; + + private const string TYPE_BINARYDATA_NAME = "ДвоичныеДанные"; + + private static readonly TypeComparer _comparer = new TypeComparer(); + private readonly SortedSet _types = new SortedSet(_comparer); + + internal TypeDescriptionBuilder() + { + } + + public TypeDescriptionBuilder SourceDescription(TypeDescription source) + { + _numberQualifiers = source.NumberQualifiers; + _stringQualifiers = source.StringQualifiers; + _dateQualifiers = source.DateQualifiers; + _binaryDataQualifiers = source.BinaryDataQualifiers; + return AddTypes(source.TypesInternal()); + } + + public TypeDescriptionBuilder AddTypes(IEnumerable types) + { + foreach (var type in types) + { + if (type.TypeValue.ImplementingClass != typeof(BslUndefinedValue)) + _types.Add(type); + } + return this; + } + + public TypeDescriptionBuilder RemoveTypes(IEnumerable types) + { + foreach (var type in types) + { + _types.Remove(type); + } + return this; + } + + public TypeDescriptionBuilder SetNumberQualifiers(NumberQualifiers nq) + { + _numberQualifiers = nq; + return this; + } + + public TypeDescriptionBuilder SetStringQualifiers(StringQualifiers sq) + { + _stringQualifiers = sq; + return this; + } + + public TypeDescriptionBuilder SetDateQualifiers(DateQualifiers dq) + { + _dateQualifiers = dq; + return this; + } + + public TypeDescriptionBuilder SetBinaryDataQualifiers(BinaryDataQualifiers bq) + { + _binaryDataQualifiers = bq; + return this; + } + + public TypeDescription Build() + { + var hasNumber = _types.Any(type => type.TypeValue == BasicTypes.Number); + var hasString = _types.Any(type => type.TypeValue == BasicTypes.String); + var hasDate = _types.Any(type => type.TypeValue == BasicTypes.Date); + var hasBinaryData = _types.Any(x => x.TypeValue.Name == TYPE_BINARYDATA_NAME); + + if (!hasNumber || _numberQualifiers == null) _numberQualifiers = new NumberQualifiers(); + if (!hasString || _stringQualifiers == null) _stringQualifiers = new StringQualifiers(); + if (!hasDate || _dateQualifiers == null) _dateQualifiers = new DateQualifiers(); + if (!hasBinaryData || _binaryDataQualifiers == null) _binaryDataQualifiers = new BinaryDataQualifiers(); + + return new TypeDescription(_types, + _numberQualifiers, + _stringQualifiers, + _dateQualifiers, + _binaryDataQualifiers + ); + + } + + public static TypeDescriptionBuilder OfType(TypeDescriptor commonType) + { + var builder = new TypeDescriptionBuilder(); + builder.AddTypes(new[] { new BslTypeValue(commonType) }); + return builder; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XDTO/IXDTOSerializableXML.cs b/src/OneScript.StandardLibrary/XDTO/IXDTOSerializableXML.cs similarity index 82% rename from src/ScriptEngine.HostedScript/Library/XDTO/IXDTOSerializableXML.cs rename to src/OneScript.StandardLibrary/XDTO/IXDTOSerializableXML.cs index a681819dd..b98201a20 100644 --- a/src/ScriptEngine.HostedScript/Library/XDTO/IXDTOSerializableXML.cs +++ b/src/OneScript.StandardLibrary/XDTO/IXDTOSerializableXML.cs @@ -5,9 +5,9 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.HostedScript.Library.Xml; +using OneScript.StandardLibrary.Xml; -namespace ScriptEngine.HostedScript.Library.XDTO +namespace OneScript.StandardLibrary.XDTO { public interface IXDTOSerializableXML { diff --git a/src/OneScript.StandardLibrary/XDTO/XDTOSerializer.cs b/src/OneScript.StandardLibrary/XDTO/XDTOSerializer.cs new file mode 100644 index 000000000..8506f7df5 --- /dev/null +++ b/src/OneScript.StandardLibrary/XDTO/XDTOSerializer.cs @@ -0,0 +1,267 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Xml; +using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Xml; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XDTO +{ + [ContextClass("СериализаторXDTO", "XDTOSerializer")] + public sealed class XDTOSerializer : AutoContext + { + private readonly XmlGlobalFunctions _xmlGlobalFunctions; + + private XDTOSerializer(IGlobalsManager globalsManager) + { + _xmlGlobalFunctions = globalsManager.GetInstance(); + } + + private void WriteXMLSimpleData(XmlWriterImpl xmlWriter, + string name, + string value, + XMLExpandedName type, + XMLTypeAssignment typeAssigment, + XMLForm form) + { + XmlNamespaceContext namespaceContext; + switch (form) + { + case XMLForm.Attribute: + namespaceContext = xmlWriter.NamespaceContext; + AddNamespaceMapping(namespaceContext, xmlWriter, "", XmlSchema.Namespace); + + xmlWriter.WriteStartAttribute(name); + xmlWriter.WriteText(value); + xmlWriter.WriteEndAttribute(); + break; + + case XMLForm.Text: + xmlWriter.WriteText(value); + break; + + default: + + xmlWriter.WriteStartElement(name); + + namespaceContext = xmlWriter.NamespaceContext; + AddNamespaceMapping(namespaceContext, xmlWriter, "", XmlSchema.Namespace); + AddNamespaceMapping(namespaceContext, xmlWriter, "xsi", XmlSchema.InstanceNamespace); + + if (typeAssigment == XMLTypeAssignment.Explicit) + { + xmlWriter.WriteStartAttribute("type", XmlSchema.InstanceNamespace); + xmlWriter.WriteText(type.LocalName); + xmlWriter.WriteEndAttribute(); + } + + xmlWriter.WriteText(value); + + xmlWriter.WriteEndElement(); + break; + } + } + + private void WriteXMLUndefined(XmlWriterImpl xmlWriter, string name, XMLForm form) + { + if (form == XMLForm.Element) + { + XmlNamespaceContext namespaceContext = xmlWriter.NamespaceContext; + AddNamespaceMapping(namespaceContext, xmlWriter, "", XmlSchema.Namespace); + AddNamespaceMapping(namespaceContext, xmlWriter, "xsi", XmlSchema.InstanceNamespace); + + xmlWriter.WriteStartElement(name); + xmlWriter.WriteStartAttribute("nil", XmlSchema.InstanceNamespace); + xmlWriter.WriteText("true"); + xmlWriter.WriteEndAttribute(); + xmlWriter.WriteEndElement(); + } + } + + private void AddNamespaceMapping(XmlNamespaceContext namespaceContext, XmlWriterImpl xmlWriter, string prefix, string uri) + { + if (namespaceContext.LookupPrefix(uri) == ValueFactory.Create()) + xmlWriter.WriteNamespaceMapping(prefix, uri); + } + + #region OneScript + + #region Properties + + [ContextProperty("Фабрика", "Factory")] + public IValue Factory { get; } + + #endregion + + #region Methods + + [ContextMethod("XMLЗначение", "XMLValue")] + public IValue XMLValue(BslValue givenType, string presentation) => _xmlGlobalFunctions.XMLValue(givenType, presentation); + + [ContextMethod("XMLСтрока", "XMLString")] + public string XMLString(BslValue value) => _xmlGlobalFunctions.XMLString(value); + + //XMLТип(XMLType) + //XMLТипЗнч(XMLTypeOf) + //ВозможностьЧтенияXML(CanReadXML) + //ЗаписатьJSON(WriteJSON) + //ЗаписатьXDTO(WriteXDTO) + + [ContextMethod("ЗаписатьXML", "WriteXML")] + public void WriteXML(XmlWriterImpl xmlWriter, + BslValue value, + XMLTypeAssignment typeAssigment = XMLTypeAssignment.Implicit, + XMLForm form = XMLForm.Element) + { + if (value.SystemType == BasicTypes.Undefined) + { + WriteXML(xmlWriter, null, "Undefined", typeAssigment, form); + } + else if (value.SystemType == BasicTypes.String) + { + WriteXML(xmlWriter, XMLString(value), "string", typeAssigment, form); + } + else if (value.SystemType == BasicTypes.Number) + { + WriteXML(xmlWriter, XMLString(value), "decimal", typeAssigment, form); + } + else if (value.SystemType == BasicTypes.Boolean) + { + WriteXML(xmlWriter, XMLString(value), "boolean", typeAssigment, form); + } + else if (value.SystemType == BasicTypes.Date) + { + WriteXML(xmlWriter, XMLString(value), "dateTime", typeAssigment, form); + } + else + { + if(!(value is IXDTOSerializableXML seriazable)) + throw RuntimeException.InvalidArgumentType(); + + seriazable.WriteXML(xmlWriter, this); + } + } + + private void WriteXML(XmlWriterImpl xmlWriter, + string xmlString, + string name, + XMLTypeAssignment typeAssigment = XMLTypeAssignment.Implicit, + XMLForm form = XMLForm.Element) + { + XMLExpandedName xmlType; + if (name == "Undefined") + { + WriteXMLUndefined(xmlWriter, name, form); + } + else + { + xmlType = new XMLExpandedName(XmlSchema.InstanceNamespace, name); + WriteXMLSimpleData(xmlWriter, name, xmlString, xmlType, typeAssigment, form); + } + + } + + //ИзXMLТипа(FromXMLType) + //ПолучитьXMLТип(GetXMLType) + //ПрочитатьJSON(ReadJSON) + //ПрочитатьXDTO(ReadXDTO) + + [ContextMethod("ПрочитатьXML", "ReadXML")] + public IValue ReadXML(XmlReaderImpl xmlReader, IValue valueType = null) + { + BslTypeValue typeValue = null; + + if (valueType is BslTypeValue typeTypeValue) + typeValue = typeTypeValue; + + else if (xmlReader.NodeType.Equals(XmlNodeTypeEnum.FromNativeValue(XmlNodeType.Element))) + { + IValue xsiType = xmlReader.GetAttribute(ValueFactory.Create("type"), XmlSchema.InstanceNamespace); + IValue xsiNil = xmlReader.GetAttribute(ValueFactory.Create("nil"), XmlSchema.InstanceNamespace); + + if (xsiType.SystemType == BasicTypes.String) + { + switch (xsiType.ToString()) + { + case "string": + typeValue = new BslTypeValue(BasicTypes.String); + break; + + case "decimal": + typeValue = new BslTypeValue(BasicTypes.Number); + break; + + case "boolean": + typeValue = new BslTypeValue(BasicTypes.Boolean); + break; + + case "dateTime": + typeValue = new BslTypeValue(BasicTypes.Date); + break; + + default: + break; + } + } + else if (xsiNil.SystemType == BasicTypes.String) + typeValue = new BslTypeValue(BasicTypes.Undefined); + } + + if (typeValue == null) + throw RuntimeException.InvalidArgumentValue(); + + Type implType = typeValue.TypeValue.ImplementingClass; + + IValue result = ValueFactory.Create(); + + if (typeValue.Equals(new BslTypeValue(BasicTypes.Undefined))) + { + result = ValueFactory.Create(); + xmlReader.Skip(); + } + else if (typeof(BslPrimitiveValue).IsAssignableFrom(implType)) + { + xmlReader.Read(); + if (xmlReader.NodeType.Equals(XmlNodeTypeEnum.FromNativeValue(XmlNodeType.Text))) + { + result = XMLValue(typeValue, xmlReader.Value); + xmlReader.Read(); + } + else + throw RuntimeException.InvalidArgumentValue(); + } + else if (typeof(IXDTOSerializableXML).IsAssignableFrom(implType)) + result = Activator.CreateInstance(implType, xmlReader, this) as IValue; + + xmlReader.Read(); + return result; + } + + #endregion + + #region Constructors + + [ScriptConstructor(Name = "По умолчанию")] + public static XDTOSerializer CreateInstance(TypeActivationContext context) + { + var globalsManager = context.Services.Resolve(); + return new XDTOSerializer(globalsManager); + } + + #endregion + + #endregion + } +} diff --git a/src/OneScript.StandardLibrary/XDTO/XMLForm.cs b/src/OneScript.StandardLibrary/XDTO/XMLForm.cs new file mode 100644 index 000000000..a014f9634 --- /dev/null +++ b/src/OneScript.StandardLibrary/XDTO/XMLForm.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XDTO +{ + [EnumerationType("ФормаXML", "XMLForm")] + public enum XMLForm + { + [EnumValue("Элемент", "Element")] + Element, + + [EnumValue("Текст", "Text")] + Text, + + [EnumValue("Атрибут", "Attribute")] + Attribute + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Collections/XMLSchemaSet.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XMLSchemaSet.cs new file mode 100644 index 000000000..6849d999f --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XMLSchemaSet.cs @@ -0,0 +1,114 @@ +/*---------------------------------------------------------- +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 System.Xml.Schema; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Collections +{ + [ContextClass("НаборСхемXML", "XMLSchemaSet")] + public sealed class XMLSchemaSet : AutoCollectionContext + { + private readonly XmlSchemaSet _schemaSet; + private readonly List _items; + private XMLSchemaSet() + { + _items = new List(); + _schemaSet = new XmlSchemaSet(); + _schemaSet.ValidationEventHandler += SchemaSet_ValidationError; + } + + #region OneScript + + #region Methods + + [ContextMethod("Добавить", "Add")] + public void Add(Objects.XMLSchema schema) + { + _items.Add(schema); + _schemaSet.Add(schema.SchemaObject); + } + + [ContextMethod("Количество", "Count")] + public override int Count() => _schemaSet.Count; + + [ContextMethod("Получить", "Get")] + public Objects.XMLSchema Get(BslValue value) + { + switch (value) + { + case BslStringValue s: + return _items.FirstOrDefault(x => x.TargetNamespace.Equals((string)s)); + + case BslNumericValue n: + return _items[(int)n]; + + default: + throw RuntimeException.InvalidArgumentType(); + } + } + + [ContextMethod("Проверить", "Validate")] + public bool Validate() + { + _schemaSet.Compile(); + return _schemaSet.IsCompiled; + } + + [ContextMethod("Удалить", "Delete")] + public void Delete(string namespaceUri) + { + Objects.XMLSchema item = _items.Find(x => x.TargetNamespace.Equals(namespaceUri)); + if (item is Objects.XMLSchema) + { + _items.Remove(item); + _schemaSet.Remove(item.SchemaObject); + } + } + + #endregion + + #region Constructors + + [ScriptConstructor(Name = "По умолчанию")] + public static XMLSchemaSet Constructor() => new XMLSchemaSet(); + + #endregion + + #endregion + + #region IEnumerable + + public override IEnumerator GetEnumerator() => _items.GetEnumerator(); + + #endregion + + #region SchemaSetEventHandlers + + private void SchemaSet_ValidationError(object sender, ValidationEventArgs args) + { + switch (args.Severity) + { + case XmlSeverityType.Error: + SystemLogger.Write($"ERROR:{args.Message}"); + break; + + default: + SystemLogger.Write($"WARNING:{args.Message}"); + break; + } + } + + #endregion + } +} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComplexFinalUnion.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSComplexFinalUnion.cs similarity index 78% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComplexFinalUnion.cs rename to src/OneScript.StandardLibrary/XMLSchema/Collections/XSComplexFinalUnion.cs index d67ffc127..6b8b24765 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComplexFinalUnion.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSComplexFinalUnion.cs @@ -6,21 +6,25 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.Types; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Collections { [ContextClass("ОбъединениеЗавершенностиСоставногоТипаXS", "XSComplexFinalUnion")] - public class XSComplexFinalUnion : AutoContext + public sealed class XSComplexFinalUnion : AutoContext { private readonly ArrayImpl _values; private bool Contains(XmlSchemaDerivationMethod value) { - XSComplexFinal enumValue = XSComplexFinal.FromNativeValue(value); + var enumValue = EnumerationXSComplexFinal.FromNativeValue(value); IValue idx = _values.Find(enumValue); - return idx.DataType == DataType.Number; + return idx.SystemType == BasicTypes.Number; } public XSComplexFinalUnion() => _values = ArrayImpl.Constructor(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Collections/XSComponentFixedList.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSComponentFixedList.cs new file mode 100644 index 000000000..962311a1a --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSComponentFixedList.cs @@ -0,0 +1,54 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Collections +{ + [ContextClass("ФиксированныйСписокКомпонентXS", "XSComponentFixedList")] + public sealed class XSComponentFixedList : AutoCollectionContext + { + + private readonly List _items; + + public XSComponentFixedList() => _items = new List(); + + public static XSComponentFixedList EmptyList() => new XSComponentFixedList(); + + public void Add(IXSComponent value) => _items.Add(value); + public void Remove(IXSComponent value) => _items.Remove(value); + public void Clear() => _items.Clear(); + public void RemoveAll(Predicate predicate) => _items.RemoveAll(predicate); + + #region OneScript + + #region Methods + + [ContextMethod("Количество", "Count")] + public override int Count() => _items.Count; + + [ContextMethod("Получить", "Get")] + public IXSComponent Get(int index) => _items[index]; + + [ContextMethod("Содержит", "Contains")] + public bool Contains(IXSComponent value) => _items.Contains(value); + + #endregion + + #endregion + + #region IEnumerable + + public override IEnumerator GetEnumerator() => _items.GetEnumerator(); + + #endregion + } +} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComponentList.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSComponentList.cs similarity index 79% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComponentList.cs rename to src/OneScript.StandardLibrary/XMLSchema/Collections/XSComponentList.cs index 9e990b67c..7ea606648 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComponentList.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSComponentList.cs @@ -4,16 +4,19 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; -using System.Collections; using System.Collections.Generic; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using OneScript.Values; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Collections { [ContextClass("СписокКомпонентXS", "XSComponentList")] - public class XSComponentList : AutoContext, ICollectionContext, IEnumerable + public sealed class XSComponentList : AutoCollectionContext { private readonly List _items; @@ -57,7 +60,7 @@ public void Add(IXSComponent value) } [ContextMethod("Количество", "Count")] - public int Count() => _items.Count; + public override int Count() => _items.Count; [ContextMethod("Очистить", "Clear")] public void Clear() @@ -73,18 +76,18 @@ public void Clear() public bool Contains(IXSComponent value) => _items.Contains(value); [ContextMethod("Удалить", "Delete")] - public void Delete(IValue value) + public void Delete(BslValue value) { int index; - switch (value.DataType) + switch (value) { - case DataType.Number: - index = (int)value.AsNumber(); + case BslNumericValue n: + index = (int)n; break; - case DataType.Object: - index = _items.IndexOf(value as IXSComponent); + case IXSComponent xsComponent: + index = _items.IndexOf(xsComponent); if (index == -1) return; break; @@ -117,15 +120,7 @@ public void Set(int index, IXSComponent value) #region ICollectionContext - public CollectionEnumerator GetManagedIterator() => new CollectionEnumerator(GetEnumerator()); - - #endregion - - #region IEnumerable - - public IEnumerator GetEnumerator() => _items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public override IEnumerator GetEnumerator() => _items.GetEnumerator(); #endregion } diff --git a/src/OneScript.StandardLibrary/XMLSchema/Collections/XSDisallowedSubstitutionsUnion.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSDisallowedSubstitutionsUnion.cs new file mode 100644 index 000000000..a663f4538 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSDisallowedSubstitutionsUnion.cs @@ -0,0 +1,55 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Collections +{ + [ContextClass("ОбъединениеНедопустимыхПодстановкиXS", "XSDisallowedSubstitutionsUnion")] + public sealed class XsDisallowedSubstitutionsUnion : AutoContext + { + private readonly ArrayImpl _values; + + private bool Contains(XmlSchemaDerivationMethod value) + { + var enumValue = EnumerationXSDisallowedSubstitutions.FromNativeValue(value); + var idx = _values.Find(enumValue); + return (idx.SystemType != BasicTypes.Undefined); + } + + internal XsDisallowedSubstitutionsUnion() => _values = ArrayImpl.Constructor(); + + #region OneScript + + #region Properties + + [ContextProperty("Все", "All")] + public bool All => Contains(XmlSchemaDerivationMethod.All); + + [ContextProperty("Ограничение", "Restriction")] + public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); + + [ContextProperty("Расширение", "Extension")] + public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); + + #endregion + + #region Methods + + [ContextMethod("Значения", "Values")] + public ArrayImpl Values() => _values; + + #endregion + + #endregion + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Collections/XSNamedComponentMap.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSNamedComponentMap.cs new file mode 100644 index 000000000..b29844a9f --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSNamedComponentMap.cs @@ -0,0 +1,75 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Collections +{ + [ContextClass("КоллекцияИменованныхКомпонентXS", "XSNamedComponentMap")] + public sealed class XSNamedComponentMap : AutoCollectionContext + { + private readonly List _items; + + internal XSNamedComponentMap() => _items = new List(); + + internal void Add(IXSNamedComponent value) + { + if (string.IsNullOrWhiteSpace(value.Name)) + return; + + _items.Add(value); + } + + internal void Delete(IXSNamedComponent value) => _items.Remove(value); + + internal void Clear() => _items.Clear(); + + #region OneScript + + #region Methods + + [ContextMethod("Количество", "Count")] + public override int Count() => _items.Count; + + [ContextMethod("Получить", "Get")] + public IXSNamedComponent Get(IValue value) + { + if (value.SystemType == BasicTypes.String) + { + return _items.FirstOrDefault(x => x.Name.Equals(value.ToString())); + } + else if (value.SystemType == BasicTypes.Number) + { + return _items[(int)value.AsNumber()]; + } + else + { + throw RuntimeException.InvalidArgumentType(); + } + } + + public IXSNamedComponent Get(string name, string namespaceURI) + { + throw new NotImplementedException(); + } + + #endregion + + #endregion + + public override IEnumerator GetEnumerator() => _items.GetEnumerator(); + + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Collections/XSProhibitedSubstitutionsUnion.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSProhibitedSubstitutionsUnion.cs new file mode 100644 index 000000000..c8b7b46a2 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSProhibitedSubstitutionsUnion.cs @@ -0,0 +1,56 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Collections +{ + [ContextClass("ОбъединениеЗапрещенныхПодстановокXS", "XSProhibitedSubstitutionsUnion")] + public sealed class XsProhibitedSubstitutionsUnion : AutoContext + { + private readonly ArrayImpl _values; + + private bool Contains(XmlSchemaDerivationMethod value) + { + var enumValue = EnumerationXSProhibitedSubstitutions.FromNativeValue(value); + IValue idx = _values.Find(enumValue); + return (idx.SystemType != BasicTypes.Undefined); + } + + internal XsProhibitedSubstitutionsUnion() => _values = ArrayImpl.Constructor(); + + #region OneScript + + #region Properties + + [ContextProperty("Все", "All")] + public bool All => Contains(XmlSchemaDerivationMethod.All); + + [ContextProperty("Ограничение", "Restriction")] + public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); + + [ContextProperty("Расширение", "Extension")] + public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); + + #endregion + + #region Methods + + [ContextMethod("Значения", "Values")] + public ArrayImpl Values() => _values; + + #endregion + + #endregion + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSchemaFinalUnion.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSchemaFinalUnion.cs new file mode 100644 index 000000000..875e42d15 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSchemaFinalUnion.cs @@ -0,0 +1,61 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Collections +{ + [ContextClass("ОбъединениеЗавершенностиСхемыXS", "XSSchemaFinalUnion")] + public sealed class XSSchemaFinalUnion : AutoContext + { + private readonly ArrayImpl _values; + + private bool Contains(XmlSchemaDerivationMethod value) + { + var enumValue = EnumerationXSSchemaFinal.FromNativeValue(value); + var idx = _values.Find(enumValue); + return (idx.SystemType != BasicTypes.Undefined); + } + + internal XSSchemaFinalUnion() => _values = ArrayImpl.Constructor(); + + #region OneScript + + #region Properties + + [ContextProperty("Все", "All")] + public bool All => Contains(XmlSchemaDerivationMethod.All); + + [ContextProperty("Объединение", "Union")] + public bool Union => Contains(XmlSchemaDerivationMethod.Union); + + [ContextProperty("Ограничение", "Restriction")] + public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); + + [ContextProperty("Расширение", "Extension")] + public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); + + [ContextProperty("Список", "List")] + public bool List => Contains(XmlSchemaDerivationMethod.List); + + #endregion + + #region Methods + + [ContextMethod("Значения", "Values")] + public ArrayImpl Values() => _values; + + #endregion + + #endregion + } +} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSimpleFinalUnion.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSimpleFinalUnion.cs similarity index 75% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSimpleFinalUnion.cs rename to src/OneScript.StandardLibrary/XMLSchema/Collections/XSSimpleFinalUnion.cs index b490b1d99..1cd14e6aa 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSimpleFinalUnion.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSimpleFinalUnion.cs @@ -4,22 +4,26 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.Types; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Collections { [ContextClass("ОбъединениеЗавершенностиПростогоТипаXS", "XSSimpleFinalUnion")] - public class XSSimpleFinalUnion : AutoContext + public sealed class XSSimpleFinalUnion : AutoContext { private ArrayImpl _values; private bool Contains(XmlSchemaDerivationMethod _value) { - XSSimpleFinal _enumValue = EnumerationXSSimpleFinal.FromNativeValue(_value); - IValue _idx = _values.Find(_enumValue); - return (_idx.DataType != DataType.Undefined); + var enumValue = EnumerationXSSimpleFinal.FromNativeValue(_value); + var idx = _values.Find(enumValue); + return idx.SystemType != BasicTypes.Undefined; } public XSSimpleFinalUnion() => _values = ArrayImpl.Constructor(); diff --git a/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSubstitutionGroupExclusionsUnion.cs b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSubstitutionGroupExclusionsUnion.cs new file mode 100644 index 000000000..bee4847ff --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Collections/XSSubstitutionGroupExclusionsUnion.cs @@ -0,0 +1,55 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Collections +{ + [ContextClass("ОбъединениеИсключенийГруппПодстановкиXS", "XSSubstitutionGroupExclusionsUnion")] + public sealed class XsSubstitutionGroupExclusionsUnion : AutoContext + { + private readonly ArrayImpl _values; + private bool Contains(XmlSchemaDerivationMethod value) + { + var enumValue = EnumerationXSSubstitutionGroupExclusions.FromNativeValue(value); + IValue idx = _values.Find(enumValue); + return (idx.SystemType != BasicTypes.Undefined); + } + + internal XsSubstitutionGroupExclusionsUnion() => _values = ArrayImpl.Constructor(); + + #region OneScript + + #region Properties + + [ContextProperty("Все", "All")] + public bool All => Contains(XmlSchemaDerivationMethod.All); + + [ContextProperty("Ограничение", "Restriction")] + public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); + + [ContextProperty("Расширение", "Extension")] + public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); + + #endregion + + #region Methods + + [ContextMethod("Значения", "Values")] + public ArrayImpl Values() => _values; + + #endregion + + #endregion + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSAttributeUseCategory.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSAttributeUseCategory.cs new file mode 100644 index 000000000..9c40f03a6 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSAttributeUseCategory.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Xml.Schema; +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [EnumerationType("КатегорияИспользованияАтрибутаXS", "XSAttributeUseCategory")] + public enum XSAttributeUseCategory + { + [EnumValue("ПустаяСсылка", "EmptyRef")] + EmptyRef = XmlSchemaUse.None, + + [EnumValue("Необязательно", "Optional")] + Optional = XmlSchemaUse.Optional, + + [EnumValue("Запрещено", "Prohibited")] + Prohibited = XmlSchemaUse.Prohibited, + + [EnumValue("Обязательно", "Required")] + Required = XmlSchemaUse.Required + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSComplexFinal.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSComplexFinal.cs new file mode 100644 index 000000000..eb2d2d115 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSComplexFinal.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [SystemEnum("ЗавершенностьСоставногоТипаXS", "XSComplexFinal")] + public sealed class EnumerationXSComplexFinal : ClrEnumWrapperCached + { + private EnumerationXSComplexFinal(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Все", "All", XmlSchemaDerivationMethod.All); + MakeValue("Ограничение", "Restriction", XmlSchemaDerivationMethod.Restriction); + MakeValue("Список", "List", XmlSchemaDerivationMethod.List); + } + + public static EnumerationXSComplexFinal CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t,v) => new EnumerationXSComplexFinal(t,v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSComponentType.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSComponentType.cs new file mode 100644 index 000000000..38aa045ab --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSComponentType.cs @@ -0,0 +1,112 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [EnumerationType("ТипКомпонентыXS", "XSComponentType")] + public enum XSComponentType + { + + [EnumValue("Аннотация", "Annotation")] + Annotation, + + [EnumValue("Включение", "Include")] + Include, + + [EnumValue("ГруппаМодели", "ModelGroup")] + ModelGroup, + + [EnumValue("Документация", "Documentation")] + Documentation, + + [EnumValue("Импорт", "Import")] + Import, + + [EnumValue("ИнформацияПриложения", "AppInfo")] + AppInfo, + + [EnumValue("ИспользованиеАтрибута", "AttributeUse")] + AttributeUse, + + [EnumValue("МаксимальноВключающийФасет", "MaxInclusiveFacet")] + MaxInclusiveFacet, + + [EnumValue("МаксимальноИсключающийФасет", "MaxExclusiveFacet")] + MaxExclusiveFacet, + + [EnumValue("Маска", "Wildcard")] + Wildcard, + + [EnumValue("МинимальноВключающийФасет", "MinInclusiveFacet")] + MinInclusiveFacet, + + [EnumValue("МинимальноИсключающийФасет", "MinExclusiveFacet")] + MinExclusiveFacet, + + [EnumValue("ОбъявлениеАтрибута", "AttributeDeclaration")] + AttributeDeclaration, + + [EnumValue("ОбъявлениеНотации", "NotationDeclaration")] + NotationDeclaration, + + [EnumValue("ОбъявлениеЭлемента", "ElementDeclaration")] + ElementDeclaration, + + [EnumValue("ОпределениеXPath", "XPathDefinition")] + XPathDefinition, + + [EnumValue("ОпределениеГруппыАтрибутов", "AttributeGroupDefinition")] + AttributeGroupDefinition, + + [EnumValue("ОпределениеГруппыМодели", "ModelGroupDefinition")] + ModelGroupDefinition, + + [EnumValue("ОпределениеОграниченияИдентичности", "IdentityConstraintDefinition")] + IdentityConstraintDefinition, + + [EnumValue("ОпределениеПростогоТипа", "SimpleTypeDefinition")] + SimpleTypeDefinition, + + [EnumValue("ОпределениеСоставногоТипа", "ComplexTypeDefinition")] + ComplexTypeDefinition, + + [EnumValue("Переопределение", "Redefine")] + Redefine, + + [EnumValue("Схема", "Schema")] + Schema, + + [EnumValue("ФасетДлины", "LengthFacet")] + LengthFacet, + + [EnumValue("ФасетКоличестваРазрядовДробнойЧасти", "FractionDigitsFacet")] + FractionDigitsFacet, + + [EnumValue("ФасетМаксимальнойДлины", "MaxLengthFacet")] + MaxLengthFacet, + + [EnumValue("ФасетМинимальнойДлины", "MinLengthFacet")] + MinLengthFacet, + + [EnumValue("ФасетОбразца", "PatternFacet")] + PatternFacet, + + [EnumValue("ФасетОбщегоКоличестваРазрядов", "TotalDigitsFacet")] + TotalDigitsFacet, + + [EnumValue("ФасетПеречисления", "EnumerationFacet")] + EnumerationFacet, + + [EnumValue("ФасетПробельныхСимволов", "WhitespaceFacet")] + WhitespaceFacet, + + [EnumValue("Фрагмент", "Particle")] + Particle + } +} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSCompositor.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSCompositor.cs similarity index 78% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSCompositor.cs rename to src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSCompositor.cs index 3a0e01851..23de20a0c 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSCompositor.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSCompositor.cs @@ -4,36 +4,38 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System.Xml.Schema; +using OneScript.Contexts.Enums; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Enumerations { /// /// Вид группы модели /// /// - [EnumerationType("XSCompositor", "ВидГруппыМоделиXS")] + [EnumerationType("ВидГруппыМоделиXS", "XSCompositor")] public enum XSCompositor { /// /// Требует наличия элементов группы без требования последовательности /// /// - [EnumItem("All", "Все")] + [EnumValue("Все", "All")] All, /// /// Требует наличия только одного из элементов группы /// /// - [EnumItem("Choice", "Выбор")] + [EnumValue("Выбор", "Choice")] Choice, /// /// Требует чтобы элементы следовали в указанной последовательности /// /// - [EnumItem("Sequence", "Последовательность")] + [EnumValue("Последовательность", "Sequence")] Sequence } } diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSConstraint.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSConstraint.cs new file mode 100644 index 000000000..61ac05ad0 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSConstraint.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + /// + /// Описывает варианты ограничения значения + /// + [EnumerationType("ОграничениеЗначенияXS", "XSConstraint")] + public enum XSConstraint + { + /// + /// Используется ограничение по умолчанию + /// + [EnumValue("ПоУмолчанию", "Default")] + Default, + + /// + /// Используется фиксированное значение + /// + [EnumValue("Фиксированное", "Fixed")] + Fixed + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSContentModel.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSContentModel.cs new file mode 100644 index 000000000..aec9d62a7 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSContentModel.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + /// + /// Определяет вид модели содержания. + /// + [EnumerationType("МодельСодержимогоXS", "XSContentModel")] + public enum XSContentModel + { + [EnumValue("ПустаяСсылка", "EmptyRef")] + EmptyRef, + + /// + /// Простая модель содержания. + /// + [EnumValue("Простая", "Simple")] + Simple, + + /// + /// Составная модель содержания. + /// + [EnumValue("Составная", "Complex")] + Complex + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSDerivationMethod.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSDerivationMethod.cs new file mode 100644 index 000000000..0a28e6c00 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSDerivationMethod.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [EnumerationType("МетодНаследованияXS", "XSDerivationMethod")] + public enum XSDerivationMethod + { + [EnumValue("ПустаяСсылка", "EmptyRef")] + EmptyRef, + + [EnumValue("Ограничение", "Restriction")] + Restriction, + + [EnumValue("Расширение", "Extension")] + Extension + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSDisallowedSubstitutions.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSDisallowedSubstitutions.cs new file mode 100644 index 000000000..db80ce228 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSDisallowedSubstitutions.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [SystemEnum("НедопустимыеПодстановкиXS", "XSDisallowedSubstitutions")] + public sealed class EnumerationXSDisallowedSubstitutions : ClrEnumWrapperCached + { + private EnumerationXSDisallowedSubstitutions(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Все", "All", XmlSchemaDerivationMethod.All); + MakeValue("Ограничение", "Restriction", XmlSchemaDerivationMethod.Restriction); + MakeValue("Подстановка", "Substitution", XmlSchemaDerivationMethod.Substitution); + MakeValue("Расширение", "Extension", XmlSchemaDerivationMethod.Extension); + } + + public static EnumerationXSDisallowedSubstitutions CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t,v) => new EnumerationXSDisallowedSubstitutions(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSForm.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSForm.cs new file mode 100644 index 000000000..256a3335f --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSForm.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [SystemEnum("ФормаПредставленияXS", "XSForm")] + public sealed class EnumerationXSForm : ClrEnumWrapperCached + { + private EnumerationXSForm(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Квалифицированная", "Qualified", XmlSchemaForm.Qualified); + MakeValue("Неквалифицированная", "Unqualified", XmlSchemaForm.Unqualified); + } + + public static EnumerationXSForm CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t,v) => new EnumerationXSForm(t,v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs new file mode 100644 index 000000000..0a78b22e1 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs @@ -0,0 +1,42 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.StandardLibrary.XMLSchema.Objects; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + /// + /// Типы ограничения идентичности. + /// + /// + [EnumerationType("КатегорияОграниченияИдентичностиXS", "XSIdentityConstraintCategory")] + public enum XSIdentityConstraintCategory + { + /// + /// Ограничение идентичности по ключу + /// + /// + [EnumValue("Ключ", "Key")] + Key, + + /// + /// Ограничение идентичности по ссылке + /// + /// + [EnumValue("СсылкаНаКлюч", "KeyRef")] + KeyRef, + + /// + /// Ограничение идентичности по опредению уникальности + /// + /// + [EnumValue("Уникальность", "Unique")] + Unique + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSNamespaceConstraintCategory.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSNamespaceConstraintCategory.cs new file mode 100644 index 000000000..4e237eff9 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSNamespaceConstraintCategory.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [EnumerationType("КатегорияОграниченияПространствИменXS", "XSNamespaceConstraintCategory")] + public enum XSNamespaceConstraintCategory + { + [EnumValue("ПустаяСсылка", "EmptyRef")] + EmptyRef, + + [EnumValue("Кроме", "Not")] + Not, + + [EnumValue("Любое", "Any")] + Any, + + [EnumValue("Набор", "Set")] + Set + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSProcessContents.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSProcessContents.cs new file mode 100644 index 000000000..ad094cec5 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSProcessContents.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Xml.Schema; +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [EnumerationType("ОбработкаСодержимогоXS", "XSProcessContents")] + public enum XSProcessContents + { + [EnumValue("ПустаяСсылка", "EmptyRef")] + EmptyRef = XmlSchemaContentProcessing.None, + + [EnumValue("Пропустить", "Skip")] + Skip = XmlSchemaContentProcessing.Skip, + + [EnumValue("Слабая", "Lax")] + Lax = XmlSchemaContentProcessing.Lax, + + [EnumValue("Строгая", "Strict")] + Strict = XmlSchemaContentProcessing.Strict + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSProhibitedSubstitutions.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSProhibitedSubstitutions.cs new file mode 100644 index 000000000..a1e8aca1a --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSProhibitedSubstitutions.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [SystemEnum("ЗапрещенныеПодстановкиXS", "XSProhibitedSubstitutions")] + public sealed class EnumerationXSProhibitedSubstitutions : ClrEnumWrapperCached + { + private EnumerationXSProhibitedSubstitutions(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Все", "All", XmlSchemaDerivationMethod.All); + MakeValue("Ограничение", "Restriction", XmlSchemaDerivationMethod.Restriction); + MakeValue("Расширение", "Extension", XmlSchemaDerivationMethod.Extension); + } + + public static EnumerationXSProhibitedSubstitutions CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new EnumerationXSProhibitedSubstitutions(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSchemaFinal.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSchemaFinal.cs new file mode 100644 index 000000000..1490c287b --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSchemaFinal.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [SystemEnum("ЗавершенностьСхемыXS", "XSSchemaFinal")] + public sealed class EnumerationXSSchemaFinal : ClrEnumWrapperCached + { + private EnumerationXSSchemaFinal(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Все", "All", XmlSchemaDerivationMethod.All); + MakeValue("Объединение", "Union", XmlSchemaDerivationMethod.Union); + MakeValue("Ограничение", "Restriction", XmlSchemaDerivationMethod.Restriction); + MakeValue("Расширение", "Extension", XmlSchemaDerivationMethod.Extension); + MakeValue("Список", "List", XmlSchemaDerivationMethod.List); + } + + + public static EnumerationXSSchemaFinal CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new EnumerationXSSchemaFinal(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSimpleFinal.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSimpleFinal.cs new file mode 100644 index 000000000..09230581b --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSimpleFinal.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [SystemEnum("ЗавершенностьПростогоТипаXS", "XSSimpleFinal")] + public sealed class EnumerationXSSimpleFinal : ClrEnumWrapperCached + { + private EnumerationXSSimpleFinal(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Все", "All", XmlSchemaDerivationMethod.All); + MakeValue("Объединение", "Union", XmlSchemaDerivationMethod.Union); + MakeValue("Ограничение", "Restriction", XmlSchemaDerivationMethod.Restriction); + MakeValue("Список", "List", XmlSchemaDerivationMethod.List); + } + + public static EnumerationXSSimpleFinal CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new EnumerationXSSimpleFinal(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSimpleTypeVariety.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSimpleTypeVariety.cs new file mode 100644 index 000000000..ffb40801b --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSimpleTypeVariety.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [EnumerationType("ВариантПростогоТипаXS", "XSSimpleTypeVariety")] + public enum XSSimpleTypeVariety + { + [EnumValue("Атомарная", "Atomic")] + Atomic, + + [EnumValue("Объединение", "Union")] + Union, + + [EnumValue("Список", "List")] + List + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSubstitutionGroupExclusions.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSubstitutionGroupExclusions.cs new file mode 100644 index 000000000..e84d04226 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSSubstitutionGroupExclusions.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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.Xml.Schema; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [SystemEnum("ИсключенияГруппПодстановкиXS", "XSSubstitutionGroupExclusions")] + public sealed class EnumerationXSSubstitutionGroupExclusions : ClrEnumWrapperCached + { + private EnumerationXSSubstitutionGroupExclusions(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Все", "All", XmlSchemaDerivationMethod.All); + MakeValue("Ограничение", "Restriction", XmlSchemaDerivationMethod.Restriction); + MakeValue("Расширение", "Extension", XmlSchemaDerivationMethod.Extension); + } + + public static EnumerationXSSubstitutionGroupExclusions CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new EnumerationXSSubstitutionGroupExclusions(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSWhitespaceHandling.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSWhitespaceHandling.cs new file mode 100644 index 000000000..5af6e3184 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSWhitespaceHandling.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + [EnumerationType("ОбработкаПробельныхСимволовXS", "XSWhitespaceHandling")] + public enum XSWhitespaceHandling + { + [EnumValue("Заменять", "Replace")] + Replace, + + [EnumValue("Сворачивать", "Collapse")] + Collapse, + + [EnumValue("Сохранять", "Preserve")] + Preserve + } +} diff --git a/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSXPathVariety.cs b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSXPathVariety.cs new file mode 100644 index 000000000..4699c104a --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Enumerations/XSXPathVariety.cs @@ -0,0 +1,32 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; +using OneScript.StandardLibrary.XMLSchema.Objects; + +namespace OneScript.StandardLibrary.XMLSchema.Enumerations +{ + /// + /// Содержит варианты использования выражения XPath. + /// + /// + [EnumerationType("ВариантXPathXS", "XSXPathVariety")] + public enum XSXPathVariety + { + /// + /// Используется в качестве поля + /// + [EnumValue("Поле", "Field")] + Field, + + /// + /// Используется в качестве селектора + /// + [EnumValue("Селектор", "Selector")] + Selector + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAnnotated.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAnnotated.cs similarity index 79% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAnnotated.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAnnotated.cs index 17c762981..4a597ad1b 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAnnotated.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAnnotated.cs @@ -4,7 +4,10 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.XMLSchema + +using OneScript.StandardLibrary.XMLSchema.Objects; + +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { public interface IXSAnnotated : IXSComponent { diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAnnotationItem.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAnnotationItem.cs similarity index 86% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAnnotationItem.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAnnotationItem.cs index a5d28da06..4ca3cef23 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAnnotationItem.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAnnotationItem.cs @@ -5,7 +5,9 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.XMLSchema +using OneScript.StandardLibrary.XMLSchema.Objects; + +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { /// /// Интерфейс для реализации элементов аннотации diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAttribute.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAttribute.cs similarity index 87% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAttribute.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAttribute.cs index 944401228..20de67d07 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSAttribute.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSAttribute.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { internal interface IXSAttribute :IXSComponent { diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSComponent.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSComponent.cs similarity index 77% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSComponent.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSComponent.cs index f2a8be5a2..f80b4bb27 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSComponent.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSComponent.cs @@ -4,10 +4,15 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; + using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Objects; +using ScriptEngine.Machine; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { public interface IXSComponent : IRuntimeContextInstance, IValue { @@ -17,7 +22,7 @@ public interface IXSComponent : IRuntimeContextInstance, IValue XSComponentFixedList Components { get; } IXSComponent Container { get; } IXSComponent RootContainer { get; } - XMLSchema Schema { get; } + Objects.XMLSchema Schema { get; } XSComponentType ComponentType { get; } //DOMElement diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSDirective.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSDirective.cs similarity index 79% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSDirective.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSDirective.cs index 410d4f561..abe4ecd48 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSDirective.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSDirective.cs @@ -5,11 +5,11 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { public interface IXSDirective : IXSComponent { - XMLSchema ResolvedSchema { get; set; } + Objects.XMLSchema ResolvedSchema { get; set; } string SchemaLocation { get; set; } } } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSFacet.cs similarity index 82% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSFacet.cs index 009d3bfea..24f1a40b4 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSFacet.cs @@ -5,7 +5,9 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.XMLSchema +using OneScript.StandardLibrary.XMLSchema.Objects; + +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { public interface IXSFacet : IXSAnnotated { diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSFragment.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSFragment.cs similarity index 87% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSFragment.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSFragment.cs index eaed4986b..dca26648f 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSFragment.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSFragment.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { public interface IXSFragment : IXSAnnotated { diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSNamedComponent.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSNamedComponent.cs similarity index 94% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSNamedComponent.cs rename to src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSNamedComponent.cs index 42621af65..93fc86834 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSNamedComponent.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSNamedComponent.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Interfaces { public interface IXSNamedComponent : IXSComponent { diff --git a/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSType.cs b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSType.cs new file mode 100644 index 000000000..8929e2d14 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Interfaces/IXSType.cs @@ -0,0 +1,15 @@ +/*---------------------------------------------------------- +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.XMLSchema.Interfaces +{ + public interface IXSType : IXSAnnotated + { + string NamespaceURI { get; } + string Name { get; set; } + } +} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XMLSchema.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XMLSchema.cs similarity index 86% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XMLSchema.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XMLSchema.cs index 58bf22c3f..5d43e7ec1 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XMLSchema.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XMLSchema.cs @@ -10,21 +10,31 @@ This Source Code Form is subject to the terms of the using System.Text; using System.Xml; using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.XDTO; -using ScriptEngine.HostedScript.Library.Xml; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.XDTO; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("СхемаXML", "XMLSchema")] - public class XMLSchema : AutoContext, IXSComponent, IXDTOSerializableXML + public sealed class XMLSchema : AutoContext, IXSComponent, IXDTOSerializableXML { private readonly XmlSchema _schema; - private XMLSchema() + private XMLSchema() : this(new XmlSchema()) { } + + public XMLSchema(XmlSchema xmlSchema) { - _schema = new XmlSchema(); + _schema = xmlSchema; Components = new XSComponentFixedList(); Annotations = new XSComponentFixedList(); @@ -36,7 +46,7 @@ private XMLSchema() Content.Inserted += ContentInserted; Content.Cleared += ContentCleared; - BlockDefault = new XSDisallowedSubstitutionsUnion(); + BlockDefault = new XsDisallowedSubstitutionsUnion(); FinalDefault = new XSSchemaFinalUnion(); AttributeDeclarations = new XSNamedComponentMap(); @@ -93,26 +103,18 @@ private static void ValidationCallbackOne(object sender, ValidationEventArgs arg internal static string XMLStringIValue(IValue value) { - switch (value.DataType) - { - case DataType.Undefined: - return ""; - - case DataType.String: - return value.AsString(); - - case DataType.Boolean: - return XmlConvert.ToString(value.AsBoolean()); - - case DataType.Date: - return XmlConvert.ToString(value.AsDate(), XmlDateTimeSerializationMode.Unspecified); - - case DataType.Number: - return XmlConvert.ToString(value.AsNumber()); - - default: - throw RuntimeException.InvalidArgumentType(); - } + if (value.SystemType == BasicTypes.Undefined) + return ""; + else if(value.SystemType == BasicTypes.String) + return value.ToString(); + else if(value.SystemType == BasicTypes.Boolean) + return XmlConvert.ToString(value.AsBoolean()); + else if(value.SystemType == BasicTypes.Date) + return XmlConvert.ToString(value.AsDate(), XmlDateTimeSerializationMode.Unspecified); + else if(value.SystemType == BasicTypes.Number) + return XmlConvert.ToString(value.AsNumber()); + + throw RuntimeException.InvalidArgumentType(); } @@ -148,7 +150,7 @@ internal static string XMLStringIValue(IValue value) public XSComponentFixedList Annotations { get; } [ContextProperty("БлокировкаПоУмолчанию", "BlockDefault")] - public XSDisallowedSubstitutionsUnion BlockDefault { get; } + public XsDisallowedSubstitutionsUnion BlockDefault { get; } [ContextProperty("Версия", "Version")] public string Version @@ -207,17 +209,17 @@ public string TargetNamespace public XMLSchema SchemaForSchema { get; } [ContextProperty("ФормаАтрибутовПоУмолчанию", "AttributeFormDefault")] - public XSForm AttributeFormDefault + public ClrEnumValueWrapper AttributeFormDefault { - get => XSForm.FromNativeValue(_schema.AttributeFormDefault); - set => _schema.AttributeFormDefault = XSForm.ToNativeValue(value); + get => EnumerationXSForm.FromNativeValue(_schema.AttributeFormDefault); + set => _schema.AttributeFormDefault = value.UnderlyingValue; } [ContextProperty("ФормаЭлементовПоУмолчанию", "ElementFormDefault")] - public XSForm ElementFormDefault + public ClrEnumValueWrapper ElementFormDefault { - get => XSForm.FromNativeValue(_schema.ElementFormDefault); - set => _schema.ElementFormDefault = XSForm.ToNativeValue(value); + get => EnumerationXSForm.FromNativeValue(_schema.ElementFormDefault); + set => _schema.ElementFormDefault = value.UnderlyingValue; } [ContextProperty("Язык", "Lang")] diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XMLSchemaSerializer.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XMLSchemaSerializer.cs similarity index 95% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XMLSchemaSerializer.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XMLSchemaSerializer.cs index 3d53381d5..80421b16d 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XMLSchemaSerializer.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XMLSchemaSerializer.cs @@ -7,12 +7,13 @@ This Source Code Form is subject to the terms of the using System.Xml; using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; -using ScriptEngine.Machine; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Interfaces; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { - internal class XMLSchemaSerializer + internal static class XMLSchemaSerializer { internal static IXSComponent CreateInstance(XmlSchemaObject xmlSchemaObject) { @@ -92,7 +93,8 @@ private static IXSComponent CreateIXSAnnotated(XmlSchemaAnnotated xmlAnnotated) else if (xmlAnnotated is XmlSchemaGroup xmlGroup) return new XSModelGroupDefinition(xmlGroup); - + else if (xmlAnnotated is XmlSchemaXPath xmlPath) + return new XSXPathDefinition(xmlPath); else throw RuntimeException.InvalidArgumentType(); diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAnnotation.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAnnotation.cs similarity index 90% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAnnotation.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSAnnotation.cs index 8f481f804..c063335a3 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAnnotation.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAnnotation.cs @@ -7,13 +7,17 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("АннотацияXS", "XSAnnotation")] - public class XSAnnotation : AutoContext, IXSComponent + public sealed class XSAnnotation : AutoContext, IXSComponent { internal readonly XmlSchemaAnnotation InternalObject; @@ -45,7 +49,7 @@ internal XSAnnotation(XmlSchemaAnnotation annotation) } internal static void SetComponentAnnotation(XSAnnotation annotation, XmlSchemaAnnotated annotatedObject) - => annotatedObject.Annotation = annotation is XSAnnotation ? annotation.SchemaObject : null; + => annotatedObject.Annotation = annotation?.SchemaObject; #region OneScript diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAppInfo.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAppInfo.cs similarity index 87% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAppInfo.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSAppInfo.cs index 9fc93a29e..98b5e8b29 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAppInfo.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAppInfo.cs @@ -4,15 +4,20 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Diagnostics.Contracts; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ИнформацияДляПриложенияXS", "XSAppInfo")] - public class XSAppInfo : AutoContext, IXSAnnotationItem + public sealed class XSAppInfo : AutoContext, IXSAnnotationItem { private readonly XmlSchemaAppInfo _appInfo; @@ -29,7 +34,7 @@ public class XSAppInfo : AutoContext, IXSAnnotationItem public XSAnnotation Annotation => null; [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container { get; private set; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeDeclaration.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeDeclaration.cs similarity index 92% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeDeclaration.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeDeclaration.cs index e80fb6c63..cbb47d518 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeDeclaration.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeDeclaration.cs @@ -8,14 +8,19 @@ This Source Code Form is subject to the terms of the using System; using System.Xml; using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; +using OneScript.Contexts; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; +using OneScript.Values; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ОбъявлениеАтрибутаXS", "XSAttributeDeclaration")] - public class XSAttributeDeclaration : AutoContext, IXSAnnotated, IXSAttribute, IXSNamedComponent + public sealed class XSAttributeDeclaration : AutoContext, IXSAnnotated, IXSAttribute, IXSNamedComponent { private readonly XmlSchemaAttribute _attribute; private XSAnnotation _annotation; @@ -186,10 +191,10 @@ public XMLExpandedName Reference } [ContextProperty("Форма", "Form")] - public XSForm Form + public ClrEnumValueWrapper Form { - get => XSForm.FromNativeValue(_attribute.Form); - set => _attribute.Form = XSForm.ToNativeValue(value); + get => EnumerationXSForm.FromNativeValue(_attribute.Form); + set => _attribute.Form = value.UnderlyingValue; } [ContextProperty("ЭтоГлобальноеОбъявление", "IsGlobal")] diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeGroupDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeGroupDefinition.cs similarity index 94% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeGroupDefinition.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeGroupDefinition.cs index 3bb37009a..578d100c9 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeGroupDefinition.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeGroupDefinition.cs @@ -8,13 +8,17 @@ This Source Code Form is subject to the terms of the using System; using System.Xml; using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; +using OneScript.Contexts; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ОпределениеГруппыАтрибутовXS", "XSAttributeGroupDefinition")] - public class XSAttributeGroupDefinition : AutoContext, IXSAnnotated, IXSNamedComponent + public sealed class XSAttributeGroupDefinition : AutoContext, IXSAnnotated, IXSNamedComponent { private XmlSchemaAnnotated _attributeGroup; private XSAnnotation _annotation; @@ -155,11 +159,11 @@ public XMLExpandedName Reference set { _reference = value; - if (_reference is XMLExpandedName) + if (_reference != null) { _attributeGroup = new XmlSchemaAttributeGroupRef { - RefName = _reference?.NativeValue + RefName = _reference.NativeValue }; Content.Clear(); } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeUse.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeUse.cs similarity index 91% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeUse.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeUse.cs index 28446aeb1..9a3d11754 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSAttributeUse.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSAttributeUse.cs @@ -7,13 +7,17 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ИспользованиеАтрибутаXS", "XSAttributeUse")] - public class XSAttributeUse : AutoContext, IXSComponent + public sealed class XSAttributeUse : AutoContext, IXSComponent { private IValue _value; @@ -79,7 +83,7 @@ public XSAttributeDeclaration AttributeDeclaration set { _attributeDeclaration = value; - if (_attributeDeclaration is XSAttributeDeclaration) + if (_attributeDeclaration != null) { _attributeDeclaration.BindToContainer(RootContainer, this); var attribute = _attributeDeclaration.SchemaObject; diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSComplexTypeDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSComplexTypeDefinition.cs new file mode 100644 index 000000000..a64033baa --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSComplexTypeDefinition.cs @@ -0,0 +1,507 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics.Contracts; +using System.Xml; +using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Objects +{ + [ContextClass("ОпределениеСоставногоТипаXS", "XSComplexTypeDefinition")] + public sealed class XSComplexTypeDefinition : AutoContext, IXSType, IXSNamedComponent + { + private readonly XmlSchemaComplexType _type; + private readonly XSComponentFixedList _components = new XSComponentFixedList(); + private readonly XSComponentList _attributes = new XSComponentList(); + private XSAnnotation _annotation; + private XSAnnotation _contentModelAnnotation; + private XSAnnotation _derivationAnnotation; + private XMLExpandedName _baseTypeName; + private IXSComponent _content; + private XSWildcard _attributeWildcard; + private XSDerivationMethod _derivationMethod; + private XSContentModel _contentModel; + + private XSComplexTypeDefinition() : this(new XmlSchemaComplexType()) { } + + internal XSComplexTypeDefinition(XmlSchemaComplexType complexType) + { + _type = complexType; + + if (_type.Annotation is XmlSchemaAnnotation annotation) + { + _annotation = XMLSchemaSerializer.CreateXSAnnotation(annotation); + _annotation.BindToContainer(RootContainer, this); + } + + InitContentModel(); + + foreach (XmlSchemaObject item in _type.Attributes) + { + IXSComponent component = XMLSchemaSerializer.CreateInstance(item); + component.BindToContainer(RootContainer, this); + _attributes.Add(component); + _components.Add(component); + } + _attributes.Inserted += Attributes_Inserted; + _attributes.Cleared += Attributes_Cleared; + } + + private void InitContentModel() + { + if (_type.ContentModel is XmlSchemaSimpleContent simpleContent) + InitSimpleContent(simpleContent); + + else if (_type.ContentModel is XmlSchemaComplexContent complexContent) + InitComplexContent(complexContent); + + else + InitEmptyContent(); + } + + private void InitSimpleContent(XmlSchemaSimpleContent simpleContent) + { + _contentModel = XSContentModel.Simple; + if (simpleContent.Content is XmlSchemaSimpleContentExtension contentExtension) + { + _derivationMethod = XSDerivationMethod.Extension; + InitBaseTypeName(contentExtension.BaseTypeName); + InitAttributeWildcard(contentExtension.AnyAttribute); + } + else if (simpleContent.Content is XmlSchemaSimpleContentRestriction contentRestriction) + { + _derivationMethod = XSDerivationMethod.Restriction; + InitBaseTypeName(contentRestriction.BaseTypeName); + InitAttributeWildcard(contentRestriction.AnyAttribute); + } + else + { + _derivationMethod = XSDerivationMethod.EmptyRef; + InitContent(_type.Particle); + } + } + + private void InitComplexContent(XmlSchemaComplexContent complexContent) + { + _contentModel = XSContentModel.Complex; + if (complexContent.Content is XmlSchemaComplexContentExtension contentExtension) + { + _derivationMethod = XSDerivationMethod.Extension; + InitBaseTypeName(contentExtension.BaseTypeName); + InitContent(contentExtension.Particle); + InitAttributeWildcard(contentExtension.AnyAttribute); + } + else if (complexContent.Content is XmlSchemaComplexContentRestriction contentRestriction) + { + _derivationMethod = XSDerivationMethod.Restriction; + InitBaseTypeName(contentRestriction.BaseTypeName); + InitContent(contentRestriction.Particle); + InitAttributeWildcard(contentRestriction.AnyAttribute); + } + else + { + _derivationMethod = XSDerivationMethod.EmptyRef; + InitContent(_type.Particle); + } + } + + private void InitEmptyContent() + { + _contentModel = XSContentModel.EmptyRef; + InitContent(_type.Particle); + } + + private void InitBaseTypeName(XmlQualifiedName xmlQualifiedName) + { + if (xmlQualifiedName is XmlQualifiedName qualifiedName) + _baseTypeName = XMLSchemaSerializer.CreateXMLExpandedName(qualifiedName); + } + + private void InitAttributeWildcard(XmlSchemaAnyAttribute xmlAnyAttribute) + { + if (xmlAnyAttribute is XmlSchemaAnyAttribute anyAttribute) + _attributeWildcard = XMLSchemaSerializer.CreateXSWildcard(anyAttribute); + } + + private void InitContent(XmlSchemaParticle xmlParticle) + { + if (xmlParticle is XmlSchemaParticle particle) + _content = XMLSchemaSerializer.CreateInstance(particle); + } + + private void OnSetContentModelDerivation() + { + if (_contentModel == XSContentModel.Simple) + { + _type.ContentModel = new XmlSchemaSimpleContent(); + + if (_derivationMethod == XSDerivationMethod.Extension) + _type.ContentModel.Content = new XmlSchemaSimpleContentExtension(); + + else if (_derivationMethod == XSDerivationMethod.Restriction) + _type.ContentModel.Content = new XmlSchemaSimpleContentRestriction(); + + else + _type.ContentModel.Content = default(XmlSchemaContent); + } + else if (_contentModel == XSContentModel.Complex) + { + _type.ContentModel = new XmlSchemaComplexContent(); + + if (_derivationMethod == XSDerivationMethod.Extension) + _type.ContentModel.Content = new XmlSchemaComplexContentExtension(); + + else if (_derivationMethod == XSDerivationMethod.Restriction) + _type.ContentModel.Content = new XmlSchemaComplexContentRestriction(); + + else + _type.ContentModel.Content = default(XmlSchemaContent); + } + else + _type.ContentModel = default(XmlSchemaContentModel); + + OnSetBaseTypeName(); + OnSetAttributeWildcard(); + OnSetContentModelAnnotation(); + OnSetDerivationAnnotation(); + } + + private void OnSetBaseTypeName() + { + XmlQualifiedName baseTypeName = _baseTypeName?.NativeValue; + + if (_type.ContentModel is XmlSchemaComplexContent complexModel) + { + if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) + contentExtension.BaseTypeName = baseTypeName; + + else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) + contentRestriction.BaseTypeName = baseTypeName; + } + else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) + { + if (simpleModel.Content is XmlSchemaSimpleContentExtension contentExtension) + contentExtension.BaseTypeName = baseTypeName; + + else if (simpleModel.Content is XmlSchemaSimpleContentRestriction contentRestriction) + contentRestriction.BaseTypeName = baseTypeName; + } + } + + private void OnSetAttributeWildcard() + { + + XmlSchemaAnyAttribute anyAttribute = _attributeWildcard?.SchemaObject as XmlSchemaAnyAttribute; + + if (_type.ContentModel is XmlSchemaComplexContent complexModel) + { + if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) + contentExtension.AnyAttribute = anyAttribute; + + else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) + contentRestriction.AnyAttribute = anyAttribute; + } + else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) + { + if (simpleModel.Content is XmlSchemaSimpleContentExtension contentExtension) + contentExtension.AnyAttribute = anyAttribute; + + else if (simpleModel.Content is XmlSchemaSimpleContentRestriction contentRestriction) + contentRestriction.AnyAttribute = anyAttribute; + } + } + + private void OnSetContentModelAnnotation() + { + if (_type.ContentModel is XmlSchemaComplexContent complexModel) + XSAnnotation.SetComponentAnnotation(_contentModelAnnotation, complexModel); + + else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) + XSAnnotation.SetComponentAnnotation(_contentModelAnnotation, simpleModel); + } + + private void OnSetDerivationAnnotation() + { + if (_type.ContentModel is XmlSchemaComplexContent complexModel) + { + if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) + XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentExtension); + + else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) + XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentRestriction); + } + else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) + { + if (simpleModel.Content is XmlSchemaSimpleContentExtension contentExtension) + XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentExtension); + + else if (simpleModel.Content is XmlSchemaSimpleContentRestriction contentRestriction) + XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentRestriction); + } + } + + private void OnSetContent() + { + XmlSchemaParticle xmlParticle; + + if (_content is XSParticle particle) + xmlParticle = particle.SchemaObject; + + else if (_content is IXSFragment fragment) + xmlParticle = fragment.SchemaObject as XmlSchemaParticle; + + else if (_content is XSModelGroupDefinition groupDefinition) + xmlParticle = groupDefinition.SchemaObject as XmlSchemaGroupRef; + + else if (_content is XSModelGroup group) + xmlParticle = group.SchemaObject; + + else + xmlParticle = null; + + if (_type.ContentModel is XmlSchemaComplexContent complexModel) + { + if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) + contentExtension.Particle = xmlParticle; + + else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) + contentRestriction.Particle = xmlParticle; + } + else + _type.Particle = xmlParticle; + } + + #region OneScript + + #region Properties + + [ContextProperty("Аннотация", "Annotation")] + public XSAnnotation Annotation + { + get => _annotation; + set + { + _annotation = value; + _annotation?.BindToContainer(RootContainer, this); + XSAnnotation.SetComponentAnnotation(_annotation, _type); + } + } + + [ContextProperty("Компоненты", "Components")] + public XSComponentFixedList Components => _components; + + [ContextProperty("Контейнер", "Container")] + public IXSComponent Container { get; private set; } + + [ContextProperty("КорневойКонтейнер", "RootContainer")] + public IXSComponent RootContainer { get; private set; } + + [ContextProperty("Схема", "Schema")] + public XMLSchema Schema => RootContainer.Schema; + + [ContextProperty("ТипКомпоненты", "ComponentType")] + public XSComponentType ComponentType => XSComponentType.ComplexTypeDefinition; + + [ContextProperty("ЭлементDOM", "DOMElement")] + public IValue DOMElement => ValueFactory.Create(); + + [ContextProperty("URIПространстваИмен", "NamespaceURI")] + public string NamespaceURI => _type.SourceUri; + + [ContextProperty("Имя", "Name")] + public string Name + { + get => _type.Name; + set => _type.Name = value; + } + + [ContextProperty("БазовыйТип", "BaseType")] + public XSComplexTypeDefinition BaseType { get; } + + [ContextProperty("КорневойТип", "RootType")] + public XSComplexTypeDefinition RootType { get; } + + [ContextProperty("Абстрактный", "Abstract")] + public bool Abstract + { + get => _type.IsAbstract; + set => _type.IsAbstract = value; + } + + [ContextProperty("АннотацияМоделиСодержимого", "ContentModelAnnotation")] + public XSAnnotation ContentModelAnnotation + { + get => _contentModelAnnotation; + set + { + _contentModelAnnotation = value; + _contentModelAnnotation?.BindToContainer(RootContainer, this); + OnSetContentModelAnnotation(); + } + } + + [ContextProperty("АннотацияНаследования", "DerivationAnnotation")] + public XSAnnotation DerivationAnnotation + { + get => _derivationAnnotation; + set + { + _derivationAnnotation = value; + _derivationAnnotation?.BindToContainer(RootContainer, this); + OnSetDerivationAnnotation(); + } + } + + [ContextProperty("Атрибуты", "Attributes")] + public XSComponentList Attributes => _attributes; + + [ContextProperty("Блокировка", "Block")] + public XsProhibitedSubstitutionsUnion Block { get; } + + [ContextProperty("Завершенность", "Final")] + public XSComplexFinalUnion Final { get; } + + [ContextProperty("ЗапрещенныеПодстановки", "ProhibitedSubstitutions")] + public XsProhibitedSubstitutionsUnion ProhibitedSubstitutions { get; } + + [ContextProperty("ИмяБазовогоТипа", "BaseTypeName")] + public XMLExpandedName BaseTypeName + { + get => _baseTypeName; + set + { + _baseTypeName = value; + OnSetBaseTypeName(); + } + } + + [ContextProperty("МаскаАтрибутов", "AttributeWildcard")] + public XSWildcard AttributeWildcard + { + get => _attributeWildcard; + set + { + _attributeWildcard = value; + _attributeWildcard?.BindToContainer(RootContainer, this); + OnSetAttributeWildcard(); + } + } + + [ContextProperty("МетодНаследования", "DerivationMethod")] + public XSDerivationMethod DerivationMethod + { + get => _derivationMethod; + set + { + _derivationMethod = value; + OnSetContentModelDerivation(); + } + } + + [ContextProperty("МодельСодержимого", "ContentModel")] + public XSContentModel ContentModel + { + get => _contentModel; + set + { + _contentModel = value; + OnSetContentModelDerivation(); + } + } + + //ОпределениеБазовогоТипа(BaseTypeDefinition) + + [ContextProperty("Смешанный", "Mixed")] + public bool Mixed => _type.ContentModel is XmlSchemaComplexContent complexContent && complexContent.IsMixed; + + [ContextProperty("Содержимое", "Content")] + public IXSComponent Content + { + get => _content; + set + { + _content = value; + _content.BindToContainer(RootContainer, this); + OnSetContent(); + } + } + + //Фасеты(Facets) + + #endregion + + #region Methods + + [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); + + [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] + public void UpdateDOMElement() => throw new NotImplementedException(); + + [ContextMethod("Содержит", "Contains")] + public bool Contains(IXSComponent component) => _components.Contains(component); + + [ContextMethod("ЭтоОпределениеЗациклено", "IsCircular")] + public bool IsCircular() => throw new NotImplementedException(); + + [ContextMethod("ИспользованиеАтрибутов", "AttributeUses")] + public XSComponentFixedList AttributeUses() => throw new NotImplementedException(); + + #endregion + + #region Constructors + + [ScriptConstructor(Name = "По умолчанию")] + public static XSComplexTypeDefinition Constructor() => new XSComplexTypeDefinition(); + + #endregion + + #endregion + + #region IXSComponent + + public void BindToContainer(IXSComponent rootContainer, IXSComponent container) + { + RootContainer = rootContainer; + Container = container; + } + + XmlSchemaObject IXSComponent.SchemaObject => _type; + public XmlSchemaComplexType SchemaObject => _type; + + #endregion + + #region XSComponentListEvents + + private void Attributes_Inserted(object sender, XSComponentListEventArgs e) + { + IXSComponent component = e.Component; + Contract.Requires((component is IXSAttribute) || (component is XSAttributeGroupDefinition)); + component.BindToContainer(RootContainer, this); + _components.Add(component); + + _type.Attributes.Add(component.SchemaObject); + } + + private void Attributes_Cleared(object sender, EventArgs e) + { + _components.RemoveAll(x => (x is IXSAttribute)); + + _type.Attributes.Clear(); + } + + #endregion + } +} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSDocumentation.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSDocumentation.cs similarity index 87% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSDocumentation.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSDocumentation.cs index dbdf205a7..198dfe43f 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSDocumentation.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSDocumentation.cs @@ -8,12 +8,16 @@ This Source Code Form is subject to the terms of the using System; using System.Diagnostics.Contracts; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ДокументацияXS", "XSDocumentation")] - public class XSDocumentation : AutoContext, IXSAnnotationItem + public sealed class XSDocumentation : AutoContext, IXSAnnotationItem { private readonly XmlSchemaDocumentation _documentation; @@ -30,7 +34,7 @@ public class XSDocumentation : AutoContext, IXSAnnotationItem public XSAnnotation Annotation => null; [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container { get; private set; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSElementDeclaration.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSElementDeclaration.cs similarity index 91% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSElementDeclaration.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSElementDeclaration.cs index c037f8642..27ccf2bcd 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSElementDeclaration.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSElementDeclaration.cs @@ -8,14 +8,19 @@ This Source Code Form is subject to the terms of the using System; using System.Xml; using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; +using OneScript.Contexts; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ОбъявлениеЭлементаXS", "XSElementDeclaration")] - public class XSElementDeclaration : AutoContext, IXSFragment, IXSNamedComponent + public sealed class XSElementDeclaration : AutoContext, IXSFragment, IXSNamedComponent { private readonly XmlSchemaElement _element; private XSAnnotation _annotation; @@ -189,17 +194,17 @@ public XMLExpandedName Reference } [ContextProperty("Форма", "Form")] - public XSForm Form + public ClrEnumValueWrapper Form { - get => XSForm.FromNativeValue(_element.Form); - set => _element.Form = XSForm.ToNativeValue(value); + get => EnumerationXSForm.FromNativeValue(_element.Form); + set => _element.Form = value.UnderlyingValue; } [ContextProperty("ЭтоГлобальноеОбъявление", "IsGlobal")] public bool IsGlobal => Container is XMLSchema; [ContextProperty("ЭтоСсылка", "IsReference")] - public bool IsReference => _refName is XMLExpandedName; + public bool IsReference => _refName != null; [ContextProperty("Абстрактный", "Abstract")] public bool Abstract @@ -209,7 +214,7 @@ public bool Abstract } [ContextProperty("Блокировка", "Block")] - public XSDisallowedSubstitutionsUnion Block { get; } + public XsDisallowedSubstitutionsUnion Block { get; } [ContextProperty("ВозможноПустой", "Nillable")] public bool Nillable @@ -219,13 +224,13 @@ public bool Nillable } [ContextProperty("Завершенность", "Final")] - public XSSubstitutionGroupExclusionsUnion Final { get; } + public XsSubstitutionGroupExclusionsUnion Final { get; } [ContextProperty("ИсключенияГруппПодстановки", "SubstitutionGroupExclusions")] - public XSSubstitutionGroupExclusionsUnion SubstitutionGroupExclusions { get; } + public XsSubstitutionGroupExclusionsUnion SubstitutionGroupExclusions { get; } [ContextProperty("НедопустимыеПодстановки", "DisallowedSubstitutions")] - public XSDisallowedSubstitutionsUnion DisallowedSubstitutions { get; } + public XsDisallowedSubstitutionsUnion DisallowedSubstitutions { get; } [ContextProperty("ОграниченияИдентичности", "IdentityConstraints")] public XSComponentList IdentityConstraints { get; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSEnumerationFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSEnumerationFacet.cs similarity index 89% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSEnumerationFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSEnumerationFacet.cs index ca06a9025..7f9ce9831 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSEnumerationFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSEnumerationFacet.cs @@ -6,14 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетПеречисленияXS", "XSEnumerationFacet")] - public class XSEnumerationFacet : AutoContext, IXSFacet + public sealed class XSEnumerationFacet : AutoContext, IXSFacet { private readonly XmlSchemaEnumerationFacet _facet; private XSAnnotation _annotation; @@ -50,7 +55,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -126,9 +131,7 @@ public IValue Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSFractionDigitsFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSFractionDigitsFacet.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSFractionDigitsFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSFractionDigitsFacet.cs index 08c519cd3..9f806fca7 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSFractionDigitsFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSFractionDigitsFacet.cs @@ -6,15 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетКоличестваРазрядовДробнойЧастиXS", "XSFractionDigitsFacet")] - public class XSFractionDigitsFacet : AutoContext, IXSFacet + public sealed class XSFractionDigitsFacet : AutoContext, IXSFacet { private readonly XmlSchemaFractionDigitsFacet _facet; private XSAnnotation _annotation; @@ -49,7 +53,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -117,9 +121,7 @@ public decimal Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSIdentityConstraintDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSIdentityConstraintDefinition.cs similarity index 93% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSIdentityConstraintDefinition.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSIdentityConstraintDefinition.cs index 7254279a5..d14c2c391 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSIdentityConstraintDefinition.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSIdentityConstraintDefinition.cs @@ -7,14 +7,18 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ОпределениеОграниченияИдентичностиXS", "XSIdentityConstraintDefinition")] - public class XSIdentityConstraintDefinition : AutoContext, IXSAnnotated, IXSNamedComponent + public sealed class XSIdentityConstraintDefinition : AutoContext, IXSAnnotated, IXSNamedComponent { private XmlSchemaIdentityConstraint _constraint; private XSAnnotation _annotation; diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSImport.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSImport.cs similarity index 80% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSImport.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSImport.cs index 342be4c0a..5a62e242d 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSImport.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSImport.cs @@ -4,11 +4,16 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { /// @@ -16,16 +21,17 @@ namespace ScriptEngine.HostedScript.Library.XMLSchema /// /// [ContextClass("ИмпортXS", "XSImport")] - public class XSImport : AutoContext, IXSDirective + public sealed class XSImport : AutoContext, IXSDirective { private readonly XmlSchemaImport _import; + private XMLSchema _resolvedSchema; - private XSImport() => _import = new XmlSchemaImport(); + private XSImport() : this(new XmlSchemaImport()) { } internal XSImport(XmlSchemaImport import) - : this() { _import = import; + _resolvedSchema = new XMLSchema(_import.Schema); } #region OneScript @@ -36,7 +42,7 @@ internal XSImport(XmlSchemaImport import) public XSAnnotation Annotation => null; [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container { get; private set; } @@ -51,10 +57,13 @@ internal XSImport(XmlSchemaImport import) public XSComponentType ComponentType => XSComponentType.Import; [ContextProperty("РазрешеннаяСхема", "ResolvedSchema")] - public XMLSchema ResolvedSchema - { - get => ResolvedSchema; - set => ResolvedSchema = value; + public XMLSchema ResolvedSchema + { + get => _resolvedSchema; + set { + _resolvedSchema = value; + _import.Schema = _resolvedSchema.SchemaObject; + } } [ContextProperty("РасположениеСхемы", "SchemaLocation")] diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSInclude.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSInclude.cs similarity index 75% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSInclude.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSInclude.cs index 7d2b41daa..7deead4ed 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSInclude.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSInclude.cs @@ -4,24 +4,30 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ВключениеXS", "XSInclude")] - public class XSInclude : AutoContext, IXSDirective + public sealed class XSInclude : AutoContext, IXSDirective { private readonly XmlSchemaInclude _include; + private XMLSchema _resolvedSchema; - private XSInclude() => _include = new XmlSchemaInclude(); + private XSInclude() : this(new XmlSchemaInclude()) { } internal XSInclude(XmlSchemaInclude include) - : this() { _include = include; + _resolvedSchema = new XMLSchema(_include.Schema); } #region OneScript @@ -32,7 +38,7 @@ internal XSInclude(XmlSchemaInclude include) public XSAnnotation Annotation => null; [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container { get; private set; } @@ -49,8 +55,12 @@ internal XSInclude(XmlSchemaInclude include) [ContextProperty("РазрешеннаяСхема", "ResolvedSchema")] public XMLSchema ResolvedSchema { - get => ResolvedSchema; - set => ResolvedSchema = value; + get => _resolvedSchema; + set + { + _resolvedSchema = value; + _include.Schema = _resolvedSchema.SchemaObject; + } } [ContextProperty("РасположениеСхемы", "SchemaLocation")] @@ -65,7 +75,7 @@ public string SchemaLocation #region Methods [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive = false) => throw new NotImplementedException(); + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] public void UpdateDOMElement() => throw new NotImplementedException(); diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSLengthFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSLengthFacet.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSLengthFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSLengthFacet.cs index 01f2f0148..c1c727bec 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSLengthFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSLengthFacet.cs @@ -6,15 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетДлиныXS", "XSLengthFacet")] - public class XSLengthFacet : AutoContext, IXSFacet + public sealed class XSLengthFacet : AutoContext, IXSFacet { private readonly XmlSchemaLengthFacet _facet; private XSAnnotation _annotation; @@ -49,7 +53,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -117,9 +121,7 @@ public decimal Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxExclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxExclusiveFacet.cs similarity index 89% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxExclusiveFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxExclusiveFacet.cs index 6c2c1d578..fe2549c67 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxExclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxExclusiveFacet.cs @@ -6,14 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетМаксимальногоИсключающегоЗначенияXS", "XSMaxExclusiveFacet")] - public class XSMaxExclusiveFacet : AutoContext, IXSFacet + public sealed class XSMaxExclusiveFacet : AutoContext, IXSFacet { private readonly XmlSchemaMaxExclusiveFacet _facet; private XSAnnotation _annotation; @@ -50,7 +55,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -132,9 +137,7 @@ public IValue Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxInclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxInclusiveFacet.cs similarity index 89% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxInclusiveFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxInclusiveFacet.cs index 113b1452e..7cc1324af 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxInclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxInclusiveFacet.cs @@ -6,14 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетМаксимальногоВключающегоЗначенияXS", "XSMaxInclusiveFacet")] - public class XSMaxInclusiveFacet : AutoContext, IXSFacet + public sealed class XSMaxInclusiveFacet : AutoContext, IXSFacet { private readonly XmlSchemaMaxInclusiveFacet _facet; private XSAnnotation _annotation; @@ -50,7 +55,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -132,9 +137,7 @@ public IValue Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxLengthFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxLengthFacet.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxLengthFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxLengthFacet.cs index f688f0192..4a5a5764f 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMaxLengthFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMaxLengthFacet.cs @@ -6,15 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетМаксимальнойДлиныXS", "XSMaxLengthFacet")] - public class XSMaxLengthFacet : AutoContext, IXSFacet + public sealed class XSMaxLengthFacet : AutoContext, IXSFacet { private readonly XmlSchemaMaxLengthFacet _facet; private XSAnnotation _annotation; @@ -49,7 +53,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -117,9 +121,7 @@ public decimal Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinExclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinExclusiveFacet.cs similarity index 89% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinExclusiveFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinExclusiveFacet.cs index 9433e2b9a..881b61d2a 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinExclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinExclusiveFacet.cs @@ -6,20 +6,26 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетМинимальногоИсключающегоЗначенияXS", "XSMinExclusiveFacet")] - public class XSMinExclusiveFacet : AutoContext, IXSFacet + public sealed class XSMinExclusiveFacet : AutoContext, IXSFacet { private readonly XmlSchemaMinExclusiveFacet _facet; private XSAnnotation _annotation; private IValue _value; private XSMinExclusiveFacet() => _facet = new XmlSchemaMinExclusiveFacet(); + internal XSMinExclusiveFacet(XmlSchemaMinExclusiveFacet minExclusiveFace) { _facet = minExclusiveFace; @@ -31,6 +37,7 @@ internal XSMinExclusiveFacet(XmlSchemaMinExclusiveFacet minExclusiveFace) _annotation.BindToContainer(RootContainer, this); } } + #region OneScript #region Properties @@ -48,7 +55,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -130,9 +137,7 @@ public IValue Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinInclusiveFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinInclusiveFacet.cs similarity index 89% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinInclusiveFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinInclusiveFacet.cs index 20ec39375..4108af205 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinInclusiveFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinInclusiveFacet.cs @@ -6,20 +6,26 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетМинимальногоВключающегоЗначенияXS", "XSMinInclusiveFacet")] - public class XSMinInclusiveFacet : AutoContext, IXSFacet + public sealed class XSMinInclusiveFacet : AutoContext, IXSFacet { private readonly XmlSchemaMinInclusiveFacet _facet; private XSAnnotation _annotation; private IValue _value; private XSMinInclusiveFacet() => _facet = new XmlSchemaMinInclusiveFacet(); + internal XSMinInclusiveFacet(XmlSchemaMinInclusiveFacet minInclusiveFacet) { _facet = minInclusiveFacet; @@ -49,7 +55,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -131,9 +137,7 @@ public IValue Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinLengthFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinLengthFacet.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinLengthFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinLengthFacet.cs index 65ed05add..282b1422e 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSMinLengthFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSMinLengthFacet.cs @@ -6,15 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетМинимальнойДлиныXS", "XSMinLengthFacet")] - public class XSMinLengthFacet : AutoContext, IXSFacet + public sealed class XSMinLengthFacet : AutoContext, IXSFacet { private readonly XmlSchemaMinLengthFacet _facet; private XSAnnotation _annotation; @@ -49,7 +53,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -117,9 +121,7 @@ public decimal Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSModelGroup.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroup.cs similarity index 94% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSModelGroup.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroup.cs index eb400459b..ed4905be5 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSModelGroup.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroup.cs @@ -7,13 +7,18 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ГруппаМоделиXS", "XSModelGroup")] - public class XSModelGroup : AutoContext, IXSFragment + public sealed class XSModelGroup : AutoContext, IXSFragment { private XmlSchemaGroupBase _group; private XSAnnotation _annotation; diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSModelGroupDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroupDefinition.cs similarity index 90% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSModelGroupDefinition.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroupDefinition.cs index 479143615..f9c99c43c 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSModelGroupDefinition.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSModelGroupDefinition.cs @@ -8,14 +8,18 @@ This Source Code Form is subject to the terms of the using System; using System.Xml; using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ОпределениеГруппыМоделиXS", "XSModelGroupDefinition")] - public class XSModelGroupDefinition : AutoContext, IXSFragment, IXSNamedComponent + public sealed class XSModelGroupDefinition : AutoContext, IXSFragment, IXSNamedComponent { private XmlSchemaAnnotated _group; private string _name; @@ -38,7 +42,7 @@ internal XSModelGroupDefinition(XmlSchemaGroup xmlGroup) if (xmlGroup.Particle is XmlSchemaGroupBase xmlGroupBase) { - IXSComponent component = XMLSchemaSerializer.CreateInstance(xmlGroupBase); + var component = XMLSchemaSerializer.CreateInstance(xmlGroupBase); if (component is XSParticle particle) _modelGroup = particle; @@ -48,7 +52,7 @@ internal XSModelGroupDefinition(XmlSchemaGroup xmlGroup) } } - internal XSModelGroupDefinition(XmlSchemaGroupRef xmlGroupRef) + internal XSModelGroupDefinition(in XmlSchemaGroupRef xmlGroupRef) { _group = xmlGroupRef; @@ -79,7 +83,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container { get; private set; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSNotationDeclaration.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSNotationDeclaration.cs similarity index 91% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSNotationDeclaration.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSNotationDeclaration.cs index 010fdba59..a12229e61 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSNotationDeclaration.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSNotationDeclaration.cs @@ -7,12 +7,16 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ОбъявлениеНотацииXS", "XSNotationDeclaration")] - public class XSNotationDeclaration : AutoContext, IXSAnnotated, IXSNamedComponent + public sealed class XSNotationDeclaration : AutoContext, IXSAnnotated, IXSNamedComponent { private readonly XmlSchemaNotation _notation; private XSAnnotation _annotation; diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSParticle.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSParticle.cs similarity index 91% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSParticle.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSParticle.cs index 3a0c12c1d..adc340feb 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSParticle.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSParticle.cs @@ -7,13 +7,19 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using OneScript.Types; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФрагментXS", "XSParticle")] - public class XSParticle : AutoContext, IXSComponent + public sealed class XSParticle : AutoContext, IXSComponent { private IXSFragment _term; private IValue _minOccurs; @@ -51,10 +57,10 @@ internal XSParticle(XmlSchemaParticle particle) private void SetMaxOccurs(XmlSchemaParticle particle, IValue maxOccurs) { - if (maxOccurs.DataType == DataType.Undefined) + if (maxOccurs.SystemType == BasicTypes.Undefined) particle.MaxOccursString = null; - else if (maxOccurs.DataType == DataType.Number) + else if (maxOccurs.SystemType == BasicTypes.Number) { decimal number = maxOccurs.AsNumber(); if (number >= 0) @@ -68,10 +74,10 @@ private void SetMaxOccurs(XmlSchemaParticle particle, IValue maxOccurs) private void SetMinOccurs(XmlSchemaParticle particle, IValue minOccurs) { - if (minOccurs.DataType == DataType.Undefined) + if (minOccurs.SystemType == BasicTypes.Undefined) particle.MinOccursString = null; - else if (minOccurs.DataType == DataType.Number) + else if (minOccurs.SystemType == BasicTypes.Number) { decimal number = minOccurs.AsNumber(); if (number >= 0) diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSPatternFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSPatternFacet.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSPatternFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSPatternFacet.cs index 93340eaf3..56a81e02e 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSPatternFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSPatternFacet.cs @@ -6,14 +6,18 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетОбразцаXS", "XSPatternFacet")] - public class XSPatternFacet : AutoContext, IXSFacet + public sealed class XSPatternFacet : AutoContext, IXSFacet { private readonly XmlSchemaPatternFacet _facet; private XSAnnotation _annotation; @@ -48,7 +52,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -116,9 +120,7 @@ public string Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSRedefine.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSRedefine.cs similarity index 85% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSRedefine.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSRedefine.cs index 2d59dba5c..752d39371 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSRedefine.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSRedefine.cs @@ -7,30 +7,30 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ПереопределениеXS", "XSRedefine")] - public class XSRedefine : AutoContext, IXSDirective + public sealed class XSRedefine : AutoContext, IXSDirective { private readonly XmlSchemaRedefine _redefine; + private XMLSchema _resolvedSchema; - private XSRedefine() - { - _redefine = new XmlSchemaRedefine(); - Components = new XSComponentFixedList(); - - Content = new XSComponentList(); - Content.Inserted += Content_Inserted; - Content.Cleared += Content_Cleared; - } + private XSRedefine() : this (new XmlSchemaRedefine()) { } internal XSRedefine(XmlSchemaRedefine redefine) - : this() { _redefine = redefine; + _resolvedSchema = new XMLSchema(_redefine.Schema); + + Components = new XSComponentFixedList(); + Content = new XSComponentList(); Content.Inserted -= Content_Inserted; Content.Cleared -= Content_Cleared; @@ -43,7 +43,7 @@ internal XSRedefine(XmlSchemaRedefine redefine) Components.Add(component); Content.Add(component); } - + Content.Inserted += Content_Inserted; Content.Cleared += Content_Cleared; } @@ -73,8 +73,12 @@ internal XSRedefine(XmlSchemaRedefine redefine) [ContextProperty("РазрешеннаяСхема", "ResolvedSchema")] public XMLSchema ResolvedSchema { - get => ResolvedSchema; - set => ResolvedSchema = value; + get => _resolvedSchema; + set + { + _resolvedSchema = value; + _redefine.Schema = _resolvedSchema.SchemaObject; + } } [ContextProperty("РасположениеСхемы", "SchemaLocation")] diff --git a/src/OneScript.StandardLibrary/XMLSchema/Objects/XSSimpleTypeDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSSimpleTypeDefinition.cs new file mode 100644 index 000000000..fa039c378 --- /dev/null +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSSimpleTypeDefinition.cs @@ -0,0 +1,350 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics.Contracts; +using System.Xml; +using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.Xml; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.XMLSchema.Objects +{ + [ContextClass("ОпределениеПростогоТипаXS", "XSSimpleTypeDefinition")] + public sealed class XSSimpleTypeDefinition : AutoContext, IXSType, IXSNamedComponent + { + private readonly XmlSchemaSimpleType _type; + private readonly XSComponentFixedList _components = new XSComponentFixedList(); + private readonly XSComponentList _typeDefinitions = new XSComponentList(); + private readonly XSComponentList _facets = new XSComponentList(); + + private XSAnnotation _annotation; + private XMLExpandedName _baseTypeName; + private XMLExpandedName _itemTypeName; + private XSSimpleTypeVariety _variety; + + private XSSimpleTypeDefinition() : this(new XmlSchemaSimpleType()) { } + + internal XSSimpleTypeDefinition(XmlSchemaSimpleType simpleType) + { + _type = simpleType; + + if (_type.Annotation is XmlSchemaAnnotation annotation) + { + _annotation = XMLSchemaSerializer.CreateXSAnnotation(annotation); + _annotation.BindToContainer(RootContainer, this); + } + + InitContentTypeVariety(); + + _facets.Inserted += Facets_Inserted; + _facets.Cleared += Facets_Cleared; + + _typeDefinitions.Inserted += MemberTypeDefinitions_Inserted; + _typeDefinitions.Cleared += MemberTypeDefinitions_Cleared; + } + + private void InitContentTypeVariety() + { + if (_type.Content is XmlSchemaSimpleTypeList typeList) + InitListVariety(typeList); + + else if (_type.Content is XmlSchemaSimpleTypeUnion typeUnion) + InitUnionVariety(typeUnion); + + else if (_type.Content is XmlSchemaSimpleTypeRestriction typeRestriction) + InitAtomicVariety(typeRestriction); + + else + { + var newRestriction = new XmlSchemaSimpleTypeRestriction(); + _type.Content = newRestriction; + InitAtomicVariety(newRestriction); + } + } + + private void InitListVariety(XmlSchemaSimpleTypeList typeList) + { + _variety = XSSimpleTypeVariety.List; + InitItemTypeName(typeList.ItemTypeName); + } + + private void InitUnionVariety(XmlSchemaSimpleTypeUnion typeUnion) + { + _variety = XSSimpleTypeVariety.Union; + foreach (var item in typeUnion.BaseTypes) + { + var component = XMLSchemaSerializer.CreateInstance(item); + component.BindToContainer(RootContainer, this); + _typeDefinitions.Add(component); + _components.Add(component); + } + } + + private void InitAtomicVariety(XmlSchemaSimpleTypeRestriction typeRestriction) + { + _variety = XSSimpleTypeVariety.Atomic; + InitBaseTypeName(typeRestriction.BaseTypeName); + + foreach (var item in typeRestriction.Facets) + { + var component = XMLSchemaSerializer.CreateInstance(item); + component.BindToContainer(RootContainer, this); + _facets.Add(component); + _components.Add(component); + } + } + + private void InitItemTypeName(XmlQualifiedName xmlQualifiedName) + { + if (xmlQualifiedName is XmlQualifiedName qualifiedName) + _itemTypeName = new XMLExpandedName(qualifiedName); + } + + private void InitBaseTypeName(XmlQualifiedName xmlQualifiedName) + { + if (xmlQualifiedName is XmlQualifiedName qualifiedName) + _baseTypeName = new XMLExpandedName(qualifiedName); + } + + private void SetContentTypeVariety(XSSimpleTypeVariety value) + { + if (_variety == value) return; + _variety = value; + + switch (_variety) + { + case XSSimpleTypeVariety.List: + _type.Content = new XmlSchemaSimpleTypeList(); + _itemTypeName = default; + break; + + case XSSimpleTypeVariety.Union: + _type.Content = new XmlSchemaSimpleTypeUnion(); + _typeDefinitions.Clear(); + break; + + case XSSimpleTypeVariety.Atomic: + _type.Content = new XmlSchemaSimpleTypeRestriction(); + _baseTypeName = default; + _facets.Clear(); + break; + + default: + break; + } + } + + private void SetBaseTypeName(XMLExpandedName value) + { + if (_baseTypeName == value) return; + Contract.Requires(Variety == XSSimpleTypeVariety.Atomic); + _baseTypeName = value; + + var content = _type.Content as XmlSchemaSimpleTypeRestriction; + content.BaseTypeName = _baseTypeName?.NativeValue; + } + + private void SetItemTypeName(XMLExpandedName value) + { + if (_itemTypeName == value) return; + Contract.Requires(Variety == XSSimpleTypeVariety.List); + _itemTypeName = value; + + var content = _type.Content as XmlSchemaSimpleTypeList; + content.ItemTypeName = _itemTypeName?.NativeValue; + } + + #region OneScript + + #region Properties + + [ContextProperty("Аннотация", "Annotation")] + public XSAnnotation Annotation + { + get => _annotation; + set + { + _annotation = value; + _annotation?.BindToContainer(RootContainer, this); + XSAnnotation.SetComponentAnnotation(_annotation, _type); + } + } + + [ContextProperty("Компоненты", "Components")] + public XSComponentFixedList Components => _components; + + [ContextProperty("Контейнер", "Container")] + public IXSComponent Container { get; private set; } + + [ContextProperty("КорневойКонтейнер", "RootContainer")] + public IXSComponent RootContainer { get; private set; } + + [ContextProperty("Схема", "Schema")] + public XMLSchema Schema => RootContainer.Schema; + + [ContextProperty("ТипКомпоненты", "ComponentType")] + public XSComponentType ComponentType => XSComponentType.SimpleTypeDefinition; + + [ContextProperty("ЭлементDOM", "DOMElement")] + public IValue DOMElement => ValueFactory.Create(); + + [ContextProperty("URIПространстваИмен", "NamespaceURI")] + public string NamespaceURI => _type.SourceUri; + + [ContextProperty("Имя", "Name")] + public string Name + { + get => _type.Name; + set => _type.Name = value; + } + + [ContextProperty("БазовыйТип", "BaseType")] + public XSSimpleTypeDefinition BaseType { get; } + + [ContextProperty("КорневойТип", "RootType")] + public XSSimpleTypeDefinition RootType { get; } + + [ContextProperty("АннотацияВарианта", "VarietyAnnotation")] + public XSAnnotation VarietyAnnotation { get; set; } + + [ContextProperty("Вариант", "Variety")] + public XSSimpleTypeVariety Variety + { + get => _variety; + set => SetContentTypeVariety(value); + } + + [ContextProperty("Завершенность", "Final")] + public XSSimpleFinalUnion Final { get; } + + [ContextProperty("ИменаТиповОбъединения", "MemberTypeNames")] + public XMLExpandedNameList MemberTypeNames { get; } + + [ContextProperty("ИмяБазовогоТипа", "BaseTypeName")] + public XMLExpandedName BaseTypeName + { + get => _baseTypeName; + set => SetBaseTypeName(value); + } + + [ContextProperty("ИмяТипаЭлемента", "ItemTypeName")] + public XMLExpandedName ItemTypeName + { + get => _itemTypeName; + set => SetItemTypeName(value); + } + + [ContextProperty("ОпределениеБазовогоТипа", "BaseTypeDefinition")] + public XSSimpleTypeDefinition BaseTypeDefinition { get; set; } + + [ContextProperty("ОпределениеПримитивногоТипа", "PrimitiveTypeDefinition")] + public XSSimpleTypeDefinition PrimitiveTypeDefinition { get; set; } + + [ContextProperty("ОпределениеТипаЭлемента", "ItemTypeDefinition")] + public XSSimpleTypeDefinition ItemTypeDefinition { get; set; } + + [ContextProperty("ОпределенияТиповОбъединения", "MemberTypeDefinitions")] + public XSComponentList MemberTypeDefinitions => _typeDefinitions; + + [ContextProperty("Фасеты", "Facets")] + public XSComponentList Facets => _facets; + + #endregion + + #region Methods + + [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] + public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); + + [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] + public void UpdateDOMElement() => throw new NotImplementedException(); + + [ContextMethod("Содержит", "Contains")] + public bool Contains(IXSComponent component) => Components.Contains(component); + + [ContextMethod("ЭтоОпределениеЗациклено", "IsCircular")] + public bool IsCircular() => throw new NotImplementedException(); + + #endregion + + #region Constructors + + [ScriptConstructor(Name = "По умолчанию")] + public static XSSimpleTypeDefinition Constructor() => new XSSimpleTypeDefinition(); + + #endregion + + #endregion + + #region IXSComponent + + public void BindToContainer(IXSComponent rootContainer, IXSComponent container) + { + RootContainer = rootContainer; + Container = container; + } + + XmlSchemaObject IXSComponent.SchemaObject => _type; + public XmlSchemaSimpleType SchemaObject => _type; + + #endregion + + #region XSComponentListEvents + + private void Facets_Inserted(object sender, XSComponentListEventArgs e) + { + var component = e.Component; + Contract.Requires(_variety == XSSimpleTypeVariety.Atomic); + Contract.Requires(component is IXSFacet); + + component.BindToContainer(RootContainer, this); + _components.Add(component); + + var content = _type.Content as XmlSchemaSimpleTypeRestriction; + content.Facets.Add(component.SchemaObject); + } + + private void Facets_Cleared(object sender, EventArgs e) + { + Contract.Requires(_variety == XSSimpleTypeVariety.Atomic); + _components.Clear(); + + var content = _type.Content as XmlSchemaSimpleTypeRestriction; + content.Facets.Clear(); + } + + private void MemberTypeDefinitions_Inserted(object sender, XSComponentListEventArgs e) + { + var component = e.Component; + Contract.Requires(_variety == XSSimpleTypeVariety.Union); + Contract.Requires(component is XSSimpleTypeDefinition); + + component.BindToContainer(RootContainer, this); + _components.Add(component); + + var content = _type.Content as XmlSchemaSimpleTypeUnion; + content.BaseTypes.Add(component.SchemaObject); + } + + private void MemberTypeDefinitions_Cleared(object sender, EventArgs e) + { + Contract.Requires(_variety == XSSimpleTypeVariety.Union); + _components.Clear(); + + var content = _type.Content as XmlSchemaSimpleTypeUnion; + content.BaseTypes.Clear(); + } + + #endregion + } +} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSTotalDigitsFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSTotalDigitsFacet.cs similarity index 88% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSTotalDigitsFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSTotalDigitsFacet.cs index a57b9b152..dbb3190f6 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSTotalDigitsFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSTotalDigitsFacet.cs @@ -6,15 +6,19 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетОбщегоКоличестваРазрядовXS", "XSTotalDigitsFacet")] - public class XSTotalDigitsFacet : AutoContext, IXSFacet + public sealed class XSTotalDigitsFacet : AutoContext, IXSFacet { private readonly XmlSchemaTotalDigitsFacet _facet; private XSAnnotation _annotation; @@ -49,7 +53,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -117,9 +121,7 @@ public decimal Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSWhitespaceFacet.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWhitespaceFacet.cs similarity index 90% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSWhitespaceFacet.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSWhitespaceFacet.cs index ad028cb04..02870bbb6 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSWhitespaceFacet.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWhitespaceFacet.cs @@ -6,14 +6,18 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Diagnostics.Contracts; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ФасетПробельныхСимволовXS", "XSWhitespaceFacet")] - public class XSWhitespaceFacet : AutoContext, IXSFacet + public sealed class XSWhitespaceFacet : AutoContext, IXSFacet { private readonly XmlSchemaWhiteSpaceFacet _facet; private XSAnnotation _annotation; @@ -48,7 +52,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container => SimpleTypeDefinition; @@ -152,9 +156,7 @@ public XSWhitespaceHandling Value void IXSComponent.BindToContainer(IXSComponent rootContainer, IXSComponent container) { - if (!(container is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - + Contract.Requires(container is XSSimpleTypeDefinition); RootContainer = rootContainer; SimpleTypeDefinition = container as XSSimpleTypeDefinition; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSWildcard.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWildcard.cs similarity index 94% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSWildcard.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSWildcard.cs index bbe8da81c..33ffd8740 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSWildcard.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSWildcard.cs @@ -7,13 +7,18 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; -using ScriptEngine.Machine; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("МаскаXS", "XSWildcard")] - public class XSWildcard : AutoContext, IXSAnnotated, IXSFragment + public sealed class XSWildcard : AutoContext, IXSFragment { private XmlSchemaAnnotated _wildcard; private XSAnnotation _annotation; @@ -38,6 +43,7 @@ internal XSWildcard(XmlSchemaAny xmlAny) SetNamespaceConstraint(); } + internal XSWildcard(XmlSchemaAnyAttribute xmlAnyAttribute) { _wildcard = xmlAnyAttribute; @@ -115,7 +121,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container { get; private set; } diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSXPathDefinition.cs b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSXPathDefinition.cs similarity index 85% rename from src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSXPathDefinition.cs rename to src/OneScript.StandardLibrary/XMLSchema/Objects/XSXPathDefinition.cs index 58a026897..9377abadd 100644 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSXPathDefinition.cs +++ b/src/OneScript.StandardLibrary/XMLSchema/Objects/XSXPathDefinition.cs @@ -7,18 +7,24 @@ This Source Code Form is subject to the terms of the using System; using System.Xml.Schema; +using OneScript.Contexts; +using OneScript.StandardLibrary.XMLSchema.Collections; +using OneScript.StandardLibrary.XMLSchema.Enumerations; +using OneScript.StandardLibrary.XMLSchema.Interfaces; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.XMLSchema +namespace OneScript.StandardLibrary.XMLSchema.Objects { [ContextClass("ОпределенияXPathXS", "XSXPathDefinition")] - public class XSXPathDefinition : AutoContext, IXSAnnotated, IXSNamedComponent + public sealed class XSXPathDefinition : AutoContext, IXSAnnotated, IXSNamedComponent { private readonly XmlSchemaXPath _xpath; private XSAnnotation _annotation; private XSXPathDefinition() => _xpath = new XmlSchemaXPath(); - + + internal XSXPathDefinition(XmlSchemaXPath xpath) => _xpath = xpath; + #region OneScript #region Properties @@ -35,7 +41,7 @@ public XSAnnotation Annotation } [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components => null; + public XSComponentFixedList Components => XSComponentFixedList.EmptyList(); [ContextProperty("Контейнер", "Container")] public IXSComponent Container { get; private set; } diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XMLExpandedName.cs b/src/OneScript.StandardLibrary/Xml/XMLExpandedName.cs similarity index 81% rename from src/ScriptEngine.HostedScript/Library/Xml/XMLExpandedName.cs rename to src/OneScript.StandardLibrary/Xml/XMLExpandedName.cs index 1ed076494..ac1062281 100644 --- a/src/ScriptEngine.HostedScript/Library/Xml/XMLExpandedName.cs +++ b/src/OneScript.StandardLibrary/Xml/XMLExpandedName.cs @@ -6,10 +6,13 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Xml; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Values; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Xml +namespace OneScript.StandardLibrary.Xml { /// /// Представляет полное имя XML. @@ -46,17 +49,17 @@ public XMLExpandedName(string namespaceURI, string localName) /// Локальное имя. /// Новое полное имя XML. [ScriptConstructor(Name = "По умолчанию")] - public static XMLExpandedName Create(IValue namespaceURI, IValue localName) - => new XMLExpandedName(namespaceURI.AsString(), localName.AsString()); + public static XMLExpandedName Create(string namespaceURI, string localName) + => new XMLExpandedName(namespaceURI, localName); - public override bool Equals(IValue other) + public override bool Equals(BslValue other) { - if (other.AsObject() is XMLExpandedName _expandedName) + if (other?.AsObject() is XMLExpandedName _expandedName) return NativeValue.Equals(_expandedName.NativeValue); else return base.Equals(other); } - public override string AsString() => $"{{{NamespaceURI}}}{LocalName}"; + public override string ToString(IBslProcess process) => $"{{{NamespaceURI}}}{LocalName}"; } } \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XMLExpandedNameList.cs b/src/OneScript.StandardLibrary/Xml/XMLExpandedNameList.cs similarity index 85% rename from src/ScriptEngine.HostedScript/Library/Xml/XMLExpandedNameList.cs rename to src/OneScript.StandardLibrary/Xml/XMLExpandedNameList.cs index 53818e4ab..03ecd08d6 100644 --- a/src/ScriptEngine.HostedScript/Library/Xml/XMLExpandedNameList.cs +++ b/src/OneScript.StandardLibrary/Xml/XMLExpandedNameList.cs @@ -5,18 +5,18 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine.Contexts; -using System.Collections; using System.Collections.Generic; +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Xml +namespace OneScript.StandardLibrary.Xml { /// /// Список расширенных имен XML. /// /// - /// - public class XMLExpandedNameList: AutoContext, ICollectionContext, IEnumerable + /// + public class XMLExpandedNameList: AutoCollectionContext { private readonly List _items; @@ -52,7 +52,7 @@ public class XMLExpandedNameList: AutoContext, ICollectionC /// /// Количество элементов [ContextMethod("Количество", "Count")] - public int Count() => _items.Count; + public override int Count() => _items.Count; /// /// Очищает коллекцию @@ -87,17 +87,9 @@ public class XMLExpandedNameList: AutoContext, ICollectionC #endregion - #region ICollectionContext - - public CollectionEnumerator GetManagedIterator() => new CollectionEnumerator(GetEnumerator()); - - #endregion - #region IEnumerable - public IEnumerator GetEnumerator() => _items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public override IEnumerator GetEnumerator() => _items.GetEnumerator(); #endregion } diff --git a/src/OneScript.StandardLibrary/Xml/XMLTypeAssignment.cs b/src/OneScript.StandardLibrary/Xml/XMLTypeAssignment.cs new file mode 100644 index 000000000..37d47fb1c --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XMLTypeAssignment.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Xml +{ + [EnumerationType("НазначениеТипаXML", "XMLTypeAssignment")] + public enum XMLTypeAssignment + { + [EnumValue("Неявное", "Implicit")] + Implicit, + + [EnumValue("Явное", "Explicit")] + Explicit + } +} diff --git a/src/OneScript.StandardLibrary/Xml/XSLTransform.cs b/src/OneScript.StandardLibrary/Xml/XSLTransform.cs new file mode 100644 index 000000000..a352bfdfd --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XSLTransform.cs @@ -0,0 +1,218 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Xml; +using System.Xml.Xsl; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + /// + /// Предназначен для преобразования текстов XML. + /// + [ContextClass("ПреобразованиеXSL", "XSLTransform")] + public class XSLTransform : AutoContext + { + private XslCompiledTransform _xslTransform; + private XsltArgumentList _argumentList; + + /// + /// Загружает таблицу стилей XSL + /// + /// ЧтениеXML. Объект чтения XML, из которого будет загружена таблица стилей XSL. + [ContextMethod("ЗагрузитьТаблицуСтилей", "LoadXSLStylesheet")] + public void LoadXSLStylesheet(XmlReaderImpl xmlReader) + { + _xslTransform.Load(xmlReader.GetNativeReader()); + } + + /// + /// Загружает таблицу стилей XSL + /// + /// Строка. Строка, содержащая описание преобразования XSL. + [ContextMethod("ЗагрузитьТаблицуСтилейXSLИзСтроки", "LoadXSLStylesheetFromString")] + public void LoadXSLStylesheetFromString(string xmlString) + { + XmlReaderImpl _reader = new XmlReaderImpl(); + + _reader.SetString(xmlString); + + LoadXSLStylesheet(_reader); + } + + /// + /// Загружает описание преобразования XSL из узла DOM. + /// + /// УзелDOM. Узел DOM, представляющий собой шаблон XSL. + [ContextMethod("ЗагрузитьТаблицуСтилейXSLИзУзла", "LoadXSLStylesheetFromNode")] + public void LoadXSLStylesheetFromNode(IValue xmlNode) + { + throw new NotImplementedException(); + } + + /// + /// Загружает описание преобразования XSL из файла. + /// + /// Строка. Имя файла, из которого должно быть загружено описание преобразования XSL. + [ContextMethod("ЗагрузитьТаблицуСтилейXSLИзФайла", "LoadXSLStylesheetFromFile")] + public void LoadXSLStylesheetFromFile(string fileName) + { + XmlReaderImpl _reader = new XmlReaderImpl(); + + _reader.OpenFile(fileName); + + LoadXSLStylesheet(_reader); + } + + /// + /// Очищает внутреннее состояние. + /// + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _xslTransform = new XslCompiledTransform(); + _argumentList = new XsltArgumentList(); + } + + /// + /// Выполняет преобразование XML-документа. + /// Используется описание преобразования и значения параметров, ранее установленные в данном объекте. + /// + /// ЧтениеXML. Объект чтения XML, из которого будет прочитан исходный XML документ для преобразования. + /// ЗаписьXML. Объект записи XML, в который будет записан результат преобразования. + [ContextMethod("Преобразовать", "Transform")] + public void Transform(XmlReaderImpl xmlReader, XmlWriterImpl xmlWriter) + { + + XmlReader _reader = xmlReader.GetNativeReader(); + XmlWriter _writer = xmlWriter.GetNativeWriter(); + + _xslTransform.Transform(_reader, _argumentList, _writer); + } + + /// + /// Выполняет преобразование XML-документа. + /// Используется описание преобразования и значения параметров, ранее установленные в данном объекте. + /// + /// Строка. Строка, в которой находится XML-документ. + /// ЗаписьXML. Объект записи XML, в который будет записан результат преобразования. + /// Указание данного параметра имеет смысл, если преобразование выполняется в документ XML. + /// При указании данного параметра результат преобразования будет записываться в объект ЗаписьXML, + /// возвращаемое значение в данном случае будет отсутствовать. + /// Строка. Результат преобразования. + [ContextMethod("ПреобразоватьИзСтроки", "TransformFromString")] + public string TransformFromString(string xmlString, XmlWriterImpl xmlWriter = null) + { + XmlReaderImpl _reader = new XmlReaderImpl(); + + _reader.SetString(xmlString); + + XmlWriterImpl _writer = new XmlWriterImpl(); + _writer.SetString(); + + Transform(_reader, _writer); + + string result = _writer.Close().ToString(); + + if (xmlWriter != null) + xmlWriter.WriteRaw(result); + + return result; + } + + /// + /// Выполняет преобразование XML-документа. + /// Используется описание преобразования и значения параметров, ранее установленные в данном объекте. + /// + /// УзелDOM. Узел DOM - исходное дерево для преобразования XSL. + /// ЗаписьXML. Объект записи XML, в который будет записан результат преобразования. + /// Указание данного параметра имеет смысл, если преобразование выполняется в документ XML. + /// При указании данного параметра результат преобразования будет записываться в объект ЗаписьXML, + /// возвращаемое значение в данном случае будет отсутствовать. + /// Строка. Результат преобразования. + [ContextMethod("ПреобразоватьИзУзла", "TransformFromNode")] + public string TransformFromNode(IValue xmlNode, XmlWriterImpl xmlWriter = null) + { + throw new NotImplementedException(); + } + + /// + /// Выполняет преобразование XML-документа. + /// Используется описание преобразования и значения параметров, ранее установленные в данном объекте. + /// + /// Строка. Имя файла, в котором находится преобразуемый XML-документ. + /// ЗаписьXML. Объект записи XML, в который будет записан результат преобразования. + /// Указание данного параметра имеет смысл, если преобразование выполняется в документ XML. + /// При указании данного параметра результат преобразования будет записываться в объект ЗаписьXML, + /// возвращаемое значение в данном случае будет отсутствовать. + /// Строка. Результат преобразования. + [ContextMethod("ПреобразоватьИзФайла", "TransformFromFile")] + public string TransformFromFile(string fileName, XmlWriterImpl xmlWriter = null) + { + XmlReaderImpl _reader = new XmlReaderImpl(); + + _reader.OpenFile(fileName); + + XmlWriterImpl _writer = new XmlWriterImpl(); + _writer.SetString(); + + Transform(_reader, _writer); + + string result = _writer.Close().ToString(); + + if (xmlWriter != null) + xmlWriter.WriteRaw(result); + + return result; + } + + /// + /// Добавляет значение параметра преобразования. + /// + /// Строка. Полное имя параметра. + /// Булево, Число, Строка. Значение параметра. + [ContextMethod("ДобавитьПараметр", "AddParameter")] + public void AddParameter(string fullName, IValue value) + { + var _value = ContextValuesMarshaller.ConvertToClrObject(value); + _argumentList.AddParam(fullName, "", _value); + } + + /// + /// Удаляет значение параметра преобразования. + /// + /// Строка. Полное имя параметра. + [ContextMethod("УдалитьПараметр", "RemoveParameter")] + public void RemoveParameter(string fullName) + { + _argumentList.RemoveParam(fullName, ""); + } + + /// + /// Создает XSLTransform + /// + /// XSLTransform + public XSLTransform() + { + _xslTransform = new XslCompiledTransform(); + _argumentList = new XsltArgumentList(); + } + + /// + /// Создает ПреобразованиеXSL + /// + /// ПреобразованиеXSL + [ScriptConstructor] + public static IRuntimeContextInstance Constructor() + { + return new XSLTransform(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Xml/XmlGlobalFunctions.cs b/src/OneScript.StandardLibrary/Xml/XmlGlobalFunctions.cs new file mode 100644 index 000000000..97533cd58 --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XmlGlobalFunctions.cs @@ -0,0 +1,187 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Xml; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.TypeDescriptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + [GlobalContext(Category="Функции работы с XML")] + public class XmlGlobalFunctions : GlobalContextBase + { + private static readonly Dictionary _allowedEnums + = new Dictionary(); + + private XmlGlobalFunctions(IGlobalsManager mgr) + { + lock (_allowedEnums) + { + _allowedEnums.Clear(); + foreach (var e in new[] { + (typeof(ClrEnumValueWrapper), typeof(AllowedSignEnum)), + (typeof(ClrEnumValueWrapper), typeof(AllowedLengthEnum)), + (typeof(ClrEnumValueWrapper), typeof(DateFractionsEnum)) + }) + { + _allowedEnums.Add(e.Item1, (EnumerationContext)mgr.GetInstance(e.Item2)); + } + } + } + + /// + /// Получает XML представление значения для помещения в текст элемента или значение атрибута XML. + /// + /// + /// Значение. Допустимые типы: Булево, Число, Строка, Дата, УникальныйИдентификатор, ДвоичныеДанные, + /// Неопределено, Null, а также значения перечислений ДопустимыйЗнак, ДопустимаяДлина, ЧастиДаты + /// + /// + /// Строковое представление значения. Для двоичных данных - строка в формате Вase64. + /// При недопустимом типе значения выбрасывается исключение + /// + /// + [ContextMethod("XMLСтрока", "XMLString")] + public string XMLString(BslValue value) + { + if (value.SystemType == BasicTypes.String) + return value.ToString(); + else if (value.SystemType == BasicTypes.Undefined || value.SystemType == BasicTypes.Null) + return ""; + else if(value.SystemType == BasicTypes.Boolean) + return XmlConvert.ToString(value.AsBoolean()); + else if(value.SystemType == BasicTypes.Date) + return XmlConvert.ToString(value.AsDate(), XmlDateTimeSerializationMode.Unspecified); + else if(value.SystemType == BasicTypes.Number) + return XmlConvert.ToString(value.AsNumber()); + else + { + if (value is BinaryDataContext bdc) + { + return Convert.ToBase64String(bdc.Buffer, Base64FormattingOptions.InsertLineBreaks); + } + if (value is GuidWrapper guid) + { + return guid.ToString(); + } + else if (_allowedEnums.ContainsKey(value.GetType())) + { + return value.ToString(); + } + } + + throw RuntimeException.InvalidArgumentValue(); + } + + /// + /// Выполняет преобразование из строки, полученной из текста элемента или значения атрибута XML, + /// в значение в соответствии с указанным типом. Действие, обратное действию метода XMLСтрока + /// + /// + /// Тип, значение которого надо получить при преобразовании из строкового представления XML. + /// Допустимые типы: Булево, Число, Строка, Дата, УникальныйИдентификатор, ДвоичныеДанные, + /// Неопределено, Null, перечисления ДопустимыйЗнак, ДопустимаяДлина, ЧастиДаты + /// + /// + /// Строка, содержащая строковое представление значения соответствующего типа + /// + /// + /// Значение заданного типа. + /// При недопустимом типе или неправильном строковом представлении выбрасывается исключение + /// + /// + [ContextMethod("XMLЗначение", "XMLValue")] + public IValue XMLValue(BslValue givenType, string presentation) + { + if (givenType.SystemType != BasicTypes.Type) + { + throw RuntimeException.InvalidNthArgumentType(1); + } + + var dataType = givenType as BslTypeValue; + Debug.Assert(dataType != null); + + var typeValue = dataType.TypeValue; + + if(typeValue.Equals(BasicTypes.Boolean)) + { + return ValueFactory.Create(XmlConvert.ToBoolean(presentation)); + } + else if (typeValue.Equals(BasicTypes.Date)) + { + return ValueFactory.Create(XmlConvert.ToDateTime(presentation, XmlDateTimeSerializationMode.Unspecified)); + } + else if (typeValue.Equals(BasicTypes.Number)) + { + return ValueFactory.Create(XmlConvert.ToDecimal(presentation)); + } + else if (typeValue.Equals(BasicTypes.String)) + { + return ValueFactory.Create(presentation); + } + else if (typeValue.Equals(BasicTypes.Undefined)) + { + if (presentation.Trim() == "") + return ValueFactory.Create(); + + throw RuntimeException.InvalidNthArgumentValue(2); + } + else if (typeValue.Equals(BasicTypes.Null)) + { + if (presentation.Trim() == "") + return ValueFactory.CreateNullValue(); + + throw RuntimeException.InvalidNthArgumentValue(2); + } + else if (typeValue.ImplementingClass == typeof(GuidWrapper)) + { + try + { + return new GuidWrapper(presentation); + } + catch + { + throw RuntimeException.InvalidNthArgumentValue(2); + } + } + else if (typeValue.ImplementingClass == typeof(BinaryDataContext)) + { + byte[] bytes = Convert.FromBase64String(presentation); + return new BinaryDataContext(bytes); + } + else if (_allowedEnums.TryGetValue(typeValue.ImplementingClass, out var enumerationContext)) + { + try + { + return enumerationContext[presentation]; + } + catch (RuntimeException) + { + throw RuntimeException.InvalidNthArgumentValue(2); + } + } + + throw RuntimeException.InvalidNthArgumentType(1); + } + + public static IAttachableContext CreateInstance(IGlobalsManager mgr) + { + return new XmlGlobalFunctions(mgr); + } + + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XmlNamespaceContext.cs b/src/OneScript.StandardLibrary/Xml/XmlNamespaceContext.cs similarity index 96% rename from src/ScriptEngine.HostedScript/Library/Xml/XmlNamespaceContext.cs rename to src/OneScript.StandardLibrary/Xml/XmlNamespaceContext.cs index 56a2195b6..b1c24fa53 100644 --- a/src/ScriptEngine.HostedScript/Library/Xml/XmlNamespaceContext.cs +++ b/src/OneScript.StandardLibrary/Xml/XmlNamespaceContext.cs @@ -4,13 +4,16 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; + using System; using System.Collections.Generic; using System.Linq; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Xml +namespace OneScript.StandardLibrary.Xml { [ContextClass("КонтекстПространствИменXML", "XMLNamespaceContext")] public class XmlNamespaceContext : AutoContext diff --git a/src/OneScript.StandardLibrary/Xml/XmlNodeTypeEnum.cs b/src/OneScript.StandardLibrary/Xml/XmlNodeTypeEnum.cs new file mode 100644 index 000000000..46618868e --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XmlNodeTypeEnum.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Xml; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + [SystemEnum("ТипУзлаXML", "XMLNodeType")] + public class XmlNodeTypeEnum : ClrEnumWrapperCached + { + private XmlNodeTypeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("Атрибут", "Attribute", XmlNodeType.Attribute); + MakeValue("ИнструкцияОбработки", "ProcessingInstruction", XmlNodeType.ProcessingInstruction); + MakeValue("Комментарий", "Comment", XmlNodeType.Comment); + MakeValue("КонецСущности", "EndEntity", XmlNodeType.EndEntity); + MakeValue("КонецЭлемента", "EndElement", XmlNodeType.EndElement); + MakeValue("НачалоЭлемента", "StartElement", XmlNodeType.Element); + MakeValue("Ничего", "None", XmlNodeType.None); + MakeValue("Нотация", "Notation", XmlNodeType.Notation); + MakeValue("ОбъявлениеXML", "XMLDeclaration", XmlNodeType.XmlDeclaration); + MakeValue("ОпределениеТипаДокумента", "DocumentTypeDefinition", XmlNodeType.DocumentType); + MakeValue("ПробельныеСимволы", "Whitespace", XmlNodeType.Whitespace); + MakeValue("СекцияCDATA", "CDATASection", XmlNodeType.CDATA); + MakeValue("СсылкаНаСущность", "EntityReference", XmlNodeType.EntityReference); + MakeValue("Сущность", "Entity", XmlNodeType.Entity); + MakeValue("Текст", "Text", XmlNodeType.Text); + } + + public static XmlNodeTypeEnum CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new XmlNodeTypeEnum(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/Xml/XmlReaderImpl.cs b/src/OneScript.StandardLibrary/Xml/XmlReaderImpl.cs new file mode 100644 index 000000000..d560c2b5c --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XmlReaderImpl.cs @@ -0,0 +1,527 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Xml; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + [ContextClass("ЧтениеXML","XMLReader")] + public class XmlReaderImpl : AutoContext, IDisposable + { + XmlReader _reader; + XmlTextReader _txtReader; + XmlReaderSettingsImpl _settings = XmlReaderSettingsImpl.Constructor(); + bool _ignoreWhitespace = true; + bool _ignoreWSChanged = false; + + EmptyElemCompabilityState _emptyElemReadState = EmptyElemCompabilityState.Off; + bool _attributesLoopReset = false; + + private enum EmptyElemCompabilityState + { + Off, + EmptyElementEntered, + EmptyElementRead + } + + public XmlReader GetNativeReader() + { + return _reader; + } + + [ContextMethod("ОткрытьФайл", "OpenFile")] + public void OpenFile(string path, XmlReaderSettingsImpl settings=null) + { + _settings = settings ?? XmlReaderSettingsImpl.Create(); + _txtReader = new XmlTextReader(File.OpenRead(path), XmlNodeType.Document, _settings.Context); + + InitReader(); + } + + [ContextMethod("УстановитьСтроку", "SetString")] + public void SetString(string content, XmlReaderSettingsImpl settings = null) + { + _settings = settings ?? XmlReaderSettingsImpl.Create(); + _txtReader = new XmlTextReader(content, XmlNodeType.Document, _settings.Context); + + InitReader(); + } + + private void InitReader() + { + if (_reader != null) + _reader.Dispose(); + + _ignoreWhitespace = _settings.IgnoreWhitespace; + if (_settings.UseIgnorableWhitespace) + _settings.Settings.IgnoreWhitespace = false; + + _reader = XmlReader.Create(_txtReader, _settings.Settings); + + _ignoreWSChanged = false; + _emptyElemReadState = EmptyElemCompabilityState.Off; + _attributesLoopReset = false; + } + + #region Свойства + + [ContextProperty("Параметры", "Settings")] + public IValue Settings => _settings; + + [ContextProperty("ПробельныеСимволы", "Space")] + public IValue Space => _settings.Space; + + [ContextProperty("ВерсияXML", "XMLVersion")] + public string XMLVersion => _settings.Version; + + [ContextProperty("Язык", "Lang")] + public string Lang => _settings.Language; + + [ContextProperty("ИгнорироватьПробелы", "IgnoreWhitespace")] + public bool IgnoreWhitespace + { + get { return _ignoreWhitespace; } + set + { + if (value == _ignoreWhitespace) + return; + + _ignoreWhitespace = value; + + if (_settings.UseIgnorableWhitespace) + return; + + var settings = _settings.Settings.Clone(); + settings.IgnoreWhitespace = _ignoreWhitespace; + + _reader = XmlReader.Create(_txtReader, settings); + _ignoreWSChanged = (_reader.ReadState != _txtReader.ReadState); + } + } + + [ContextProperty("КодировкаXML", "XMLEncoding")] + public string XMLEncoding => _txtReader?.Encoding?.WebName ?? "UTF-8"; + + [ContextProperty("КодировкаИсточника", "InputEncoding")] + public string InputEncoding => XMLEncoding; + + [ContextProperty("Автономный", "Standalone")] + public bool Standalone => throw new NotSupportedException(); + + #endregion + + #region Свойства текущего узла + + [ContextProperty("URIПространстваИмен", "NamespaceURI")] + public string NamespaceURI => _reader?.NamespaceURI ?? string.Empty; + + [ContextProperty("БазовыйURI", "BaseURI")] + public string BaseURI => _reader?.BaseURI ?? string.Empty; + + [ContextProperty("ИмеетЗначение", "HasValue")] + public bool HasValue => _reader?.HasValue ?? false; + + [ContextProperty("Значение", "Value")] + public string Value => HasValue ? _reader.Value : string.Empty; + + + [ContextProperty("ИмеетИмя", "HasName")] + public bool HasName => _reader != null ? _reader.LocalName != String.Empty : false; + + [ContextProperty("Имя", "Name")] + public string Name => _reader?.Name ?? string.Empty; + + [ContextProperty("ИмяНотации", "NotationName")] + public string NotationName => throw new NotSupportedException(); + + private int Depth + { + get + { + if (_reader.NodeType == XmlNodeType.EndElement) + return _reader.Depth; + + if (_emptyElemReadState == EmptyElemCompabilityState.EmptyElementRead) + return _reader.Depth; + + return _reader.Depth + 1; + } + } + + [ContextProperty("КонтекстПространствИмен", "NamespaceContext")] + public IValue NamespaceContext + { + get + { + if (_reader == null) + return ValueFactory.Create(); + + return new XmlNamespaceContext(Depth, _txtReader.GetNamespacesInScope(XmlNamespaceScope.All)); + } + } + + [ContextProperty("ЛокальноеИмя", "LocalName")] + public string LocalName => _reader?.LocalName ?? string.Empty; + + + [ContextProperty("Префикс", "Prefix")] + public string Prefix => _reader?.Prefix ?? string.Empty; + + [ContextProperty("ПубличныйИдентификатор", "PublicId")] + public string PublicId => throw new NotSupportedException(); + + [ContextProperty("СистемныйИдентификатор", "SystemId")] + public string SystemId => throw new NotSupportedException(); + + [ContextProperty("ТипУзла", "NodeType")] + public ClrEnumValueWrapper NodeType + { + get + { + XmlNodeType nodeType; + if (_reader == null) + { + nodeType = XmlNodeType.None; + } + else if (_emptyElemReadState == EmptyElemCompabilityState.EmptyElementRead) + { + nodeType = XmlNodeType.EndElement; + } + else if (_settings.CDATASectionAsText && _reader.NodeType == XmlNodeType.CDATA) + { + nodeType = XmlNodeType.Text; + } + else if (!_settings.UseIgnorableWhitespace && _reader.NodeType == XmlNodeType.Whitespace) + { + nodeType = XmlNodeType.Text; + } + else + { + nodeType = _reader.NodeType; + } + + return XmlNodeTypeEnum.FromNativeValue(nodeType); + } + } + + [ContextProperty("ЭтоАтрибутПоУмолчанию", "IsDefaultAttribute")] + public bool? IsDefaultAttribute + { + get + { + if (_reader == null || _reader.NodeType != XmlNodeType.Attribute) + return null; + + return _reader.IsDefault; + } + } + + [ContextProperty("ЭтоПробельныеСимволы", "IsWhitespace")] + public bool IsWhitespace + { + get + { + return _reader != null && + (_reader.NodeType == XmlNodeType.Whitespace || + IsCharacters && string.IsNullOrWhiteSpace(_reader.Value) ); + } + } + + [ContextProperty("ЭтоСимвольныеДанные", "IsCharacters")] + public bool IsCharacters + { + get + { + return _reader != null && + (_reader.NodeType == XmlNodeType.Text || _reader.NodeType == XmlNodeType.CDATA || + _reader.NodeType == XmlNodeType.SignificantWhitespace); + } + } + + #endregion + + #region Методы + [ContextMethod("URIПространстваИменАтрибута", "AttributeNamespaceURI")] + public string AttributeNamespaceURI(int index) + { + throw new NotImplementedException(); + } + + [ContextMethod("ЗначениеАтрибута", "AttributeValue")] + public IValue AttributeValue(IValue indexOrName, string URIIfGiven = null) + { + string attributeValue = null; + + if (_reader == null) + { + attributeValue = string.Empty; + } + else if (indexOrName.SystemType == BasicTypes.Number) + { + int index = (int)indexOrName.AsNumber(); + if (index < _reader.AttributeCount) + attributeValue = _reader.GetAttribute(index); + } + else if (indexOrName.SystemType == BasicTypes.String) + { + if (URIIfGiven == null) + attributeValue = _reader.GetAttribute(indexOrName.ToString()!); + else + attributeValue = _reader.GetAttribute(indexOrName.ToString()!, URIIfGiven); + } + else + { + throw RuntimeException.InvalidArgumentType(); + } + + if (attributeValue != null) + return ValueFactory.Create(attributeValue); + else + return ValueFactory.Create(); + + } + + [ContextMethod("ИмяАтрибута", "AttributeName")] + public string AttributeName(int index) + { + if (_reader == null || index + 1 > _reader.AttributeCount) + return string.Empty; + + _reader.MoveToAttribute(index); + var name = _reader.Name; + _reader.MoveToElement(); + + return name; + } + [ContextMethod("КоличествоАтрибутов", "AttributeCount")] + public int AttributeCount() + { + return _reader?.AttributeCount ?? 0; // несовместимо: 1С возвращает 4294967295 (0xFFFF) + } + + [ContextMethod("ЛокальноеИмяАтрибута", "AttributeLocalName")] + public string AttributeLocalName(int index) + { + if (_reader == null || index + 1 > _reader.AttributeCount) + return string.Empty; + + _reader.MoveToAttribute(index); + var name = _reader.LocalName; + _reader.MoveToElement(); + + return name; + } + + [ContextMethod("ПервоеОбъявление", "FirstDeclaration")] + public bool FirstDeclaration() + { + throw new NotImplementedException(); + } + + [ContextMethod("ПервыйАтрибут", "FirstAttribute")] + public bool FirstAttribute() + { + return _reader?.MoveToFirstAttribute() ?? false; + } + + [ContextMethod("ПолучитьАтрибут", "GetAttribute")] + public IValue GetAttribute(IValue indexOrName, string URIIfGiven = null) + { + return AttributeValue(indexOrName, URIIfGiven); + } + + [ContextMethod("ПрефиксАтрибута", "AttributePrefix")] + public string AttributePrefix(int index) + { + if (_reader == null || index+1 > _reader.AttributeCount) + return string.Empty; + + _reader.MoveToAttribute(index); + var name = _reader.Prefix; + _reader.MoveToElement(); + + return name; + } + + [ContextMethod("Пропустить", "Skip")] + public void Skip() + { + if (_reader == null) + return; + + if (_emptyElemReadState == EmptyElemCompabilityState.EmptyElementEntered) + { + _emptyElemReadState = EmptyElemCompabilityState.EmptyElementRead; + return; + } + + V8CompatibleSkip(); + CheckEmptyElementEntering(); + } + + private void V8CompatibleSkip() + { + if (_reader.NodeType == XmlNodeType.Element) + { + int initialDepth = _reader.Depth; + while (_reader.Read() && _reader.Depth > initialDepth) ; + System.Diagnostics.Debug.Assert(_reader.NodeType == XmlNodeType.EndElement); + } + else + { + _reader.Skip(); + } + } + + [ContextMethod("Прочитать", "Read")] + public bool Read() + { + var readingDone = ReadInternal(); + if (readingDone && _reader.NodeType == XmlNodeType.XmlDeclaration && _settings.IgnoreXMLDeclaration) + { + readingDone = ReadInternal(); + } + return readingDone; + } + + private bool ReadInternal() + { + if (_reader == null) + return false; + + if (_emptyElemReadState == EmptyElemCompabilityState.EmptyElementEntered) + { + _emptyElemReadState = EmptyElemCompabilityState.EmptyElementRead; + return true; + } + else if (_reader.NodeType == XmlNodeType.XmlDeclaration || _reader.NodeType == XmlNodeType.DocumentType) + { + var readingDone = _reader.Read(); + while (readingDone && _reader.NodeType == XmlNodeType.Whitespace) + { + readingDone = _reader.Read(); + } + return readingDone; + } + else + { + bool readingDone = _ignoreWSChanged ? ReadWhenStateChanged() : _reader.Read(); + CheckEmptyElementEntering(); + return readingDone; + } + } + + private bool ReadWhenStateChanged() + { + bool readingDone; + var ln = _txtReader.LineNumber; + var lp = _txtReader.LinePosition; + do + { + readingDone = _reader.Read(); + if (!readingDone) + break; + } + while (ln == _txtReader.LineNumber && lp == _txtReader.LinePosition); + + return readingDone; + } + + private void CheckEmptyElementEntering() + { + _attributesLoopReset = false; + if (_reader.IsEmptyElement) + _emptyElemReadState = EmptyElemCompabilityState.EmptyElementEntered; + else + _emptyElemReadState = EmptyElemCompabilityState.Off; + } + + private bool IsEndElement() + { + var isEnd = (NodeType == XmlNodeTypeEnum.FromNativeValue(XmlNodeType.EndElement)); + return isEnd; + } + + private bool ReadAttributeInternal() + { + if (_reader == null) + return false; + + if (IsEndElement() && !_attributesLoopReset) + { + _attributesLoopReset = true; + return _reader.MoveToFirstAttribute(); + } + + return _reader.MoveToNextAttribute(); + } + + [ContextMethod("ПрочитатьАтрибут", "ReadAttribute")] + public bool ReadAttribute() + { + return ReadAttributeInternal(); + } + + [ContextMethod("СледующееОбъявление", "NextDeclaration")] + public void NextDeclaration() + { + throw new NotImplementedException(); + } + + [ContextMethod("СледующийАтрибут", "NextAttribute")] + public bool NextAttribute() + { + return ReadAttributeInternal(); + } + + [ContextMethod("ТипАтрибута", "AttributeType")] + public void AttributeType() + { + throw new NotImplementedException(); + } + + [ContextMethod("Закрыть", "Close")] + public void Close() + { + Dispose(); + } + + [ContextMethod("ПерейтиКСодержимому", "MoveToContent")] + public IValue MoveToContent() + { + var nodeType = _reader.MoveToContent(); + CheckEmptyElementEntering(); + return XmlNodeTypeEnum.FromNativeValue(nodeType); + } + + #endregion + + public void Dispose() + { + if (_reader != null) + { + _reader.Close(); + _reader = null; + } + } + + [ScriptConstructor] + public static XmlReaderImpl Create() + { + return new XmlReaderImpl(); + } + + } +} diff --git a/src/OneScript.StandardLibrary/Xml/XmlReaderSettingsImpl.cs b/src/OneScript.StandardLibrary/Xml/XmlReaderSettingsImpl.cs new file mode 100644 index 000000000..67643b7c4 --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XmlReaderSettingsImpl.cs @@ -0,0 +1,138 @@ +/*---------------------------------------------------------- +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.Xml; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + /// + /// Параметры, используемые для формирования XML + /// + [ContextClass("ПараметрыЧтенияXML", "XmlReaderSettings")] + public class XmlReaderSettingsImpl : AutoContext + { + readonly XmlParserContext _context; + readonly XmlReaderSettings _settings; + + public XmlReaderSettingsImpl(string version, XmlParserContext context, XmlReaderSettings settings, + bool ignoreXMLDeclaration=true, + bool ignoreDocumentType=true, + bool CDataSectionAsText=true, // умолчание отличается от конструктора скрипта + bool useIgnorableWhitespace=false) + { + Version = version; + _context = context; + _settings = settings; + IgnoreXMLDeclaration = ignoreXMLDeclaration; + IgnoreDocumentType = ignoreDocumentType; + CDATASectionAsText = CDataSectionAsText; + UseIgnorableWhitespace = useIgnorableWhitespace; + } + + public XmlReaderSettings Settings => _settings; + public XmlParserContext Context => _context; + + /// + /// Версия XML + /// + [ContextProperty("Версия", "Version")] + public string Version { get; } + + /// + /// Язык + /// + [ContextProperty("Язык", "Language")] + public string Language => _context.XmlLang; + + [ContextProperty("ПробельныеСимволы", "Space")] + public IValue Space => XmlSpaceEnum.FromNativeValue(_context.XmlSpace); + + [ContextProperty("ТипПроверкиПравильности","ValidationType")] + public IValue ValidationTypeImpl => XmlValidationTypeEnum.FromNativeValue(_settings.ValidationType); + + [ContextProperty("ИгнорироватьОбъявлениеXML", "IgnoreXMLDeclaration")] + public bool IgnoreXMLDeclaration { get; } + + [ContextProperty("ИгнорироватьТипДокумента", "IgnoreDocumentType")] + public bool IgnoreDocumentType { get; } + + [ContextProperty("ИгнорироватьИнструкцииОбработки", "IgnoreDataProcessorInstructions")] + public bool IgnoreDataProcessorInstructions => _settings.IgnoreProcessingInstructions; + + [ContextProperty("ИгнорироватьКомментарии", "IgnoreComments")] + public bool IgnoreComments => _settings.IgnoreComments; + + [ContextProperty("ИгнорироватьПробельныеСимволы", "IgnoreWhitespace")] + public bool IgnoreWhitespace => _settings.IgnoreWhitespace; + + [ContextProperty("СекцииCDATAКакТекст", "CDATASectionAsText")] + public bool CDATASectionAsText { get; } + + [ContextProperty("ИспользоватьИгнорируемыеПробельныеСимволы", "UseIgnorableWhitespace")] + public bool UseIgnorableWhitespace { get; } + + [ScriptConstructor] + public static XmlReaderSettingsImpl Constructor( + string version = null, + string lang = null, + ClrEnumValueWrapper spaceChars = null, + ClrEnumValueWrapper validityCheckType = null, + bool ignoreXMLDeclaration = true, + bool ignoreDocumentType = true, + bool ignoreDataProcessorInstructions = false, + bool ignoreComments = false, + bool ignoreSpaceCharacters = true, + bool CDATASectionAsText = false, + bool useIgnorableWhitespace = false) + { + var context = new XmlParserContext(null, null, + lang ?? "", + ContextValuesMarshaller.ConvertWrappedEnum(spaceChars, XmlSpace.Default)) + { + Encoding = System.Text.Encoding.UTF8 + }; + + var settings = new XmlReaderSettings + { + ValidationType = ContextValuesMarshaller.ConvertWrappedEnum(validityCheckType, ValidationType.None), + IgnoreComments = ignoreComments, + IgnoreProcessingInstructions = ignoreDataProcessorInstructions, + IgnoreWhitespace = ignoreSpaceCharacters, + DtdProcessing = ignoreDocumentType ? DtdProcessing.Ignore : DtdProcessing.Parse, + }; + + return new XmlReaderSettingsImpl( + version ?? "1.0", + context, + settings, + ignoreXMLDeclaration, + ignoreDocumentType, + CDATASectionAsText, + useIgnorableWhitespace); + } + + public static XmlReaderSettingsImpl Create() + { + var context = new XmlParserContext(null, null,"",XmlSpace.Default); + + var settings = new XmlReaderSettings + { + ValidationType = ValidationType.None, + IgnoreComments = true, // отличается от конструктора скрипта + IgnoreProcessingInstructions = false, + IgnoreWhitespace = true, + DtdProcessing = DtdProcessing.Ignore, + }; + + return new XmlReaderSettingsImpl("1.0", context, settings); + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Xml/XmlSpaceEnum.cs b/src/OneScript.StandardLibrary/Xml/XmlSpaceEnum.cs new file mode 100644 index 000000000..436db9024 --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XmlSpaceEnum.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +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.Xml; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + [SystemEnum("ПробельныеСимволыXML", "XMLSpace")] + public class XmlSpaceEnum : ClrEnumWrapperCached + { + private XmlSpaceEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("ПоУмолчанию", "Default", XmlSpace.Default); + MakeValue("Сохранять", "Preserve", XmlSpace.Preserve); + } + + public static XmlSpaceEnum CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new XmlSpaceEnum(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/Xml/XmlValidationTypeEnum.cs b/src/OneScript.StandardLibrary/Xml/XmlValidationTypeEnum.cs new file mode 100644 index 000000000..9ddb0fbb9 --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XmlValidationTypeEnum.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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.Xml; +using OneScript.Contexts.Enums; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + [SystemEnum("ТипПроверкиXML", "XMLValidationType")] + public class XmlValidationTypeEnum : ClrEnumWrapperCached + { + private XmlValidationTypeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + MakeValue("НетПроверки", "NoValidate", ValidationType.None); + MakeValue("ОпределениеТипаДокумента", "DocumentTypeDefinition", ValidationType.DTD); + MakeValue("СхемаXML", "XMLSchema", ValidationType.Schema); + } + + public static XmlValidationTypeEnum CreateInstance(ITypeManager typeManager) + { + return CreateInstance(typeManager, (t, v) => new XmlValidationTypeEnum(t, v)); + } + } +} diff --git a/src/OneScript.StandardLibrary/Xml/XmlWriterImpl.cs b/src/OneScript.StandardLibrary/Xml/XmlWriterImpl.cs new file mode 100644 index 000000000..466cd7b16 --- /dev/null +++ b/src/OneScript.StandardLibrary/Xml/XmlWriterImpl.cs @@ -0,0 +1,494 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Xml +{ + [ContextClass("ЗаписьXML", "XMLWriter")] + public class XmlWriterImpl : AutoContext, IDisposable + { + private TextWriterWithSettings _internalTextWriter; + private XmlTextWriter _writer; + private XmlWriterSettingsImpl _settings = (XmlWriterSettingsImpl)XmlWriterSettingsImpl.Constructor(); + private int _depth; + private Stack> _nsmap = new Stack>(); + private StringBuilder _stringBuffer; + + private const string DEFAULT_INDENT_STRING = " "; + + public XmlWriterImpl() + { + Indent = true; + _nsmap.Push(new Dictionary()); + } + + private void EnterScope() + { + ++_depth; + var newMap = _nsmap.Peek().ToDictionary((kv) => kv.Key, (kv) => kv.Value); + _nsmap.Push(newMap); + } + + private void ExitScope() + { + _nsmap.Pop(); + --_depth; + } + + #region Properties + + [ContextProperty("Отступ","Indent")] + public bool Indent { get; set; } + + [ContextProperty("КонтекстПространствИмен", "NamespaceContext")] + public XmlNamespaceContext NamespaceContext + { + get + { + return new XmlNamespaceContext(_depth, _nsmap.Peek()); + } + } + + [ContextProperty("Параметры", "Settings")] + public XmlWriterSettingsImpl Settings + { + get { return _settings; } + } + + #endregion + + #region Methods + + [ContextMethod("ЗаписатьАтрибут","WriteAttribute")] + public void WriteAttribute(string localName, string valueOrNamespace, string value = null) + { + if(value == null) + { + _writer.WriteAttributeString(localName, valueOrNamespace); + } + else + { + _writer.WriteAttributeString(localName, valueOrNamespace, value); + } + } + + [ContextMethod("ЗаписатьБезОбработки","WriteRaw")] + public void WriteRaw(string data) + { + _writer.WriteRaw(data); + } + + [ContextMethod("ЗаписатьИнструкциюОбработки","WriteProcessingInstruction")] + public void WriteProcessingInstruction(string name, string text) + { + _writer.WriteProcessingInstruction(name, text); + } + + [ContextMethod("ЗаписатьКомментарий","WriteComment")] + public void WriteComment(string text) + { + _writer.WriteComment(text); + } + + [ContextMethod("ЗаписатьКонецАтрибута","WriteEndAttribute")] + public void WriteEndAttribute() + { + _writer.WriteEndAttribute(); + } + + [ContextMethod("ЗаписатьКонецЭлемента","WriteEndElement")] + public void WriteEndElement() + { + _internalTextWriter.TrimEndSlashes = true; + _writer.WriteEndElement(); + _internalTextWriter.TrimEndSlashes = false; + ExitScope(); + } + + [ContextMethod("ЗаписатьНачалоАтрибута","WriteStartAttribute")] + public void WriteStartAttribute(string name, string ns = null) + { + if(ns == null) + { + _writer.WriteStartAttribute(name); + } + else + { + _writer.WriteStartAttribute(name, ns); + } + + } + + [ContextMethod("ЗаписатьНачалоЭлемента","WriteStartElement")] + public void WriteStartElement(string name, string ns = null) + { + if (ns == null) + { + _writer.WriteStartElement(name); + } + else + { + _writer.WriteStartElement(name, ns); + } + EnterScope(); + } + + [ContextMethod("ЗаписатьОбъявлениеXML","WriteXMLDeclaration")] + public void WriteXMLDeclaration() + { + _writer.WriteStartDocument(); + } + + [ContextMethod("ЗаписатьСекциюCDATA","WriteCDATASection")] + public void WriteCDATASection(string data) + { + _writer.WriteCData(data); + } + + [ContextMethod("ЗаписатьСоответствиеПространстваИмен","WriteNamespaceMapping")] + public void WriteNamespaceMapping(string prefix, string uri) + { + _writer.WriteAttributeString("xmlns", prefix, null, uri); + _nsmap.Peek()[prefix] = uri; + } + + [ContextMethod("ЗаписатьСсылкуНаСущность","WriteEntityReference")] + public void WriteEntityReference(string name) + { + _writer.WriteEntityRef(name); + } + + [ContextMethod("ЗаписатьТекст","WriteText")] + public void WriteText(string text) + { + _writer.WriteString(text); + } + + [ContextMethod("ЗаписатьТекущий","WriteCurrent")] + public void WriteCurrent(XmlReaderImpl reader) + { + var nodeType = reader.NodeType.UnderlyingValue; + switch (nodeType) + { + case XmlNodeType.Element: + CopyElementAndAttributes(reader, this); + break; + case XmlNodeType.XmlDeclaration: + WriteXMLDeclaration(); + break; + case XmlNodeType.DocumentType: + CopyDocumentType(reader, this); + break; + case XmlNodeType.Attribute: + CopyAttribute(reader.Name, reader.Value, reader, this); + break; + case XmlNodeType.EndElement: + WriteEndElement(); + break; + case XmlNodeType.CDATA: + WriteCDATASection(reader.Value); + break; + case XmlNodeType.Text: + WriteText(reader.Value); + break; + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + WriteText(reader.Value); + break; + case XmlNodeType.Comment: + WriteComment(reader.Value); + break; + case XmlNodeType.EntityReference: + WriteEntityReference(reader.Name); + break; + case XmlNodeType.ProcessingInstruction: + WriteProcessingInstruction(reader.Name, reader.Value); + break; + case XmlNodeType.Entity: + case XmlNodeType.EndEntity: + case XmlNodeType.Document: + case XmlNodeType.DocumentFragment: + case XmlNodeType.Notation: + throw new RuntimeException(new Localization.BilingualString($"Копирование узла {nodeType} не поддерживается")); + default: + break; + } + } + + private static void CopyDocumentType(XmlReaderImpl reader, XmlWriterImpl writer) + { + writer.WriteDocumentType(reader.Name, reader.Value); + } + + private static void CopyElementAndAttributes(XmlReaderImpl reader, XmlWriterImpl writer) + { + writer.WriteStartElement(reader.Name); + var attributeCount = reader.AttributeCount(); + if (attributeCount != 0) + { + for (var attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) + { + var attributeName = reader.AttributeName(attributeIndex); + var attributeValue = reader.GetAttribute(ValueFactory.Create(attributeIndex)).ExplicitString(); + CopyAttribute(attributeName, attributeValue, reader, writer); + } + } + } + + private static void CopyAttribute(string attributeNameIn, string attributeValue, XmlReaderImpl reader, XmlWriterImpl writer) + { + var splittedName = splitName(attributeNameIn); + if (!string.IsNullOrEmpty(splittedName.prefix)) + { + var readerNsContext = (XmlNamespaceContext)reader.NamespaceContext; + var writerNsContext = (XmlNamespaceContext)writer.NamespaceContext; + if (splittedName.prefix.Equals("xmlns", StringComparison.Ordinal)) + { + writer.WriteNamespaceMapping(splittedName.localName, attributeValue); + return; + } + else + { + var uri = readerNsContext.LookupNamespaceUri(splittedName.prefix); + if (uri is BslStringValue) + { + var value = uri.ExplicitString(); + var currentPrefix = writerNsContext.LookupPrefix(value); + if (!(currentPrefix is BslStringValue && currentPrefix.ExplicitString().Equals(splittedName.prefix, StringComparison.Ordinal))) + { + writer.WriteNamespaceMapping(splittedName.prefix, value); + } + splittedName.prefix = value; + } + } + } + writer.WriteAttribute(splittedName.localName, splittedName.prefix, attributeValue); + } + + private static (string prefix, string localName) splitName(string nameOrLocalName) + { + var parts = nameOrLocalName.Split(':'); + if (parts.Length > 1) return (parts[0], parts[1]); + if (parts[0].Equals("xmlns", StringComparison.Ordinal)) return (parts[0], ""); + return ("", parts[0]); + } + + [ContextMethod("ЗаписатьТипДокумента","WriteDocumentType")] + public void WriteDocumentType(string name, string varArg2, string varArg3 = null, string varArg4 = null) + { + if(varArg4 != null) + { + _writer.WriteDocType(name, varArg2, varArg3, varArg4); + } + else if(varArg3 != null) + { + _writer.WriteDocType(name, null, varArg2, varArg3); + } + else + { + _writer.WriteDocType(name, null, null, varArg2); + } + } + + [ContextMethod("НайтиПрефикс","LookupPrefix")] + public IValue LookupPrefix(string uri) + { + string prefix = _writer.LookupPrefix(uri); + if (prefix == null) + return ValueFactory.Create(); + return ValueFactory.Create(prefix); + } + + [ContextMethod("Закрыть","Close")] + public IValue Close() + { + if(IsOpenForString()) + { + _writer.Flush(); + _writer.Close(); + + Dispose(); + + var result = _stringBuffer.ToString(); + _stringBuffer = null; + return ValueFactory.Create(result); + } + else + { + _writer.Flush(); + _writer.Close(); + Dispose(); + + return ValueFactory.Create(); + } + + } + + private void ApplySettings(BslValue encodingOrSettings) + { + if (encodingOrSettings is XmlWriterSettingsImpl settings) + { + _settings = settings; + } + else if ((encodingOrSettings?.SystemType ?? BasicTypes.String) == BasicTypes.String) + { + _settings = XmlWriterSettingsImpl.Constructor( + encodingOrSettings?.ToString(), + null, + Indent, + false, + DEFAULT_INDENT_STRING); + } + else + { + throw RuntimeException.InvalidArgumentType(nameof(encodingOrSettings)); + } + } + + [ContextMethod("ОткрытьФайл","OpenFile")] + public void OpenFile(string path, BslValue encodingOrSettings = null, IValue addBOM = null) + { + ApplySettings(encodingOrSettings); + var fs = new FileStream(path, FileMode.Create, FileAccess.Write); + var clrSettings = _settings.GetClrSettings(addBOM?.AsBoolean() ?? true); + _internalTextWriter = new TextWriterWithSettings(fs, clrSettings); + _writer = new XmlTextWriter(_internalTextWriter); + SetDefaultOptions(); + } + + [ContextMethod("УстановитьСтроку","SetString")] + public void SetString(BslValue encodingOrSettings = null) + { + ApplySettings(encodingOrSettings); + _stringBuffer = new StringBuilder(); + _internalTextWriter = new TextWriterWithSettings(_stringBuffer, _settings.GetClrSettings()); + _writer = new XmlTextWriter(_internalTextWriter); + SetDefaultOptions(); + } + + private void SetDefaultOptions() + { + if (Settings.Indent) + { + _writer.IndentChar = '\xfeff'; + _writer.Formatting = Formatting.Indented; + _writer.Indentation = 1; + } + } + + #endregion + + private bool IsOpenForString() + { + return _stringBuffer != null; + } + + private sealed class TextWriterWithSettings : TextWriter + { + private readonly TextWriter _baseObject; + private readonly XmlWriterSettings _settings; + + public TextWriterWithSettings(Stream stream, XmlWriterSettings settings) + { + _baseObject = new StreamWriter(stream, settings.Encoding); + _settings = settings; + } + + public TextWriterWithSettings(StringBuilder buffer, XmlWriterSettings settings) + { + _baseObject = new StringWriter(buffer); + _settings = settings; + } + + /// + /// Признак необходимости заменять строку ` /` на `/`. + /// См. https://github.com/EvilBeaver/OneScript/issues/768#issuecomment-397848410 + /// + public bool TrimEndSlashes { get; set; } + + public override Encoding Encoding => _settings.Encoding; + + public override void Write(char value) + { + if (value == '\xfeff') + { + _baseObject.Write(_settings.IndentChars); + } + else + { + _baseObject.Write(value); + } + } + + public override void Write(string value) + { + if (value == " /" && TrimEndSlashes) + { + base.Write("/"); + return; + } + base.Write(value); + } + + // netcore3.1 использует эту перегрузку вместо старой со string + public override void Write(char[] buffer) + { + if (TrimEndSlashes && + buffer != default + && buffer.Length == 3 + && buffer[0] == ' ' + && buffer[1] == '/' + && buffer[2] == '>') + { + Write(buffer, 1, 2); + } + else + { + base.Write(buffer); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _baseObject.Close(); + } + + base.Dispose(disposing); + } + } + + public void Dispose() + { + if (_writer != null) + _writer.Close(); + + _writer = null; + } + + [ScriptConstructor] + public static XmlWriterImpl Create() + { + return new XmlWriterImpl(); + } + + public XmlWriter GetNativeWriter() => _writer; + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XmlWriterSettingsImpl.cs b/src/OneScript.StandardLibrary/Xml/XmlWriterSettingsImpl.cs similarity index 79% rename from src/ScriptEngine.HostedScript/Library/Xml/XmlWriterSettingsImpl.cs rename to src/OneScript.StandardLibrary/Xml/XmlWriterSettingsImpl.cs index 965ffc034..d03a76627 100644 --- a/src/ScriptEngine.HostedScript/Library/Xml/XmlWriterSettingsImpl.cs +++ b/src/OneScript.StandardLibrary/Xml/XmlWriterSettingsImpl.cs @@ -4,11 +4,13 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; + using System.Xml; +using OneScript.Contexts; +using OneScript.StandardLibrary.Text; +using ScriptEngine.Machine.Contexts; -namespace ScriptEngine.HostedScript.Library.Xml +namespace OneScript.StandardLibrary.Xml { /// /// Параметры, используемые для формирования XML @@ -72,18 +74,19 @@ public XmlWriterSettings GetClrSettings(bool addBom = false) } [ScriptConstructor] - public static XmlWriterSettingsImpl Constructor(IValue encoding = null, - IValue version = null, IValue indent = null, IValue indentAttributes = null, - IValue indentChars = null) + public static XmlWriterSettingsImpl Constructor( + string encoding = null, + string version = null, + bool indent = true, + bool indentAttributes = false, + string indentChars = null) { - var _indent = ContextValuesMarshaller.ConvertParam(indent, true); - var _indentAttributes = ContextValuesMarshaller.ConvertParam(indentAttributes); - - return new XmlWriterSettingsImpl(encoding?.AsString() ?? "UTF-8", - version?.AsString() ?? "1.0", - _indent, - _indentAttributes, - indentChars?.AsString() ?? "\t"); + return new XmlWriterSettingsImpl( + encoding ?? "UTF-8", + version ?? "1.0", + indent, + indentAttributes, + indentChars ?? "\t"); } } } \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Zip/DotNetZipEncoding.cs b/src/OneScript.StandardLibrary/Zip/DotNetZipEncoding.cs new file mode 100644 index 000000000..78563993f --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/DotNetZipEncoding.cs @@ -0,0 +1,62 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Text; +using Ionic.Zip; + +namespace OneScript.StandardLibrary.Zip +{ + internal static class DotNetZipEncoding + { + private static volatile bool _encodingIsSet; + private static readonly object _locker = new object(); + + static DotNetZipEncoding() + { + _encodingIsSet = false; + } + + public static void SetDefault(Encoding encoding) + { + if (_encodingIsSet) + return; + + lock (_locker) + { + if (_encodingIsSet) + return; + + SetDefaultEncodingViaProperty(encoding); + _encodingIsSet = true; + } + } + + private static void SetDefaultEncodingViaProperty(Encoding encoding) + { + ZipFile.DefaultEncoding = encoding; + } + + private static void SetDefaultEncodingViaReflection(Encoding encoding) + { + const string fieldName = "_defaultEncoding"; + + var field = typeof(ZipFile).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic); + if (field == null) + throw new InvalidOperationException("Field for default encoding not found"); + + field.SetValue(null, encoding); + + if (!Equals(ZipFile.DefaultEncoding, encoding)) + { + throw new InvalidOperationException( + "Field is set, but property DefaultEncoding hasn't changed. Probably another property implementation in library"); + } + } + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Zip/FileNamesEncodingInZipFile.cs b/src/OneScript.StandardLibrary/Zip/FileNamesEncodingInZipFile.cs new file mode 100644 index 000000000..3b482625f --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/FileNamesEncodingInZipFile.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Zip +{ + [EnumerationType("КодировкаИменФайловВZipФайле","FileNamesEncodingInZipFile")] + public enum FileNamesEncodingInZipFile + { + [EnumValue("Авто")] + Auto, + + [EnumValue("UTF8")] + Utf8, + + [EnumValue("КодировкаОСДополнительноUTF8","OSEncodingWithUTF8")] + OsEncodingWithUtf8 + } +} \ No newline at end of file diff --git a/src/OneScript.StandardLibrary/Zip/ZipCompressionLevelEnum.cs b/src/OneScript.StandardLibrary/Zip/ZipCompressionLevelEnum.cs new file mode 100644 index 000000000..43511b4c3 --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipCompressionLevelEnum.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Zip +{ + [EnumerationType("УровеньСжатияZIP", "ZIPCompressionLevel")] + public enum ZipCompressionLevel + { + [EnumValue("Оптимальный", "Optimal")] + Optimal, + [EnumValue("Минимальный", "Minimum")] + Minimal, + [EnumValue("Максимальный", "Maximum")] + Maximal + } +} diff --git a/src/OneScript.StandardLibrary/Zip/ZipCompressionMethodEnum.cs b/src/OneScript.StandardLibrary/Zip/ZipCompressionMethodEnum.cs new file mode 100644 index 000000000..f21b16429 --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipCompressionMethodEnum.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Zip +{ + [EnumerationType("МетодСжатияZIP", "ZIPCompressionMethod")] + public enum ZipCompressionMethod + { + [EnumValue("Сжатие", "Deflate")] + Deflate, + [EnumValue("Копирование", "Copy")] + Copy + } +} diff --git a/src/OneScript.StandardLibrary/Zip/ZipEncryptionMethodEnum.cs b/src/OneScript.StandardLibrary/Zip/ZipEncryptionMethodEnum.cs new file mode 100644 index 000000000..e01cd4a6a --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipEncryptionMethodEnum.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Zip +{ + [EnumerationType("МетодШифрованияZIP", "ZIPEncryptionMethod")] + public enum ZipEncryptionMethod + { + [EnumValue("AES128")] + Aes128, + [EnumValue("AES192")] + Aes192, + [EnumValue("AES256")] + Aes256, + [EnumValue("Zip20")] + Zip20 + } +} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipFileEntriesCollection.cs b/src/OneScript.StandardLibrary/Zip/ZipFileEntriesCollection.cs similarity index 75% rename from src/ScriptEngine.HostedScript/Library/Zip/ZipFileEntriesCollection.cs rename to src/OneScript.StandardLibrary/Zip/ZipFileEntriesCollection.cs index 1c772dd24..e09410c7d 100644 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipFileEntriesCollection.cs +++ b/src/OneScript.StandardLibrary/Zip/ZipFileEntriesCollection.cs @@ -4,16 +4,18 @@ This Source Code Form is subject to the terms of the 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 Ionic.Zip; +using OneScript.Contexts; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -using System.Collections.Generic; -using System.Linq; -namespace ScriptEngine.HostedScript.Library.Zip +namespace OneScript.StandardLibrary.Zip { [ContextClass("ЭлементыZipФайла", "ZipFileEntries")] - public class ZipFileEntriesCollection : AutoContext, ICollectionContext, IEnumerable + public class ZipFileEntriesCollection : AutoCollectionContext { readonly List _entries; @@ -23,7 +25,7 @@ public ZipFileEntriesCollection(IEnumerable entries) } [ContextMethod("Количество", "Count")] - public int Count() + public override int Count() { return _entries.Count; } @@ -59,19 +61,9 @@ public override IValue GetIndexedValue(IValue index) return _entries[idx]; } - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public IEnumerator GetEnumerator() + public override IEnumerator GetEnumerator() { return _entries.GetEnumerator(); } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } } } diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipFileEntryContext.cs b/src/OneScript.StandardLibrary/Zip/ZipFileEntryContext.cs similarity index 98% rename from src/ScriptEngine.HostedScript/Library/Zip/ZipFileEntryContext.cs rename to src/OneScript.StandardLibrary/Zip/ZipFileEntryContext.cs index 6bc72f0ee..925c1631a 100644 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipFileEntryContext.cs +++ b/src/OneScript.StandardLibrary/Zip/ZipFileEntryContext.cs @@ -4,11 +4,13 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + +using System; using Ionic.Zip; +using OneScript.Contexts; using ScriptEngine.Machine.Contexts; -using System; -namespace ScriptEngine.HostedScript.Library.Zip +namespace OneScript.StandardLibrary.Zip { /// /// Описание элемента, находящегося в Zip архиве. diff --git a/src/OneScript.StandardLibrary/Zip/ZipReader.cs b/src/OneScript.StandardLibrary/Zip/ZipReader.cs new file mode 100644 index 000000000..ac16efd10 --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipReader.cs @@ -0,0 +1,174 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Text; +using Ionic.Zip; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.IO; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Binary; +using OneScript.Types; + +namespace OneScript.StandardLibrary.Zip +{ + /// + /// Объект чтения ZIP файлов. + /// + [ContextClass("ЧтениеZipФайла", "ZipFileReader")] + public class ZipReader : AutoContext, IDisposable + { + ZipFile _zip; + ZipFileEntriesCollection _entriesWrapper; + + public ZipReader() + { + } + + public ZipReader(IValue filenameOrStream, string password = null, FileNamesEncodingInZipFile encoding = FileNamesEncodingInZipFile.Auto) + { + Open(filenameOrStream, password, encoding); + } + + private void CheckIfOpened() + { + if(_zip == null) + throw new RuntimeException("Архив не открыт"); + } + + /// + /// Открывает архив для чтения. + /// + /// Имя ZIP файла или Поток, который требуется открыть для чтения. + /// Пароль к файлу, если он зашифрован. + /// Кодировка имен файлов в архиве. + [ContextMethod("Открыть","Open")] + public void Open(IValue filenameOrStream, string password = null, FileNamesEncodingInZipFile encoding = FileNamesEncodingInZipFile.Auto) + { + // fuck non-russian encodings on non-ascii files + DotNetZipEncoding.SetDefault(Encoding.GetEncoding(866)); + + if (filenameOrStream.SystemType == BasicTypes.String) + { + _zip = ZipFile.Read(filenameOrStream.ToString(), new ReadOptions { Encoding = ChooseEncoding(encoding) }); + } + else if (filenameOrStream is IStreamWrapper stream) + { + _zip = ZipFile.Read(stream.GetUnderlyingStream(), new ReadOptions { Encoding = ChooseEncoding(encoding) }); + } + else + { + throw RuntimeException.InvalidArgumentType(nameof(filenameOrStream)); + } + + _zip.Password = password; + } + + private Encoding ChooseEncoding(FileNamesEncodingInZipFile encoding) + { + if (encoding == FileNamesEncodingInZipFile.Auto || encoding == FileNamesEncodingInZipFile.OsEncodingWithUtf8) + return null; + + return Encoding.UTF8; + + } + + /// + /// Извлечение всех файлов из архива + /// + /// Строка. Каталог в который извлекаются файлы + /// РежимВосстановленияПутейФайловZIP + [ContextMethod("ИзвлечьВсе","ExtractAll")] + public void ExtractAll(string where, ZipRestoreFilePathsMode restorePaths = default) + { + CheckIfOpened(); + _zip.FlattenFoldersOnExtract = restorePaths == ZipRestoreFilePathsMode.DontRestore; + _zip.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently; + _zip.ExtractAll(where); + } + + /// + /// Извлечение элемента из архива + /// + /// ЭлементZipФайла. Извлекаемый элемент. + /// Каталог, в который извлекается элемент. + /// РежимВосстановленияПутейФайлов + /// Пароль элемента (если отличается от пароля к архиву) + [ContextMethod("Извлечь", "Extract")] + public void Extract(ZipFileEntryContext entry, string destination, ZipRestoreFilePathsMode restorePaths = default, string password = null) + { + CheckIfOpened(); + if (entry == null) + { + // Передали Неопределено + // TODO сделать более строгий маршалинг с учетом всех тыщ особенностей для строк и прочего + throw RuntimeException.InvalidNthArgumentType(1); + } + + var realEntry = entry.GetZipEntry(); + _zip.FlattenFoldersOnExtract = restorePaths == ZipRestoreFilePathsMode.DontRestore; + realEntry.Password = password; + + using (FileStream streamToExtract = new FileStream(Path.Combine(destination, entry.Name), FileMode.Create)) + { + realEntry.Extract(streamToExtract); + } + } + + /// + /// Закрыть архив и освободить объект. + /// + [ContextMethod("Закрыть", "Close")] + public void Close() + { + Dispose(); + } + + /// + /// Коллекция элементов архива. + /// + [ContextProperty("Элементы", "Elements")] + public ZipFileEntriesCollection Elements + { + get + { + CheckIfOpened(); + + if (_entriesWrapper == null) + _entriesWrapper = new ZipFileEntriesCollection(_zip.Entries); + + return _entriesWrapper; + } + } + + [ScriptConstructor(Name = "Формирование неинициализированного объекта")] + public static ZipReader Construct() + { + return new ZipReader(); + } + + [ScriptConstructor(Name = "На основании имени файла или потока")] + public static ZipReader Constructor(IValue dataSource, string password = null) + { + var dataSourceRawValue = dataSource ?? ValueFactory.CreateInvalidValueMarker(); + + return new ZipReader(dataSourceRawValue, password); + } + + public void Dispose() + { + _entriesWrapper = null; + if (_zip != null) + { + _zip.Dispose(); + _zip = null; + } + } + } +} diff --git a/src/OneScript.StandardLibrary/Zip/ZipRestoreFilePathsModeEnum.cs b/src/OneScript.StandardLibrary/Zip/ZipRestoreFilePathsModeEnum.cs new file mode 100644 index 000000000..12ed8cd6a --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipRestoreFilePathsModeEnum.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Zip +{ + [EnumerationType("РежимВосстановленияПутейФайловZIP", "ZIPRestoreFilePathsMode")] + public enum ZipRestoreFilePathsMode + { + [EnumValue("Восстанавливать", "Restore")] + Restore, + + [EnumValue("НеВосстанавливать", "DontRestore")] + DontRestore + } +} diff --git a/src/OneScript.StandardLibrary/Zip/ZipStorePathModeEnum.cs b/src/OneScript.StandardLibrary/Zip/ZipStorePathModeEnum.cs new file mode 100644 index 000000000..e52198c64 --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipStorePathModeEnum.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Zip +{ + [EnumerationType("РежимСохраненияПутейZIP", "ZIPStorePathsMode")] + public enum ZipStorePathMode + { + [EnumValue("НеСохранятьПути", "DontStorePath")] + DontStorePath, + [EnumValue("СохранятьОтносительныеПути", "StoreRelativePath")] + StoreRelativePath, + [EnumValue("СохранятьПолныеПути", "StoreFullPath")] + StoreFullPath + } +} diff --git a/src/OneScript.StandardLibrary/Zip/ZipSubDirProcessingModeEnum.cs b/src/OneScript.StandardLibrary/Zip/ZipSubDirProcessingModeEnum.cs new file mode 100644 index 000000000..33117ebd8 --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipSubDirProcessingModeEnum.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts.Enums; + +namespace OneScript.StandardLibrary.Zip +{ + [EnumerationType("РежимОбработкиПодкаталоговZIP", "ZIPSubDirProcessingMode")] + public enum ZipSubDirProcessingMode + { + [EnumValue("НеОбрабатывать", "DontProcess")] + DontRecurse, + [EnumValue("ОбрабатыватьРекурсивно", "ProcessRecursively")] + Recurse + } +} diff --git a/src/OneScript.StandardLibrary/Zip/ZipWriter.cs b/src/OneScript.StandardLibrary/Zip/ZipWriter.cs new file mode 100644 index 000000000..dbef29cc2 --- /dev/null +++ b/src/OneScript.StandardLibrary/Zip/ZipWriter.cs @@ -0,0 +1,355 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Ionic.Zip; +using Ionic.Zlib; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.StandardLibrary.Zip +{ + /// + /// Объект записи ZIP-архивов. + /// + [ContextClass("ЗаписьZipФайла", "ZipFileWriter")] + public class ZipWriter : AutoContext, IDisposable + { + private ZipFile _zip; + private string _filename; + + public ZipWriter() + { + + } + + /// + /// Открыть архив для записи. + /// + /// Имя файла будущего архива + /// Пароль на архив + /// Комментарий к архиву + /// МетодСжатияZIP (Сжатие/Копирование) + /// УровеньСжатияZIP (Минимальный/Оптимальный/Максимальный) + /// МетодШифрованияZIP (в текущей реализации не поддерживается) + /// Кодировка имен файлов в архиве. + [ContextMethod("Открыть", "Open")] + public void Open( + string filename, + string password = null, + string comment = null, + ZipCompressionMethod compressionMethod = default, + ZipCompressionLevel compressionLevel = default, + ZipEncryptionMethod? encryptionMethod = default, + FileNamesEncodingInZipFile encoding = FileNamesEncodingInZipFile.Auto) + { + DotNetZipEncoding.SetDefault(Encoding.GetEncoding(866)); // fuck non-russian encodings on non-ascii files + _filename = filename; + _zip = new ZipFile(); + _zip.AlternateEncoding = Encoding.UTF8; + _zip.AlternateEncodingUsage = ChooseEncodingMode(encoding); + _zip.Password = password; + _zip.Comment = comment; + _zip.CompressionMethod = MakeZipCompressionMethod(compressionMethod); + _zip.CompressionLevel = MakeZipCompressionLevel(compressionLevel); + _zip.UseZip64WhenSaving = Zip64Option.AsNecessary; + _zip.Encryption = MakeZipEncryption(encryptionMethod); + } + + private ZipOption ChooseEncodingMode(FileNamesEncodingInZipFile encoding) + { + if (encoding == FileNamesEncodingInZipFile.OsEncodingWithUtf8) + return ZipOption.AsNecessary; + + return ZipOption.Always; + } + + /// + /// Записывает и закрывает файл архива. + /// + [ContextMethod("Записать", "Write")] + public void Write() + { + CheckIfOpened(); + + _zip.Save(_filename); + Dispose(true); + } + + /// + /// Добавление файла к архиву. + /// + /// Имя файла, помещаемого в архив, или маска. + /// РежимСохраненияПутейZIP (НеСохранятьПути/СохранятьОтносительныеПути/СохранятьПолныеПути) + /// РежимОбработкиПодкаталоговZIP (НеОбрабатывать/ОбрабатыватьРекурсивно) + [ContextMethod("Добавить", "Add")] + public void Add(string file, ZipStorePathMode? storePathMode = default, ZipSubDirProcessingMode recurseSubdirectories = default) + { + CheckIfOpened(); + + var pathIsMasked = file.IndexOfAny(new[] { '*', '?' }) >= 0; + + var recursiveFlag = recurseSubdirectories != ZipSubDirProcessingMode.DontRecurse; + var searchOption = recursiveFlag ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + + if(pathIsMasked) + { + AddFilesByMask(file, searchOption, storePathMode); + } + else if (Directory.Exists(file)) + { + AddDirectory(file, searchOption, storePathMode); + } + else if (File.Exists(file)) + { + AddSingleFile(file, storePathMode); + } + + } + + private void AddDirectory(string dir, SearchOption searchOption, ZipStorePathMode? storePathMode) + { + string allFilesMask; + + if (System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == PlatformID.MacOSX) + allFilesMask = "*"; + else + allFilesMask = "*.*"; + + var filesToAdd = Directory.EnumerateFiles(dir, allFilesMask, searchOption); + AddEnumeratedFiles(filesToAdd, GetPathForParentFolder(dir), storePathMode); + } + + private string GetPathForParentFolder(string dir) + { + var rootPath = GetRelativePath(dir, Directory.GetCurrentDirectory()); + if (rootPath == "") + rootPath = Path.Combine(Directory.GetCurrentDirectory(), dir, ".."); + else + rootPath = Directory.GetCurrentDirectory(); + return rootPath; + } + + private void AddSingleFile(string file, ZipStorePathMode? storePathMode) + { + if (storePathMode == null) + storePathMode = ZipStorePathMode.StoreRelativePath; + + var currDir = Directory.GetCurrentDirectory(); + + string pathInArchive; + if (storePathMode == ZipStorePathMode.StoreFullPath) + pathInArchive = null; + else if (storePathMode == ZipStorePathMode.StoreRelativePath) + { + var relativePath = GetRelativePath(file, currDir); + if (relativePath == "") + pathInArchive = "."; + else + pathInArchive = Path.GetDirectoryName(relativePath); + } + else + pathInArchive = ""; + + _zip.AddFile(file, pathInArchive); + } + + private void AddFilesByMask(string file, SearchOption searchOption, ZipStorePathMode? storePathMode) + { + // надо разделить на каталог и маску + var pathEnd = file.LastIndexOfAny(new[] { '\\', '/' }); + string path; + string mask; + IEnumerable filesToAdd; + + if (pathEnd > 1) + { + path = file.Substring(0, pathEnd); + var maskLen = file.Length - pathEnd - 1; + if (maskLen > 0) + mask = file.Substring(pathEnd + 1, maskLen); + else + { + // маска была не в конце пути + // 1С такое откидывает + return; + } + + // несуществующие пути или пути к файлам, вместо папок 1С откидывает + if (!Directory.Exists(path)) + return; + } + else if (pathEnd == 0) + { + path = ""; + mask = file.Substring(1); + } + else + { + path = ""; + mask = file; + } + + filesToAdd = Directory.EnumerateFiles(path, mask, searchOption); + var relativePath = Path.GetFullPath(path); + AddEnumeratedFiles(filesToAdd, relativePath, storePathMode); + + } + + private void AddEnumeratedFiles(IEnumerable filesToAdd, string relativePath, ZipStorePathMode? storePathMode) + { + if (storePathMode == null) + storePathMode = ZipStorePathMode.DontStorePath; + + foreach (var item in filesToAdd) + { + string pathInArchive; + if (storePathMode == ZipStorePathMode.StoreRelativePath) + pathInArchive = Path.GetDirectoryName(GetRelativePath(item, relativePath)); + else if (storePathMode == ZipStorePathMode.StoreFullPath) + pathInArchive = null; + else + pathInArchive = ""; + + _zip.AddFile(item, pathInArchive); + } + } + + // возвращает относительный путь или "", если путь не является относительным + private string GetRelativePath(string filespec, string rootfolder) + { + var currDir = Directory.GetCurrentDirectory(); + + DirectoryInfo directory = new DirectoryInfo(Path.Combine(currDir, rootfolder)); + var folderpath = directory.FullName; + + var filepath = Path.Combine(currDir, filespec); + + if (Directory.Exists(filespec)) + { + DirectoryInfo dir = new DirectoryInfo(filepath); + filepath = dir.FullName; + } + else { + FileInfo file = new FileInfo(filepath); + filepath = file.FullName; + } + + if (!filepath.StartsWith(folderpath)) + return ""; + + var res = filepath.Substring(folderpath.Length + 1); + if (res == "") + res = "."; + return res; + } + + private CompressionMethod MakeZipCompressionMethod(ZipCompressionMethod compressionMethod) + { + if (compressionMethod == ZipCompressionMethod.Deflate) + return CompressionMethod.Deflate; + if (compressionMethod == ZipCompressionMethod.Copy) + return CompressionMethod.None; + + throw RuntimeException.InvalidArgumentValue(); + + } + + private CompressionLevel MakeZipCompressionLevel(ZipCompressionLevel compressionLevel) + { + if (compressionLevel == default) + return CompressionLevel.Default; + + if (compressionLevel == ZipCompressionLevel.Minimal) + return CompressionLevel.BestSpeed; + if (compressionLevel == ZipCompressionLevel.Optimal) + return CompressionLevel.Default; + if (compressionLevel == ZipCompressionLevel.Maximal) + return CompressionLevel.BestCompression; + + throw RuntimeException.InvalidArgumentValue(); + } + + private EncryptionAlgorithm MakeZipEncryption(ZipEncryptionMethod? encryptionMethod) + { + if (encryptionMethod == null) + return EncryptionAlgorithm.None; + + if(encryptionMethod == ZipEncryptionMethod.Zip20) + return EncryptionAlgorithm.PkzipWeak; + if (encryptionMethod == ZipEncryptionMethod.Aes128) + return EncryptionAlgorithm.WinZipAes128; + if (encryptionMethod == ZipEncryptionMethod.Aes192) + return EncryptionAlgorithm.Unsupported; + if (encryptionMethod == ZipEncryptionMethod.Aes256) + return EncryptionAlgorithm.WinZipAes256; + + throw RuntimeException.InvalidArgumentValue(); + + } + + private void CheckIfOpened() + { + if (_zip == null) + throw new RuntimeException("Архив не открыт"); + } + + [ScriptConstructor(Name = "Формирование неинициализированного объекта")] + public static ZipWriter Construct() + { + return new ZipWriter(); + } + + [ScriptConstructor(Name = "На основании имени файла")] + public static ZipWriter ConstructByFileOptions( + string filename, + string password = null, + string comment = null, + ZipCompressionMethod compressionMethod = default, + ZipCompressionLevel compressionLevel = default, + ClrEnumValueWrapper encryptionMethod = null, + FileNamesEncodingInZipFile encoding = FileNamesEncodingInZipFile.Auto) + { + var zip = new ZipWriter(); + zip.Open(filename, + password, + comment, + compressionMethod, + compressionLevel, + encryptionMethod?.UnderlyingValue, + encoding); + + return zip; + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_zip != null) + { + _zip.Dispose(); + _zip = null; + } + } + } + + #endregion + } +} diff --git a/src/OneScript.Web.Server/ConnectionInfoWrapper.cs b/src/OneScript.Web.Server/ConnectionInfoWrapper.cs new file mode 100644 index 000000000..c501167f8 --- /dev/null +++ b/src/OneScript.Web.Server/ConnectionInfoWrapper.cs @@ -0,0 +1,99 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Net; +using OneScript.Localization; + +namespace OneScript.Web.Server +{ + /// + /// Сетвевая информация и контексте подключения + /// + [ContextClass("ИнформацияОСоединении", "ConnectionInfo")] + public class ConnectionInfoWrapper: AutoContext + { + private readonly ConnectionInfo _connectionInfo; + + public ConnectionInfoWrapper(ConnectionInfo connectionInfo) + { + _connectionInfo = connectionInfo; + } + + /// + /// Идентификатор подключения + /// + [ContextProperty("Идентификатор", "Id")] + public string Id + { + get => _connectionInfo.Id; + set => _connectionInfo.Id = value; + } + + /// + /// IP адрес клиента + /// + [ContextProperty("УдаленныйIpАдрес", "RemoteIpAddress")] + public string RemoteIpAddress + { + get => _connectionInfo.RemoteIpAddress?.ToString() ?? ""; + set + { + if (IPAddress.TryParse(value, out var ip)) + _connectionInfo.RemoteIpAddress = ip; + else + throw new RuntimeException(new BilingualString($"Ошибка разбора IP адреса: {value}", $"Failed to parse IP address: {value}")); + } + } + + /// + /// Порт клиента + /// + [ContextProperty("УдаленныйПорт", "RemotePort")] + public IValue RemotePort + { + get => BslNumericValue.Create(_connectionInfo.RemotePort); + set + { + _connectionInfo.RemotePort = (int)value.AsNumber(); + } + } + + /// + /// IP адрес сервера + /// + [ContextProperty("ЛокальныйIpАдрес", "LocalIpAddress")] + public string LocalIpAddress + { + get => _connectionInfo.LocalIpAddress?.ToString() ?? ""; + set + { + if (IPAddress.TryParse(value, out var ip)) + _connectionInfo.LocalIpAddress = ip; + else + throw new RuntimeException(new BilingualString($"Ошибка разбора IP адреса: {value}", $"Failed to parse IP address: {value}")); + } + } + + /// + /// Порт сервера + /// + [ContextProperty("ЛокальныйПорт", "LocalPort")] + public IValue LocalPort + { + get => BslNumericValue.Create(_connectionInfo.LocalPort); + set + { + _connectionInfo.LocalPort = (int)value.AsNumber(); + } + } + } +} diff --git a/src/OneScript.Web.Server/CookieOptionsWrapper.cs b/src/OneScript.Web.Server/CookieOptionsWrapper.cs new file mode 100644 index 000000000..0aabccecf --- /dev/null +++ b/src/OneScript.Web.Server/CookieOptionsWrapper.cs @@ -0,0 +1,125 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System; + +namespace OneScript.Web.Server +{ + [ContextClass("ПараметрыCookie", "CookieOptions")] + public class CookieOptionsWrapper: AutoContext + { + internal readonly CookieOptions _cookieOptions = new CookieOptions(); + + [ContextProperty("Домен", "Domain")] + public IValue Domain + { + get + { + if (_cookieOptions.Domain == null) + return BslUndefinedValue.Instance; + else + return BslStringValue.Create(_cookieOptions.Domain); + } + set + { + if (value is BslUndefinedValue) + _cookieOptions.Domain = null; + else + _cookieOptions.Domain = ContextValuesMarshaller.ConvertValueStrict(value); + } + } + + [ContextProperty("Путь", "Path")] + public IValue Path + { + get => _cookieOptions.Path == null ? BslUndefinedValue.Instance : BslStringValue.Create(_cookieOptions.Path); + set + { + if (value is BslUndefinedValue) + _cookieOptions.Path = null; + else + _cookieOptions.Path = ContextValuesMarshaller.ConvertValueStrict(value);; + } + } + + [ContextProperty("Истекает", "Expires")] + public IValue Expires + { + get + { + if (_cookieOptions.Expires.HasValue) + return BslDateValue.Create(_cookieOptions.Expires.Value.UtcDateTime); + else + return BslUndefinedValue.Instance; + } + set + { + if (value is BslUndefinedValue) + _cookieOptions.Expires = null; + else + _cookieOptions.Expires = new DateTimeOffset(value.AsDate()); + } + } + + [ContextProperty("Безопасный", "Secure")] + public bool Secure + { + get => _cookieOptions.Secure; + set => _cookieOptions.Secure = value; + } + + [ContextProperty("РежимSameSite", "SameSiteMode")] + public SameSiteModeEnum SameSiteMode + { + get => (SameSiteModeEnum)_cookieOptions.SameSite; + set => _cookieOptions.SameSite = (Microsoft.AspNetCore.Http.SameSiteMode)value; + } + + [ContextProperty("ТолькоHttp", "HttpOnly")] + public bool HttpOnly + { + get => _cookieOptions.HttpOnly; + set => _cookieOptions.HttpOnly = value; + } + + [ContextProperty("МаксимальныйВозраст", "MaxAge")] + public IValue MaxAge + { + get + { + if (_cookieOptions.MaxAge.HasValue) + return BslNumericValue.Create((decimal)_cookieOptions.MaxAge.Value.TotalSeconds); + else + return BslUndefinedValue.Instance; + } + set + { + if (value is BslUndefinedValue) + _cookieOptions.MaxAge = null; + else + _cookieOptions.MaxAge = TimeSpan.FromSeconds((double)value.AsNumber()); + } + } + + [ContextProperty("Важный", "IsEssential")] + public bool IsEssential + { + get => _cookieOptions.IsEssential; + set => _cookieOptions.IsEssential = value; + } + + [ScriptConstructor] + public static CookieOptionsWrapper Create() + { + return new CookieOptionsWrapper(); + } + } +} diff --git a/src/OneScript.Web.Server/EngineBuilderExtensions.cs b/src/OneScript.Web.Server/EngineBuilderExtensions.cs new file mode 100644 index 000000000..7c9515bb1 --- /dev/null +++ b/src/OneScript.Web.Server/EngineBuilderExtensions.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using ScriptEngine.Hosting; +using ScriptEngine.Machine; + +namespace OneScript.Web.Server +{ + public static class EngineBuilderExtensions + { + public static ExecutionContext AddWebServer(this ExecutionContext env) + { + return env.AddAssembly(typeof(WebServer).Assembly); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Web.Server/FormCollectionWrapper.cs b/src/OneScript.Web.Server/FormCollectionWrapper.cs new file mode 100644 index 000000000..f33515468 --- /dev/null +++ b/src/OneScript.Web.Server/FormCollectionWrapper.cs @@ -0,0 +1,81 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using OneScript.StandardLibrary.Collections; +using System.Collections.Generic; +using Microsoft.Extensions.Primitives; +using OneScript.Exceptions; +using OneScript.Types; + +namespace OneScript.Web.Server +{ + [ContextClass("Форма", "Form")] + public class FormCollectionWrapper : AutoCollectionContext + { + private readonly IFormCollection _items; + + internal FormCollectionWrapper(IFormCollection headers) + { + _items = headers; + } + + public override bool IsIndexed => true; + + public override StringValuesWrapper GetIndexedValue(IValue index) + { + if (index.SystemType != BasicTypes.String) + throw RuntimeException.InvalidArgumentType(); + + return _items.TryGetValue(index.ToString()!, out var result) ? result : StringValues.Empty; + } + + internal bool ContainsKey(string key) + { + return _items.ContainsKey(key); + } + + public IEnumerable Keys() + { + foreach (var key in _items.Keys) + yield return ValueFactory.Create(key); + } + + #region ICollectionContext Members + + [ContextMethod("Получить", "Get")] + public StringValuesWrapper Get(IValue key) + { + return GetIndexedValue(key); + } + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _items.Count; + } + + #endregion + + #region IEnumerable Members + + public override IEnumerator GetEnumerator() + { + foreach (var item in _items) + { + yield return new KeyAndValueImpl(ValueFactory.Create(item.Key), (StringValuesWrapper)item.Value); + } + } + + #endregion + + [ContextProperty("Файлы", "Files", CanWrite = false)] + public FormFileCollectionWrapper Files => new(_items.Files); + } +} diff --git a/src/OneScript.Web.Server/FormFileCollectionWrapper.cs b/src/OneScript.Web.Server/FormFileCollectionWrapper.cs new file mode 100644 index 000000000..160d23240 --- /dev/null +++ b/src/OneScript.Web.Server/FormFileCollectionWrapper.cs @@ -0,0 +1,69 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Collections.Generic; +using OneScript.Exceptions; +using OneScript.Values; +using OneScript.Types; + +namespace OneScript.Web.Server +{ + [ContextClass("ФайлыФормы", "FormFiles")] + public class FormFileCollectionWrapper : AutoCollectionContext + { + private readonly IFormFileCollection _items; + + internal FormFileCollectionWrapper(IFormFileCollection items) + { + _items = items; + } + + public override bool IsIndexed => true; + + public override IValue GetIndexedValue(IValue index) + { + if (index.SystemType != BasicTypes.String) + throw RuntimeException.InvalidArgumentType(); + + var result = _items.GetFile(index.ToString()!); + + if (result == null) + return BslUndefinedValue.Instance; + else + return new FormFileWrapper(result); + } + + #region ICollectionContext Members + + [ContextMethod("Получить", "Get")] + public IValue Retrieve(IValue key) + { + return GetIndexedValue(key); + } + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _items.Count; + } + + #endregion + + #region IEnumerable Members + + public override IEnumerator GetEnumerator() + { + foreach (var item in _items) + yield return new FormFileWrapper(item); + } + + #endregion + } +} diff --git a/src/OneScript.Web.Server/FormFileWrapper.cs b/src/OneScript.Web.Server/FormFileWrapper.cs new file mode 100644 index 000000000..61e4da39c --- /dev/null +++ b/src/OneScript.Web.Server/FormFileWrapper.cs @@ -0,0 +1,47 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using OneScript.Values; +using OneScript.StandardLibrary.Binary; + +namespace OneScript.Web.Server +{ + [ContextClass("ФайлФормы", "FormFile")] + public class FormFileWrapper : AutoContext + { + private readonly IFormFile _item; + + internal FormFileWrapper(IFormFile item) + { + _item = item; + } + + [ContextProperty("ТипКонтента", "ContentType", CanWrite = false)] + public IValue ContentType => BslStringValue.Create(_item.ContentType); + + [ContextProperty("РасположениеКонтента", "ContentDisposition", CanWrite = false)] + public IValue ContentDisposition => BslStringValue.Create(_item.ContentDisposition); + + [ContextProperty("Заголовки", "Headers", CanWrite = false)] + public HeaderDictionaryWrapper Headers => new(_item.Headers); + + [ContextProperty("Длина", "Length", CanWrite = false)] + public IValue Length => BslNumericValue.Create(_item.Length); + + [ContextProperty("Имя", "Name", CanWrite = false)] + public IValue Name => BslStringValue.Create(_item.Name); + + [ContextProperty("ИмяФайла", "FileName", CanWrite = false)] + public IValue FileName => BslStringValue.Create(_item.FileName); + + [ContextMethod("ОткрытьПотокЧтения", "OpenReadStream")] + public GenericStream OpenReadStream() => new(_item.OpenReadStream()); + } +} diff --git a/src/OneScript.Web.Server/HeaderDictionaryWrapper.cs b/src/OneScript.Web.Server/HeaderDictionaryWrapper.cs new file mode 100644 index 000000000..95599f31a --- /dev/null +++ b/src/OneScript.Web.Server/HeaderDictionaryWrapper.cs @@ -0,0 +1,389 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Collections.Generic; +using System.Linq; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; +using Microsoft.Extensions.Primitives; +using OneScript.Exceptions; +using OneScript.Execution; + +namespace OneScript.Web.Server +{ + [ContextClass("СловарьЗаголовков", "HeaderDictionary")] + public class HeaderDictionaryWrapper : AutoCollectionContext + { + private readonly IHeaderDictionary _items; + + [ContextProperty("Accept", CanWrite = false)] + public StringValuesWrapper Accept => _items.Accept; + + [ContextProperty("AcceptCharset", CanWrite = false)] + public StringValuesWrapper AcceptCharset => _items.AcceptCharset; + + [ContextProperty("AcceptEncoding", CanWrite = false)] + public StringValuesWrapper AcceptEncoding => _items.AcceptEncoding; + + [ContextProperty("AcceptLanguage", CanWrite = false)] + public StringValuesWrapper AcceptLanguage => _items.AcceptLanguage; + + [ContextProperty("AcceptRanges", CanWrite = false)] + public StringValuesWrapper AcceptRanges => _items.AcceptRanges; + + [ContextProperty("AccessControlAllowCredentials", CanWrite = false)] + public StringValuesWrapper AccessControlAllowCredentials => _items.AccessControlAllowCredentials; + + [ContextProperty("AccessControlAllowHeaders", CanWrite = false)] + public StringValuesWrapper AccessControlAllowHeaders => _items.AccessControlAllowHeaders; + + [ContextProperty("AccessControlAllowMethods", CanWrite = false)] + public StringValuesWrapper AccessControlAllowMethods => _items.AccessControlAllowMethods; + + [ContextProperty("AccessControlAllowOrigin", CanWrite = false)] + public StringValuesWrapper AccessControlAllowOrigin => _items.AccessControlAllowOrigin; + + [ContextProperty("AccessControlExposeHeaders", CanWrite = false)] + public StringValuesWrapper AccessControlExposeHeaders => _items.AccessControlExposeHeaders; + + [ContextProperty("AccessControlMaxAge", CanWrite = false)] + public StringValuesWrapper AccessControlMaxAge => _items.AccessControlMaxAge; + + [ContextProperty("AccessControlRequestHeaders", CanWrite = false)] + public StringValuesWrapper AccessControlRequestHeaders => _items.AccessControlRequestHeaders; + + [ContextProperty("AccessControlRequestMethod", CanWrite = false)] + public StringValuesWrapper AccessControlRequestMethod => _items.AccessControlRequestMethod; + + [ContextProperty("Age", CanWrite = false)] + public StringValuesWrapper Age => _items.Age; + + [ContextProperty("Allow", CanWrite = false)] + public StringValuesWrapper Allow => _items.Allow; + + [ContextProperty("AltSvc", CanWrite = false)] + public StringValuesWrapper AltSvc => _items.AltSvc; + + [ContextProperty("Authorization", CanWrite = false)] + public StringValuesWrapper Authorization => _items.Authorization; + + [ContextProperty("Baggage", CanWrite = false)] + public StringValuesWrapper Baggage => _items.Baggage; + + [ContextProperty("CacheControl", CanWrite = false)] + public StringValuesWrapper CacheControl => _items.CacheControl; + + [ContextProperty("Connection", CanWrite = false)] + public StringValuesWrapper Connection => _items.Connection; + + [ContextProperty("ContentDisposition", CanWrite = false)] + public StringValuesWrapper ContentDisposition => _items.ContentDisposition; + + [ContextProperty("ContentEncoding", CanWrite = false)] + public StringValuesWrapper ContentEncoding => _items.ContentEncoding; + + [ContextProperty("ContentLanguage", CanWrite = false)] + public StringValuesWrapper ContentLanguage => _items.ContentLanguage; + public long? ContentLength => _items.ContentLength; + + [ContextProperty("ContentLocation", CanWrite = false)] + public StringValuesWrapper ContentLocation => _items.ContentLocation; + + [ContextProperty("ContentMD5", CanWrite = false)] + public StringValuesWrapper ContentMD5 => _items.ContentMD5; + + [ContextProperty("ContentRange", CanWrite = false)] + public StringValuesWrapper ContentRange => _items.ContentRange; + + [ContextProperty("ContentSecurityPolicy", CanWrite = false)] + public StringValuesWrapper ContentSecurityPolicy => _items.ContentSecurityPolicy; + + [ContextProperty("ContentSecurityPolicyReportOnly", CanWrite = false)] + public StringValuesWrapper ContentSecurityPolicyReportOnly => _items.ContentSecurityPolicyReportOnly; + + [ContextProperty("ContentType", CanWrite = false)] + public StringValuesWrapper ContentType => _items.ContentType; + + [ContextProperty("Cookie", CanWrite = false)] + public StringValuesWrapper Cookie => _items.Cookie; + + [ContextProperty("CorrelationContext", CanWrite = false)] + public StringValuesWrapper CorrelationContext => _items.CorrelationContext; + + [ContextProperty("Date", CanWrite = false)] + public StringValuesWrapper Date => _items.Date; + + [ContextProperty("ETag", CanWrite = false)] + public StringValuesWrapper ETag => _items.ETag; + + [ContextProperty("Expect", CanWrite = false)] + public StringValuesWrapper Expect => _items.Expect; + + [ContextProperty("Expires", CanWrite = false)] + public StringValuesWrapper Expires => _items.Expires; + + [ContextProperty("From", CanWrite = false)] + public StringValuesWrapper From => _items.From; + + [ContextProperty("GrpcAcceptEncoding", CanWrite = false)] + public StringValuesWrapper GrpcAcceptEncoding => _items.GrpcAcceptEncoding; + + [ContextProperty("GrpcEncoding", CanWrite = false)] + public StringValuesWrapper GrpcEncoding => _items.GrpcEncoding; + + [ContextProperty("GrpcMessage", CanWrite = false)] + public StringValuesWrapper GrpcMessage => _items.GrpcMessage; + + [ContextProperty("GrpcStatus", CanWrite = false)] + public StringValuesWrapper GrpcStatus => _items.GrpcStatus; + + [ContextProperty("GrpcTimeout", CanWrite = false)] + public StringValuesWrapper GrpcTimeout => _items.GrpcTimeout; + + [ContextProperty("Host", CanWrite = false)] + public StringValuesWrapper Host => _items.Host; + + [ContextProperty("IfMatch", CanWrite = false)] + public StringValuesWrapper IfMatch => _items.IfMatch; + + [ContextProperty("IfModifiedSince", CanWrite = false)] + public StringValuesWrapper IfModifiedSince => _items.IfModifiedSince; + + [ContextProperty("IfNoneMatch", CanWrite = false)] + public StringValuesWrapper IfNoneMatch => _items.IfNoneMatch; + + [ContextProperty("IfRange", CanWrite = false)] + public StringValuesWrapper IfRange => _items.IfRange; + + [ContextProperty("IfUnmodifiedSince", CanWrite = false)] + public StringValuesWrapper IfUnmodifiedSince => _items.IfUnmodifiedSince; + + [ContextProperty("KeepAlive", CanWrite = false)] + public StringValuesWrapper KeepAlive => _items.KeepAlive; + + [ContextProperty("LastModified", CanWrite = false)] + public StringValuesWrapper LastModified => _items.LastModified; + + [ContextProperty("Link", CanWrite = false)] + public StringValuesWrapper Link => _items.Link; + + [ContextProperty("Location", CanWrite = false)] + public StringValuesWrapper Location => _items.Location; + + [ContextProperty("MaxForwards", CanWrite = false)] + public StringValuesWrapper MaxForwards => _items.MaxForwards; + + [ContextProperty("Origin", CanWrite = false)] + public StringValuesWrapper Origin => _items.Origin; + + [ContextProperty("Pragma", CanWrite = false)] + public StringValuesWrapper Pragma => _items.Pragma; + + [ContextProperty("ProxyAuthenticate", CanWrite = false)] + public StringValuesWrapper ProxyAuthenticate => _items.ProxyAuthenticate; + + [ContextProperty("ProxyAuthorization", CanWrite = false)] + public StringValuesWrapper ProxyAuthorization => _items.ProxyAuthorization; + + [ContextProperty("ProxyConnection", CanWrite = false)] + public StringValuesWrapper ProxyConnection => _items.ProxyConnection; + + [ContextProperty("Range", CanWrite = false)] + public StringValuesWrapper Range => _items.Range; + + [ContextProperty("Referer", CanWrite = false)] + public StringValuesWrapper Referer => _items.Referer; + + [ContextProperty("RequestId", CanWrite = false)] + public StringValuesWrapper RequestId => _items.RequestId; + + [ContextProperty("RetryAfter", CanWrite = false)] + public StringValuesWrapper RetryAfter => _items.RetryAfter; + + [ContextProperty("SecWebSocketAccept", CanWrite = false)] + public StringValuesWrapper SecWebSocketAccept => _items.SecWebSocketAccept; + + [ContextProperty("SecWebSocketExtensions", CanWrite = false)] + public StringValuesWrapper SecWebSocketExtensions => _items.SecWebSocketExtensions; + + [ContextProperty("SecWebSocketKey", CanWrite = false)] + public StringValuesWrapper SecWebSocketKey => _items.SecWebSocketKey; + + [ContextProperty("SecWebSocketProtocol", CanWrite = false)] + public StringValuesWrapper SecWebSocketProtocol => _items.SecWebSocketProtocol; + + [ContextProperty("SecWebSocketVersion", CanWrite = false)] + public StringValuesWrapper SecWebSocketVersion => _items.SecWebSocketVersion; + + [ContextProperty("Server", CanWrite = false)] + public StringValuesWrapper Server => _items.Server; + + [ContextProperty("SetCookie", CanWrite = false)] + public StringValuesWrapper SetCookie => _items.SetCookie; + + [ContextProperty("StrictTransportSecurity", CanWrite = false)] + public StringValuesWrapper StrictTransportSecurity => _items.StrictTransportSecurity; + + [ContextProperty("TE", CanWrite = false)] + public StringValuesWrapper TE => _items.TE; + + [ContextProperty("TraceParent", CanWrite = false)] + public StringValuesWrapper TraceParent => _items.TraceParent; + + [ContextProperty("TraceState", CanWrite = false)] + public StringValuesWrapper TraceState => _items.TraceState; + + [ContextProperty("Trailer", CanWrite = false)] + public StringValuesWrapper Trailer => _items.Trailer; + + [ContextProperty("TransferEncoding", CanWrite = false)] + public StringValuesWrapper TransferEncoding => _items.TransferEncoding; + + [ContextProperty("Translate", CanWrite = false)] + public StringValuesWrapper Translate => _items.Translate; + + [ContextProperty("Upgrade", CanWrite = false)] + public StringValuesWrapper Upgrade => _items.Upgrade; + + [ContextProperty("UpgradeInsecureRequests", CanWrite = false)] + public StringValuesWrapper UpgradeInsecureRequests => _items.UpgradeInsecureRequests; + + [ContextProperty("UserAgent", CanWrite = false)] + public StringValuesWrapper UserAgent => _items.UserAgent; + + [ContextProperty("Vary", CanWrite = false)] + public StringValuesWrapper Vary => _items.Vary; + + [ContextProperty("Via", CanWrite = false)] + public StringValuesWrapper Via => _items.Via; + + [ContextProperty("Warning", CanWrite = false)] + public StringValuesWrapper Warning => _items.Warning; + + [ContextProperty("WebSocketSubProtocols", CanWrite = false)] + public StringValuesWrapper WebSocketSubProtocols => _items.WebSocketSubProtocols; + + [ContextProperty("WWWAuthenticate", CanWrite = false)] + public StringValuesWrapper WWWAuthenticate => _items.WWWAuthenticate; + + [ContextProperty("XContentTypeOptions", CanWrite = false)] + public StringValuesWrapper XContentTypeOptions => _items.XContentTypeOptions; + + [ContextProperty("XFrameOptions", CanWrite = false)] + public StringValuesWrapper XFrameOptions => _items.XFrameOptions; + + [ContextProperty("XPoweredBy", CanWrite = false)] + public StringValuesWrapper XPoweredBy => _items.XPoweredBy; + + [ContextProperty("XRequestedWith", CanWrite = false)] + public StringValuesWrapper XRequestedWith => _items.XRequestedWith; + + [ContextProperty("XUACompatible", CanWrite = false)] + public StringValuesWrapper XUACompatible => _items.XUACompatible; + + [ContextProperty("XXSSProtection", CanWrite = false)] + public StringValuesWrapper XXSSProtection => _items.XXSSProtection; + + public HeaderDictionaryWrapper(IHeaderDictionary headers) + { + _items = headers; + } + + public override bool IsIndexed + { + get + { + return true; + } + } + + public override StringValuesWrapper GetIndexedValue(IValue index) + { + if (index.SystemType != BasicTypes.String) + throw RuntimeException.InvalidArgumentType(); + + if (_items.TryGetValue(index.ToString()!, out var result)) + return result; + else + return StringValues.Empty; + } + + public override void SetIndexedValue(IValue index, IValue val) + { + if (index.SystemType != BasicTypes.String || val.SystemType != BasicTypes.String) + throw RuntimeException.InvalidArgumentType(); + + if (index.SystemType != BasicTypes.Undefined) + _items[index.ToString()!] = val.ToString(); + } + + internal bool ContainsKey(string key) + { + return _items.ContainsKey(key); + } + + public IEnumerable Keys() + { + foreach (var key in _items.Keys) + yield return ValueFactory.Create(key); + } + + #region ICollectionContext Members + + [ContextMethod("Получить", "Get")] + public IValue Retrieve(IValue key) + { + return GetIndexedValue(key); + } + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _items.Count; + } + + [ContextMethod("Очистить", "Clear")] + public void Clear() + { + _items.Clear(); + } + + [ContextMethod("Удалить", "Delete")] + public void Delete(string key) + { + _items.Remove(key); + } + #endregion + + #region IEnumerable Members + + public override IEnumerator GetEnumerator() + { + foreach (var item in _items) + { + yield return new KeyAndValueImpl(ValueFactory.Create(item.Key), (StringValuesWrapper)item.Value); + } + } + + #endregion + + [ContextMethod("Добавить", "Append")] + public void Append(string Key, string Value) + => _items.Append(Key, Value); + + [ContextMethod("ДобавитьСписок", "AppendList")] + public void AppendList(IBslProcess process, string Key, ArrayImpl Values) + => _items.AppendList(Key, Values.Select(i => i.AsString(process)).ToList()); + + [ContextMethod("ДобавитьРазделенныеЗапятымиЗначения", "AppendCommaSeparatedValues")] + public void AppendCommaSeparated(IBslProcess process, string Key, ArrayImpl Values) + => _items.AppendCommaSeparatedValues(Key, Values.Select(i => i.AsString(process)).ToArray()); + } +} diff --git a/src/OneScript.Web.Server/HttpContextWrapper.cs b/src/OneScript.Web.Server/HttpContextWrapper.cs new file mode 100644 index 000000000..25a1ca0d6 --- /dev/null +++ b/src/OneScript.Web.Server/HttpContextWrapper.cs @@ -0,0 +1,61 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.Web.Server.WebSockets; +using ScriptEngine.Machine.Contexts; +using OneScript.Types; + +namespace OneScript.Web.Server +{ + [ContextClass("HTTPКонтекст", "HTTPContext")] + public class HttpContextWrapper : AutoContext + { + private readonly ITypeManager _typeManager; + private readonly HttpContext _context; + private readonly PropertyWrappersCollection _wrappers = new(); + + public HttpContextWrapper(ITypeManager typeManager, HttpContext context) + { + _typeManager = typeManager; + _context = context; + } + + [ContextProperty("Запрос", "Request", CanWrite = false)] + public HttpRequestWrapper Request => + _wrappers.Get(nameof(Request), () => new HttpRequestWrapper(_context.Request)); + + [ContextProperty("Ответ", "Response", CanWrite = false)] + public HttpResponseWrapper Response => + _wrappers.Get(nameof(Response), () => new HttpResponseWrapper(_context.Response)); + + [ContextProperty("Соединение", "Connection", CanWrite = false)] + public ConnectionInfoWrapper Connection => + _wrappers.Get(nameof(Connection), () => new ConnectionInfoWrapper(_context.Connection)); + + [ContextProperty("ИдентификаторТрассировки", "TraceIdentifier", CanWrite = false)] + public string TraceIdentifier => _context.TraceIdentifier; + + [ContextProperty("ЗапросПрерван", "RequestAborted", CanWrite = false)] + public bool RequestAborted => _context.RequestAborted.IsCancellationRequested; + + [ContextProperty("Данные", "Data", CanWrite = false)] + public AutoCollectionContext Data => _wrappers.Get(nameof(Data), () => + MapWrapper.Create(_typeManager, _context.Items)); + + [ContextProperty("ВебСокеты", "WebSockets", CanWrite = false)] + public WebSocketsManagerWrapper WebSockets => _wrappers.Get(nameof(WebSockets), () => + new WebSocketsManagerWrapper(_context.WebSockets)); + + [ContextMethod("Прервать", "Abort")] + public void Abort() => _context.Abort(); + + internal HttpContext GetContext() => _context; + } +} \ No newline at end of file diff --git a/src/OneScript.Web.Server/HttpRequestWrapper.cs b/src/OneScript.Web.Server/HttpRequestWrapper.cs new file mode 100644 index 000000000..3b6f9e2bd --- /dev/null +++ b/src/OneScript.Web.Server/HttpRequestWrapper.cs @@ -0,0 +1,139 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using OneScript.StandardLibrary.Collections; + +namespace OneScript.Web.Server +{ + [ContextClass("HTTPСервисЗапрос", "HTTPServiceRequest")] + public class HttpRequestWrapper : AutoContext + { + private readonly HttpRequest _request; + private readonly PropertyWrappersCollection _wrappers = new (); + + public HttpRequestWrapper(HttpRequest request) + { + _request = request; + } + + [ContextProperty("Параметры", "Parameters", CanWrite = false)] + public FixedMapImpl Query => _wrappers.Get(nameof(Query), () => _request.Query.ToFixedMap()); + + [ContextProperty("ЕстьФормыВТипеКонтента", "HasFormContentType", CanWrite = false)] + public bool HasFormContentType => _request.HasFormContentType; + + [ContextProperty("Тело", "Body", CanWrite = false)] + public GenericStream Body => _wrappers.Get(nameof(Body), () => new GenericStream(_request.Body)); + + [ContextProperty("ТипКонтента", "ContentType", CanWrite = false)] + public IValue ContentType + { + get + { + if (_request.ContentType == null) + return BslUndefinedValue.Instance; + else + return BslStringValue.Create(_request.ContentType); + } + } + + [ContextProperty("ДлинаКонтента", "ContentLength", CanWrite = false)] + public IValue ContentLength + { + get + { + if (_request.ContentLength == null) + return BslUndefinedValue.Instance; + else + return BslNumericValue.Create((decimal)_request.ContentLength); + } + } + + [ContextProperty("Куки", "Cookie", CanWrite = false)] + public RequestCookieCollectionWrapper Cookies => _wrappers.Get(nameof(Cookies), () => new RequestCookieCollectionWrapper(_request.Cookies)); + + [ContextProperty("Заголовки", "Headers", CanWrite = false)] + public HeaderDictionaryWrapper Headers => _wrappers.Get(nameof(Headers), () => new HeaderDictionaryWrapper(_request.Headers)); + + [ContextProperty("Протокол", "Protocol", CanWrite = false)] + public string Protocol => _request.Protocol; + + [ContextProperty("СтрокаПараметров", "ParametersString", CanWrite = false)] + public IValue QueryString + { + get + { + if (_request.QueryString.HasValue) + return BslStringValue.Create(_request.QueryString.Value); + else + return BslUndefinedValue.Instance; + } + } + + [ContextProperty("Путь", "Path", CanWrite = false)] + public IValue Path + { + get + { + if (_request.Path.HasValue) + return BslStringValue.Create(_request.Path.Value); + else + return BslUndefinedValue.Instance; + } + } + + [ContextProperty("БазовыйПуть", "PathBase", CanWrite = false)] + public IValue PathBase + { + get + { + if (_request.PathBase.HasValue) + return BslStringValue.Create(_request.PathBase); + else + return BslUndefinedValue.Instance; + } + } + + [ContextProperty("Хост", "Host", CanWrite = false)] + public IValue Host + { + get + { + if (_request.Host.HasValue) + return BslStringValue.Create(_request.Host.Value); + else + return BslUndefinedValue.Instance; + } + } + + [ContextProperty("ЭтоHttps", "IsHttps", CanWrite = false)] + public bool IsHttps => _request.IsHttps; + + [ContextProperty("Схема", "Scheme", CanWrite = false)] + public string Scheme => _request.Scheme; + + [ContextProperty("Метод", "Method", CanWrite = false)] + public string Method => _request.Method; + + [ContextProperty("Форма", "Form", CanWrite = false)] + public IValue Form + { + get + { + if (_request.HasFormContentType) + return _wrappers.Get(nameof(Form), () => new FormCollectionWrapper(_request.Form)); + else + return BslUndefinedValue.Instance; + } + } + } +} diff --git a/src/OneScript.Web.Server/HttpResponseWrapper.cs b/src/OneScript.Web.Server/HttpResponseWrapper.cs new file mode 100644 index 000000000..2035b858e --- /dev/null +++ b/src/OneScript.Web.Server/HttpResponseWrapper.cs @@ -0,0 +1,105 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Json; +using OneScript.StandardLibrary.Text; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Text; +using OneScript.Execution; + +namespace OneScript.Web.Server +{ + [ContextClass("HTTPСервисОтвет", "HTTPServiceResponse")] + public class HttpResponseWrapper : AutoContext + { + private readonly HttpResponse _response; + private readonly PropertyWrappersCollection _wrappers = new(); + + public HttpResponseWrapper(HttpResponse response) + { + _response = response; + } + + [ContextProperty("Начат", "HasStarted", CanWrite = false)] + public bool HasStarted => _response.HasStarted; + + [ContextProperty("ТипКонтента", "ContentType")] + public string ContentType + { + get => _response.ContentType; + set => _response.ContentType = value; + } + + [ContextProperty("ДлинаКонтента", "ContentLength")] + public IValue ContentLength + { + get + { + if (_response.ContentLength == null) + return BslUndefinedValue.Instance; + else + return BslNumericValue.Create((decimal)_response.ContentLength); + } + set + { + if (value == null || value == BslUndefinedValue.Instance) + _response.ContentLength = null; + else + _response.ContentLength = (long)value.AsNumber(); + } + } + + [ContextProperty("Тело", "Body", CanWrite = false)] + public GenericStream Body => _wrappers.Get(nameof(Body), () => new GenericStream(_response.Body)); + + [ContextProperty("Заголовки", "Headers", CanWrite = false)] + public HeaderDictionaryWrapper Headers => + _wrappers.Get(nameof(Headers), () => new HeaderDictionaryWrapper(_response.Headers)); + + [ContextProperty("КодСостояния", "StatusCode")] + public int StatusCode + { + get => _response.StatusCode; + set => _response.StatusCode = value; + } + + [ContextProperty("Куки", "Cookie", CanWrite = false)] + public ResponseCookiesWrapper Cookies => _wrappers.Get(nameof(Cookies), () => new ResponseCookiesWrapper(_response.Cookies)); + + [ContextMethod("Записать", "Write")] + public void Write(string strData, IValue encoding = null) + { + var enc = encoding == null ? Encoding.UTF8 : TextEncodingEnum.GetEncoding(encoding); + + _response.ContentLength = enc.GetByteCount(strData); + _response.WriteAsync(strData, enc).Wait(); + } + + [ContextMethod("ЗаписатьКакJson", "WriteAsJson")] + public void WriteJson(IBslProcess process, IValue obj, IValue encoding = null) + { + var enc = encoding == null ? Encoding.UTF8 : TextEncodingEnum.GetEncoding(encoding); + + var writer = new JSONWriter(); + writer.SetString(); + + var jsonFunctions = GlobalJsonFunctions.CreateInstance() as GlobalJsonFunctions; + jsonFunctions.WriteJSON(process, writer, obj); + + var data = writer.Close(); + + _response.ContentType = $"application/json;charset={enc.WebName}"; + _response.ContentLength = enc.GetByteCount(data); + _response.WriteAsync(data, enc).Wait(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Web.Server/IEnumerableExtensions.cs b/src/OneScript.Web.Server/IEnumerableExtensions.cs new file mode 100644 index 000000000..88c689ae6 --- /dev/null +++ b/src/OneScript.Web.Server/IEnumerableExtensions.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +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 Microsoft.Extensions.Primitives; +using OneScript.StandardLibrary.Collections; +using OneScript.Values; +using System.Collections.Generic; + +namespace OneScript.Web.Server +{ + internal static class IEnumerableExtensions + { + public static MapImpl ToMap(this IEnumerable> enumerable) + { + var map = new MapImpl(); + + foreach (var kv in enumerable) + { + var key = BslStringValue.Create(kv.Key); + var value = BslStringValue.Create(kv.Value); + + map.Insert(key, value); + } + + return map; + } + + public static FixedMapImpl ToFixedMap(this IEnumerable> enumerable) + { + var map = ToMap(enumerable); + + return new FixedMapImpl(map); + } + } +} diff --git a/src/OneScript.Web.Server/OneScript.Web.Server.csproj b/src/OneScript.Web.Server/OneScript.Web.Server.csproj new file mode 100644 index 000000000..721b45cc9 --- /dev/null +++ b/src/OneScript.Web.Server/OneScript.Web.Server.csproj @@ -0,0 +1,32 @@ + + + + + + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug + AnyCPU + + + + OneScript.Web.Server + OneScript web server implementation + Akpaev E.A. + 10.0 + + + + true + false + + + + + + + + + + + + diff --git a/src/OneScript.Web.Server/PropertyWrappersCollection.cs b/src/OneScript.Web.Server/PropertyWrappersCollection.cs new file mode 100644 index 000000000..e6dfa95e4 --- /dev/null +++ b/src/OneScript.Web.Server/PropertyWrappersCollection.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace OneScript.Web.Server; + +/// +/// Класс, обеспечивающий создание оберток свойств запроса один только раз. +/// +public class PropertyWrappersCollection +{ + private readonly Dictionary _objects = new(); + + public T Get(string propName, Func factory) + { + if (_objects.TryGetValue(propName, out var value)) + return (T)value; + + value = factory(); + _objects.Add(propName, value); + + return (T)value; + } +} \ No newline at end of file diff --git a/src/OneScript.Web.Server/README.md b/src/OneScript.Web.Server/README.md new file mode 100644 index 000000000..ba6f2c9ae --- /dev/null +++ b/src/OneScript.Web.Server/README.md @@ -0,0 +1,87 @@ +# OneScript.Web.Server # + +## Проект является интерфейсом для использования веб-сервера Kestrel из состава ASP.NET Core для разработки веб-приложений ## + +Проект добавляет с систему типов OneScript новый класс + +```bsl +ВебСервер +``` + +#### Инициализация объекта сервера и запуск: + +С портом по умолчанию (8080): + +```bsl +ВебСервер = Новый ВебСервер(); +ВебСервер.Запустить(); +``` + +На указанном порту: + +```bsl +Порт = 7000; +ВебСервер = Новый ВебСервер(Порт); +ВебСервер.Запустить(); +``` + +#### Обработка запросов осуществляется посредством внедрения в конвейер обработки запросов собственных middleware: + +Middleware принимает в качестве параметра объект HTTPКонтекст, предоставляющий информацию о соединении, входящем и исходящем запросе и 2-м параметров делегат на вызов следующего middleware в контейнере. +От порядка регистрации обработчиков в конвейере зависит порядок обработки запросах и то, делегат на какой обработчик придет в параметре "СледующийОбработчик" + +```bsl +Процедура МаппингСтраниц(Контекст, СледующийОбработчик) Экспорт + + Путь = ПолучитьПутьСтраницы(Контекст); + + Если Новый Файл(Путь).Существует() Тогда + ЗаписатьФайлВОтвет(Контекст.Ответ, Путь); + Иначе + СледующийОбработчик.Вызвать(Контекст); + КонецЕсли; + +КонецПроцедуры + +Порт = 7000; +ВебСервер = Новый ВебСервер(Порт); + +ВебСервер.ДобавитьОбработчикЗапросов(ЭтотОбъект, "МаппингСтраниц"); + +ВебСервер.Запустить(); +``` + +#### Сервер позволяет обрабатывать входящие вебсокет подключения, так-же через внедрение в конвейер middleware и вызова специального метода, включающего обработку протокола ws: + +```bsl +Процедура ОбработчикВебСокетПодключений(Контекст, СледующийОбработчик) Экспорт + + Если Контекст.ВебСокеты.ЭтоВебСокетЗапрос Тогда + + Сокет = Контекст.ВебСокеты.ПодключитьВебСокет(); + + Пока Сокет.Состояние = СостояниеВебСокета.Открыт Цикл + + ПринятаяСтрока = Сокет.ПолучитьСтроку(); + ПринятыеДанные = ДесериализоватьJson(ПринятаяСтрока); + + Сообщение = Новый Структура("user, message", "Server", "You sent: " + ПринятыеДанные.message); + + Сокет.ОтправитьСтроку(СериализоватьJson(Сообщение)); + + КонецЦикла; + + Иначе + СледующийОбработчик.Вызвать(Контекст); + КонецЕсли; + +КонецПроцедуры + +Порт = 7000; +ВебСервер = Новый ВебСервер(Порт); + +ВебСервер.ИспользоватьВебСокеты(); +ВебСервер.ДобавитьОбработчикЗапросов(ЭтотОбъект, "ОбработчикВебСокетПодключений"); + +ВебСервер.Запустить(); +``` \ No newline at end of file diff --git a/src/OneScript.Web.Server/RequestCookieCollectionWrapper.cs b/src/OneScript.Web.Server/RequestCookieCollectionWrapper.cs new file mode 100644 index 000000000..36fa6e86f --- /dev/null +++ b/src/OneScript.Web.Server/RequestCookieCollectionWrapper.cs @@ -0,0 +1,47 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Collections.Generic; +using System.Linq; +using OneScript.Exceptions; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; + +namespace OneScript.Web.Server +{ + [ContextClass("КукиЗапроса", "RequestCookies")] + public class RequestCookieCollectionWrapper : AutoCollectionContext + { + private readonly IRequestCookieCollection _items; + + public string this[string key] => _items[key]; + + public override IValue GetIndexedValue(IValue index) + { + if (index.SystemType != BasicTypes.String) + throw RuntimeException.InvalidArgumentType(); + + return BslStringValue.Create(this[index.ToString()]); + } + + public override bool IsIndexed => true; + + public RequestCookieCollectionWrapper(IRequestCookieCollection headers) + { + _items = headers; + } + + public override int Count() => _items.Count; + + public override IEnumerator GetEnumerator() + => _items.Select(c => new KeyAndValueImpl(BslStringValue.Create(c.Key), BslStringValue.Create(c.Value))).GetEnumerator(); + } +} diff --git a/src/OneScript.Web.Server/RequestDelegateWrapper.cs b/src/OneScript.Web.Server/RequestDelegateWrapper.cs new file mode 100644 index 000000000..d91808653 --- /dev/null +++ b/src/OneScript.Web.Server/RequestDelegateWrapper.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.Web.Server +{ + [ContextClass("ДелегатЗапроса", "RequestDelegate")] + public class RequestDelegateWrapper : AutoContext + { + private readonly RequestDelegate _delegate; + + public RequestDelegateWrapper(RequestDelegate item) + { + _delegate = item; + } + + [ContextMethod("Вызвать", "Invoke")] + public void Invoke(HttpContextWrapper httpContext) + { + _delegate.Invoke(httpContext.GetContext()); + } + } +} diff --git a/src/OneScript.Web.Server/ResponseCookiesWrapper.cs b/src/OneScript.Web.Server/ResponseCookiesWrapper.cs new file mode 100644 index 000000000..dc5d661e7 --- /dev/null +++ b/src/OneScript.Web.Server/ResponseCookiesWrapper.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; + +namespace OneScript.Web.Server +{ + [ContextClass("КукиОтвета", "ResponseCookies")] + public class ResponseCookiesWrapper : AutoContext + { + private readonly IResponseCookies _items; + + public ResponseCookiesWrapper(IResponseCookies headers) + { + _items = headers; + } + + [ContextMethod("Добавить", "Append")] + public void Append(string key, string value, CookieOptionsWrapper cookieOptions = null) + { + if (cookieOptions is null) + _items.Append(key, value); + else + _items.Append(key, value, cookieOptions._cookieOptions); + } + + [ContextMethod("Удалить", "Delete")] + public void Delete(string key, CookieOptionsWrapper cookieOptions = null) + { + if (cookieOptions is null) + _items.Delete(key); + else + _items.Delete(key, cookieOptions._cookieOptions); + } + } +} diff --git a/src/OneScript.Web.Server/SameSiteModeEnum.cs b/src/OneScript.Web.Server/SameSiteModeEnum.cs new file mode 100644 index 000000000..7e5713d1b --- /dev/null +++ b/src/OneScript.Web.Server/SameSiteModeEnum.cs @@ -0,0 +1,28 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts.Enums; + +namespace OneScript.Web.Server +{ + [EnumerationType("РежимSameSite", "SameSiteMode", + TypeUUID = "C3D11188-2520-46CE-99A1-58CEFAE4CFE9", + ValueTypeUUID = "84C26CD9-C55C-4688-89F4-74BC97FC6F8E")] + public enum SameSiteModeEnum + { + [EnumValue("Unspecified")] + Unspecified = -1, + + [EnumValue("None")] + None = 0, + + [EnumValue("Lax")] + Lax = 1, + + [EnumValue("Strict")] + Strict = 2 + } +} diff --git a/src/OneScript.Web.Server/StringValuesWrapper.cs b/src/OneScript.Web.Server/StringValuesWrapper.cs new file mode 100644 index 000000000..4f50f173e --- /dev/null +++ b/src/OneScript.Web.Server/StringValuesWrapper.cs @@ -0,0 +1,74 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Collections.Generic; +using OneScript.Values; +using Microsoft.Extensions.Primitives; + +namespace OneScript.Web.Server +{ + [ContextClass("СтроковыеЗначения", "StringValues")] + public class StringValuesWrapper : AutoCollectionContext + { + private readonly StringValues _value; + + public static implicit operator StringValues(StringValuesWrapper d) => d._value; + public static implicit operator StringValuesWrapper(StringValues b) => new(b); + + internal StringValuesWrapper(StringValues value) + { + _value = value; + } + + public override bool IsIndexed => true; + + public override IValue GetIndexedValue(IValue index) + { + var value = (int)index.AsNumber(); + + return ValueFactory.Create(_value[value]); + } + + #region ICollectionContext Members + + [ContextMethod("Получить", "Get")] + public IValue Retrieve(IValue key) + { + return GetIndexedValue(key); + } + + [ContextMethod("Количество", "Count")] + public override int Count() + { + return _value.Count; + } + + #endregion + + #region IEnumerable Members + + public override IEnumerator GetEnumerator() + { + foreach (var item in _value) + yield return BslStringValue.Create(item); + } + + #endregion + + public override string ToString() + { + return _value.ToString(); + } + + public override int GetHashCode() + { + return _value.GetHashCode(); + } + } +} diff --git a/src/OneScript.Web.Server/WebServer.cs b/src/OneScript.Web.Server/WebServer.cs new file mode 100644 index 000000000..0dbe15af5 --- /dev/null +++ b/src/OneScript.Web.Server/WebServer.cs @@ -0,0 +1,236 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using OneScript.Contexts; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using OneScript.Execution; + +namespace OneScript.Web.Server +{ + [ContextClass("ВебСервер", "WebServer")] + public class WebServer: AutoContext + { + private readonly ExecutionContext _executionContext; + private WebApplication _app; + private readonly List<(IRuntimeContextInstance Target, string MethodName)> _middlewares = new List<(IRuntimeContextInstance Target, string MethodName)>(); + + private string _contentRoot = null; + private string _wwwRoot; + + private bool _useStaticFiles = false; + private bool _useWebSockets = false; + private (IRuntimeContextInstance Target, string MethodName)? _exceptionHandler = null; + + [ContextProperty("Порт", "Port", CanWrite = false)] + public int Port { get; private set; } + + public WebServer(ExecutionContext executionContext) + { + _executionContext = executionContext; + } + + [ScriptConstructor(Name = "С портом по умолчанию - 8080")] + public static WebServer Constructor(TypeActivationContext typeActivationContext) + { + var server = new WebServer(typeActivationContext.Services.Resolve()) + { + Port = 8080 + }; + + return server; + } + + [ScriptConstructor(Name = "С указанием порта прослушивателя")] + public static WebServer Constructor(TypeActivationContext typeActivationContext, IValue port) + { + var server = new WebServer(typeActivationContext.Services.Resolve()) + { + Port = (int)port.AsNumber() + }; + + return server; + } + + [ContextMethod("Запустить", "Run")] + public void Run() + { + ConfigureApp(); + + _app.Run(); + } + + /// + /// Останавливает веб-сервер + /// + [ContextMethod("Остановить", "Stop")] + public void Stop() + { + _app?.StopAsync().Wait(); + } + + private void ConfigureApp() + { + var appOptions = new WebApplicationOptions + { + ContentRootPath = _contentRoot, + WebRootPath = _wwwRoot + }; + + var builder = WebApplication.CreateBuilder(appOptions); + builder.WebHost.ConfigureKestrel(options => + { + var kestrelSection = builder.Configuration.GetSection("Kestrel"); + options.Configure(kestrelSection); + kestrelSection.Bind(options); + + options.AllowSynchronousIO = true; + options.ListenAnyIP(Port); + }); + + builder.Services.Configure(builder.Configuration.GetSection("FormOptions")); + + _app = builder.Build(); + + if (_useStaticFiles) + _app.UseStaticFiles(); + + if (_exceptionHandler != null) + { + UseBslExceptionHandler(); + } + else + { + UseDefaultExceptionHandler(); + } + + if (_useWebSockets) + _app.UseWebSockets(); + + _app.Use((context, next) => + { + var process = _executionContext.Services.Resolve().NewProcess(); + context.Items.Add(typeof(IBslProcess), process); + return next(); + }); + + _middlewares.ForEach(middleware => + { + _app.Use((context, next) => + { + var args = new IValue[] + { + new HttpContextWrapper(_executionContext.TypeManager, context), + new RequestDelegateWrapper(next) + }; + + var process = (IBslProcess)context.Items[typeof(IBslProcess)]; + + var methodNumber = middleware.Target.GetMethodNumber(middleware.MethodName); + middleware.Target.CallAsProcedure(methodNumber, args, process); + + return Task.CompletedTask; + }); + }); + } + + private void UseDefaultExceptionHandler() + { + _app.UseExceptionHandler(errApp => + { + errApp.Run(context => + { + var exceptionHandlerPathFeature = + context.Features.Get(); + + WriteExceptionToResponse(context, exceptionHandlerPathFeature?.Error); + + return Task.CompletedTask; + }); + }); + } + + private void UseBslExceptionHandler() + { + _app.UseExceptionHandler(handler => + { + handler.Run(context => + { + var args = new IValue[] + { + new HttpContextWrapper(_executionContext.TypeManager, context), + }; + + var methodNumber = _exceptionHandler?.Target.GetMethodNumber(_exceptionHandler?.MethodName) + ?? throw new InvalidOperationException(); + + var process = _executionContext.Services.Resolve().NewProcess(); + + try + { + _exceptionHandler?.Target.CallAsProcedure(methodNumber, args, process); + } + catch (Exception ex) + { + if (!context.Response.HasStarted) + WriteExceptionToResponse(context, ex); + else + throw; + } + + return Task.CompletedTask; + }); + }); + } + + private static void WriteExceptionToResponse(HttpContext httpContext, Exception ex) + { + httpContext.Response.StatusCode = 500; + httpContext.Response.ContentType = "text/plain;charset=utf-8"; + httpContext.Response.WriteAsync(ex.Message).Wait(); + } + + [ContextMethod("ДобавитьОбработчикЗапросов", "AddRequestsHandler")] + public void SetRequestsHandler(IRuntimeContextInstance target, string methodName) + => _middlewares.Add((target, methodName)); + + [ContextMethod("ИспользоватьВебСокеты", "UseWebSockets")] + public void UseWebSockets() => _useWebSockets = true; + + [ContextMethod("ДобавитьОбработчикИсключений", "AddExceptionsHandler")] + public void SetExceptionsHandler(IRuntimeContextInstance target, string methodName) + => _exceptionHandler = (target, methodName); + + [ContextMethod("УстановитьКаталогСервера", "SetServerDir")] + public void SetContentRoot(string path) + { + _contentRoot = path; + } + + [ContextMethod("УстановитьКорневойПуть", "SetWebRoot")] + public void SetWebRoot(string path) + { + _wwwRoot = path; + } + + [ContextMethod("ИспользоватьСтатическиеФайлы", "UseStaticFiles")] + public void UseStaticFiles() + { + _useStaticFiles = true; + } + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketAcceptContextWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketAcceptContextWrapper.cs new file mode 100644 index 000000000..932252b02 --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketAcceptContextWrapper.cs @@ -0,0 +1,99 @@ +/*---------------------------------------------------------- +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 Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System; + +namespace OneScript.Web.Server.WebSockets +{ + /// + /// Настройки согласования вебсокет подключения + /// + [ContextClass("КонтекстПодключенияВебСокета", "WebSocketsAcceptContext")] + public class WebSocketAcceptContextWrapper : AutoContext + { + internal readonly WebSocketAcceptContext _context = new(); + + /// + /// Согласовываемый субпротокол + /// + [ContextProperty("Протокол", "Protocol")] + public IValue Protocol + { + get + { + return _context.SubProtocol == null ? BslUndefinedValue.Instance : BslStringValue.Create(_context.SubProtocol); + } + set + { + _context.SubProtocol = value is BslUndefinedValue ? null : value.ToString(); + } + } + + /// + /// Периодичность отправки KeepAlive пакетов соединения (в секундах) + /// + [ContextProperty("KeepAlive")] + public IValue KeepAlive + { + get + { + if (_context.KeepAliveInterval.HasValue) + return BslNumericValue.Create((decimal)_context.KeepAliveInterval.Value.TotalSeconds); + else + return BslUndefinedValue.Instance; + } + set + { + if (value is BslUndefinedValue) + _context.KeepAliveInterval = null; + else + _context.KeepAliveInterval = TimeSpan.FromSeconds((double)value.AsNumber()); + } + } + + /// + /// Включает поддержку расширения WebSocket permessage-deflate. + /// + /// Имейте в виду, что включение сжатия через зашифрованные подключения делает приложение подверженным атакам типа CRIME/BREACH. + /// Настоятельно рекомендуется отключить сжатие при отправке данных, содержащих секреты, указав при отправке DisableCompression таких сообщений. + /// + [ContextProperty("ВключитьСжатие", "EnableCompression")] + public IValue EnableCompression + { + get => BslBooleanValue.Create(_context.DangerousEnableCompression); + set => _context.DangerousEnableCompression = value.AsBoolean(); + } + + /// + /// Отключает переключение контекста сервера при использовании сжатия. + /// Этот параметр снижает нагрузку на память при сжатии за счет потенциально худшего коэффициента сжатия + /// + [ContextProperty("ОтключитьПерехватСерверногоКонтекста", "DisableServerContextTakeover")] + public IValue DisableServerContextTakeover + { + get => BslBooleanValue.Create(_context.DisableServerContextTakeover); + set => _context.DisableServerContextTakeover = value.AsBoolean(); + } + + /// + /// Задает максимальный log.2 размера скользящего окна LZ77, который можно использовать для сжатия. + /// Этот параметр снижает нагрузку на память при сжатии за счет потенциально худшего коэффициента сжатия + /// + [ContextProperty("МаксимумWindowBits", "ServerMaxWindowBits")] + public IValue ServerMaxWindowBits + { + get => BslNumericValue.Create(_context.ServerMaxWindowBits); + set => _context.ServerMaxWindowBits = (int)value.AsNumber(); + } + + internal WebSocketAcceptContext GetContext() => _context; + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketCloseStatusWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketCloseStatusWrapper.cs new file mode 100644 index 000000000..3df8da8d3 --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketCloseStatusWrapper.cs @@ -0,0 +1,35 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts.Enums; + +namespace OneScript.Web.Server.WebSockets +{ + [EnumerationType("СтатусЗакрытияВебСокета", "WebSocketCloseStatus")] + public enum WebSocketCloseStatusWrapper + { + [EnumValue("НормальноеЗакрытие", "NormalClosure")] + NormalClosure = 1000, + [EnumValue("НедоступностьКонечнойТочки", "EndpointUnavailable")] + EndpointUnavailable = 1001, + [EnumValue("ОшибкаПротокола", "ProtocolError")] + ProtocolError = 1002, + [EnumValue("НеверныйТипСообщения", "InvalidMessageType")] + InvalidMessageType = 1003, + [EnumValue("Пустой", "Empty")] + Empty = 1005, + [EnumValue("НеверныеДанныеСообщения", "InvalidPayloadData")] + InvalidPayloadData = 1007, + [EnumValue("НарушениеПолитики", "PolicyViolation")] + PolicyViolation = 1008, + [EnumValue("СлишкомБольшоеСообщение", "MessageTooBig")] + MessageTooBig = 1009, + [EnumValue("СогласованиеРасширения", "MandatoryExtension")] + MandatoryExtension = 1010, + [EnumValue("ВнутренняяОшибкаСервера", "InternalServerError")] + InternalServerError = 1011 + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketMessageFlagsWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketMessageFlagsWrapper.cs new file mode 100644 index 000000000..bdb34c77b --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketMessageFlagsWrapper.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts.Enums; + +namespace OneScript.Web.Server.WebSockets +{ + [EnumerationType("ФлагиСообщенияВебСокета", "WebSocketMessageFlags")] + public enum WebSocketMessageFlagsWrapper + { + [EnumValue("Пусто", "None")] + None = 0, + [EnumValue("КонецСообщения", "EndOfMessage")] + EndOfMessage = 1, + [EnumValue("ОтключитьСжатие", "DisableCompression")] + DisableCompression = 2 + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketMessageTypeWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketMessageTypeWrapper.cs new file mode 100644 index 000000000..fab042506 --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketMessageTypeWrapper.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts.Enums; + +namespace OneScript.Web.Server.WebSockets +{ + [EnumerationType("ТипСообщенияВебСокета", "WebSocketMessageType")] + public enum WebSocketMessageTypeWrapper + { + [EnumValue("Текст", "Text")] + Text = 0, + [EnumValue("ДвоичныеДанные", "Binary")] + Binary = 1, + [EnumValue("Закрытие", "Close")] + Close = 2 + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketReceiveResultWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketReceiveResultWrapper.cs new file mode 100644 index 000000000..b1ca85be4 --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketReceiveResultWrapper.cs @@ -0,0 +1,67 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Net.WebSockets; + +namespace OneScript.Web.Server.WebSockets +{ + /// + /// Результат выполнения операции Receive вебсокета + /// + [ContextClass("РезультатВебСокетПолучения", "WebSocketReceiveResult")] + public class WebSocketReceiveResultWrapper: AutoContext + { + private readonly WebSocketReceiveResult _result; + + public WebSocketReceiveResultWrapper(WebSocketReceiveResult result) + { + _result = result; + } + + /// + /// Указывает причину, по которой удаленная конечная точка инициировала подтверждение закрытия. + /// + [ContextProperty("СостояниеЗакрытия", "CloseStatus", CanWrite = false)] + public WebSocketCloseStatusWrapper CloseStatus => (WebSocketCloseStatusWrapper)_result.CloseStatus; + + /// + /// Возвращает необязательное описание, описывающее, почему удаленная конечная точка инициировала подтверждение закрытия + /// + [ContextProperty("ОписаниеСостоянияЗакрытия", "CloseStatusDescription", CanWrite = false)] + public IValue CloseStatusDescription + { + get + { + if (_result.CloseStatusDescription == null) + return BslUndefinedValue.Instance; + else + return BslStringValue.Create(_result.CloseStatusDescription); + } + } + + /// + /// Указывает число байт, полученных WebSocket + /// + [ContextProperty("Количество", "Count", CanWrite = false)] + public IValue Count => BslNumericValue.Create(_result.Count); + + /// + /// Указывает, было ли сообщение получено полностью + /// + [ContextProperty("КонецСообщения", "EndOfMessage", CanWrite = false)] + public IValue EndOfMessage => BslBooleanValue.Create(_result.EndOfMessage); + + /// + /// Указывает, является ли текущее сообщение сообщением UTF-8 или двоичным сообщением + /// + [ContextProperty("ТипСообщения", "MessageType", CanWrite = false)] + public WebSocketMessageTypeWrapper MessageType => (WebSocketMessageTypeWrapper)_result.MessageType; + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketStateWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketStateWrapper.cs new file mode 100644 index 000000000..ad966b6a6 --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketStateWrapper.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts.Enums; + +namespace OneScript.Web.Server.WebSockets +{ + [EnumerationType("СостояниеВебСокета", "WebSocketState")] + public enum WebSocketStateWrapper + { + [EnumValue("Пусто", "None")] + None = 0, + [EnumValue("Подключение", "Connecting")] + Connecting = 1, + [EnumValue("Открыт", "Open")] + Open = 2, + [EnumValue("ЗапросЗакрытияОтправлен", "CloseSent")] + CloseSent = 3, + [EnumValue("ЗапросЗакрытияПринят", "CloseReceived")] + CloseReceived = 4, + [EnumValue("Закрыт", "Closed")] + Closed = 5, + [EnumValue("Прерван", "Aborted")] + Aborted = 6, + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketWrapper.cs new file mode 100644 index 000000000..e8dd2de48 --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketWrapper.cs @@ -0,0 +1,185 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.IO; +using System.Net.WebSockets; +using System.Text; +using OneScript.Execution; + +namespace OneScript.Web.Server.WebSockets +{ + /// + /// ВебСокет подключение + /// + [ContextClass("ВебСокет", "WebSocket")] + public class WebSocketWrapper: AutoContext + { + private readonly WebSocket _webSocket; + + public WebSocketWrapper(WebSocket webSocket) + { + _webSocket = webSocket; + } + + /// + /// Указывает причину, по которой удаленная конечная точка инициировала подтверждение закрытия + /// + [ContextProperty("СостояниеЗакрытия", "CloseStatus", CanWrite = false)] + public WebSocketCloseStatusWrapper CloseStatus => (WebSocketCloseStatusWrapper)_webSocket.CloseStatus; + + /// + /// Позволяет удаленной конечной точке описать причину закрытия подключения + /// + [ContextProperty("ОписаниеСостоянияЗакрытия", "CloseStatusDescription", CanWrite = false)] + public IValue CloseStatusDescription + { + get + { + if (_webSocket.CloseStatusDescription == null) + return BslUndefinedValue.Instance; + else + return BslStringValue.Create(_webSocket.CloseStatusDescription); + } + } + + /// + /// Возвращает текущее состояние соединения WebSocket + /// + [ContextProperty("Состояние", "Status", CanWrite = false)] + public WebSocketStateWrapper Status => (WebSocketStateWrapper)_webSocket.State; + + /// + /// Возвращает подпротокол, который был согласован во время подтверждения открытия + /// + [ContextProperty("Протокол", "Protocol", CanWrite = false)] + public IValue Protocol + => _webSocket.SubProtocol == null ? BslUndefinedValue.Instance : BslStringValue.Create(_webSocket.SubProtocol); + + /// + /// Отменяет соединение WebSocket и отменяет все ожидающие операции ввода-вывода + /// + [ContextMethod("Прервать", "Abort")] + public void Abort() => _webSocket.Abort(); + + /// + /// Закрывает подключение WebSocket в качестве асинхронной операции, используя подтверждение закрытия, которое определено в разделе 7 спецификации протокола WebSocket + /// + /// Статус закрытия + /// Описание причины закрытия + [ContextMethod("Закрыть", "Close")] + public void Close(WebSocketCloseStatusWrapper status, string statusDescription) + { + _webSocket.CloseAsync((WebSocketCloseStatus)status, statusDescription, default).Wait(); + } + + /// + /// Инициирует или завершает подтверждение закрытия, определенное в разделе 7 спецификации протокола WebSocket + /// + /// Статус закрытия + /// Описание причины закрытия + [ContextMethod("ЗакрытьВыходнойПоток", "CloseOutput")] + public void CloseOutput(WebSocketCloseStatusWrapper status, string statusDescription) + { + _webSocket.CloseOutputAsync((WebSocketCloseStatus)status, statusDescription, default).Wait(); + } + + /// + /// Получает данные через WebSocket соединениe + /// + /// Буфер двоичных данных + /// + [ContextMethod("Получить", "Receive")] + public WebSocketReceiveResultWrapper Receive(BinaryDataBuffer buffer) + { + var result = _webSocket.ReceiveAsync(buffer.Bytes, default).Result; + + return new WebSocketReceiveResultWrapper(result); + } + + /// + /// Получает строку через WebSocket соединение + /// + /// + [ContextMethod("ПолучитьСтроку", "ReceiveString")] + public BslStringValue ReceiveString() + { + var buffer = new byte[1024]; + using var stream = new MemoryStream(); + + WebSocketReceiveResult result; + do + { + result = _webSocket.ReceiveAsync(buffer, default).Result; + stream.Write(buffer); + } + while (!result.EndOfMessage); + + var data = stream.GetBuffer(); + + return BslStringValue.Create(Encoding.UTF8.GetString(data)); + } + + /// + /// Получает двоичные данные через WebSocket соединение + /// + /// + [ContextMethod("ПолучитьДвоичныеДанные", "ReceiveBinary")] + public byte[] ReceiveBinary() + { + var buffer = new byte[1024]; + using var stream = new MemoryStream(); + + WebSocketReceiveResult result; + do + { + result = _webSocket.ReceiveAsync(buffer, default).Result; + stream.Write(buffer); + } + while (!result.EndOfMessage); + + var data = stream.GetBuffer(); + + return data; + } + + /// + /// Отправляет данные из буфера в подключение WebSocket + /// + /// Буфер двоичных данных + /// Тип сообщения + /// Флаг сообщения + [ContextMethod("Отправить", "Send")] + public void Send(BinaryDataBuffer buffer, WebSocketMessageTypeWrapper messageType, WebSocketMessageFlagsWrapper flag) + { + _webSocket.SendAsync(buffer.Bytes, (WebSocketMessageType)messageType, (WebSocketMessageFlags)flag, default).AsTask().Wait(); + } + + /// + /// Отправляет строку в подключение WebSocket + /// + /// Строка - отправляемые данные + [ContextMethod("ОтправитьСтроку", "SendString")] + public void SendString(IBslProcess process, IValue value) + { + _webSocket.SendAsync(Encoding.UTF8.GetBytes(value.AsString(process)), WebSocketMessageType.Text, WebSocketMessageFlags.EndOfMessage, default).AsTask().Wait(); + } + + /// + /// Отправляет двоичные данные в подключение WebSocket + /// + /// ДвоичныеДанные - отправляемые данные + [ContextMethod("ОтправитьДвоичныеДанные", "SendBinary")] + public void SendBinary(byte[] value) + { + _webSocket.SendAsync(value, WebSocketMessageType.Binary, WebSocketMessageFlags.EndOfMessage, default).AsTask().Wait(); + } + } +} diff --git a/src/OneScript.Web.Server/WebSockets/WebSocketsManagerWrapper.cs b/src/OneScript.Web.Server/WebSockets/WebSocketsManagerWrapper.cs new file mode 100644 index 000000000..674c30d18 --- /dev/null +++ b/src/OneScript.Web.Server/WebSockets/WebSocketsManagerWrapper.cs @@ -0,0 +1,62 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using Microsoft.AspNetCore.Http; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using System.Linq; +using System.Net.WebSockets; + +namespace OneScript.Web.Server.WebSockets +{ + /// + /// Менеджер, управляющий установкой вебсокет соединения для HTTP запросов + /// + [ContextClass("МенеджерВебСокетов", "WebSocketsManager")] + public class WebSocketsManagerWrapper : AutoContext + { + internal readonly WebSocketManager _manager; + + public WebSocketsManagerWrapper(WebSocketManager manager) + { + _manager = manager; + } + + /// + /// Возвращает значение, указывающее, является ли запрос запросом на создание WebSocket + /// + [ContextProperty("ЭтоВебСокетЗапрос", "IsWebSocketRequest", CanWrite = false)] + public IValue IsWebSocketRequest => BslBooleanValue.Create(_manager.IsWebSocketRequest); + + /// + /// Возвращает список запрошенных подпротоколов WebSocket + /// + [ContextProperty("ЗапрошенныеПротоколы", "RequestedProtocols", CanWrite = false)] + public ArrayImpl RequestedProtocols + => new ArrayImpl(_manager.WebSocketRequestedProtocols.Select(c => BslStringValue.Create(c))); + + /// + /// Принимает WebSocket подключение + /// + /// Настройки согласования подключения + /// + [ContextMethod("ПодключитьВебСокет", "AcceptWebSocket")] + public WebSocketWrapper AcceptWebSocket(WebSocketAcceptContextWrapper context = null) + { + WebSocket webSocket; + + if (context == null) + webSocket = _manager.AcceptWebSocketAsync().Result; + else + webSocket = _manager.AcceptWebSocketAsync(context.GetContext()).Result; + + return new WebSocketWrapper(webSocket); + } + } +} diff --git a/src/OneScriptDocumenter/App.config b/src/OneScriptDocumenter/App.config deleted file mode 100644 index 8227adb98..000000000 --- a/src/OneScriptDocumenter/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/OneScriptDocumenter/AssemblyDocumenter.cs b/src/OneScriptDocumenter/AssemblyDocumenter.cs deleted file mode 100644 index 7b522d7d9..000000000 --- a/src/OneScriptDocumenter/AssemblyDocumenter.cs +++ /dev/null @@ -1,904 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml.Linq; - -namespace OneScriptDocumenter -{ - class AssemblyDocumenter - { - readonly LoadedAssembly _library; - readonly XDocument _xmlDoc; - readonly Dictionary _memberDocumentation = new Dictionary(); - - readonly TypesDictionary _typesDict; - - public AssemblyDocumenter() - { } - - public AssemblyDocumenter(string library) - { - var dir = Path.GetDirectoryName(library); - - var loader = new AssemblyLoader(dir); - - _library = loader.Load(Path.GetFileName(library)); - - // add to dictinary - - _typesDict = new TypesDictionary(); - - } - - public void FillTypesDictionary() - { - - ScriptMemberType[] contexts = { ScriptMemberType.GlobalContext, ScriptMemberType.Class, ScriptMemberType.SystemEnum, ScriptMemberType.EnumerationType }; - foreach (ScriptMemberType context in contexts) - { - var conts = _library.GetMarkedTypes(context); - foreach (var cont in conts) - - { - var attrib = _library.GetMarkup(cont, context); - - if (attrib == null) - { - continue; - } - - TypeInfo curTypeInfo = new TypeInfo(); - curTypeInfo.fullName = cont.FullName; - curTypeInfo.ShortName = cont.Name; - if (attrib.ConstructorArguments.Count > 0) - { - GetNameAndAlias(attrib, cont.Name, out curTypeInfo.nameRus, out curTypeInfo.nameEng); - } - _typesDict.add(curTypeInfo); - } - } - - _typesDict.save(); - } - - private static void GetNameAndAlias(CustomAttributeData attrib, string clrName, out string name, out string alias) - { - name = (string)attrib.ConstructorArguments[0].Value; - alias = (string)attrib.ConstructorArguments[1].Value; - if (string.IsNullOrEmpty(alias)) - { - alias = clrName; - } - } - - public AssemblyDocumenter(string library, string xmldoc) - { - using (var reader = new StreamReader(xmldoc)) - { - _xmlDoc = XDocument.Load(reader); - } - - var dir = Path.GetDirectoryName(library); - - var loader = new AssemblyLoader(dir); - - _library = loader.Load(Path.GetFileName(library)); - - // add to dictinary - - _typesDict = new TypesDictionary(); - - FillTypesDictionary(); - - } - - public string SetRusNames(string data, bool link = true) - { - - string linkStr = (link) ? "link::" : ""; - - string str = data; - - str = str.Replace("System.String", "Строка"); - str = str.Replace("System.DateTime", "Дата"); - str = str.Replace("System.Int32", "Число"); - str = str.Replace("System.Int64", "Число"); - str = str.Replace("System.Decimal", "Число"); - str = str.Replace("System.UInt32", "Число"); - str = str.Replace("System.UInt64", "Число"); - str = str.Replace("System.Boolean", "Булево"); - str = str.Replace("System.Nullable{", ""); - str = str.Replace("ScriptEngine.Machine.Contexts.SelfAwareEnumValue{", ""); - str = str.Replace("ScriptEngine.Machine.IVariable", "Произвольный"); - str = str.Replace("}", ""); - str = str.Replace("ScriptEngine.Machine.IValue", "Произвольный"); - str = str.Replace("ScriptEngine.Machine.IRuntimeContextInstance", "ИнформацияОСценарии"); - - foreach (TypeInfo curItm in _typesDict.types) - { - Regex regex = new Regex(@"(\b)(" + curItm.fullName + @")(\b)", RegexOptions.IgnoreCase); - str = regex.Replace(str, linkStr + curItm.nameRus); - } - - return str; - } - - public XDocument CreateDocumentation() - { - var asmElement = _xmlDoc.Root.Element("assembly"); - if (asmElement == null) - throw new ArgumentException("Wrong XML doc format"); - - var libName = _library.Name; - var fileLibName = asmElement.Element("name").Value; - if (String.Compare(libName, fileLibName, true, System.Globalization.CultureInfo.InvariantCulture) != 0) - throw new ArgumentNullException(String.Format("Mismatch assembly names. Expected {0}, found in XML {1}", libName, fileLibName)); - - var members = _xmlDoc.Element("doc").Element("members").Elements(); - _memberDocumentation.Clear(); - foreach (var item in members) - { - string key = item.Attribute("name").Value; - _memberDocumentation[key] = item; - } - - XDocument output = BeginOutputDoc(); - - var globalContexts = _library.GetMarkedTypes(ScriptMemberType.GlobalContext); - foreach (var globalContext in globalContexts) - { - AddGlobalContextDescription(globalContext, output.Root); - } - - var contextTypes = _library.GetMarkedTypes(ScriptMemberType.Class); - foreach (var classType in contextTypes) - { - AddContextDescription(classType, output.Root); - } - - return output; - } - - public void CreateDocumentationJSON(DocumentBlocks textBlocks) - { - - var asmElement = _xmlDoc.Root.Element("assembly"); - if (asmElement == null) - throw new ArgumentException("Wrong XML doc format"); - - var libName = _library.Name; - var fileLibName = asmElement.Element("name").Value; - if (String.Compare(libName, fileLibName, true, System.Globalization.CultureInfo.InvariantCulture) != 0) - throw new ArgumentNullException(String.Format("Mismatch assembly names. Expected {0}, found in XML {1}", libName, fileLibName)); - - var members = _xmlDoc.Element("doc").Element("members").Elements(); - _memberDocumentation.Clear(); - foreach (var item in members) - { - string key = item.Attribute("name").Value; - _memberDocumentation[key] = item; - } - - var globalContexts = _library.GetMarkedTypes(ScriptMemberType.GlobalContext); - foreach (var globalContext in globalContexts) - { - AddGlobalContextDescriptionJSON(globalContext, textBlocks); - } - if (_library.Name == "ScriptEngine.HostedScript") - { - using (var layout = new StreamReader(ExtFiles.Get("BasicMethods.json"))) - { - var content = layout.ReadToEnd().Trim(); - textBlocks.TextGlobalContext.Append(content.Substring(1, content.Length - 2)); - } - } - - var contextTypes = _library.GetMarkedTypes(ScriptMemberType.Class); - foreach (var classType in contextTypes) - { - AddContextDescriptionJSON(classType, textBlocks); - } - - var systemEnums = _library.GetMarkedTypes(ScriptMemberType.SystemEnum); - foreach (var systemEnum in systemEnums) - { - AddEnumsDescriptionJSON(systemEnum, textBlocks, ScriptMemberType.SystemEnum); - } - var enums = _library.GetMarkedTypes(ScriptMemberType.EnumerationType); - foreach (var sysEnum in enums) - { - AddEnumsDescriptionJSON(sysEnum, textBlocks, ScriptMemberType.EnumerationType); - } - - - } - - private void AddEnumsDescriptionJSON(Type sysEnum, DocumentBlocks textBlocks, ScriptMemberType sysType) - { - var attrib = _library.GetMarkup(sysEnum, sysType); - - string name, alias; - GetNameAndAlias(attrib, sysEnum.Name, out name, out alias); - - var childElement = new XElement(name); - childElement.Add(new XElement("name", name)); - childElement.Add(new XElement("name_en", alias)); - - AppendXmlDocsJSON(childElement, "T:" + sysEnum.FullName); - - AddValues(sysEnum, childElement); - - var JSONNode = JSon.XmlToJSON(childElement.ToString()); - - textBlocks.TextEnumsDescription.Append(JSONNode.Substring(1, JSONNode.Length - 2) + ","); - } - - private void AddGlobalContextDescription(Type globalContext, XContainer xElement) - { - - var childElement = new XElement("global-context"); - - childElement.Add(new XAttribute("clr-name", globalContext.FullName)); - - var attrib = _library.GetMarkup(globalContext, ScriptMemberType.GlobalContext); - if (attrib == null) - return; - - string categoryName = null; - - try - { - if (attrib.NamedArguments != null) - { - var categoryMember = attrib.NamedArguments.First(x => x.MemberName == "Category"); - categoryName = (string)categoryMember.TypedValue.Value; - } - } - catch (InvalidOperationException) - { - return; - } - - if (categoryName != null) - childElement.Add(new XElement("category", categoryName)); - - AppendXmlDocs(childElement, "T:" + globalContext.FullName); - - AddProperties(globalContext, childElement); - AddMethods(globalContext, childElement); - - xElement.Add(childElement); - } - - private void AddContextDescription(Type classType, XContainer xElement) - { - var childElement = new XElement("context"); - - childElement.Add(new XAttribute("clr-name", classType.FullName)); - var attrib = _library.GetMarkup(classType, ScriptMemberType.Class); - - string name, alias; - GetNameAndAlias(attrib, classType.Name, out name, out alias); - - childElement.Add(new XElement("name", name)); - childElement.Add(new XElement("alias", alias)); - - AppendXmlDocs(childElement, "T:" + classType.FullName); - - AddProperties(classType, childElement); - AddMethods(classType, childElement); - AddConstructors(classType, childElement); - - xElement.Add(childElement); - } - - private void AddGlobalContextDescriptionJSON(Type globalContext, DocumentBlocks textBlocks) - { - - var attrib = _library.GetMarkup(globalContext, ScriptMemberType.GlobalContext); - if (attrib == null) - return; - - string categoryName = null; - - try - { - if (attrib.NamedArguments != null) - { - var categoryMember = attrib.NamedArguments.First(x => x.MemberName == "Category"); - categoryName = (string)categoryMember.TypedValue.Value; - var childElement = new XElement(categoryName.Replace(" ", "_")); - - AppendXmlDocsJSON(childElement, "T:" + globalContext.FullName); - - AddProperties(globalContext, childElement, "JSON"); - AddMethodsJSON(globalContext, childElement); - - if (!childElement.IsEmpty) - { - var JSONNode = JSon.XmlToJSON(childElement.ToString()); - var separatorPos = JSONNode.IndexOf(":", StringComparison.InvariantCulture); - JSONNode = JSONNode.Substring(0, separatorPos).Replace("_", " ") + JSONNode.Substring(separatorPos); - textBlocks.TextGlobalContext.Append(JSONNode.Substring(1, JSONNode.Length - 2) + ","); - } - } - } - catch (InvalidOperationException) - { - return; - } - } - - private void AddContextDescriptionJSON(Type classType, DocumentBlocks textBlocks) - { - var attrib = _library.GetMarkup(classType, ScriptMemberType.Class); - - string name, alias; - GetNameAndAlias(attrib, classType.Name, out name, out alias); - - var childElement = new XElement(name); - childElement.Add(new XElement("name", name)); - childElement.Add(new XElement("name_en", alias)); - - AppendXmlDocsJSON(childElement, "T:" + classType.FullName); - - AddProperties(classType, childElement, "JSON"); - AddMethodsJSON(classType, childElement); - AddConstructorsJSON(classType, childElement); - - var JSONNode = JSon.XmlToJSON(childElement.ToString()); - var separatorPos = JSONNode.IndexOf("\"constructors\":", StringComparison.InvariantCulture); - if (separatorPos > 0) - JSONNode = JSONNode.Substring(0, separatorPos) + JSONNode.Substring(separatorPos).Replace("_", " "); - - textBlocks.TextContextDescription.Append(JSONNode.Substring(1, JSONNode.Length - 2) + ","); - } - - private void AddMethods(Type classType, XContainer childElement) - { - var collection = new XElement("methods"); - var methodArray = classType.GetMethods(); - foreach (var meth in methodArray) - { - var attrib = _library.GetMarkup(meth, ScriptMemberType.Method); - if (attrib != null) - { - var fullName = classType.FullName + "." + MethodId(meth); - string name, alias; - GetNameAndAlias(attrib, meth.Name, out name, out alias); - var element = new XElement("method"); - element.Add(new XAttribute("clr-name", fullName)); - element.Add(new XElement("name", name)); - element.Add(new XElement("alias", alias)); - - AppendXmlDocs(element, "M:" + fullName); - - collection.Add(element); - } - } - - childElement.Add(collection); - } - - private void AddMethodsJSON(Type classType, XContainer childElement) - { - var collection = new XElement("methods"); - var methodArray = classType.GetMethods(); - foreach (var meth in methodArray) - { - var attrib = _library.GetMarkup(meth, ScriptMemberType.Method); - if (attrib != null) - { - var fullName = classType.FullName + "." + MethodId(meth); - string name, alias; - GetNameAndAlias(attrib, meth.Name, out name, out alias); - var element = new XElement(name); - element.Add(new XElement("name", name)); - element.Add(new XElement("name_en", alias)); - var returns = ""; - if (meth.ReturnType.FullName != "System.Void") - { - returns = SetRusNames(meth.ReturnType.FullName, false); - if (returns != "") returns = ": " + returns; - } - element.Add(new XElement("signature", "(" + MethodIdJSON(meth, false) + ")" + returns)); - AppendXmlDocsJSON(element, "M:" + fullName); - - collection.Add(element); - } - } - - if (!collection.IsEmpty) - childElement.Add(collection); - } - - private string MethodId(MethodBase meth) - { - var sb = new StringBuilder(); - sb.Append(meth.Name); - var methParams = meth.GetParameters(); - if (methParams.Length > 0) - { - sb.Append('('); - var paramInfos = methParams.Select(x => x.ParameterType).ToArray(); - string[] paramTypeNames = new string[paramInfos.Length]; - - for (int i = 0; i < paramInfos.Length; i++) - { - var info = paramInfos[i]; - if (info.GenericTypeArguments.Length > 0) - { - var genericBuilder = BuildStringGenericTypes(info); - - paramTypeNames[i] = genericBuilder.ToString(); - } - else - { - paramTypeNames[i] = info.FullName; - } - } - sb.Append(string.Join(",", paramTypeNames)); - sb.Append(')'); - } - return sb.ToString(); - } - - private StringBuilder BuildStringGenericTypes(Type info) - { - var matches = System.Text.RegularExpressions.Regex.Matches(info.FullName, @"([\w.]+)`\d|(\[([\w0-9.=]+)(?:,\s(?:[\w0-9.= ]+))*\]),?"); - - var genericBuilder = new StringBuilder(); - - if (matches.Count == 1) - { - return genericBuilder; - } - - genericBuilder.Append(matches[0].Groups[1].ToString()); - genericBuilder.Append('{'); - bool fst = true; - foreach (var capture in matches[1].Groups[3].Captures) - { - if (!fst) - genericBuilder.Append(", "); - - genericBuilder.Append(capture.ToString()); - fst = false; - } - genericBuilder.Append('}'); - - return genericBuilder; - - } - - private string MethodIdJSON(MethodInfo meth, bool addLink = true) - { - var sb = new StringBuilder(); - var methParams = meth.GetParameters(); - if (methParams.Length > 0) - { - var paramInfos = methParams.Select(x => x.ParameterType).ToArray(); - string[] paramTypeNames = new string[paramInfos.Length]; - - for (int i = 0; i < paramInfos.Length; i++) - { - var info = paramInfos[i]; - var MethcodArg = ""; - if (info.GenericTypeArguments.Length > 0) - { - var genericBuilder = BuildStringGenericTypes(info); - MethcodArg = genericBuilder.ToString(); - } - else - { - MethcodArg = info.FullName; - } - - MethcodArg = SetRusNames(MethcodArg, addLink); - var Optional = (methParams[i].IsOptional) ? "?" : ""; - paramTypeNames[i] = methParams[i].Name + Optional + ": " + MethcodArg; - } - sb.Append(string.Join(", ", paramTypeNames)); - } - return sb.ToString(); - } - - private void AddValues(Type classType, XContainer childElement) - { - var propElementCollection = new XElement("values"); - - var propArray = classType.GetProperties(); - foreach (var prop in propArray) - { - var attrib = _library.GetMarkup(prop, ScriptMemberType.EnumerationValue); - if (attrib != null) - { - string name, alias; - GetNameAndAlias(attrib, prop.Name, out name, out alias); - - var propElement = new XElement(name); - propElement.Add(new XElement("name", name)); - propElement.Add(new XElement("name_en", alias)); - - AppendXmlDocsJSON(propElement, "P:" + classType.FullName + "." + prop.Name); - propElementCollection.Add(propElement); - } - } - var fieldsArray = classType.GetFields(); - foreach (var field in fieldsArray) - { - var attrib = _library.GetMarkup(field, ScriptMemberType.EnumItem); - if (attrib != null) - { - string name, alias; - GetNameAndAlias(attrib, field.Name, out name, out alias); - - var propElement = new XElement(name); - propElement.Add(new XElement("name", name)); - propElement.Add(new XElement("name_en", alias)); - - AppendXmlDocsJSON(propElement, "P:" + classType.FullName + "." + field.Name); - propElementCollection.Add(propElement); - } - } - - if (!propElementCollection.IsEmpty) - childElement.Add(propElementCollection); - } - - private void AddProperties(Type classType, XContainer childElement, string mode = "") - { - var propElementCollection = new XElement("properties"); - - var propArray = classType.GetProperties(); - foreach (var prop in propArray) - { - var attrib = _library.GetMarkup(prop, ScriptMemberType.Property); - if (attrib != null) - { - XElement propElement; - if (mode == "JSON") - { - propElement = fillPropElementJSON(prop, attrib, classType); - } - else - { - propElement = fillPropElement(prop, attrib, classType); - } - propElementCollection.Add(propElement); - } - } - if (!propElementCollection.IsEmpty) - childElement.Add(propElementCollection); - } - - private XElement fillPropElement(System.Reflection.PropertyInfo prop, System.Reflection.CustomAttributeData attrib, Type classType) - { - var propElement = new XElement("property"); - string name, alias; - GetNameAndAlias(attrib, prop.Name, out name, out alias); - propElement.Add(new XAttribute("clr-name", classType.FullName + "." + prop.Name)); - propElement.Add(new XElement("name", name)); - propElement.Add(new XElement("alias", alias)); - - var access = findAccess(attrib, prop); - - propElement.Add(new XElement("readable", access["canRead"])); - propElement.Add(new XElement("writeable", access["canWrite"])); - - AppendXmlDocs(propElement, "P:" + classType.FullName + "." + prop.Name); - return propElement; - - } - - private Dictionary findAccess(System.Reflection.CustomAttributeData attrib, System.Reflection.PropertyInfo prop) - { - bool? canRead = null; - bool? canWrite = null; - - if (attrib.NamedArguments != null) - { - foreach (var attributeNamedArgument in attrib.NamedArguments) - { - if (attributeNamedArgument.MemberName == "CanRead") - { - canRead = (bool)attributeNamedArgument.TypedValue.Value; - } - - if (attributeNamedArgument.MemberName == "CanWrite") - { - canWrite = (bool)attributeNamedArgument.TypedValue.Value; - } - } - } - if (canRead == null) - canRead = prop.GetMethod != null; - - if (canWrite == null) - canWrite = prop.SetMethod != null; - - var result = new Dictionary(); - result.Add("canRead", canRead); - result.Add("canWrite", canWrite); - - return result; - - } - - private XElement fillPropElementJSON(System.Reflection.PropertyInfo prop, System.Reflection.CustomAttributeData attrib, Type classType) - { - string name, alias; - GetNameAndAlias(attrib, prop.Name, out name, out alias); - var propElement = new XElement(name); - propElement.Add(new XElement("name", name)); - propElement.Add(new XElement("name_en", alias)); - - var access = findAccess(attrib, prop); - - AppendXmlDocsJSON(propElement, "P:" + classType.FullName + "." + prop.Name); - buildAccessProperty(access["canRead"], access["canWrite"], propElement); - return propElement; - } - - private void buildAccessProperty(bool? canRead, bool? canWrite, XContainer propElement) - { - var access = ((bool)canRead && (bool)canWrite) ? "Чтение/Запись" : (bool)canRead ? "Чтение" : "Запись"; - propElement.Add(new XElement("access", access)); - } - - private void AddConstructors(Type classType, XContainer childElement) - { - - int itemsCount = 0; - var collection = new XElement("constructors"); - var methodArray = classType.GetMethods(BindingFlags.Static | BindingFlags.Public); - - foreach (var meth in methodArray) - { - var attrib = _library.GetMarkup(meth, ScriptMemberType.Constructor); - if (attrib != null) - { - var fullName = classType.FullName + "." + MethodId(meth); - var element = new XElement("ctor"); - element.Add(new XAttribute("clr-name", fullName)); - - var namedArgsName = attrib.NamedArguments.FirstOrDefault(x => x.MemberName == "Name"); - if (namedArgsName.MemberInfo == null) - { - element.Add(new XElement("name", "По умолчанию")); - } - else - { - var ctorName = (string)namedArgsName.TypedValue.Value; - if (ctorName == "") - continue; - element.Add(new XElement("name", ctorName)); - } - - AppendXmlDocs(element, "M:" + fullName); - collection.Add(element); - itemsCount++; - } - } - if (itemsCount > 0) - childElement.Add(collection); - } - - private void AddConstructorsJSON(Type classType, XContainer childElement) - { - - int itemsCount = 0; - var collection = new XElement("constructors"); - var methodArray = classType.GetMethods(BindingFlags.Static | BindingFlags.Public); - - foreach (var meth in methodArray) - { - var attrib = _library.GetMarkup(meth, ScriptMemberType.Constructor); - if (attrib != null) - { - var fullName = classType.FullName + "." + MethodId(meth); - var ctorName = "По умолчанию"; - var namedArgsName = attrib.NamedArguments.FirstOrDefault(x => x.MemberName == "Name"); - if (namedArgsName.MemberInfo != null) - { - ctorName = (string)namedArgsName.TypedValue.Value; - if (ctorName == "") - continue; - } - var element = new XElement(ctorName.Replace(" ", "_")); - element.Add(new XElement("name", ctorName)); - element.Add(new XElement("signature", "(" + MethodIdJSON(meth, false) + ")")); - AppendXmlDocsJSON(element, "M:" + fullName); - collection.Add(element); - itemsCount++; - } - } - if (itemsCount > 0) - childElement.Add(collection); - } - - private void AppendXmlDocs(XContainer element, string memberName) - { - XElement xDoc; - if (_memberDocumentation.TryGetValue(memberName, out xDoc)) - { - var summary = xDoc.Element("summary"); - if (summary != null) - { - var descr = new XElement("description"); - ProcessChildNodes(descr, summary); - element.Add(descr); - } - - // parameters - var paramsList = xDoc.Elements("param"); - foreach (var paramItem in paramsList) - { - var param = new XElement("param"); - ProcessChildNodes(param, paramItem); - element.Add(param); - } - - // returns - var returnNode = xDoc.Element("returns"); - if (returnNode != null) - { - var node = new XElement("returns"); - ProcessChildNodes(node, returnNode); - element.Add(node); - } - - // other - var elems = xDoc.Elements(); - foreach (var item in elems) - { - if (item.Name == "summary" || item.Name == "param" || item.Name == "returns") - continue; - - var node = new XElement(item.Name); - ProcessChildNodes(node, item); - element.Add(node); - } - } - } - - private void AppendXmlDocsJSON(XContainer element, string memberName) - { - XElement xDoc; - if (_memberDocumentation.TryGetValue(memberName, out xDoc)) - { - foreach (var item in xDoc.Elements()) - { - if (item.Name == "param") - continue; - - var nodeName = item.Name.ToString().Replace("summary", "description"); - var xmlNode = new XElement(nodeName); - ProcessChildNodesJSON(xmlNode, item); - xmlNode.Value = xmlNode.Value.Replace("\r\n", " "); - if (xmlNode.Value != "") - element.Add(xmlNode); - } - - var paramsList = xDoc.Elements("param"); - var param = new XElement("params"); - foreach (var paramItem in paramsList) - { - ProcessChildNodesJSON(param, paramItem); - } - if (!param.IsEmpty) - element.Add(param); - - } - } - - private void ProcessChildNodes(XContainer dest, XElement source) - { - var nodes = source.Nodes(); - StringBuilder textContent = new StringBuilder(); - foreach (var node in nodes) - { - if (node.NodeType == System.Xml.XmlNodeType.Text) - { - textContent.Append(CollapseWhiteSpace(node.ToString())); - } - else if (node.NodeType == System.Xml.XmlNodeType.Element) - { - var newElem = new XElement(((XElement)node).Name); - ProcessChildNodes(newElem, (XElement)node); - dest.Add(newElem); - } - } - - foreach (var attr in source.Attributes()) - { - dest.Add(attr); - } - - if (textContent.Length > 0) - dest.Add(textContent.ToString()); - } - - private void ProcessChildNodesJSON(XContainer dest, XElement source) - { - var nodes = source.Nodes(); - StringBuilder textContent = new StringBuilder(); - foreach (var node in nodes) - { - if (node.NodeType == System.Xml.XmlNodeType.Text) - { - textContent.Append(new Regex("^\\s{12,13}", RegexOptions.Multiline).Replace(node.ToString(), "").Trim()); - } - else if (node.NodeType == System.Xml.XmlNodeType.Element) - { - if (((XElement)node).Name == "code"){ - textContent.Append(node.ToString()); - } else { - var newElem = new XElement(((XElement)node).Name); - ProcessChildNodes(newElem, (XElement)node); - dest.Add(newElem); - } - } - } - - if (((XElement)dest).Name != "params" && ((XElement)dest).Name != "returns") - { - foreach (var attr in source.Attributes()) - { - dest.Add(attr); - } - } - - if (textContent.Length > 0) { - if (((XElement)dest).Name == "example") - dest.Add(textContent.ToString().Replace("\r\n", "
").Replace("\n", " ")); - else if (((XElement)dest).Name == "params") - dest.Add(new XElement(source.FirstAttribute.Value, textContent.ToString().Replace("\r\n", " ").Replace("\n", " "))); - else - dest.Add(textContent.ToString().Replace("\r\n", " ").Replace("\n", " ")); - } - - } - - private string CollapseWhiteSpace(string p) - { - if (p == String.Empty) - return ""; - - StringBuilder sb = new StringBuilder(); - using (var sr = new StringReader(p)) - { - string line = null; - do - { - line = sr.ReadLine(); - if (!String.IsNullOrWhiteSpace(line)) - sb.AppendLine(line.Trim()); - else if (line != null && line.Length > 0) - sb.AppendLine(); - - } while (line != null); - } - - return sb.ToString(); - } - - private XDocument BeginOutputDoc() - { - XDocument result = new XDocument(); - result.Add(new XElement("contexts")); - - return result; - } - } -} diff --git a/src/OneScriptDocumenter/AssemblyJSON.cs b/src/OneScriptDocumenter/AssemblyJSON.cs deleted file mode 100644 index 415e30b38..000000000 --- a/src/OneScriptDocumenter/AssemblyJSON.cs +++ /dev/null @@ -1,155 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Text; -using System.Xml; - -namespace OneScriptDocumenter -{ - public static class JSon - { - public static string XmlToJSON(string xml) - { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(xml); - - return XmlToJSON(doc); - } - public static string XmlToJSON(XmlDocument xmlDoc) - { - StringBuilder sbJSON = new StringBuilder(); - sbJSON.Append("{ "); - XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true); - sbJSON.Append("}"); - return sbJSON.ToString(); - } - - // XmlToJSONnode: Output an XmlElement, possibly as part of a higher array - private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName) - { - if (showNodeName) - sbJSON.Append("\"" + SafeJSON(node.Name) + "\": "); - sbJSON.Append("{"); - // Build a sorted list of key-value pairs - // where key is case-sensitive nodeName - // value is an ArrayList of string or XmlElement - // so that we know whether the nodeName is an array or not. - Dictionary childNodeNames = new Dictionary(); - - // Add in all node attributes - if (node.Attributes != null) - foreach (XmlAttribute attr in node.Attributes) - StoreChildNode(childNodeNames, attr.Name, attr.InnerText); - - // Add in all nodes - foreach (XmlNode cnode in node.ChildNodes) - { - if (cnode is XmlText) - StoreChildNode(childNodeNames, "value", cnode.InnerText); - else if (cnode is XmlElement) - StoreChildNode(childNodeNames, cnode.Name, cnode); - } - - // Now output all stored info - foreach (string childname in childNodeNames.Keys) - { - List alChild = (List)childNodeNames[childname]; - if (alChild.Count == 1) - OutputNode(childname, alChild[0], sbJSON, true); - else - { - sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ "); - foreach (object Child in alChild) - OutputNode(childname, Child, sbJSON, false); - sbJSON.Remove(sbJSON.Length - 2, 2); - sbJSON.Append(" ], "); - } - } - sbJSON.Remove(sbJSON.Length - 2, 2); - sbJSON.Append(" }"); - } - - // StoreChildNode: Store data associated with each nodeName - // so that we know whether the nodeName is an array or not. - private static void StoreChildNode(Dictionary childNodeNames, string nodeName, object nodeValue) - { - // Pre-process contraction of XmlElement-s - if (nodeValue is XmlElement) - { - // Convert into "aa":null - // xx into "aa":"xx" - XmlNode cnode = (XmlNode)nodeValue; - if (cnode.Attributes.Count == 0) - { - XmlNodeList children = cnode.ChildNodes; - if (children.Count == 0) - nodeValue = null; - else if (children.Count == 1 && (children[0] is XmlText)) - nodeValue = ((XmlText)(children[0])).InnerText; - } - } - // Add nodeValue to ArrayList associated with each nodeName - // If nodeName doesn't exist then add it - List ValuesAL; - - if (childNodeNames.ContainsKey(nodeName)) - { - ValuesAL = (List)childNodeNames[nodeName]; - } - else - { - ValuesAL = new List(); - childNodeNames[nodeName] = ValuesAL; - } - ValuesAL.Add(nodeValue); - } - - private static void OutputNode(string childname, object alChild, StringBuilder sbJSON, bool showNodeName) - { - var stringChild = alChild as string; - if (alChild == null) - { - if (showNodeName) - sbJSON.Append("\"" + SafeJSON(childname) + "\": "); - sbJSON.Append("null"); - } - else if (stringChild != null) - { - if (showNodeName) - sbJSON.Append("\"" + SafeJSON(childname) + "\": "); - string sChild = (string)alChild; - sChild = sChild.Trim(); - sbJSON.Append("\"" + SafeJSON(sChild) + "\""); - } - else - XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName); - sbJSON.Append(", "); - } - - // Make a string safe for JSON - private static string SafeJSON(string sIn) - { - StringBuilder sbOut = new StringBuilder(sIn.Length); - foreach (char ch in sIn) - { - if (Char.IsControl(ch) || ch == '\'') - { - int ich = (int)ch; - sbOut.Append(@"\u" + ich.ToString("x4")); - continue; - } - else if (ch == '\"' || ch == '\\') - { - sbOut.Append('\\'); - } - sbOut.Append(ch); - } - return sbOut.ToString(); - } - } -} diff --git a/src/OneScriptDocumenter/AssemblyLoader.cs b/src/OneScriptDocumenter/AssemblyLoader.cs deleted file mode 100644 index c04d80db1..000000000 --- a/src/OneScriptDocumenter/AssemblyLoader.cs +++ /dev/null @@ -1,123 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; - -namespace OneScriptDocumenter -{ - class AssemblyLoader - { - readonly Type _classAttributeType; - readonly Type _methodAttributeType; - readonly Type _propAttributeType; - readonly Type _constructorAttributeType; - readonly Type _globalContextAttributeType; - readonly Type _systemEnumAttribute; - readonly Type _enumerationTypeAttribute; - readonly Type _systemValue; - readonly Type _enumValue; - - readonly string _baseDirectory; - - public AssemblyLoader(string baseDirectory) - { - _baseDirectory = baseDirectory; - - var engineFile = Path.Combine(_baseDirectory, "ScriptEngine.dll"); - - if (!File.Exists(engineFile)) - throw new ArgumentException("Base directory doesn't contain library ScriptEngine.dll"); - - var scriptEngineLib = Assembly.ReflectionOnlyLoadFrom(engineFile); - - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (object sender, ResolveEventArgs args) => - { - var data = args.Name.Split(','); - var filename = Path.Combine(_baseDirectory, data[0] + ".dll"); - var asmLoaded = Assembly.ReflectionOnlyLoadFrom(filename); - - if (asmLoaded == null) - asmLoaded = Assembly.ReflectionOnlyLoad(args.Name); - - return asmLoaded; - }; - - _classAttributeType = scriptEngineLib.GetType("ScriptEngine.Machine.Contexts.ContextClassAttribute", true); - _globalContextAttributeType = scriptEngineLib.GetType("ScriptEngine.Machine.Contexts.GlobalContextAttribute", true); - _methodAttributeType = scriptEngineLib.GetType("ScriptEngine.Machine.Contexts.ContextMethodAttribute", true); - _propAttributeType = scriptEngineLib.GetType("ScriptEngine.Machine.Contexts.ContextPropertyAttribute", true); - _constructorAttributeType = scriptEngineLib.GetType("ScriptEngine.Machine.Contexts.ScriptConstructorAttribute", true); - _systemEnumAttribute = scriptEngineLib.GetType("ScriptEngine.Machine.Contexts.SystemEnumAttribute", true); - _systemValue = scriptEngineLib.GetType("ScriptEngine.Machine.Contexts.EnumValueAttribute", true); - _enumerationTypeAttribute = scriptEngineLib.GetType("ScriptEngine.EnumerationTypeAttribute", true); - _enumValue = scriptEngineLib.GetType("ScriptEngine.EnumItemAttribute", true); - - foreach (var name in new string[] { "ScriptEngine.HostedScript", "DotNetZip", "Newtonsoft.Json" }) - { - var libFile = Path.Combine(_baseDirectory, name + ".dll"); - if (File.Exists(libFile)) - { - Assembly.ReflectionOnlyLoadFrom(libFile); - } - } - - } - - public LoadedAssembly Load(string assemblyName) - { - var library = Assembly.ReflectionOnlyLoadFrom(Path.Combine(_baseDirectory, assemblyName)); - - var scriptEngineLibs = library.GetReferencedAssemblies() - .Where(x => x.Name != "ScriptEngine"); - - foreach (var lib in scriptEngineLibs) - { - try - { - Assembly.ReflectionOnlyLoad(lib.FullName); - } - catch (FileNotFoundException) - { - Assembly.ReflectionOnlyLoadFrom(Path.Combine(_baseDirectory, lib.Name + ".dll")); - } - } - - return new LoadedAssembly(library, this); - } - - public Type MemberTypeToAttributeType(ScriptMemberType memberType) - { - switch (memberType) - { - case ScriptMemberType.Class: - return _classAttributeType; - case ScriptMemberType.Constructor: - return _constructorAttributeType; - case ScriptMemberType.GlobalContext: - return _globalContextAttributeType; - case ScriptMemberType.Method: - return _methodAttributeType; - case ScriptMemberType.Property: - return _propAttributeType; - case ScriptMemberType.SystemEnum: - return _systemEnumAttribute; - case ScriptMemberType.EnumerationType: - return _enumerationTypeAttribute; - case ScriptMemberType.EnumerationValue: - return _systemValue; - case ScriptMemberType.EnumItem: - return _enumValue; - default: - throw new ArgumentException("Unsupported member type"); - } - } - - } -} diff --git a/src/OneScriptDocumenter/BasicMethods.json b/src/OneScriptDocumenter/BasicMethods.json index 9bd0232e5..19acb6fd5 100644 --- a/src/OneScriptDocumenter/BasicMethods.json +++ b/src/OneScriptDocumenter/BasicMethods.json @@ -126,6 +126,11 @@ "name_en": "BegOfYear", "signature": "(Дата: Дата): Дата" }, + "НачалоНедели": { + "name": "НачалоНедели", + "name_en": "BegOfWeek", + "signature": "(Дата: Дата): Дата" + }, "НачалоМесяца": { "name": "НачалоМесяца", "name_en": "BegOfMonth", @@ -181,6 +186,11 @@ "name_en": "EndOfQuarter", "signature": "(Дата: Дата): Дата" }, + "КонецНедели": { + "name": "КонецНедели", + "name_en": "EndOfWeek", + "signature": "(Дата: Дата): Дата" + }, "НеделяГода": { "name": "НеделяГода", "name_en": "WeekOfYear", diff --git a/src/OneScriptDocumenter/Cli/DocumentWriter.cs b/src/OneScriptDocumenter/Cli/DocumentWriter.cs new file mode 100644 index 000000000..76e8d4395 --- /dev/null +++ b/src/OneScriptDocumenter/Cli/DocumentWriter.cs @@ -0,0 +1,230 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using OneScript.Localization; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Secondary; + +namespace OneScriptDocumenter.Cli +{ + public class DocumentWriter : BaseModelVisitor + { + private readonly string _baseDir; + private MarkdownWriter _writer; + + public DocumentWriter(string baseDir) + { + _baseDir = baseDir; + } + + protected override void VisitDocument(IDocument document) + { + CreateDocumentFile(_baseDir, document); + } + + private void CreateDocumentFile(string outputDir, IDocument document) + { + var fileName = ReferenceFactory.GetBslNameForAnnotatedObject(document.Owner) + ".md"; + using var writer = MarkdownWriter.OpenFile(Path.Combine(outputDir, fileName)); + + switch (document) + { + case GlobalContextModel globalContext: + WriteGlobalContext(writer, globalContext); + break; + case ClassModel classModel: + WriteClass(writer, classModel); + break; + case EnumModel enumModel: + WriteEnum(writer, enumModel); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void WriteEnum(MarkdownWriter writer, EnumModel model) + { + WriteOutlineOptions(writer); + + writer.Header1(BiString(model.Name)); + + writer.Paragraph(model.Description); + + writer.Header2("Элементы"); + + foreach (var itemModel in model.Items) + { + writer.Header3(BiString(itemModel.Name)); + writer.Paragraph(itemModel.Description); + } + } + + private void WriteClass(MarkdownWriter writer, ClassModel model) + { + WriteOutlineOptions(writer); + + writer.Header1(BiString(model.Name)); + + writer.Paragraph(model.Description); + + WriteProperties(writer, model.Properties); + WriteMethods(writer, model.Methods); + WriteConstructors(writer, model.Constructors); + } + + private void WriteGlobalContext(MarkdownWriter writer, GlobalContextModel model) + { + WriteOutlineOptions(writer); + + writer.Header1(model.Title); + + writer.Paragraph(model.Description); + + WriteProperties(writer, model.Properties); + WriteMethods(writer, model.Methods); + } + + private void WriteProperties(MarkdownWriter writer, IList propModels) + { + if (propModels == null || propModels.Count == 0) + return; + + writer.Header2("Свойства"); + foreach (var propModel in propModels) + { + writer.Header3(BiString(propModel.Name)); + + writer.BeginList(true); + writer.ListItem("Чтение: " + (propModel.CanRead ? "Да" : "Нет")); + writer.ListItem("Запись: " + (propModel.CanWrite ? "Да" : "Нет")); + writer.EndList(); + + if (!string.IsNullOrEmpty(propModel.Returns)) + writer.Paragraph("**Тип значения:** " + propModel.Returns); + + writer.Paragraph(propModel.Description); + + if (!string.IsNullOrEmpty(propModel.Example)) + { + writer.Header4("Пример"); + writer.WriteCode(propModel.Example); + } + } + } + + private void WriteMethods(MarkdownWriter writer, IList methModels) + { + if (methModels == null || methModels.Count == 0) + return; + + writer.Header2("Методы"); + foreach (var model in methModels) + { + writer.Header3(BiString(model.Name)); + + writer.Paragraph(model.Description); + + if (model.Parameters != null && model.Parameters.Count > 0) + { + writer.Header4("Параметры"); + + writer.BeginList(true); + foreach (var parameterModel in model.Parameters) + { + var itemBuilder = new StringBuilder(); + itemBuilder.Append($"**{parameterModel.Name}**: {parameterModel.Description}"); + if (parameterModel.IsOptional) + { + itemBuilder.Append(" *Необязательный*. "); + + if (!string.IsNullOrEmpty(parameterModel.DefaultValue)) + itemBuilder.Append($"Значение по умолчанию: {parameterModel.DefaultValue}"); + } + writer.ListItem(itemBuilder.ToString()); + } + writer.EndList(); + } + + if (!string.IsNullOrEmpty(model.ReturnTypeDocumentation)) + { + writer.Header4("Возвращаемое значение"); + writer.Paragraph(model.ReturnTypeDocumentation); + } + + if (!string.IsNullOrEmpty(model.Example)) + { + writer.Header4("Пример"); + writer.WriteCode(model.Example); + } + } + } + + private void WriteConstructors(MarkdownWriter writer, IList methModels) + { + if (methModels == null || methModels.Count == 0) + return; + + writer.Header2("Конструкторы"); + foreach (var model in methModels) + { + writer.Header3(model.Title); + + writer.Paragraph(model.Description); + + if (model.Parameters != null && model.Parameters.Count > 0) + { + writer.Header4("Параметры"); + + writer.BeginList(true); + foreach (var parameterModel in model.Parameters) + { + var itemBuilder = new StringBuilder(); + itemBuilder.Append($"**{parameterModel.Name}**: {parameterModel.Description}"); + if (parameterModel.IsOptional) + { + itemBuilder.Append(" *Необязательный*. "); + if (!string.IsNullOrEmpty(parameterModel.DefaultValue)) + itemBuilder.Append($"Значение по умолчанию: {parameterModel.DefaultValue}"); + } + writer.ListItem(itemBuilder.ToString()); + } + writer.EndList(); + } + + if (!string.IsNullOrEmpty(model.ReturnTypeDocumentation)) + { + writer.Header4("Возвращаемое значение"); + writer.Paragraph(model.ReturnTypeDocumentation); + } + + if (!string.IsNullOrEmpty(model.Example)) + { + writer.Header4("Пример"); + writer.WriteCode(model.Example); + } + } + } + + + private static void WriteOutlineOptions(MarkdownWriter writer) + { + writer.Raw("---\n" + + "outline: [2, 3]\n" + + "---\n"); + } + + private static string BiString(BilingualString bi) => + bi.English != null ? + $"{bi.Russian} / {bi.English}" + : bi.Russian; + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/GeneratorOptions.cs b/src/OneScriptDocumenter/Cli/GeneratorOptions.cs new file mode 100644 index 000000000..ecf3f044d --- /dev/null +++ b/src/OneScriptDocumenter/Cli/GeneratorOptions.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +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 CommandLine; + +namespace OneScriptDocumenter.Cli +{ + public class GeneratorOptions + { + [Value(0, Min = 1, HelpText = "Список dll файлов по которым создается документация")] + public IEnumerable AssemblyFiles { get; set; } + + [Option('j', "json", HelpText = "Генерировать документацию в виде json-файла")] + public string JsonFile { get; set; } + + [Option('m', "markdown", HelpText = "Выходной каталог документации в виде набора md-файлов")] + public string MarkdownDir { get; set; } + + [Option('t', "toc", HelpText = "Файл оглавления")] + public string TocFile { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/JsonGenerator.cs b/src/OneScriptDocumenter/Cli/JsonGenerator.cs new file mode 100644 index 000000000..c5e3e6fee --- /dev/null +++ b/src/OneScriptDocumenter/Cli/JsonGenerator.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.IO; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Unicode; +using OneScriptDocumenter.Model; + +namespace OneScriptDocumenter.Cli +{ + public class JsonGenerator + { + private readonly DocumentationModel _documentation; + + public JsonGenerator(DocumentationModel documentation) + { + _documentation = documentation; + } + + public void WriteFile(string outputFile) + { + ConsoleLogger.Info("Generating JSON output"); + + using var file = new FileStream(outputFile, FileMode.Create); + + JsonSerializer.Serialize(file, _documentation, new JsonSerializerOptions + { + Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic), + Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }, + WriteIndented = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + + IgnoreReadOnlyProperties = false + }); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/MainIndexWriter.cs b/src/OneScriptDocumenter/Cli/MainIndexWriter.cs new file mode 100644 index 000000000..4f6eb8d29 --- /dev/null +++ b/src/OneScriptDocumenter/Cli/MainIndexWriter.cs @@ -0,0 +1,47 @@ +/*---------------------------------------------------------- +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.IO; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Secondary; + +namespace OneScriptDocumenter.Cli +{ + public class MainIndexWriter : TocSinglePageWriter + { + private int _level = 0; + + public MainIndexWriter(string outputDir, ReferenceFactory referenceFactory) + : base(Path.Combine(outputDir, "index.md"), referenceFactory) + { + } + + public override void VisitModel(DocumentationModel model) + { + _writer.Header1("Оглавление"); + base.VisitModel(model); + Dispose(); + } + + protected override void VisitSyntaxGroup(SyntaxGroup model) + { + if (_level == 0) + { + _writer.Header2(model.Title); + _level++; + base.VisitChildItems(model); + _level--; + } + else + { + _level++; + base.VisitSyntaxGroup(model); + _level--; + } + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/MarkdownGenerator.cs b/src/OneScriptDocumenter/Cli/MarkdownGenerator.cs new file mode 100644 index 000000000..98eae77cb --- /dev/null +++ b/src/OneScriptDocumenter/Cli/MarkdownGenerator.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +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.IO; +using OneScript.Commons; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Secondary; + +namespace OneScriptDocumenter.Cli +{ + public class MarkdownGenerator + { + private readonly DocumentationModel _documentation; + private readonly ReferenceFactory _referenceFactory; + + public MarkdownGenerator(DocumentationModel documentation, ReferenceFactory referenceFactory) + { + _documentation = documentation; + _referenceFactory = referenceFactory; + } + + public void Write(string outputDir) + { + ConsoleLogger.Info("Generating markdown output"); + + var walkers = new BaseModelVisitor[] + { + new MainIndexWriter(outputDir, _referenceFactory), + new TocPagesGenerator(outputDir, _referenceFactory), + new DocumentWriter(outputDir), + new VitepressSidebarGenerator(Path.Combine(outputDir, "vitepress-toc.json"), _referenceFactory) + }; + + walkers.ForEach(w => w.VisitModel(_documentation)); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/MarkdownWriter.cs b/src/OneScriptDocumenter/Cli/MarkdownWriter.cs new file mode 100644 index 000000000..a0b6f4d7b --- /dev/null +++ b/src/OneScriptDocumenter/Cli/MarkdownWriter.cs @@ -0,0 +1,114 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; + +namespace OneScriptDocumenter.Cli +{ + public class MarkdownWriter : IDisposable + { + private const int IndentSize = 4; + + private readonly TextWriter _output; + private int _currentListLevel = 0; + + public MarkdownWriter(TextWriter output) + { + _output = output; + } + + public static MarkdownWriter OpenFile(string filePath) + { + return new MarkdownWriter(new StreamWriter(filePath)); + } + + public void Raw(string rawData) + { + _output.Write(rawData); + } + + public void Header1(string text) + { + _output.WriteLine(); + _output.WriteLine($"# {text}"); + _output.WriteLine(); + } + + public void Header2(string text) + { + _output.WriteLine(); + _output.WriteLine($"## {text}"); + _output.WriteLine(); + } + + public void Header3(string text) + { + _output.WriteLine(); + _output.WriteLine($"### {text}"); + _output.WriteLine(); + } + + public void Header4(string text) + { + _output.WriteLine(); + _output.WriteLine($"#### {text}"); + _output.WriteLine(); + } + + public void Paragraph(string text) + { + if (text == null) + return; + + _output.WriteLine(); + _output.WriteLine(text); + _output.WriteLine(); + } + + public void BeginList(bool resetLevel = false) + { + if (resetLevel) + _currentListLevel = 0; + + _currentListLevel++; + } + + public void EndList() + { + _currentListLevel--; + if (_currentListLevel < 0) + _currentListLevel = 0; + } + + public void ListItem(string text) + { + if (_currentListLevel == 0) + BeginList(); + + var indent = new string(' ', (_currentListLevel - 1) * IndentSize); + + _output.Write(indent); + _output.WriteLine("* " + text); + } + + public void Dispose() + { + _output.Flush(); + _output?.Dispose(); + } + + public void WriteCode(string code) + { + _output.WriteLine(); + _output.WriteLine("```bsl"); + _output.WriteLine(code); + _output.WriteLine("```"); + _output.WriteLine(); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/TocPagesGenerator.cs b/src/OneScriptDocumenter/Cli/TocPagesGenerator.cs new file mode 100644 index 000000000..4b326bc75 --- /dev/null +++ b/src/OneScriptDocumenter/Cli/TocPagesGenerator.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +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.IO; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Secondary; + +namespace OneScriptDocumenter.Cli +{ + public class TocPagesGenerator : BaseModelVisitor + { + private readonly string _baseDir; + private readonly ReferenceFactory _referenceFactory; + + public TocPagesGenerator(string baseDir, ReferenceFactory referenceFactory) + { + _baseDir = baseDir; + _referenceFactory = referenceFactory; + } + + protected override void VisitSyntaxGroup(SyntaxGroup model) + { + if (model.Page != null) + { + using var tocWriter = new TocSinglePageWriter(Path.Combine(_baseDir, model.Page + ".md"), _referenceFactory); + tocWriter.Visit(model); + } + + VisitChildItems(model); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/TocSinglePageWriter.cs b/src/OneScriptDocumenter/Cli/TocSinglePageWriter.cs new file mode 100644 index 000000000..68fcb4a93 --- /dev/null +++ b/src/OneScriptDocumenter/Cli/TocSinglePageWriter.cs @@ -0,0 +1,68 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Secondary; + +namespace OneScriptDocumenter.Cli +{ + public class TocSinglePageWriter : BaseModelVisitor, IDisposable + { + private readonly ReferenceFactory _referenceFactory; + protected readonly MarkdownWriter _writer; + + private int _level = 0; + + public TocSinglePageWriter(string filename, ReferenceFactory referenceFactory) + { + _referenceFactory = referenceFactory; + _writer = MarkdownWriter.OpenFile(filename); + } + + public void Visit(SyntaxGroup model) + { + _writer.Header1(model.Title); + + VisitChildItems(model); + } + + protected override void VisitChildItems(SyntaxGroup group) + { + _writer.BeginList(); + base.VisitChildItems(group); + _writer.EndList(); + } + + protected override void VisitSyntaxGroup(SyntaxGroup model) + { + if (model.Page != null) + { + var linkTarget = _referenceFactory.BaseUrl + model.Page; + _writer.ListItem(MarkdownReferenceResolver.MarkdownLink(linkTarget, model.Title)); + } + else if (model.Document == null) + { + // Это какая-то битая ссылка из toc + _writer.ListItem(MarkdownReferenceResolver.MarkdownLink("", model.Title)); + } + + base.VisitSyntaxGroup(model); + } + + protected override void VisitDocument(IDocument document) + { + var linkTarget = _referenceFactory.CreateReference(document.Owner); + _writer.ListItem(MarkdownReferenceResolver.MarkdownLink(linkTarget, document.Title)); + } + + public void Dispose() + { + _writer.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Cli/VitepressSidebarGenerator.cs b/src/OneScriptDocumenter/Cli/VitepressSidebarGenerator.cs new file mode 100644 index 000000000..01e6d2919 --- /dev/null +++ b/src/OneScriptDocumenter/Cli/VitepressSidebarGenerator.cs @@ -0,0 +1,93 @@ +/*---------------------------------------------------------- +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.IO; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Secondary; + +namespace OneScriptDocumenter.Cli +{ + public class VitepressSidebarGenerator : BaseModelVisitor + { + private readonly string _outputFile; + private readonly ReferenceFactory _referenceFactory; + + private int _level = 0; + + private Utf8JsonWriter _writer; + private const string othersGroupName = "Прочее"; + + public VitepressSidebarGenerator(string outputFile, ReferenceFactory referenceFactory) + { + _outputFile = outputFile; + _referenceFactory = referenceFactory; + } + + public override void VisitModel(DocumentationModel model) + { + using var jsonOutput = new FileStream(_outputFile, FileMode.Create); + _writer = new Utf8JsonWriter(jsonOutput, new JsonWriterOptions + { + Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic), + Indented = true + }); + + _writer.WriteStartArray(); + base.VisitModel(model); + _writer.WriteEndArray(); + _writer.Dispose(); + } + + protected override void VisitSyntaxGroup(SyntaxGroup model) + { + _writer.WriteStartObject(); + + _writer.WriteString("text", model.Title); + + var link = GenerateLink(model); + if (link != null) + { + _writer.WriteString("link", link); + } + + var hasChildren = model?.Items.Count > 0 && model.Title != othersGroupName; + if (_level >= 1 && hasChildren) + { + _writer.WriteBoolean("collapsed", true); + } + + if (hasChildren) + { + _level++; + _writer.WritePropertyName("items"); + _writer.WriteStartArray(); + base.VisitChildItems(model); + _writer.WriteEndArray(); + _level--; + } + + _writer.WriteEndObject(); + } + + private string GenerateLink(SyntaxGroup model) + { + if (model.Page != null) + { + return _referenceFactory.BaseUrl + model.Page; + } + else if (model.Document != null) + { + return _referenceFactory.CreateReference(model.Document.Owner); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/CommandLineArgs.cs b/src/OneScriptDocumenter/CommandLineArgs.cs deleted file mode 100644 index 56b99b579..000000000 --- a/src/OneScriptDocumenter/CommandLineArgs.cs +++ /dev/null @@ -1,27 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -namespace OneScriptDocumenter -{ - class CommandLineArgs - { - readonly string[] _args; - int _index = 0; - - public CommandLineArgs(string[] argsArray) - { - _args = argsArray; - } - - public string Next() - { - if (_index >= _args.Length) - return null; - - return _args[_index++]; - } - } -} diff --git a/src/OneScriptDocumenter/ConsoleLogger.cs b/src/OneScriptDocumenter/ConsoleLogger.cs new file mode 100644 index 000000000..ecbe1e744 --- /dev/null +++ b/src/OneScriptDocumenter/ConsoleLogger.cs @@ -0,0 +1,47 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter +{ + public static class ConsoleLogger + { + public static void Info(string content) + { + Console.WriteLine(content); + } + + public static void Warning(string content) + { + using var yellow = new ColorContext(ConsoleColor.Yellow); + Console.Error.WriteLine(content); + } + + public static void Error(string content) + { + using var yellow = new ColorContext(ConsoleColor.Red); + Console.Error.WriteLine(content); + } + + private class ColorContext : IDisposable + { + private readonly ConsoleColor _oldColor; + + public ColorContext(ConsoleColor newColor) + { + _oldColor = Console.ForegroundColor; + Console.ForegroundColor = newColor; + } + + public void Dispose() + { + Console.ForegroundColor = _oldColor; + } + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Documenter.cs b/src/OneScriptDocumenter/Documenter.cs deleted file mode 100644 index 1ea874941..000000000 --- a/src/OneScriptDocumenter/Documenter.cs +++ /dev/null @@ -1,279 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Xml.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace OneScriptDocumenter -{ - class DocumentBlocks - { - public StringBuilder TextGlobalContext; - public StringBuilder TextContextDescription; - public StringBuilder TextEnumsDescription; - - public DocumentBlocks() - { - TextGlobalContext = new StringBuilder(); - TextContextDescription = new StringBuilder(); - TextEnumsDescription = new StringBuilder(); - } - } - - class Documenter - { - internal XDocument CreateDocumentation(List assemblies) - { - XDocument result = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("oscript-docs")); - - foreach (var assembly in assemblies) - { - var name = Path.GetFileNameWithoutExtension(assembly); - var xmlName = Path.Combine(Path.GetDirectoryName(assembly), name + ".xml"); - if (!File.Exists(xmlName)) - { - Console.WriteLine("Missing xml-doc: {0}", xmlName); - continue; - } - - Console.WriteLine("Processing: {0}", name); - - var docMaker = new AssemblyDocumenter(assembly, xmlName); - var asmDoc = docMaker.CreateDocumentation(); - result.Root.Add(new XElement("assembly", - new XAttribute("name", name), - asmDoc.Root)); - } - Console.WriteLine("Done"); - return result; - } - internal string CreateDocumentationJSON(string pathOutput, List assemblies) - { - using (StreamWriter sbJSON = new StreamWriter(pathOutput)) - { - - DocumentBlocks textBlocks = new DocumentBlocks(); - bool isOscriptStd = false; - - var hostedScript = new AssemblyDocumenter(Path.Combine(Path.GetDirectoryName(assemblies[0]), "ScriptEngine.HostedScript.dll")); - hostedScript.FillTypesDictionary(); - - foreach (var assembly in assemblies) - { - var name = Path.GetFileNameWithoutExtension(assembly); - var xmlName = Path.Combine(Path.GetDirectoryName(assembly), name + ".xml"); - if (!File.Exists(xmlName)) - { - Console.WriteLine("Missing xml-doc: {0}", xmlName); - continue; - } - if (name == "ScriptEngine.HostedScript") - { - isOscriptStd = true; - } - - Console.WriteLine("Processing: {0}", name); - - var docMaker = new AssemblyDocumenter(assembly, xmlName); - docMaker.CreateDocumentationJSON(textBlocks); - } - JObject jsonObj = new JObject(); - var list = JsonConvert.DeserializeObject("{" + textBlocks.TextGlobalContext.ToString() + "}"); - jsonObj.Add("structureMenu", JObject.Parse(@"{ }")); - JObject structureMenu = jsonObj["structureMenu"] as JObject; - if (isOscriptStd) - { - using (var layout = new StreamReader(ExtFiles.Get("structureMenu.json"))) - { - var content = layout.ReadToEnd(); - var menu = JsonConvert.DeserializeObject(content); - - foreach (JToken curType in menu) - { - if (((JProperty)curType).Name == "global") - { - structureMenu.Add(((JProperty)curType).Name, JObject.Parse(@"{ }")); - } - else - { - structureMenu.Add(((JProperty)curType).Name, curType.First); - } - } - foreach (JToken curType in list) - { - if (((JObject)structureMenu["global"]).GetValue(((JProperty)curType).Name) == null) - { - JObject elemStructure = jsonObj["structureMenu"]["global"] as JObject; - elemStructure.Add(((JProperty)curType).Name, JObject.Parse(@"{ }")); - } - if (((JProperty)curType).Value.SelectToken("properties") != null) - { - foreach (JToken elem in ((JProperty)curType).Value.SelectToken("properties")) - { - JObject elemStructure = jsonObj["structureMenu"]["global"][((JProperty)curType).Name] as JObject; - elemStructure.Add(((JProperty)elem).Name, ""); - } - } - if (((JProperty)curType).Value.SelectToken("methods") != null) - { - foreach (JToken elem in ((JProperty)curType).Value.SelectToken("methods")) - { - JObject elemStructure = jsonObj["structureMenu"]["global"][((JProperty)curType).Name] as JObject; - elemStructure.Add(((JProperty)elem).Name, ""); - } - } - } - - } - } - else - { - structureMenu.Add("classes", JsonConvert.DeserializeObject("{\n\"Прочее\": \"\"\n }")); - } - foreach (JToken curType in list) - { - if (((JProperty)curType).Value.SelectToken("properties") != null) - { - if (jsonObj["globalvariables"] == null) - jsonObj.Add("globalvariables", JObject.Parse(@"{ }")); - - JObject globalvariables = jsonObj["globalvariables"] as JObject; - foreach (JToken prop in curType.First["properties"]) - { - globalvariables.Add(((JProperty)prop).Name, prop.First); - } - } - if (((JProperty)curType).Value.SelectToken("methods") != null) - { - if (jsonObj["globalfunctions"] == null) - jsonObj.Add("globalfunctions", JObject.Parse(@"{ }")); - - JObject globalfunctions = jsonObj["globalfunctions"] as JObject; - foreach (JToken meth in curType.First["methods"]) - { - JObject jsonDesc = new JObject(); - foreach (JToken token in meth.First) - { - if (((JProperty)token).Name != "signature" && ((JProperty)token).Name != "params") - jsonDesc.Add(((JProperty)token).Name, token.First); - } - if (((JProperty)meth).Value.SelectToken("signature") != null) - { - jsonDesc.Add("signature", JObject.Parse(@"{ }")); - JObject signature = jsonDesc["signature"] as JObject; - signature.Add("default", JObject.Parse(@"{ }")); - JObject defaultValue = signature["default"] as JObject; - defaultValue.Add("СтрокаПараметров", meth.First["signature"]); - if (((JProperty)meth).Value.SelectToken("params") != null) - { - defaultValue.Add("Параметры", meth.First["params"]); - } - else - { - defaultValue.Add("Параметры", JObject.Parse(@"{ }")); - } - } - globalfunctions.Add(((JProperty)meth).Name, jsonDesc); - } - } - } - jsonObj.Add("classes", JObject.Parse(@"{ }")); - JObject classes = jsonObj["classes"] as JObject; - var classesList = JsonConvert.DeserializeObject("{" + textBlocks.TextContextDescription.ToString() + "}"); - foreach (JToken classDesc in classesList) - { - JObject jsonDescClass = new JObject(); - foreach (JToken curType in classDesc.First) - { - if (((JProperty)curType).Name != "methods") - jsonDescClass.Add(((JProperty)curType).Name, curType.First); - } - if (((JProperty)classDesc).Value.SelectToken("methods") != null) - { - jsonDescClass.Add("methods", JObject.Parse(@"{ }")); - JObject methods = jsonDescClass["methods"] as JObject; - foreach (JToken method in classDesc.First["methods"]) - { - JObject jsonDesc = new JObject(); - if (method.First.First.Type == JTokenType.Object) - { - foreach (JToken token in method.First.First) - { - if (((JProperty)token).Name != "signature" && ((JProperty)token).Name != "params" && ((JProperty)token).Name != "remarks") - jsonDesc.Add(((JProperty)token).Name, token.First); - } - jsonDesc.Add("signature", JObject.Parse(@"{ }")); - JObject signature = jsonDesc["signature"] as JObject; - foreach (JToken varSyntax in method.First) - { - signature.Add(varSyntax["remarks"].ToString(), JObject.Parse(@"{ }")); - JObject defaultValue = signature[varSyntax["remarks"].ToString()] as JObject; - if (varSyntax.SelectToken("signature") != null) - { - defaultValue.Add("СтрокаПараметров", varSyntax["signature"]); - } - else - { - defaultValue.Add("СтрокаПараметров", JObject.Parse(@"{ }")); - } - if (varSyntax.SelectToken("params") != null) - { - defaultValue.Add("Параметры", varSyntax["params"]); - } - else - { - defaultValue.Add("Параметры", JObject.Parse(@"{ }")); - } - } - } - else - { - foreach (JToken token in method.First) - { - if (((JProperty)token).Name != "signature" && ((JProperty)token).Name != "params") - jsonDesc.Add(((JProperty)token).Name, token.First); - } - if (((JProperty)method).Value.SelectToken("signature") != null) - { - jsonDesc.Add("signature", JObject.Parse(@"{ }")); - JObject signature = jsonDesc["signature"] as JObject; - signature.Add("default", JObject.Parse(@"{ }")); - JObject defaultValue = signature["default"] as JObject; - defaultValue.Add("СтрокаПараметров", method.First["signature"]); - if (((JProperty)method).Value.SelectToken("params") != null) - { - defaultValue.Add("Параметры", method.First["params"]); - } - else - { - defaultValue.Add("Параметры", JObject.Parse(@"{ }")); - } - } - } - methods.Add(((JProperty)method).Name, jsonDesc); - } - } - classes.Add(((JProperty)classDesc).Name, jsonDescClass); - } - jsonObj.Add("systemEnum", JObject.Parse(@"{ }")); - JObject systemEnum = jsonObj["systemEnum"] as JObject; - var systemEnumList = JsonConvert.DeserializeObject("{" + textBlocks.TextEnumsDescription.ToString() + "}"); - foreach (JToken curType in systemEnumList) - { - systemEnum.Add(((JProperty)curType).Name, curType.First); - } - sbJSON.Write(JsonConvert.SerializeObject(jsonObj, Formatting.Indented)); - Console.WriteLine("Done"); - return ""; - } - } - } -} diff --git a/src/OneScriptDocumenter/ExtFiles.cs b/src/OneScriptDocumenter/ExtFiles.cs deleted file mode 100644 index 6652f6360..000000000 --- a/src/OneScriptDocumenter/ExtFiles.cs +++ /dev/null @@ -1,38 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.IO; -using System.Reflection; - -namespace OneScriptDocumenter -{ - static class ExtFiles - { - static string _root; - - private static string Root - { - get - { - if (_root == null) - { - string codeBase = Assembly.GetExecutingAssembly().CodeBase; - UriBuilder uri = new UriBuilder(codeBase); - string path = Uri.UnescapeDataString(uri.Path); - _root = Path.GetDirectoryName(path); - } - - return _root; - } - } - - public static string Get(string name) - { - return Path.Combine(Root, name); - } - } -} diff --git a/src/OneScriptDocumenter/LoadedAssembly.cs b/src/OneScriptDocumenter/LoadedAssembly.cs deleted file mode 100644 index 5ecb8f3c0..000000000 --- a/src/OneScriptDocumenter/LoadedAssembly.cs +++ /dev/null @@ -1,86 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; - -namespace OneScriptDocumenter -{ - class LoadedAssembly - { - private readonly Assembly _library; - private readonly AssemblyLoader _assemblyLoader; - - private Type[] _allTypes; - - public LoadedAssembly(Assembly library, AssemblyLoader assemblyLoader) - { - _library = library; - _assemblyLoader = assemblyLoader; - } - - public string Name - { - get - { - return _library.GetName().Name; - } - } - - public Type[] AllTypes - { - get - { - if (_allTypes == null) - _allTypes = _library.GetTypes(); - - return _allTypes; - } - } - - public Type[] GetMarkedTypes(ScriptMemberType markupElement) - { - var attributeType = _assemblyLoader.MemberTypeToAttributeType(markupElement); - var types = new List(); - - foreach (var t in AllTypes) - { - try - { - var attr = t.GetCustomAttributesData(); - foreach (var currentAttr in attr) - { - if (currentAttr.AttributeType == attributeType) - types.Add(t); - } - } - catch (FileNotFoundException) - { - Console.WriteLine($"Skipping type {t} due to load error"); - } - } - - return types.ToArray(); - } - - public CustomAttributeData GetMarkup(MemberInfo member, ScriptMemberType markupElement) - { - var type = _assemblyLoader.MemberTypeToAttributeType(markupElement); - return member.GetCustomAttributesData().FirstOrDefault(attr => attr.AttributeType == type); - } - - public CustomAttributeData GetMarkup(Type type, ScriptMemberType markupElement) - { - var attributeType = _assemblyLoader.MemberTypeToAttributeType(markupElement); - var result = type.GetCustomAttributesData().FirstOrDefault(attr => attr.AttributeType == attributeType); - return result; - } - - } -} diff --git a/src/OneScriptDocumenter/Markdown.xslt b/src/OneScriptDocumenter/Markdown.xslt deleted file mode 100644 index ea10115f0..000000000 --- a/src/OneScriptDocumenter/Markdown.xslt +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - -# / - - - - - - - - -# - - - - - -## Свойства - - - - -### / - -Доступ: - - Чтение/Запись - Чтение - Запись - Недоступно - - - - -Тип значения: - - - - - - - -## Методы - - - - -### / () - - - - -#### Возвращаемое значение - - - - - - - - - - - -#### Пример: - - - - - - - - - - - - - - - - - - -## Конструкторы - - - - - -### - - - - - - - -#### Параметры - - -* **: - - - - - - - - - - - - - - - - diff --git a/src/OneScriptDocumenter/MarkdownGen.cs b/src/OneScriptDocumenter/MarkdownGen.cs deleted file mode 100644 index d939498b0..000000000 --- a/src/OneScriptDocumenter/MarkdownGen.cs +++ /dev/null @@ -1,19 +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 MarkdownDeep; - -namespace OneScriptDocumenter -{ - class MarkdownGen : MarkdownDeep.Markdown - { - public override void OnPrepareLink(HtmlTag tag) - { - tag.attributes["href"] = tag.attributes["href"] + ".htm"; - base.OnPrepareLink(tag); - } - } -} diff --git a/src/OneScriptDocumenter/Model/AbstractSyntaxItem.cs b/src/OneScriptDocumenter/Model/AbstractSyntaxItem.cs deleted file mode 100644 index 87a535dc0..000000000 --- a/src/OneScriptDocumenter/Model/AbstractSyntaxItem.cs +++ /dev/null @@ -1,18 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System.Collections.Generic; - -namespace OneScriptDocumenter.Model -{ - abstract class AbstractSyntaxItem - { - public MultilangString Caption { get; set; } - public MultilangString Description { get; set; } - - public IList Children { get; protected set; } - } -} diff --git a/src/OneScriptDocumenter/Model/ClassModel.cs b/src/OneScriptDocumenter/Model/ClassModel.cs new file mode 100644 index 000000000..bf985bcd7 --- /dev/null +++ b/src/OneScriptDocumenter/Model/ClassModel.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; + +namespace OneScriptDocumenter.Model +{ + public class ClassModel : TypeModel + { + public ClassModel() : base(DocumentKind.Class) + { + } + + public List Properties { get; set; } + public List Methods { get; set; } + public List Constructors { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/DocumentKind.cs b/src/OneScriptDocumenter/Model/DocumentKind.cs new file mode 100644 index 000000000..0fe366486 --- /dev/null +++ b/src/OneScriptDocumenter/Model/DocumentKind.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter.Model +{ + public enum DocumentKind + { + GlobalContext, + Enum, + Class + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/Documentation.cs b/src/OneScriptDocumenter/Model/Documentation.cs deleted file mode 100644 index e52b806af..000000000 --- a/src/OneScriptDocumenter/Model/Documentation.cs +++ /dev/null @@ -1,14 +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.Collections.Generic; - -namespace OneScriptDocumenter.Model -{ - class Documentation : List - { - } -} diff --git a/src/OneScriptDocumenter/Model/DocumentationModel.cs b/src/OneScriptDocumenter/Model/DocumentationModel.cs new file mode 100644 index 000000000..b8c9062ea --- /dev/null +++ b/src/OneScriptDocumenter/Model/DocumentationModel.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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; + +namespace OneScriptDocumenter.Model +{ + public class DocumentationModel + { + public IEnumerable Items { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/EnumModel.cs b/src/OneScriptDocumenter/Model/EnumModel.cs new file mode 100644 index 000000000..135e78e13 --- /dev/null +++ b/src/OneScriptDocumenter/Model/EnumModel.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +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 OneScript.Localization; + +namespace OneScriptDocumenter.Model +{ + public class EnumModel : TypeModel + { + public EnumModel() : base(DocumentKind.Enum) + { + } + + public List Items { get; set; } = new List(); + } + + public class EnumItemModel + { + public BilingualString Name { get; set; } + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/GlobalContextModel.cs b/src/OneScriptDocumenter/Model/GlobalContextModel.cs new file mode 100644 index 000000000..87af708e7 --- /dev/null +++ b/src/OneScriptDocumenter/Model/GlobalContextModel.cs @@ -0,0 +1,37 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using OneScriptDocumenter.Model.Json; + +namespace OneScriptDocumenter.Model +{ + public class GlobalContextModel : IDocument + { + public GlobalContextModel() + { + Kind = DocumentKind.GlobalContext; + } + + public DocumentKind Kind { get; } + public string Title { get; set; } + + public string Description { get; set; } + + public List Properties { get; set; } + + public List Methods { get; set; } + + [JsonConverter(typeof(OwnerConverter))] + public Type Owner { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IEnumerable SeeAlso { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/IDocument.cs b/src/OneScriptDocumenter/Model/IDocument.cs new file mode 100644 index 000000000..1e3a1250c --- /dev/null +++ b/src/OneScriptDocumenter/Model/IDocument.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; + +namespace OneScriptDocumenter.Model +{ + public interface IDocument + { + public DocumentKind Kind { get; } + + public string Title { get; } + + public Type Owner { get; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/Json/OwnerConverter.cs b/src/OneScriptDocumenter/Model/Json/OwnerConverter.cs new file mode 100644 index 000000000..eda98a73f --- /dev/null +++ b/src/OneScriptDocumenter/Model/Json/OwnerConverter.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using OneScriptDocumenter.Secondary; + +namespace OneScriptDocumenter.Model.Json +{ + public class OwnerConverter : JsonConverter + { + public override Type Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options) + { + var linkId = ReferenceFactory.GetBslNameForAnnotatedObject(value); + var encText = JsonEncodedText.Encode(linkId, options.Encoder); + + writer.WriteStringValue(encText); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/Json/SyntaxGroupJsonConverter.cs b/src/OneScriptDocumenter/Model/Json/SyntaxGroupJsonConverter.cs new file mode 100644 index 000000000..30997ffe7 --- /dev/null +++ b/src/OneScriptDocumenter/Model/Json/SyntaxGroupJsonConverter.cs @@ -0,0 +1,42 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OneScriptDocumenter.Model.Json +{ + public class SyntaxGroupJsonConverter : JsonConverter + { + public override SyntaxGroup Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, SyntaxGroup value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("title"); + writer.WriteStringValue(value.Title); + + if (value.Document != null) + { + writer.WritePropertyName("document"); + JsonSerializer.Serialize(writer, value.Document, value.Document.GetType(), options); + } + + if (value.Items?.Count != 0) + { + writer.WritePropertyName("items"); + JsonSerializer.Serialize(writer, value.Items, options); + } + + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/MethodModel.cs b/src/OneScriptDocumenter/Model/MethodModel.cs new file mode 100644 index 000000000..6a8f93587 --- /dev/null +++ b/src/OneScriptDocumenter/Model/MethodModel.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +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.Text.Json.Serialization; +using OneScript.Localization; + +namespace OneScriptDocumenter.Model +{ + public class MethodModel + { + public BilingualString Name { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string Description { get; set; } + public string Title => Name.Russian; + + public List Parameters { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string ReturnTypeDocumentation { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string Example { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IEnumerable SeeAlso { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/ModelExtensions.cs b/src/OneScriptDocumenter/Model/ModelExtensions.cs new file mode 100644 index 000000000..416bf216e --- /dev/null +++ b/src/OneScriptDocumenter/Model/ModelExtensions.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter.Model +{ + public static class ModelExtensions + { + public static SyntaxGroup AddChildDocument(this SyntaxGroup group, IDocument document) + { + var docGroup = new SyntaxGroup + { + Document = document + }; + + group.Items.Add(docGroup); + return docGroup; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/MultilangString.cs b/src/OneScriptDocumenter/Model/MultilangString.cs deleted file mode 100644 index 59f615dfb..000000000 --- a/src/OneScriptDocumenter/Model/MultilangString.cs +++ /dev/null @@ -1,79 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; - -namespace OneScriptDocumenter.Model -{ - class MultilangString : IEnumerable> - { - public const string DEFAULT_LANG = "ru"; - - private readonly Dictionary _variants = new Dictionary(); - - public MultilangString(string content) - { - if (content == null) - throw new ArgumentNullException(); - - Set(DEFAULT_LANG, content); - } - - public MultilangString(IDictionary strings) - { - if (strings == null) - throw new ArgumentNullException(); - - _variants = new Dictionary(strings); - } - - private void Set(string lang, string content) - { - if (lang == null || content == null) - throw new ArgumentNullException(); - - _variants[lang] = content; - } - - public string DefaultString - { - get - { - return _variants[DEFAULT_LANG]; - } - set - { - _variants[DEFAULT_LANG] = value; - } - } - - public override string ToString() - { - return DefaultString; - } - - public string ToString(string lang) - { - return _variants[lang]; - } - - public IEnumerator> GetEnumerator() - { - return _variants.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public static MultilangString New(string content) - { - return new MultilangString(content); - } - } -} diff --git a/src/OneScriptDocumenter/Model/ParameterModel.cs b/src/OneScriptDocumenter/Model/ParameterModel.cs new file mode 100644 index 000000000..6ffe404f2 --- /dev/null +++ b/src/OneScriptDocumenter/Model/ParameterModel.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Text.Json.Serialization; + +namespace OneScriptDocumenter.Model +{ + public class ParameterModel + { + public string Name { get; set; } + public string Description { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string DefaultValue { get; set; } + + public bool IsOptional { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/PropertyModel.cs b/src/OneScriptDocumenter/Model/PropertyModel.cs new file mode 100644 index 000000000..57162da51 --- /dev/null +++ b/src/OneScriptDocumenter/Model/PropertyModel.cs @@ -0,0 +1,37 @@ +/*---------------------------------------------------------- +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.Text.Json.Serialization; +using OneScript.Localization; + +namespace OneScriptDocumenter.Model +{ + public class PropertyModel + { + public BilingualString Name { get; set; } + + public string ClrName { get; set; } + + public string Title => Name.Russian; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string Description { get; set; } = ""; + + public bool CanRead { get; set; } + public bool CanWrite { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string Returns { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string Example { get; set; } = ""; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IEnumerable SeeAlso { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/SyntaxCategory.cs b/src/OneScriptDocumenter/Model/SyntaxCategory.cs deleted file mode 100644 index 6408a9abf..000000000 --- a/src/OneScriptDocumenter/Model/SyntaxCategory.cs +++ /dev/null @@ -1,18 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System.Collections.Generic; - -namespace OneScriptDocumenter.Model -{ - class SyntaxCategory : AbstractSyntaxItem - { - public SyntaxCategory() - { - Children = new List(); - } - } -} diff --git a/src/OneScriptDocumenter/Model/SyntaxGroup.cs b/src/OneScriptDocumenter/Model/SyntaxGroup.cs new file mode 100644 index 000000000..1c4ce1d31 --- /dev/null +++ b/src/OneScriptDocumenter/Model/SyntaxGroup.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +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.Text.Json.Serialization; +using OneScriptDocumenter.Model.Json; + +namespace OneScriptDocumenter.Model +{ + [JsonConverter(typeof(SyntaxGroupJsonConverter))] + public class SyntaxGroup + { + private string _selfTitle; + + public string Title + { + get => _selfTitle ?? Document?.Title; + set => _selfTitle = value; + } + + public IDocument Document { get; set; } + public IList Items { get; set; } = new List(); + + public string Page { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Model/SyntaxItem.cs b/src/OneScriptDocumenter/Model/SyntaxItem.cs deleted file mode 100644 index 25cb85bee..000000000 --- a/src/OneScriptDocumenter/Model/SyntaxItem.cs +++ /dev/null @@ -1,12 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -namespace OneScriptDocumenter.Model -{ - class SyntaxItem : AbstractSyntaxItem - { - } -} diff --git a/src/OneScriptDocumenter/Model/TypeModel.cs b/src/OneScriptDocumenter/Model/TypeModel.cs new file mode 100644 index 000000000..b105a10c7 --- /dev/null +++ b/src/OneScriptDocumenter/Model/TypeModel.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using OneScript.Localization; +using OneScriptDocumenter.Model.Json; + +namespace OneScriptDocumenter.Model +{ + public abstract class TypeModel : IDocument + { + protected TypeModel(DocumentKind kind) + { + Kind = kind; + } + + public DocumentKind Kind { get; } + public BilingualString Name { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string Description { get; set; } + + public string Title => Name.Russian; + + [JsonConverter(typeof(OwnerConverter))] + public Type Owner { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string Example { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IEnumerable SeeAlso { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/OneScriptDocumenter.csproj b/src/OneScriptDocumenter/OneScriptDocumenter.csproj index 1c97d9451..63368fda1 100644 --- a/src/OneScriptDocumenter/OneScriptDocumenter.csproj +++ b/src/OneScriptDocumenter/OneScriptDocumenter.csproj @@ -3,50 +3,42 @@ - net452 + $(TargetFrameworkVersion) Exe OneScript Libraries Documenter + Debug;Release;LinuxDebug + AnyCPU + 8 1C (BSL) language parser - - - - + + true + false + - - Designer - - - - Always - - + Always - - Always - - - Designer - Always - Always + - + + + - + \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/AssemblyDocsCollector.cs b/src/OneScriptDocumenter/Primary/AssemblyDocsCollector.cs new file mode 100644 index 000000000..bffd1ce23 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/AssemblyDocsCollector.cs @@ -0,0 +1,278 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Xml.Linq; +using OneScript.Contexts; +using OneScript.Contexts.Enums; + +namespace OneScriptDocumenter.Primary +{ + public class AssemblyDocsCollector + { + private readonly Assembly _assembly; + private XmlDocHolder _xmlDocs; + + public AssemblyDocsCollector(Assembly assembly) + { + _assembly = assembly; + } + + public bool CollectDocumentation(PrimaryDocumentation destination) + { + var assemblyDir = Path.GetDirectoryName(_assembly.Location); + Trace.Assert(assemblyDir != null); + + var assemblyName = _assembly.GetName().Name; + var xmlDoc = Path.Combine(assemblyDir, assemblyName + ".xml"); + + bool docsExist = File.Exists(xmlDoc); + _xmlDocs = new XmlDocHolder(assemblyName); + if (docsExist) + { + try + { + _xmlDocs.Read(xmlDoc); + } + catch (Exception e) + { + docsExist = false; + ConsoleLogger.Warning(e.ToString()); + } + } + + foreach (var type in _assembly.GetTypes()) + { + foreach (var currentAttr in type.GetCustomAttributes(false)) + { + if (currentAttr is GlobalContextAttribute glob) + { + AddType(destination, type, glob); + } + else if (currentAttr is ContextClassAttribute classAttribute) + { + AddType(destination, type, classAttribute); + } + else if (currentAttr is EnumerationTypeAttribute simpleEnum) + { + AddType(destination, type, simpleEnum); + } + else if (currentAttr is SystemEnumAttribute systemEnum) + { + AddType(destination, type, systemEnum); + } + else if (currentAttr is DocumentationProviderAttribute docAttr) + { + AddType(destination, type, docAttr); + } + } + } + + return docsExist; + } + + private void AddType(PrimaryDocumentation documentation, Type type, + DocumentationProviderAttribute classAttribute) + { + var doc = new PrimaryBslDocument(); + doc.Owner = type; + doc.OwnerKind = OwnerKind.DocumentationProvider; + doc.Title = classAttribute.Category; + doc.XmlDocIdentifier = TypeKey(type); + doc.SelfDoc = _xmlDocs[doc.XmlDocIdentifier]; + + AddProperties(doc.Properties, type, documentation.ReferenceCollector); + AddMethods(doc.Methods, type, documentation.ReferenceCollector); + + documentation.ReferenceCollector.Register(doc.XmlDocIdentifier, type); + documentation.Add(doc); + } + + private void AddType(PrimaryDocumentation documentation, Type type, SystemEnumAttribute classAttribute) + { + var doc = new PrimaryBslDocument(); + doc.Owner = type; + doc.OwnerKind = OwnerKind.SystemEnum; + doc.Title = classAttribute.Name; + doc.XmlDocIdentifier = TypeKey(type); + doc.SelfDoc = _xmlDocs[doc.XmlDocIdentifier]; + + documentation.ReferenceCollector.Register(doc.XmlDocIdentifier, type); + documentation.Add(doc); + } + + private void AddType(PrimaryDocumentation documentation, Type type, EnumerationTypeAttribute enumAttribute) + { + var doc = new PrimaryBslDocument(); + doc.Owner = type; + doc.OwnerKind = OwnerKind.SimpleEnum; + doc.Title = enumAttribute.Name; + doc.XmlDocIdentifier = TypeKey(type); + doc.SelfDoc = _xmlDocs[doc.XmlDocIdentifier]; + + foreach (var fieldInfo in type.GetFields().Where(f => f.GetCustomAttributes(typeof(EnumValueAttribute), false).Any())) + { + var fieldKey = FieldKey(fieldInfo); + var xDoc = _xmlDocs[fieldKey]; + doc.Fields.Add(fieldInfo, xDoc); + documentation.ReferenceCollector.Register(fieldKey, fieldInfo); + } + + documentation.ReferenceCollector.Register(doc.XmlDocIdentifier, type); + documentation.Add(doc); + } + + private void AddType(PrimaryDocumentation documentation, Type type, ContextClassAttribute classAttribute) + { + var doc = new PrimaryBslDocument(); + doc.Owner = type; + doc.OwnerKind = OwnerKind.Class; + doc.Title = classAttribute.Name; + doc.XmlDocIdentifier = TypeKey(type); + doc.SelfDoc = _xmlDocs[doc.XmlDocIdentifier]; + + AddProperties(doc.Properties, type, documentation.ReferenceCollector); + AddMethods(doc.Methods, type, documentation.ReferenceCollector); + AddConstructors(doc.Constructors, type, documentation.ReferenceCollector); + + documentation.ReferenceCollector.Register(doc.XmlDocIdentifier, type); + documentation.Add(doc); + } + + private void AddType(PrimaryDocumentation documentation, Type type, GlobalContextAttribute glob) + { + var doc = new PrimaryBslDocument(); + doc.Owner = type; + doc.OwnerKind = OwnerKind.GlobalContext; + doc.Title = glob.Category ?? type.Name; + doc.XmlDocIdentifier = TypeKey(type); + doc.SelfDoc = _xmlDocs[doc.XmlDocIdentifier]; + + AddProperties(doc.Properties, type, documentation.ReferenceCollector); + AddMethods(doc.Methods, type, documentation.ReferenceCollector); + + documentation.ReferenceCollector.Register(doc.XmlDocIdentifier, type); + documentation.Add(doc); + } + + private void AddProperties(Dictionary propDocuments, Type type, + ReferenceCollector documentationReferenceCollector) + { + foreach (var propertyInfo in type.GetProperties() + .Where(p => p + .GetCustomAttributes(false) + .Any(attr => attr is ContextPropertyAttribute { SkipForDocumenter: false } || attr is DocumentedMemberAttribute))) + { + var propertyKey = PropertyKey(propertyInfo); + var doc = _xmlDocs[propertyKey]; + propDocuments.Add(propertyInfo, doc); + documentationReferenceCollector.Register(propertyKey, propertyInfo); + } + } + + private void AddMethods(Dictionary methDocuments, Type type, + ReferenceCollector documentationReferenceCollector) + { + foreach (var methodInfo in type + .GetMethods(BindingFlags.Instance | BindingFlags.Public) + .Where(m => m.GetCustomAttributes(false) + .Any(attr => attr is ContextMethodAttribute { SkipForDocumenter: false } || attr is DocumentedMemberAttribute))) + { + var methodKey = MethodKey(methodInfo); + var doc = _xmlDocs[methodKey]; + methDocuments.Add(methodInfo, doc); + documentationReferenceCollector.Register(methodKey, methodInfo); + } + } + + private void AddConstructors(Dictionary docConstructors, Type type, + ReferenceCollector documentationReferenceCollector) + { + foreach (var methodInfo in type + .GetMethods(BindingFlags.Static | BindingFlags.Public) + .Where(m => m.GetCustomAttributes(typeof(ScriptConstructorAttribute), false).Any())) + { + var methodKey = MethodKey(methodInfo); + var doc = _xmlDocs[methodKey]; + docConstructors.Add(methodInfo, doc); + documentationReferenceCollector.Register(methodKey, methodInfo); + } + } + + private static string TypeKey(Type type) => $"T:{type.FullName}"; + + private static string MethodKey(MethodBase method) => $"M:{method.DeclaringType!.FullName}.{MethodId(method)}"; + + private static string PropertyKey(PropertyInfo prop) => $"P:{prop.DeclaringType!.FullName}.{prop.Name}"; + + private static string FieldKey(FieldInfo field) => $"F:{field.DeclaringType!.FullName}.{field.Name}"; + + private static string MethodId(MethodBase meth) + { + var sb = new StringBuilder(); + sb.Append(meth.Name); + var methParams = meth.GetParameters(); + if (methParams.Length > 0) + { + sb.Append('('); + var paramInfos = methParams.Select(x => x.ParameterType).ToArray(); + string[] paramTypeNames = new string[paramInfos.Length]; + + for (int i = 0; i < paramInfos.Length; i++) + { + var info = paramInfos[i]; + if (info.GenericTypeArguments.Length > 0) + { + var genericBuilder = BuildStringGenericTypes(info); + + paramTypeNames[i] = genericBuilder.ToString(); + } + else + { + paramTypeNames[i] = info.FullName; + } + } + sb.Append(string.Join(",", paramTypeNames)); + sb.Append(')'); + } + return sb.ToString(); + } + + private static StringBuilder BuildStringGenericTypes(Type info) + { + var matches = System.Text.RegularExpressions.Regex.Matches(info.FullName, @"([\w.]+)`\d|(\[([\w0-9.=]+)(?:,\s(?:[\w0-9.= ]+))*\]),?"); + + var genericBuilder = new StringBuilder(); + + if (matches.Count == 1) + { + return genericBuilder; + } + + genericBuilder.Append(matches[0].Groups[1]); + genericBuilder.Append('{'); + var fst = true; + foreach (var capture in matches[1].Groups[3].Captures) + { + if (!fst) + genericBuilder.Append(", "); + + genericBuilder.Append(capture); + fst = false; + } + genericBuilder.Append('}'); + + return genericBuilder; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/AssemblyLoader.cs b/src/OneScriptDocumenter/Primary/AssemblyLoader.cs new file mode 100644 index 000000000..a56b6f586 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/AssemblyLoader.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace OneScriptDocumenter.Primary +{ + public class AssemblyLoader : IDisposable + { + //private readonly HashSet _loadContexts = new HashSet(); + + public Dictionary LoadAssemblies(IReadOnlyCollection assemblyPaths) + { + var assemblies = new Dictionary(); + foreach (var assemblyPath in assemblyPaths) + { + var context = new DocumentableLibraryLoadContext(assemblyPath); + //_loadContexts.Add(context); + var assembly = context.LoadFromAssemblyPath(assemblyPath); + assemblies.Add(assembly.GetName().Name!, assembly); + } + + return assemblies; + } + + public void Dispose() + { + // foreach (var loadContext in _loadContexts) + // { + // loadContext.Unload(); + // } + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/DocumentableLibraryLoadContext.cs b/src/OneScriptDocumenter/Primary/DocumentableLibraryLoadContext.cs new file mode 100644 index 000000000..2b758c722 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/DocumentableLibraryLoadContext.cs @@ -0,0 +1,74 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using OneScript.Contexts; + +namespace OneScriptDocumenter.Primary +{ + public class DocumentableLibraryLoadContext : AssemblyLoadContext + { + private readonly AssemblyDependencyResolver _dependencyResolver; + private readonly HashSet _assembliesOfCurrentApp; + private readonly string _assemblyFile; + + public DocumentableLibraryLoadContext(string assemblyFile) : base(isCollectible: false) + { + _assemblyFile = assemblyFile; + _dependencyResolver = new AssemblyDependencyResolver(assemblyFile); + + // Мы должны не грузить в данный контекст библиотеку Core + // Тогда она будет загружена из контекста AppDomain + // и сравнение типов у атрибутов разметки будет работать. + // В противном случае сравнение ContextClassAttribute из разных DLL будет давать false на равенство + var coreAsm = typeof(ContextClassAttribute).Assembly; + _assembliesOfCurrentApp = AppDomain.CurrentDomain.GetAssemblies() + .First(a => a.FullName == coreAsm.FullName) + .GetReferencedAssemblies() + .Select(a => a.FullName) + .ToHashSet(); + _assembliesOfCurrentApp.Add(coreAsm.FullName); + } + + protected override Assembly Load(AssemblyName assemblyName) + { + if (_assembliesOfCurrentApp.Contains(assemblyName.FullName)) + { + return null; + } + + var assemblyPath = _dependencyResolver.ResolveAssemblyToPath(assemblyName); + + if (assemblyPath != null) + return LoadFromAssemblyPath(assemblyPath); + + if (assemblyName.FullName.StartsWith("System.") || assemblyName.FullName.StartsWith("Microsoft.")) + { + return Assembly.Load(assemblyName); + } + + ConsoleLogger.Error($"Error loading {_assemblyFile}. Dependency {assemblyName.FullName} probably not in dll directory"); + return null; + + } + + protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + { + string libraryPath = _dependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName); + if (libraryPath != null) + { + return LoadUnmanagedDllFromPath(libraryPath); + } + + return IntPtr.Zero; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/IReferencesRegistry.cs b/src/OneScriptDocumenter/Primary/IReferencesRegistry.cs new file mode 100644 index 000000000..20d577666 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/IReferencesRegistry.cs @@ -0,0 +1,14 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter.Primary +{ + public interface IReferencesRegistry + { + ReferencableElement? Get(string key); + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/OwnerKind.cs b/src/OneScriptDocumenter/Primary/OwnerKind.cs new file mode 100644 index 000000000..582d94339 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/OwnerKind.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 OneScriptDocumenter.Primary +{ + public enum OwnerKind + { + GlobalContext, + SystemEnum, + SimpleEnum, + Class, + DocumentationProvider + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/PrimaryBslDocument.cs b/src/OneScriptDocumenter/Primary/PrimaryBslDocument.cs new file mode 100644 index 000000000..5835b6e86 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/PrimaryBslDocument.cs @@ -0,0 +1,33 @@ +/*---------------------------------------------------------- +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/. +----------------------------------------------------------*/ + +#nullable enable +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Xml.Linq; + +namespace OneScriptDocumenter.Primary +{ + public class PrimaryBslDocument + { + public Type Owner { get; set; } + + public OwnerKind OwnerKind { get; set; } + + public string XmlDocIdentifier { get; set; } = ""; + + public string Title { get; set; } = ""; + + public XElement? SelfDoc { get; set; } + + public Dictionary Properties { get; } = new Dictionary(); + public Dictionary Methods { get; } = new Dictionary(); + public Dictionary Fields { get; } = new Dictionary(); + public Dictionary Constructors { get; } = new Dictionary(); + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/PrimaryDocsCollector.cs b/src/OneScriptDocumenter/Primary/PrimaryDocsCollector.cs new file mode 100644 index 000000000..52f5916bc --- /dev/null +++ b/src/OneScriptDocumenter/Primary/PrimaryDocsCollector.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +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; + +namespace OneScriptDocumenter.Primary +{ + public class PrimaryDocsCollector + { + public PrimaryDocumentation Collect(IReadOnlyCollection inputAssemblies) + { + ConsoleLogger.Info("Collecting primary documentation"); + + var documentation = new PrimaryDocumentation(); + + using var assemblyReader = new AssemblyLoader(); + + var assemblies = assemblyReader.LoadAssemblies(inputAssemblies); + foreach (var keyAndValue in assemblies) + { + var assembly = keyAndValue.Value; + + var collector = new AssemblyDocsCollector(assembly); + + if (!collector.CollectDocumentation(documentation)) + { + ConsoleLogger.Warning($"XmlDoc file for assembly {assembly.Location} not found"); + } + } + + return documentation; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/PrimaryDocumentation.cs b/src/OneScriptDocumenter/Primary/PrimaryDocumentation.cs new file mode 100644 index 000000000..ef34375bf --- /dev/null +++ b/src/OneScriptDocumenter/Primary/PrimaryDocumentation.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace OneScriptDocumenter.Primary +{ + public class PrimaryDocumentation : IEnumerable + { + private readonly Dictionary _documents = + new Dictionary(); + + public ReferenceCollector ReferenceCollector { get; } = new ReferenceCollector(); + + public void Add(PrimaryBslDocument doc) + { + _documents.Add(doc.Owner, doc); + } + + public PrimaryBslDocument Get(Type key) => _documents[key]; + + public IEnumerator GetEnumerator() + { + return _documents.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/ReferencableElement.cs b/src/OneScriptDocumenter/Primary/ReferencableElement.cs new file mode 100644 index 000000000..5312ce2cc --- /dev/null +++ b/src/OneScriptDocumenter/Primary/ReferencableElement.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/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; + +namespace OneScriptDocumenter.Primary +{ + public struct ReferencableElement + { + public Type Owner { get; set; } + public MemberInfo Member { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/ReferenceCollector.cs b/src/OneScriptDocumenter/Primary/ReferenceCollector.cs new file mode 100644 index 000000000..0e0e9ac81 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/ReferenceCollector.cs @@ -0,0 +1,46 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace OneScriptDocumenter.Primary +{ + public class ReferenceCollector : IReferencesRegistry + { + private readonly Dictionary _referenceTargets = + new Dictionary(); + + public void Register(string typeKey, Type target) + { + _referenceTargets.Add(typeKey, new ReferencableElement + { + Owner = target + }); + } + + public void Register(string memberKey, MemberInfo member) + { + _referenceTargets.Add(memberKey, new ReferencableElement + { + Owner = member.DeclaringType, + Member = member + }); + } + + public ReferencableElement? Get(string key) + { + if (_referenceTargets.TryGetValue(key, out var result)) + { + return result; + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Primary/XmlDocHolder.cs b/src/OneScriptDocumenter/Primary/XmlDocHolder.cs new file mode 100644 index 000000000..2354c7352 --- /dev/null +++ b/src/OneScriptDocumenter/Primary/XmlDocHolder.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Linq; + +namespace OneScriptDocumenter.Primary +{ + public class XmlDocHolder + { + private readonly string _libraryName; + private readonly Dictionary _memberDocumentation = new Dictionary(); + + public XmlDocHolder(string libraryName) + { + _libraryName = libraryName; + } + + public void Read(string docPath) + { + using var reader = new StreamReader(docPath); + var xmlDoc = XDocument.Load(reader); + + var asmElement = xmlDoc.Root?.Element("assembly"); + if (asmElement == null) + throw new ArgumentException("Wrong XML doc format"); + + var fileLibName = asmElement.Element("name")?.Value; + if (string.Compare(_libraryName, fileLibName, true, System.Globalization.CultureInfo.InvariantCulture) != 0) + throw new ArgumentNullException( + $"Mismatch assembly names. Expected {_libraryName}, found in XML {fileLibName}"); + + var members = xmlDoc.Element("doc")?.Element("members")?.Elements() ?? Array.Empty(); + _memberDocumentation.Clear(); + foreach (var item in members) + { + var key = item.Attribute("name")?.Value ?? throw new ArgumentException($"Member {item} has no attribute \"name\""); + _memberDocumentation[key] = item; + } + } + + public XElement this[string key] + { + get + { + var hasKey = _memberDocumentation.TryGetValue(key, out var result); + return hasKey ? result : null; + } + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Program.cs b/src/OneScriptDocumenter/Program.cs index ba3f59a52..794ba4e41 100644 --- a/src/OneScriptDocumenter/Program.cs +++ b/src/OneScriptDocumenter/Program.cs @@ -4,310 +4,85 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml; -using System.Xml.Linq; -using System.Xml.XPath; -using System.Xml.Xsl; using System.Linq; +using CommandLine; +using OneScriptDocumenter.Cli; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Primary; +using OneScriptDocumenter.Secondary; +using MarkdownGenerator = OneScriptDocumenter.Cli.MarkdownGenerator; namespace OneScriptDocumenter { - static class Program + public static class Program { - static int Main(string[] args) - { - int retCode = 0; - - try - { - if (args.Length > 0 && args[0] == "markdown") - { - retCode = GenerateMarkdown(args); - } - else if (args.Length > 0 && args[0] == "html") - { - retCode = GenerateHtml(args); - } - else if (args.Length > 0 && args[0] == "json") - { - retCode = GenerateJSON(args); - } - else - { - retCode = GenerateXml(args); - } - - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - retCode = 128; - } - - if (System.Diagnostics.Debugger.IsAttached) - Console.ReadKey(); - - return retCode; - - } - - private static int GenerateHtml(string[] args) + public static int Main(string[] args) { - var cmdLineArgs = new CommandLineArgs(args); - cmdLineArgs.Next(); // пропуск ключевого слова + var parserResult = Parser.Default.ParseArguments(args); - var xmlDoc = cmdLineArgs.Next(); - if (xmlDoc == null) + switch (parserResult.Tag) { - ShowUsage(); - return 1; - } - - var outputDir = cmdLineArgs.Next(); - if (outputDir == null) - outputDir = Directory.GetCurrentDirectory(); - - var baseUrl = cmdLineArgs.Next(); - - var inputDir = Path.Combine(Environment.GetEnvironmentVariable("TMP"), Path.GetRandomFileName()); - Directory.CreateDirectory(inputDir); - - CreateDocumentation(xmlDoc, inputDir, baseUrl); - - var files = Directory.EnumerateFiles(inputDir, "*.md", SearchOption.AllDirectories) - .Select(x => new { FullPath = x, RelativePath = x.Substring(inputDir.Length + 1) }); - - Directory.CreateDirectory(outputDir); - var mdGen = new MarkdownGen(); - mdGen.ExtraMode = true; - mdGen.UrlBaseLocation = "stdlib"; - - var layout = ReadHTMLLayout(); - - foreach (var file in files) - { - Console.WriteLine("Processing {0}", file.RelativePath); - using (var inputFile = new StreamReader(file.FullPath)) - { - var content = inputFile.ReadToEnd(); - var outputFile = Path.Combine(outputDir, file.RelativePath.Substring(0, file.RelativePath.Length - 2) + "htm"); - - var html = mdGen.Transform(content); - Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); - - using (var outStream = new StreamWriter(outputFile, false, Encoding.UTF8)) + case ParserResultType.Parsed: + try { - var full_html = layout.Replace("$title$", Path.GetFileNameWithoutExtension(outputFile)) - .Replace("$body$", html); - outStream.Write(full_html); + return ProcessOptions(parserResult.Value); } - } - } - - Directory.Delete(inputDir, true); - Console.WriteLine("Done"); - return 0; - } - - private static string ReadHTMLLayout() - { - using (var sr = new StreamReader(ExtFiles.Get("html_layout.htm"))) - { - return sr.ReadToEnd(); + catch(Exception e) + { + ConsoleLogger.Error(e.ToString()); + return 1; + } + default: + return 1; } } - private static int GenerateJSON(string[] args) + private static int ProcessOptions(GeneratorOptions options) { - var cmdLineArgs = new CommandLineArgs(args); - cmdLineArgs.Next(); // пропуск ключевого слова - - var outputFile = cmdLineArgs.Next(); - if (outputFile == null) - { - ShowUsage(); - return 1; - } - - List assemblies = new List(); - - while (true) + if (string.IsNullOrEmpty(options.JsonFile) && string.IsNullOrEmpty(options.MarkdownDir)) { - var arg = cmdLineArgs.Next(); - if (arg == null) - break; - - assemblies.Add(arg); - } - - if (assemblies.Count == 0) - { - ShowUsage(); + ConsoleLogger.Error("Must have either --json or --markdown options"); return 1; } - return CreateDocumentationJSON(outputFile, assemblies); + var primaryGenerator = new PrimaryDocsCollector(); + var primaryDocs = primaryGenerator.Collect(options.AssemblyFiles.ToList()); - } + var refFactory = new ReferenceFactory(); + var referenceResolver = new MarkdownReferenceResolver(primaryDocs.ReferenceCollector, refFactory); + + var tocFile = options.TocFile; + var docBuilder = new DocumentationModelBuilder(primaryDocs, referenceResolver, tocFile); + var secondaryDocs = docBuilder.Build(); - private static int GenerateXml(string[] args) - { - var cmdLineArgs = new CommandLineArgs(args); - - var outputFile = cmdLineArgs.Next(); - if (outputFile == null) + if (!string.IsNullOrEmpty(options.JsonFile)) { - ShowUsage(); - return 1; + WriteJson(secondaryDocs, options.JsonFile); } - - List assemblies = new List(); - - while (true) + + if (!string.IsNullOrEmpty(options.MarkdownDir)) { - var arg = cmdLineArgs.Next(); - if (arg == null) - break; - - assemblies.Add(arg); + WriteMarkdown(secondaryDocs, options.MarkdownDir); } - - if (assemblies.Count == 0) - { - ShowUsage(); - return 1; - } - - return CreateDocumentation(outputFile, assemblies); - - } - - private static int GenerateMarkdown(string[] args) - { - var cmdLineArgs = new CommandLineArgs(args); - cmdLineArgs.Next(); // пропуск ключевого слова - - var xmlDoc = cmdLineArgs.Next(); - if (xmlDoc == null) - { - ShowUsage(); - return 1; - } - - var outputDir = cmdLineArgs.Next(); - if (outputDir == null) - outputDir = Directory.GetCurrentDirectory(); - - var baseUrl = cmdLineArgs.Next(); - - return CreateDocumentation(xmlDoc, outputDir, baseUrl); - - } - - private static int CreateDocumentation(string outputFile, List assemblies) - { - var documenter = new Documenter(); - var doc = documenter.CreateDocumentation(assemblies); - - doc.Save(outputFile); + + ConsoleLogger.Info("Generation completed"); return 0; } - private static int CreateDocumentationJSON(string outputFile, List assemblies) + private static void WriteMarkdown(DocumentationModel documentation, string outputDir) { - var documenter = new Documenter(); - documenter.CreateDocumentationJSON(outputFile, assemblies); - - return 0; + var refFactory = new ReferenceFactory(); + var mdGenerator = new MarkdownGenerator(documentation, refFactory); + mdGenerator.Write(outputDir); } - private static int CreateDocumentation(string xmlDocPath, string pathOutput, string baseUrl) + private static void WriteJson(DocumentationModel documentation, string outputFile) { - XDocument doc; - using (var fs = new FileStream(xmlDocPath, FileMode.Open, FileAccess.Read)) - { - doc = XDocument.Load(fs); - } - - string docContent = doc.ToString(); - - XslCompiledTransform xslt = new XslCompiledTransform(); - xslt.Load(ExtFiles.Get("markdown.xslt")); - XPathDocument xpathdocument = new XPathDocument(new StringReader(docContent)); - - var stream = new MemoryStream(); - XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8); - - xslt.Transform(xpathdocument, null, writer, null); - - stream.Position = 0; - XDocument xdoc = XDocument.Load(stream); - writer.Close(); - - if (!Directory.Exists(pathOutput)) - Directory.CreateDirectory(pathOutput); - - var contentStdlibPath = Path.Combine(pathOutput, "stdlib"); - Directory.CreateDirectory(contentStdlibPath); - - var tocBuilder = new StringBuilder(); - var knownNodes = new HashSet(); - if (baseUrl == null) - baseUrl = ""; - - using (var layout = new StreamReader(ExtFiles.Get("toc_layout.md"))) - { - var content = layout.ReadToEnd(); - tocBuilder.Append(content); - tocBuilder.Replace("$base_url$", baseUrl); - var matches = Regex.Matches(content, @"(?=\S)\[(.*)\]\S"); - foreach (Match match in matches) - { - var uri = match.Groups[1].Value; - knownNodes.Add(uri); - } - } - - using (var tocWriter = new StreamWriter(Path.Combine(pathOutput, "stdlib.md"))) - { - tocWriter.Write(tocBuilder.ToString()); - tocBuilder.Clear(); - - foreach (var fileNode in xdoc.Root.Elements("document")) - { - string name = fileNode.Attribute("href").Value.Replace(".md", ""); - string link = name.Replace(" ", "%20"); - - string path = Path.Combine(contentStdlibPath, fileNode.Attribute("href").Value); - var file = new FileStream(path, FileMode.Create); - using (var fileWriter = new StreamWriter(file)) - { - fileWriter.Write(fileNode.Value); - } - - if (!knownNodes.Contains(name)) - tocWriter.WriteLine("* [{0}]({1}/{2})", name, baseUrl, link); - - } - } - - return 0; - } - - static void ShowUsage() - { - Console.WriteLine("Usage:"); - Console.WriteLine("OneScriptDocumenter.exe [...]"); - Console.WriteLine("OneScriptDocumenter.exe json [...]"); - Console.WriteLine("OneScriptDocumenter.exe markdown [baseurl]"); - Console.WriteLine("OneScriptDocumenter.exe html [baseurl]"); + var jsonWriter = new JsonGenerator(documentation); + jsonWriter.WriteFile(outputFile); } - } -} +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/ScriptMemberTypes.cs b/src/OneScriptDocumenter/ScriptMemberTypes.cs deleted file mode 100644 index fd674afac..000000000 --- a/src/OneScriptDocumenter/ScriptMemberTypes.cs +++ /dev/null @@ -1,21 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -namespace OneScriptDocumenter -{ - enum ScriptMemberType - { - GlobalContext, - SystemEnum, - EnumerationType, - EnumerationValue, - EnumItem, - Class, - Method, - Property, - Constructor - } -} diff --git a/src/OneScriptDocumenter/Secondary/BaseModelVisitor.cs b/src/OneScriptDocumenter/Secondary/BaseModelVisitor.cs new file mode 100644 index 000000000..462b8fa55 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/BaseModelVisitor.cs @@ -0,0 +1,72 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter.Model; + +namespace OneScriptDocumenter.Secondary +{ + public abstract class BaseModelVisitor + { + public virtual void VisitModel(DocumentationModel model) + { + VisitItems(model.Items); + } + + private void VisitItems(IEnumerable modelItems) + { + foreach (var syntaxGroup in modelItems) + { + VisitSyntaxGroup(syntaxGroup); + } + } + + protected virtual void VisitChildItems(SyntaxGroup group) + { + if (group.Items != null) + VisitItems(group.Items); + } + + protected virtual void VisitSyntaxGroup(SyntaxGroup model) + { + if (model.Document != null) + { + VisitDocument(model.Document); + } + + VisitChildItems(model); + } + + protected virtual void VisitDocument(IDocument document) + { + switch (document) + { + case GlobalContextModel model: + VisitGlobalContext(model); + break; + case ClassModel model: + VisitClass(model); + break; + case EnumModel model: + VisitEnum(model); + break; + } + } + + protected virtual void VisitClass(ClassModel model) + { + } + + protected virtual void VisitGlobalContext(GlobalContextModel model) + { + } + + protected virtual void VisitEnum(EnumModel model) + { + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/DoNothingReferenceResolver.cs b/src/OneScriptDocumenter/Secondary/DoNothingReferenceResolver.cs new file mode 100644 index 000000000..a8d44ae30 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/DoNothingReferenceResolver.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter.Secondary +{ + public class DoNothingReferenceResolver : IReferenceResolver + { + public string Resolve(string linkTarget, string linkText) + { + return linkText; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/DocumentationModelBuilder.cs b/src/OneScriptDocumenter/Secondary/DocumentationModelBuilder.cs new file mode 100644 index 000000000..bb52b7c93 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/DocumentationModelBuilder.cs @@ -0,0 +1,448 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using System.Xml.Linq; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Types; +using OneScript.Values; +using OneScriptDocumenter.Model; +using OneScriptDocumenter.Primary; +using ArgumentException = System.ArgumentException; + +namespace OneScriptDocumenter.Secondary +{ + public class DocumentationModelBuilder + { + private readonly PrimaryDocumentation _primaryDocs; + private readonly string _tocFile; + + private XmlDocConverter _docConverter; + + public DocumentationModelBuilder(PrimaryDocumentation primaryDocs, IReferenceResolver referenceResolver, string tocFile) + { + _primaryDocs = primaryDocs; + _tocFile = tocFile; + + _docConverter = new XmlDocConverter(referenceResolver); + } + + public DocumentationModel Build() + { + ConsoleLogger.Info("Collecting secondary documentation"); + + List items; + if (!string.IsNullOrEmpty(_tocFile)) + { + items = ReadTableOfContents(); + } + else + { + items = new List(); + } + + return BuildDocumentation(items); + } + + private List ReadTableOfContents() + { + using var filestream = new FileStream(_tocFile, FileMode.Open); + + return JsonSerializer.Deserialize(filestream).Items; + } + + private DocumentationModel BuildDocumentation(List items) + { + var model = new DocumentationModel(); + var categoryOthers = new SyntaxGroup + { + Title = items.Count == 0 ? "Документация" : "Прочее", + Page = "others" + }; + + var documentsByName = new Dictionary(); + + foreach (var primaryDoc in _primaryDocs) + { + IDocument document; + switch (primaryDoc.OwnerKind) + { + case OwnerKind.GlobalContext: + case OwnerKind.DocumentationProvider: + { + document = MakeGlobalContextNode(primaryDoc); + break; + } + case OwnerKind.SystemEnum: + { + document = MakeSystemEnumNode(primaryDoc); + break; + } + case OwnerKind.SimpleEnum: + { + document = MakeSimpleEnumNode(primaryDoc); + break; + } + case OwnerKind.Class: + { + document = MakeClassNode(primaryDoc); + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + + if (document != null) + documentsByName.Add(primaryDoc.Title, document); + } + + var toc = BuildTableOfContents(items, documentsByName).ToList(); + foreach (var keyValuePair in documentsByName.OrderBy(kv => kv.Key)) + { + categoryOthers.AddChildDocument(keyValuePair.Value); + } + + toc.Add(categoryOthers); + model.Items = toc; + + return model; + } + + private IDocument MakeSystemEnumNode(PrimaryBslDocument primaryDoc) + { + var typeMarkup = MarkupProvider.GetEnumMarkup(primaryDoc.Owner); + // TODO сделать модель енумов для ручных енумов + return new EnumModel + { + Owner = primaryDoc.Owner, + Name = new BilingualString(typeMarkup.Name, typeMarkup.Alias), + Description = XmlSummary(primaryDoc.SelfDoc), + Items = LoadSystemEnumItems(primaryDoc.Owner), + Example = XmlTextBlock(primaryDoc.SelfDoc, "example"), + SeeAlso = XmlSeeAlso(primaryDoc.SelfDoc) + }; + } + + private List LoadSystemEnumItems(Type enumType) + { + var items = new List(); + var instantiator = enumType.GetMethod("CreateInstance", BindingFlags.Static | BindingFlags.Public); + if (instantiator == null) + return items; + + var enumInstance = (IEnumerable)instantiator.Invoke(null, new[] { new FakeTypeManager() }); + if (enumInstance == null) + return items; + + + foreach (var unknownTypeItem in enumInstance) + { + if (unknownTypeItem is EnumerationValue enumValue) + { + items.Add(new EnumItemModel + { + Name = new BilingualString(enumValue.Name, enumValue.Alias) + }); + } + } + + return items; + } + + private IDocument MakeSimpleEnumNode(PrimaryBslDocument primaryDoc) + { + var typeMarkup = MarkupProvider.GetEnumMarkup(primaryDoc.Owner); + + return new EnumModel + { + Owner = primaryDoc.Owner, + Name = new BilingualString(typeMarkup.Name, typeMarkup.Alias), + Description = XmlSummary(primaryDoc.SelfDoc), + Example = XmlTextBlock(primaryDoc.SelfDoc, "example"), + Items = primaryDoc.Fields.Select(kv => ConvertEnumValue(kv.Key, kv.Value)).ToList(), + SeeAlso = XmlSeeAlso(primaryDoc.SelfDoc) + }; + } + + private IDocument MakeClassNode(PrimaryBslDocument primaryDoc) + { + var typeMarkup = MarkupProvider.GetClassMarkup(primaryDoc.Owner); + return new ClassModel + { + Owner = primaryDoc.Owner, + Name = new BilingualString(typeMarkup.Name, typeMarkup.Alias), + Description = XmlSummary(primaryDoc.SelfDoc), + Example = XmlTextBlock(primaryDoc.SelfDoc, "example"), + Properties = primaryDoc.Properties.Select(kv => ConvertProperty(kv.Key, kv.Value)).ToList(), + Methods = primaryDoc.Methods.Select(kv => ConvertMethod(kv.Key, kv.Value)).ToList(), + Constructors = primaryDoc.Constructors.Select(kv => ConvertConstructor(kv.Key, kv.Value)).ToList(), + SeeAlso = XmlSeeAlso(primaryDoc.SelfDoc) + }; + } + + private IEnumerable BuildTableOfContents(List items, Dictionary documentsByName) + { + var tocCategories = new List(); + VisitTocItems(items, tocCategories, documentsByName); + + return tocCategories; + } + + private void VisitTocItems(IEnumerable items, IList categories, + Dictionary categoriesByName) + { + if (items == null) + return; + + foreach (var tocItem in items) + { + var declaredCategory = new SyntaxGroup(); + + if (categoriesByName.TryGetValue(tocItem.Text, out var selfDoc)) + { + declaredCategory.Document = selfDoc; + categoriesByName.Remove(tocItem.Text); + } + else + { + declaredCategory.Title = tocItem.Text; + declaredCategory.Page = tocItem.GeneratePage; + } + + categories.Add(declaredCategory); + + VisitTocItems(tocItem.Items, declaredCategory.Items, categoriesByName); + } + } + + private IDocument MakeGlobalContextNode(PrimaryBslDocument primaryDoc) + { + var contextModel = new GlobalContextModel(); + contextModel.Owner = primaryDoc.Owner; + contextModel.Title = primaryDoc.Title; + contextModel.Description = XmlSummary(primaryDoc.SelfDoc); + contextModel.Properties = primaryDoc.Properties.Select(kv => ConvertProperty(kv.Key, kv.Value)).ToList(); + contextModel.Methods = primaryDoc.Methods.Select(kv => ConvertMethod(kv.Key, kv.Value)).ToList(); + contextModel.SeeAlso = XmlSeeAlso(primaryDoc.SelfDoc); + + return contextModel; + } + + private PropertyModel ConvertProperty(PropertyInfo property, XElement documentation) + { + var model = new PropertyModel(); + + try + { + var markup = MarkupProvider.GetPropertyMarkup(property); + model.Name = new BilingualString(markup.Name, markup.Alias); + (model.CanRead, model.CanWrite) = PropertyReadWriteFlags(property, markup); + } + catch (ArgumentException) + { + var markup = MarkupProvider.GetMemberDocumentationMarkup(property); + model.Name = new BilingualString(markup.Name, markup.Alias); + (model.CanRead, model.CanWrite) = PropertyReadWriteFlags(property); + } + + model.Description = XmlSummary(documentation); + model.Returns = XmlTextBlock(documentation, "value"); + + model.ClrName = property.Name; + model.Example = XmlTextBlock(documentation, "example", true); + model.SeeAlso = XmlSeeAlso(documentation); + + return model; + } + + private (bool canRead, bool canWrite) PropertyReadWriteFlags(PropertyInfo property, ContextPropertyAttribute markup) + { + var hasReader = property.GetGetMethod() != null; + var hasWriter = property.GetSetMethod() != null; + + return (hasReader && markup.CanRead, hasWriter && markup.CanWrite); + } + + private (bool canRead, bool canWrite) PropertyReadWriteFlags(PropertyInfo property) + { + var hasReader = property.GetGetMethod() != null; + var hasWriter = property.GetSetMethod() != null; + + return (hasReader, hasWriter); + } + + private MethodModel ConvertMethod(MethodInfo method, XElement documentation) + { + var model = new MethodModel(); + + try + { + var markup = MarkupProvider.GetMethodMarkup(method); + model.Name = new BilingualString(markup.Name, markup.Alias); + } + catch (ArgumentException) + { + var markup = MarkupProvider.GetMemberDocumentationMarkup(method); + model.Name = new BilingualString(markup.Name, markup.Alias); + } + + model.Description = XmlSummary(documentation); + model.ReturnTypeDocumentation = XmlReturns(documentation); + model.Example = XmlTextBlock(documentation, "example", true); + model.Parameters = CreateParameters(method, documentation, p => p.ParameterType != typeof(IBslProcess)); + model.SeeAlso = XmlSeeAlso(documentation); + + return model; + } + + private List CreateParameters(MethodInfo method, XElement documentation, Func filter) + { + var documentedParams = documentation?.Elements("param") + .ToDictionary(elem => elem.Attribute("name")?.Value ?? + throw new ArgumentException($"No name attribute in param section for method {method.DeclaringType}.{method.Name}")) + ?? new Dictionary(); + + var models = new List(); + + foreach (var parameter in method.GetParameters().Where(filter)) + { + documentedParams.TryGetValue(parameter.Name!, out var paramDoc); + var model = new ParameterModel + { + Name = parameter.Name, + Description = _docConverter.ConvertTextBlock(paramDoc), + DefaultValue = parameter.DefaultValue?.ToString(), + IsOptional = parameter.IsOptional + }; + + models.Add(model); + } + + return models; + } + + private MethodModel ConvertConstructor(MethodInfo method, XElement documentation) + { + var model = new MethodModel(); + + try + { + var markup = MarkupProvider.GetConstructorMarkup(method); + model.Name = new BilingualString(markup.Name ?? "Основной"); + } + catch (ArgumentException) + { + var markup = MarkupProvider.GetMemberDocumentationMarkup(method); + model.Name = new BilingualString(markup.Name, markup.Alias); + } + + if (documentation != null) + { + model.Description = XmlSummary(documentation); + model.ReturnTypeDocumentation = XmlReturns(documentation); + model.Example = XmlTextBlock(documentation, "example", true); + model.Parameters = CreateParameters(method, documentation, p => p.ParameterType != typeof(TypeActivationContext)); + } + + return model; + } + + private EnumItemModel ConvertEnumValue(FieldInfo field, XElement documentation) + { + var markup = MarkupProvider.GetEnumValueMarkup(field); + return new EnumItemModel + { + Name = new BilingualString(markup.Name, markup.Alias), + Description = XmlSummary(documentation) + }; + } + + private string XmlSummary(XElement docs) + { + return XmlTextBlock(docs, "summary"); + } + + private string XmlReturns(XElement docs) + { + return XmlTextBlock(docs, "returns"); + } + + private string XmlTextBlock(XElement docs, string nodeName, bool isCode = false) + { + var stringValue = _docConverter.ConvertTextBlock(docs?.Element(nodeName), !isCode); + return stringValue == "" ? null : // Чтобы свойство не писал сериализатор JSON + stringValue; + } + + private IReadOnlyCollection XmlSeeAlso(XElement doc) + { + var links = _docConverter.ConvertSeeAlsoList(doc); + return links.Count == 0 ? null : links; + } + + private class FakeTypeManager : ITypeManager + { + public TypeDescriptor GetTypeByName(string name) + { + throw new NotImplementedException(); + } + + public TypeDescriptor GetTypeByFrameworkType(Type type) + { + throw new NotImplementedException(); + } + + public bool TryGetType(string name, out TypeDescriptor type) + { + throw new NotImplementedException(); + } + + public bool TryGetType(Type frameworkType, out TypeDescriptor type) + { + throw new NotImplementedException(); + } + + public TypeDescriptor RegisterType(string name, string alias, Type implementingClass) + { + return default; + } + + public void RegisterType(TypeDescriptor typeDescriptor) + { + + } + + public ITypeFactory GetFactoryFor(TypeDescriptor type) + { + throw new NotImplementedException(); + } + + public bool IsKnownType(Type type) + { + throw new NotImplementedException(); + } + + public bool IsKnownType(string typeName) + { + throw new NotImplementedException(); + } + + public IReadOnlyList RegisteredTypes() + { + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/IReferenceResolver.cs b/src/OneScriptDocumenter/Secondary/IReferenceResolver.cs new file mode 100644 index 000000000..3a51992b0 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/IReferenceResolver.cs @@ -0,0 +1,14 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter.Secondary +{ + public interface IReferenceResolver + { + public string Resolve(string linkTarget, string linkText); + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/MarkdownReferenceResolver.cs b/src/OneScriptDocumenter/Secondary/MarkdownReferenceResolver.cs new file mode 100644 index 000000000..5caf18513 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/MarkdownReferenceResolver.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +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 OneScriptDocumenter.Primary; + +namespace OneScriptDocumenter.Secondary +{ + public class MarkdownReferenceResolver : IReferenceResolver + { + private readonly IReferencesRegistry _registry; + private readonly ReferenceFactory _referenceFactory; + + public MarkdownReferenceResolver(IReferencesRegistry registry, ReferenceFactory referenceFactory) + { + _registry = registry; + _referenceFactory = referenceFactory; + } + + public string Resolve(string linkTarget, string linkText) + { + var referenceOrNull = _registry.Get(linkTarget); + if (referenceOrNull == null) + { + ConsoleLogger.Warning($"Can't resolve reference {linkTarget}"); + return linkText; + } + + var reference = referenceOrNull.Value; + + var ownerType = reference.Owner; + var memberInfo = reference.Member; + + string target; + if (linkText != "") + { + target = _referenceFactory.CreateReference(ownerType, memberInfo); + } + else + { + var data = _referenceFactory.CreateReferenceWithText(ownerType, memberInfo); + linkText = data.LinkText; + target = data.LinkTarget; + } + + return MarkdownLink(target, linkText); + } + + public static string MarkdownLink(string linkTarget, string linkText) + { + return $"[{linkText}]({linkTarget})"; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/MarkupProvider.cs b/src/OneScriptDocumenter/Secondary/MarkupProvider.cs new file mode 100644 index 000000000..2f4f255a6 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/MarkupProvider.cs @@ -0,0 +1,107 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Contexts.Enums; + +namespace OneScriptDocumenter.Secondary +{ + public static class MarkupProvider + { + public static ContextClassAttribute GetClassMarkup(Type target) + { + return target.GetCustomAttribute() ?? + throw new ArgumentException($"Type {target} is not marked with class attribute"); + } + + public static INameAndAliasProvider GetEnumMarkup(Type target) + { + try + { + return target.GetCustomAttributes() + .Where(a => a is SystemEnumAttribute || a is EnumerationTypeAttribute) + .Cast() + .Single(); + } + catch (InvalidOperationException e) + { + throw new ArgumentException($"Type {target} is not marked with enum attribute", e); + } + } + + public static INameAndAliasProvider GetEnumValueMarkup(FieldInfo target) + { + try + { + return target.GetCustomAttribute(); + } + catch (AmbiguousMatchException e) + { + throw new ArgumentException($"Enum value {target} is not marked with enum attribute", e); + } + } + + public static ContextMethodAttribute GetMethodMarkup(MethodInfo target) + { + var markup = target.GetCustomAttribute() + ?? throw new ArgumentException($"Method {target} is not marked with method attribute"); + + if (markup.SkipForDocumenter) + { + throw new ArgumentException($"Method {target} is skipped for documentation"); + } + + if (markup.IsDeprecated) + { + throw new ArgumentException($"Method {target} is deprecated"); + } + + return markup; + } + + public static INameAndAliasProvider GetMemberDocumentationMarkup(MemberInfo target) + { + try + { + return target.GetCustomAttribute(); + } + catch (AmbiguousMatchException e) + { + throw new ArgumentException($"Member {target} is not marked with docs attribute", e); + } + } + + public static ScriptConstructorAttribute GetConstructorMarkup(MethodInfo target) + { + try + { + return target.GetCustomAttribute(); + } + catch (AmbiguousMatchException e) + { + throw new ArgumentException($"Method {target} is marked by several constructor attributes", e); + } + } + + public static ContextPropertyAttribute GetPropertyMarkup(PropertyInfo target) + { + try + { + return target + .GetCustomAttributes() + .Single(attr => !attr.SkipForDocumenter); + } + catch (InvalidOperationException e) + { + throw new ArgumentException($"Property {target} is not marked with property attribute", e); + } + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/ReferenceFactory.cs b/src/OneScriptDocumenter/Secondary/ReferenceFactory.cs new file mode 100644 index 000000000..917f34e91 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/ReferenceFactory.cs @@ -0,0 +1,79 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Reflection; +using OneScript.Contexts; + +namespace OneScriptDocumenter.Secondary +{ + public class ReferenceFactory + { + private string _baseUrl = "/syntax/"; + + public struct ReferenceData + { + public string LinkTarget { get; set; } + public string LinkText { get; set; } + } + + public ReferenceData CreateReferenceWithText(Type ownerType, MemberInfo memberInfo = null) + { + var typeNameInLink = GetBslNameForAnnotatedObject(ownerType); + var memberNameInLink = GetBslNameForAnnotatedObject(memberInfo); + + return new ReferenceData + { + LinkTarget = LinkTarget(typeNameInLink, memberNameInLink), + LinkText = memberNameInLink ?? typeNameInLink + }; + } + + public string CreateReference(Type ownerType, MemberInfo memberInfo = null) + { + var typeNameInLink = GetBslNameForAnnotatedObject(ownerType); + var memberNameInLink = GetBslNameForAnnotatedObject(memberInfo); + + return $"{_baseUrl}{LinkTarget(typeNameInLink, memberNameInLink)}"; + } + + public string BaseUrl + { + get => _baseUrl; + set => _baseUrl = AdoptBaseUrl(value); + } + + private string LinkTarget(string typeNameInLink, string memberNameInLink) + { + return memberNameInLink == null ? typeNameInLink : typeNameInLink + "#" + memberNameInLink; + } + + private static string AdoptBaseUrl(string url) + { + var finalBaseUrl = url; + if (finalBaseUrl == "") + finalBaseUrl = "/"; + else if (!finalBaseUrl.EndsWith("/")) + finalBaseUrl += "/"; + + return finalBaseUrl; + } + + public static string GetBslNameForAnnotatedObject(MemberInfo memberInfo) + { + if (memberInfo == null) + return null; + + return memberInfo.GetCustomAttributes() + .Where(a => a is INameAndAliasProvider) + .Cast() + .Select(na => !string.IsNullOrEmpty(na.Alias) ? na.Alias : na.Name) + .FirstOrDefault() ?? memberInfo.Name; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/TableOfContents.cs b/src/OneScriptDocumenter/Secondary/TableOfContents.cs new file mode 100644 index 000000000..f3428671d --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/TableOfContents.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/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace OneScriptDocumenter.Secondary +{ + public class TableOfContents + { + [JsonPropertyName("items")] + public List Items { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/TocItem.cs b/src/OneScriptDocumenter/Secondary/TocItem.cs new file mode 100644 index 000000000..0ccbf0e77 --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/TocItem.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +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.Text.Json.Serialization; + +namespace OneScriptDocumenter.Secondary +{ + public class TocItem + { + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("items")] + public List Items { get; set; } + + [JsonPropertyName("page")] + public string GeneratePage { get; set; } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/Secondary/XmlDocConverter.cs b/src/OneScriptDocumenter/Secondary/XmlDocConverter.cs new file mode 100644 index 000000000..201fadf2d --- /dev/null +++ b/src/OneScriptDocumenter/Secondary/XmlDocConverter.cs @@ -0,0 +1,165 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace OneScriptDocumenter.Secondary +{ + public class XmlDocConverter + { + private readonly IReferenceResolver _referenceResolver; + + public XmlDocConverter(IReferenceResolver referenceResolver) + { + _referenceResolver = referenceResolver; + } + + public string ConvertTextBlock(XElement? textBlock, bool resolveLinks = true) + { + if (textBlock == null) + return ""; + + var markdownContent = new StringBuilder(); + + VisitNodes(markdownContent, textBlock); + + return markdownContent.ToString(); + } + + public IReadOnlyCollection ConvertSeeAlsoList(XElement docs) + { + if (docs == null) + return Array.Empty(); + + var seeAlso = docs.Elements("seealso").ToList(); + if (seeAlso.Count == 0) + return Array.Empty(); + + return seeAlso.Select(node => + { + var markdownContent = new StringBuilder(); + ProcessLinkElement(node, markdownContent); + return markdownContent.ToString(); + }).ToList(); + } + + private void ProcessLinkElement(XElement node, StringBuilder markdownContent) + { + var targetAttribute = node.Attribute("cref"); + if (targetAttribute == null) + { + // Не будем специально обрабатывать этот элемент + VisitNodes(markdownContent, node); + } + else + { + var referenceTarget = targetAttribute.Value; + var referenceText = node.Value; + var linkCode = _referenceResolver.Resolve(referenceTarget, referenceText); + markdownContent.Append(linkCode); + } + } + + private void VisitNodes(StringBuilder markdownContent, XElement textBlock) + { + bool trimFirstLine = true; + foreach (var node in textBlock.Nodes()) + { + switch (node.NodeType) + { + case XmlNodeType.Text: + ProcessTextBlock(markdownContent, (XText)node, trimFirstLine); + break; + case XmlNodeType.Element: + trimFirstLine = false; + ProcessInlineElement(markdownContent, (XElement)node); + break; + } + } + } + + private void ProcessInlineElement(StringBuilder markdownContent, XElement node) + { + if (node.Name == "see") + { + ProcessLinkElement(node, markdownContent); + } + else if (node.Name == "c") + { + markdownContent.Append('`'); + markdownContent.Append(node.Value); + markdownContent.Append('`'); + } + else if (node.Name == "para") + { + markdownContent.AppendLine(); + VisitNodes(markdownContent, node); + markdownContent.AppendLine(); + } + } + + private void ProcessTextBlock(StringBuilder sb, XText textNode, bool trimFirstLine) + { + int indentOfFirstLine = 0; + bool lineWritten = false; + bool firstLineFound = false; + + using var sr = new StringReader(textNode.Value); + + string line = null; + do + { + line = sr.ReadLine(); + if (line != null) + { + if (string.IsNullOrWhiteSpace(line)) + { + lineWritten = firstLineFound; + continue; + } + + if (lineWritten) + { + sb.AppendLine(); + } + + if (firstLineFound) + { + sb.Append(line.Substring(indentOfFirstLine)); + } + else + { + firstLineFound = true; + indentOfFirstLine = CalculateIndent(line); + sb.Append(trimFirstLine ? line.TrimStart() : line); + } + + lineWritten = true; + } + } while (line != null); + } + + private int CalculateIndent(string line) + { + for (int i = 0; i < line.Length; i++) + { + if (!char.IsWhiteSpace(line[i])) + { + return i; + } + } + + return 0; + } + } +} \ No newline at end of file diff --git a/src/OneScriptDocumenter/TypesDictionary.cs b/src/OneScriptDocumenter/TypesDictionary.cs deleted file mode 100644 index bbf780a6b..000000000 --- a/src/OneScriptDocumenter/TypesDictionary.cs +++ /dev/null @@ -1,66 +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.Collections.Generic; -using Newtonsoft.Json; - -namespace OneScriptDocumenter -{ - class TypeInfo - { - public string fullName = ""; - public string ShortName = ""; - public string nameEng = ""; - public string nameRus = ""; - } - - class TypesDictionary - { - readonly List list; - - public TypesDictionary() - { - if (System.IO.File.Exists(@"map.json")) - { - string jsonData = System.IO.File.ReadAllText(@"map.json"); - list = JsonConvert.DeserializeObject>(jsonData); - } - else - { - list = new List(); - } - } - - public TypeInfo findByFullName(string fullName) - { - - foreach (TypeInfo curType in list) - { - if (curType.fullName == fullName) - { - return curType; - } - } - - return null; - } - - public void add(TypeInfo value) - { - if (findByFullName(value.fullName) == null) - { - list.Add(value); - } - } - - public void save() - { - System.IO.File.WriteAllText(@"map.json", JsonConvert.SerializeObject(list)); - } - - public List types { get { return list; } } - } -} diff --git a/src/OneScriptDocumenter/default_toc.json b/src/OneScriptDocumenter/default_toc.json new file mode 100644 index 000000000..a6981843c --- /dev/null +++ b/src/OneScriptDocumenter/default_toc.json @@ -0,0 +1,253 @@ +{ + "items": [ + { + "text": "Глобальный контекст", + "items": [ + {"text": "Файловые операции"}, + {"text": "Процедуры и функции взаимодействия с системой"}, + {"text": "Работа с настройками системы"}, + {"text": "Операции со строками"}, + {"text": "Динамическое подключение сценариев"}, + {"text": "Работа с переменными окружения"}, + {"text": "Процедуры и функции работы с двоичными данными"}, + {"text": "Побитовые операции с целыми числами"}, + {"text": "Функции работы с XML"}, + {"text": "Процедуры и функции работы с JSON"}, + {"text": "Работа с макетами"}, + {"text": "Процедуры и функции взаимодействия с часовыми поясами и временными зонами"}, + {"text": "Работа с консолью"}, + {"text": "Прочие функции"} + ] + }, + { + "text": "Универсальные коллекции", + "items": [ + {"text": "Массив"}, + {"text": "Соответствие"}, + {"text": "Структура"}, + { + "text": "СписокЗначений", + "items": [ + {"text": "ЭлементСпискаЗначений"} + ] + }, + { + "text": "ТаблицаЗначений", + "items": [ + {"text": "КолонкаТаблицыЗначений"}, + {"text": "КоллекцияКолонокТаблицыЗначений"}, + {"text": "СтрокаТаблицыЗначений"}, + {"text": "ИндексыКоллекции"}, + {"text": "ИндексКоллекции"} + ] + }, + { + "text": "ДеревоЗначений", + "items": [ + {"text": "СтрокаДереваЗначений"}, + {"text": "КолонкаДереваЗначений"}, + {"text": "КоллекцияКолонокДереваЗначений"}, + {"text": "КоллекцияСтрокДереваЗначений"} + ] + }, + {"text": "КлючИЗначение"}, + {"text": "ФиксированныйМассив"}, + {"text": "ФиксированноеСоответствие"}, + {"text": "ФиксированнаяСтруктура"} + ] + }, + { + "text": "Работа с сетью", + "items": [ + {"text": "HTTPСоединение"}, + {"text": "HTTPЗапрос"}, + {"text": "HTTPОтвет"}, + {"text": "ИнтернетПрокси"}, + {"text": "TCPСоединение"}, + {"text": "TCPСервер"}, + {"text": "ОписаниеФайлаPostЗапроса"}, + {"text": "ДанныеPOSTЗапроса"} + ] + }, + { + "text": "Работа с ZIP", + "items": [ + {"text": "ЗаписьZipФайла"}, + {"text": "ЧтениеZipФайла"}, + {"text": "ЭлементыZipФайла"}, + {"text": "ЭлементZipФайла"}, + {"text": "МетодСжатияZIP"}, + {"text": "РежимСохраненияПутейZIP"}, + {"text": "УровеньСжатияZIP"}, + {"text": "МетодШифрованияZIP"}, + {"text": "РежимВосстановленияПутейФайловZIP"}, + {"text": "РежимОбработкиПодкаталоговZIP"}, + {"text": "КодировкаИменФайловВZipФайле"} + ] + }, + { + "text": "Работа с двоичными данными", + "items": [ + {"text": "БуферДвоичныхДанных"}, + {"text": "ФайловыйПоток"}, + {"text": "ПотокВПамяти"}, + {"text": "Поток"}, + {"text": "МенеджерФайловыхПотоков"}, + {"text": "ДвоичныеДанные"}, + {"text": "ЧтениеДанных"}, + {"text": "ЗаписьДанных"}, + {"text": "РезультатЧтенияДанных"} + ] + }, + { + "text": "Регулярные выражения", + "items": [ + {"text": "РегулярноеВыражение"}, + {"text": "ГруппаРегулярногоВыражения"}, + {"text": "КоллекцияГруппРегулярногоВыражения"}, + {"text": "КоллекцияСовпаденийРегулярногоВыражения"}, + {"text": "СовпадениеРегулярногоВыражения"} + ] + }, + { + "text": "Работа с фоновыми заданиями", + "items": [ + {"text": "ФоновоеЗадание"}, + {"text": "МенеджерФоновыхЗаданий"}, + {"text": "БлокировкаРесурса"} + ] + }, + { + "text": "Работа с форматами сериализации", + "items": [ + { + "text": "Работа с XML", + "page": "xml", + "items": [ + {"text": "ЧтениеXML"}, + {"text": "ЗаписьXML"}, + {"text": "КонтекстПространствИменXML"}, + {"text": "ПараметрыЗаписиXML"}, + {"text": "ПараметрыЧтенияXML"}, + {"text": "ТипУзлаXML"}, + {"text": "ПробельныеСимволыXML"}, + {"text": "РасширенноеИмяXML"}, + {"text": "ПреобразованиеXSL"} + ] + }, + { + "text": "Работа с JSON", + "page": "json", + "items": [ + {"text": "ЧтениеJSON"}, + {"text": "ЗаписьJSON"}, + {"text": "ФорматДатыJSON"}, + {"text": "ТипЗначенияJSON"}, + {"text": "ПараметрыЗаписиJSON"}, + {"text": "ЭкранированиеСимволовJSON"}, + {"text": "ПереносСтрокJSON"} + ] + }, + { + "text": "Схемы XML и XDTO", + "page": "xdto", + "items": [ + {"text": "СериализаторXDTO"}, + {"text": "НаборСхемXML"}, + {"text": "ОбъединениеЗавершенностиСоставногоТипаXS"}, + {"text": "ФиксированныйСписокКомпонентXS"}, + {"text": "СписокКомпонентXS"}, + {"text": "ОбъединениеНедопустимыхПодстановкиXS"}, + {"text": "КоллекцияИменованныхКомпонентXS"}, + {"text": "ЗапрещенныеПодстановкиXS"}, + {"text": "НедопустимыеПодстановкиXS"}, + {"text": "ОбъединениеЗапрещенныхПодстановокXS"}, + {"text": "ИсключенияГруппПодстановкиXS"}, + {"text": "ОбъединениеЗавершенностиСхемыXS"}, + {"text": "ОбъединениеЗавершенностиПростогоТипаXS"}, + {"text": "ОбъединениеИсключенийГруппПодстановкиXS"}, + {"text": "СхемаXML"}, + {"text": "АннотацияXS"}, + {"text": "ФормаПредставленияXS"}, + {"text": "ИнформацияДляПриложенияXS"}, + {"text": "ОбъявлениеАтрибутаXS"}, + {"text": "ОпределениеГруппыАтрибутовXS"}, + {"text": "ИспользованиеАтрибутаXS"}, + {"text": "ОпределениеСоставногоТипаXS"}, + {"text": "ДокументацияXS"}, + {"text": "ОбъявлениеЭлементаXS"}, + {"text": "ФасетПеречисленияXS"}, + {"text": "ФасетКоличестваРазрядовДробнойЧастиXS"}, + {"text": "ОпределениеОграниченияИдентичностиXS"}, + {"text": "ИмпортXS"}, + {"text": "ВключениеXS"}, + {"text": "ФасетДлиныXS"}, + {"text": "ФасетМаксимальногоИсключающегоЗначенияXS"}, + {"text": "ФасетМаксимальногоВключающегоЗначенияXS"}, + {"text": "ФасетМаксимальнойДлиныXS"}, + {"text": "ФасетМинимальногоИсключающегоЗначенияXS"}, + {"text": "ФасетМинимальногоВключающегоЗначенияXS"}, + {"text": "ФасетМинимальнойДлиныXS"}, + {"text": "ГруппаМоделиXS"}, + {"text": "ОпределениеГруппыМоделиXS"}, + {"text": "ОбъявлениеНотацииXS"}, + {"text": "ФрагментXS"}, + {"text": "ФасетОбразцаXS"}, + {"text": "ПереопределениеXS"}, + {"text": "ОпределениеПростогоТипаXS"}, + {"text": "ФасетОбщегоКоличестваРазрядовXS"}, + {"text": "ФасетПробельныхСимволовXS"}, + {"text": "МаскаXS"}, + {"text": "ОпределенияXPathXS"}, + {"text": "ОбработкаПробельныхСимволовXS"}, + {"text": "ВариантXPathXS"}, + {"text": "ВариантПростогоТипаXS"}, + {"text": "ОбработкаСодержимогоXS"}, + {"text": "КатегорияОграниченияПространствИменXS"}, + {"text": "КатегорияОграниченияИдентичностиXS"}, + {"text": "МетодНаследованияXS"}, + {"text": "МодельСодержимогоXS"}, + {"text": "ОграничениеЗначенияXS"}, + {"text": "ВидГруппыМоделиXS"}, + {"text": "ТипКомпонентыXS"}, + {"text": "КатегорияИспользованияАтрибутаXS"}, + {"text": "НазначениеТипаXML"}, + {"text": "ФормаXML"}, + {"text": "ЗавершенностьПростогоТипаXS"}, + {"text": "ЗавершенностьСоставногоТипаXS"}, + {"text": "ЗавершенностьСхемыXS"}, + {"text": "ТипПроверкиXML"} + ] + } + ] + }, + { + "text": "Веб-сервер Kestrel", + "items": [ + {"text": "ВебСервер"}, + {"text": "HTTPКонтекст"}, + {"text": "HTTPСервисЗапрос"}, + {"text": "HTTPСервисОтвет"}, + {"text": "ВебСокет"}, + {"text": "ДелегатЗапроса"}, + {"text": "ИнформацияОСоединении"}, + {"text": "КонтекстПодключенияВебСокета"}, + {"text": "КукиЗапроса"}, + {"text": "КукиОтвета"}, + {"text": "МенеджерВебСокетов"}, + {"text": "ПараметрыCookie"}, + {"text": "РежимSameSite"}, + {"text": "РезультатВебСокетПолучения"}, + {"text": "СловарьЗаголовков"}, + {"text": "СостояниеВебСокета"}, + {"text": "СтатусЗакрытияВебСокета"}, + {"text": "СтроковыеЗначения"}, + {"text": "ТипСообщенияВебСокета"}, + {"text": "ФайлФормы"}, + {"text": "ФайлыФормы"}, + {"text": "ФлагиСообщенияВебСокета"}, + {"text": "Форма"} + ] + } + ] +} diff --git a/src/OneScriptDocumenter/html_layout.htm b/src/OneScriptDocumenter/html_layout.htm deleted file mode 100644 index 3fea89e9d..000000000 --- a/src/OneScriptDocumenter/html_layout.htm +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - $title$ - - - $body$ - - \ No newline at end of file diff --git a/src/OneScriptDocumenter/packages.config b/src/OneScriptDocumenter/packages.config deleted file mode 100644 index 8f3473a80..000000000 --- a/src/OneScriptDocumenter/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/OneScriptDocumenter/structureMenu.json b/src/OneScriptDocumenter/structureMenu.json deleted file mode 100644 index cfcc0f1f8..000000000 --- a/src/OneScriptDocumenter/structureMenu.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "global" : { - "Базовые методы встроенного языка" : "", - "Файловые операции" : "", - "Процедуры и функции взаимодействия с системой" : "", - "Функции работы с XML" : "", - "Работа с настройками системы" : "", - "Операции со строками" : "", - "Прочие функции" : "" - }, - "classes" : { - "Универсальные коллекции" : { - "Массив" : "", - "Соответствие" : "", - "Структура" : "", - "СписокЗначений" : { - "ЭлементСпискаЗначений" : "" - }, - "ТаблицаЗначений" : { - "КолонкаТаблицыЗначений" : "", - "КоллекцияКолонокТаблицыЗначений" : "", - "СтрокаТаблицыЗначений" : "", - "ИндексыКоллекции" : "", - "ИндексКоллекции" : "" - }, - "ДеревоЗначений" : { - "СтрокаДереваЗначений" : "", - "КолонкаДереваЗначений" : "", - "КоллекцияКолонокДереваЗначений" : "", - "КоллекцияСтрокДереваЗначений" : "" - }, - "КлючИЗначение" : "", - "ФиксированныйМассив" : "", - "ФиксированноеСоответствие" : "", - "ФиксированнаяСтруктура" : "" - }, - "Работа с сетью" : { - "HTTPСоединение" : "", - "HTTPЗапрос" : "", - "HTTPОтвет" : "", - "ИнтернетПрокси" : "", - "TCPСоединение" : "", - "TCPСервер" : "", - "ОписаниеФайлаPostЗапроса" : "", - "ДанныеPOSTЗапроса" : "" - }, - "Работа с двоичными данными" : { - "БуферДвоичныхДанных" : "", - "ФайловыйПоток" : "", - "ПотокВПамяти" : "", - "Поток" : "", - "МенеджерФайловыхПотоков" : "", - }, - "Работа с ZIP" : { - "ЗаписьZipФайла" : "", - "ЧтениеZipФайла" : "", - "ЭлементыZipФайла" : "", - "ЭлементZipФайла" : "" - }, - "Регулярные выражения" : { - "РегулярноеВыражение" : "", - "ГруппаРегулярногоВыражения" : "", - "КоллекцияГруппРегулярногоВыражения" : "", - "КоллекцияСовпаденийРегулярногоВыражения" : "", - "СовпадениеРегулярногоВыражения" : "" - }, - "Прочее" : "" - } -} diff --git a/src/OneScriptDocumenter/toc_layout.md b/src/OneScriptDocumenter/toc_layout.md deleted file mode 100644 index 6adebbb54..000000000 --- a/src/OneScriptDocumenter/toc_layout.md +++ /dev/null @@ -1,136 +0,0 @@ -# Стандартная библиотека классов и функций -Ниже приведен перечень классов, доступных в контексте скрипта. - -## Глобальный контекст - -* [Файловые операции]($base_url$/Файловые%20операции) -* [Процедуры и функции взаимодействия с системой]($base_url$/Процедуры%20и%20функции%20взаимодействия%20с%20системой) -* [Функции работы с XML]($base_url$/Функции%20работы%20с%20XML) -* [Работа с настройками системы]($base_url$/Работа%20с%20настройками%20системы) -* [Операции со строками]($base_url$/Операции%20со%20строками) -* [Работа с переменными окружения]($base_url$/Работа%20с%20переменными%20окружения) -* [Процедуры и функции работы с двоичными данными]($base_url$/Процедуры%20и%20функции%20работы%20с%20двоичными%20данными) -* [Побитовые операции с целыми числами]($base_url$/Побитовые%20операции%20с%20целыми%20числами) -* [Процедуры и функции работы с JSON]($base_url$/Процедуры%20и%20функции%20работы%20с%20JSON) -* [Работа с макетами]($base_url$/Работа%20с%20макетами) -* [Процедуры и функции взаимодействия с часовыми поясами и временными зонами]($base_url$/Процедуры%20и%20функции%20взаимодействия%20с%20часовыми%20поясами%20и%20временными%20зонами) -* [Прочие функции]($base_url$/Прочие%20функции) - -## Доступные классы - -### Универсальные коллекции - -* [Массив]($base_url$/Массив) -* [Соответствие]($base_url$/Соответствие) -* [Структура]($base_url$/Структура) -* [СписокЗначений]($base_url$/СписокЗначений) - * [ЭлементСпискаЗначений]($base_url$/ЭлементСпискаЗначений) -* [ТаблицаЗначений]($base_url$/ТаблицаЗначений) - * [КолонкаТаблицыЗначений]($base_url$/КолонкаТаблицыЗначений) - * [КоллекцияКолонокТаблицыЗначений]($base_url$/КоллекцияКолонокТаблицыЗначений) - * [СтрокаТаблицыЗначений]($base_url$/СтрокаТаблицыЗначений) - * [ИндексыКоллекции]($base_url$/ИндексыКоллекции) - * [ИндексКоллекции]($base_url$/ИндексКоллекции) -* [ДеревоЗначений]($base_url$/ДеревоЗначений) - * [СтрокаДереваЗначений]($base_url$/СтрокаДереваЗначений) - * [КолонкаДереваЗначений]($base_url$/КолонкаДереваЗначений) - * [КоллекцияКолонокДереваЗначений]($base_url$/КоллекцияКолонокДереваЗначений) - * [КоллекцияСтрокДереваЗначений]($base_url$/КоллекцияСтрокДереваЗначений) -* [КлючИЗначение]($base_url$/КлючИЗначение) -* [ФиксированныйМассив]($base_url$/ФиксированныйМассив) -* [ФиксированноеСоответствие]($base_url$/ФиксированноеСоответствие) -* [ФиксированнаяСтруктура]($base_url$/ФиксированнаяСтруктура) - -### Работа с сетью - -* [HTTPСоединение]($base_url$/HTTPСоединение) -* [HTTPЗапрос]($base_url$/HTTPЗапрос) -* [HTTPОтвет]($base_url$/HTTPОтвет) -* [ИнтернетПрокси]($base_url$/ИнтернетПрокси) -* [TCPСоединение]($base_url$/TCPСоединение) -* [TCPСервер]($base_url$/TCPСервер) -* [ОписаниеФайлаPostЗапроса]($base_url$/ОписаниеФайлаPostЗапроса) -* [ДанныеPOSTЗапроса]($base_url$/ДанныеPOSTЗапроса) - -### Работа с ZIP - -* [ЗаписьZipФайла]($base_url$/ЗаписьZipФайла) -* [ЧтениеZipФайла]($base_url$/ЧтениеZipФайла) -* [ЭлементыZipФайла]($base_url$/ЭлементыZipФайла) -* [ЭлементZipФайла]($base_url$/ЭлементZipФайла) - -### Работа с двоичными данными - -* [БуферДвоичныхДанных]($base_url$/БуферДвоичныхДанных) -* [ФайловыйПоток]($base_url$/ФайловыйПоток) -* [ПотокВПамяти]($base_url$/ПотокВПамяти) -* [Поток]($base_url$/Поток) -* [МенеджерФайловыхПотоков]($base_url$/МенеджерФайловыхПотоков) -* [ДвоичныеДанные]($base_url$/ДвоичныеДанные) -* [ЧтениеДанных]($base_url$/ЧтениеДанных) -* [ЗаписьДанных]($base_url$/ЗаписьДанных) -* [РезультатЧтенияДанных]($base_url$/РезультатЧтенияДанных) - -### Регулярные выражения - -* [РегулярноеВыражение]($base_url$/РегулярноеВыражение) -* [ГруппаРегулярногоВыражения]($base_url$/ГруппаРегулярногоВыражения) -* [КоллекцияГруппРегулярногоВыражения]($base_url$/КоллекцияГруппРегулярногоВыражения) -* [КоллекцияСовпаденийРегулярногоВыражения]($base_url$/КоллекцияСовпаденийРегулярногоВыражения) -* [СовпадениеРегулярногоВыражения]($base_url$/СовпадениеРегулярногоВыражения) - -### Работа с форматами сериализации - -* [ЧтениеXML]($base_url$/ЧтениеXML) -* [ЗаписьXML]($base_url$/ЗаписьXML) -* [КонтекстПространствИменXML]($base_url$/КонтекстПространствИменXML) -* [ПараметрыЗаписиXML]($base_url$/ПараметрыЗаписиXML) -* [ЧтениеJSON]($base_url$/ЧтениеJSON) -* [ЗаписьJSON]($base_url$/ЗаписьJSON) -* [ПараметрыЗаписиJSON]($base_url$/ПараметрыЗаписиJSON) -* [РасширенноеИмяXML]($base_url$/РасширенноеИмяXML) -* [НаборСхемXML]($base_url$/НаборСхемXML) -* [ОбъединениеЗавершенностиСоставногоТипаXS]($base_url$/ОбъединениеЗавершенностиСоставногоТипаXS) -* [ФиксированныйСписокКомпонентXS]($base_url$/ФиксированныйСписокКомпонентXS) -* [СписокКомпонентXS]($base_url$/СписокКомпонентXS) -* [ОбъединениеНедопустимыхПодстановкиXS]($base_url$/ОбъединениеНедопустимыхПодстановкиXS) -* [КоллекцияИменованныхКомпонентXS]($base_url$/КоллекцияИменованныхКомпонентXS) -* [ОбъединениеЗапрещенныхПодстановокXS]($base_url$/ОбъединениеЗапрещенныхПодстановокXS) -* [ОбъединениеЗавершенностиСхемыXS]($base_url$/ОбъединениеЗавершенностиСхемыXS) -* [ОбъединениеЗавершенностиПростогоТипаXS]($base_url$/ОбъединениеЗавершенностиПростогоТипаXS) -* [ОбъединениеИсключенийГруппПодстановкиXS]($base_url$/ОбъединениеИсключенийГруппПодстановкиXS) -* [СхемаXML]($base_url$/СхемаXML) -* [АннотацияXS]($base_url$/АннотацияXS) -* [ИнформацияДляПриложенияXS]($base_url$/ИнформацияДляПриложенияXS) -* [ОбъявлениеАтрибутаXS]($base_url$/ОбъявлениеАтрибутаXS) -* [ОпределениеГруппыАтрибутовXS]($base_url$/ОпределениеГруппыАтрибутовXS) -* [ИспользованиеАтрибутаXS]($base_url$/ИспользованиеАтрибутаXS) -* [ОпределениеСоставногоТипаXS]($base_url$/ОпределениеСоставногоТипаXS) -* [ДокументацияXS]($base_url$/ДокументацияXS) -* [ОбъявлениеЭлементаXS]($base_url$/ОбъявлениеЭлементаXS) -* [ФасетПеречисленияXS]($base_url$/ФасетПеречисленияXS) -* [ФасетКоличестваРазрядовДробнойЧастиXS]($base_url$/ФасетКоличестваРазрядовДробнойЧастиXS) -* [ОпределениеОграниченияИдентичностиXS]($base_url$/ОпределениеОграниченияИдентичностиXS) -* [ИмпортXS]($base_url$/ИмпортXS) -* [ВключениеXS]($base_url$/ВключениеXS) -* [ФасетДлиныXS]($base_url$/ФасетДлиныXS) -* [ФасетМаксимальногоИсключающегоЗначенияXS]($base_url$/ФасетМаксимальногоИсключающегоЗначенияXS) -* [ФасетМаксимальногоВключающегоЗначенияXS]($base_url$/ФасетМаксимальногоВключающегоЗначенияXS) -* [ФасетМаксимальнойДлиныXS]($base_url$/ФасетМаксимальнойДлиныXS) -* [ФасетМинимальногоИсключающегоЗначенияXS]($base_url$/ФасетМинимальногоИсключающегоЗначенияXS) -* [ФасетМинимальногоВключающегоЗначенияXS]($base_url$/ФасетМинимальногоВключающегоЗначенияXS) -* [ФасетМинимальнойДлиныXS]($base_url$/ФасетМинимальнойДлиныXS) -* [ГруппаМоделиXS]($base_url$/ГруппаМоделиXS) -* [ОпределениеГруппыМоделиXS]($base_url$/ОпределениеГруппыМоделиXS) -* [ОбъявлениеНотацииXS]($base_url$/ОбъявлениеНотацииXS) -* [ФрагментXS]($base_url$/ФрагментXS) -* [ФасетОбразцаXS]($base_url$/ФасетОбразцаXS) -* [ПереопределениеXS]($base_url$/ПереопределениеXS) -* [ОпределениеПростогоТипаXS]($base_url$/ОпределениеПростогоТипаXS) -* [ФасетОбщегоКоличестваРазрядовXS]($base_url$/ФасетОбщегоКоличестваРазрядовXS) -* [ФасетПробельныхСимволовXS]($base_url$/ФасетПробельныхСимволовXS) -* [МаскаXS]($base_url$/МаскаXS) -* [ОпределенияXPathXS]($base_url$/ОпределенияXPathXS) -* [СериализаторXDTO]($base_url$/СериализаторXDTO) - -### Прочее diff --git a/src/ScriptEngine.HostedScript/CfgFileConfigProvider.cs b/src/ScriptEngine.HostedScript/CfgFileConfigProvider.cs new file mode 100644 index 000000000..18ee7a842 --- /dev/null +++ b/src/ScriptEngine.HostedScript/CfgFileConfigProvider.cs @@ -0,0 +1,71 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using ScriptEngine.Hosting; + +namespace ScriptEngine.HostedScript +{ + public class CfgFileConfigProvider : IConfigProvider + { + public const string CONFIG_FILE_NAME = "oscript.cfg"; + + public string FilePath { get; set; } + + public bool Required { get; set; } + + public string SourceId => FilePath; + + public IReadOnlyDictionary Load() + { + return (IReadOnlyDictionary)ReadConfigFile(FilePath); + } + + public string ResolveRelativePath(string path) + { + var confDir = Path.GetDirectoryName(FilePath); + return Path.Combine(confDir, path); + } + + private IDictionary ReadConfigFile(string configPath) + { + var conf = new Dictionary(); + StreamReader reader; + try + { + reader = new StreamReader(configPath, true); + } + catch (IOException) + { + if (!Required) + return conf; + + throw; + } + + using (reader) + { + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (string.IsNullOrWhiteSpace(line) || line[0] == '#') + continue; + + var keyValue = line.Split(new[] { '=' }, 2); + if (keyValue.Length != 2) + continue; + + conf[keyValue[0].Trim()] = keyValue[1].Trim(); + } + } + + return conf; + } + } +} diff --git a/src/ScriptEngine.HostedScript/ComponentLoadingContext.cs b/src/ScriptEngine.HostedScript/ComponentLoadingContext.cs new file mode 100644 index 000000000..2eeb1e57d --- /dev/null +++ b/src/ScriptEngine.HostedScript/ComponentLoadingContext.cs @@ -0,0 +1,75 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Loader; +using OneScript.Commons; + +namespace ScriptEngine.HostedScript +{ + internal class ComponentLoadingContext : AssemblyLoadContext + { + private static readonly IDictionary _currentAssemblies = + new Dictionary(new AssemblyComparer()); + + private readonly AssemblyDependencyResolver _resolver; + + public ComponentLoadingContext(string path) + { + _resolver = new AssemblyDependencyResolver(path); + } + + protected override Assembly Load(AssemblyName assemblyName) + { + if (_currentAssemblies.TryGetValue(assemblyName, out var result)) + return result; + + var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath != null) + { + return LoadFromAssemblyPath(assemblyPath); + } + + return null; + } + + protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + { + var libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); + if (libraryPath != null) + { + return LoadUnmanagedDllFromPath(libraryPath); + } + + return IntPtr.Zero; + } + + private class AssemblyComparer : IEqualityComparer + { + public bool Equals(AssemblyName x, AssemblyName y) + { + return AssemblyName.ReferenceMatchesDefinition(x, y); + } + + public int GetHashCode(AssemblyName obj) + { + return obj.Name?.GetHashCode() ?? 0; + } + } + + private static void AddAssembly(Assembly asm) + { + _currentAssemblies.Add(asm.GetName(), asm); + } + + static ComponentLoadingContext() + { + AppDomain.CurrentDomain.GetAssemblies().ForEach(AddAssembly); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/DefaultEventProcessor.cs b/src/ScriptEngine.HostedScript/DefaultEventProcessor.cs index 3e5491f51..24549e84b 100644 --- a/src/ScriptEngine.HostedScript/DefaultEventProcessor.cs +++ b/src/ScriptEngine.HostedScript/DefaultEventProcessor.cs @@ -8,7 +8,9 @@ This Source Code Form is subject to the terms of the using System; using System.Collections; using System.Collections.Generic; -using System.Runtime.CompilerServices; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; @@ -20,16 +22,16 @@ private struct Handler { public ScriptDrivenObject Target; public string MethodName; - public Action Method; + public Action Method; } private class HandlersList : IEnumerable { - private List _handlers = new List(); + private readonly List _handlers = new List(); public void Add(ScriptDrivenObject target, string methodName) { - var exist = _handlers.Exists(x => ReferenceEquals(x.Target, target) && x.MethodName.ToLowerInvariant() == methodName.ToLowerInvariant()); + var exist = _handlers.Exists(x => ReferenceEquals(x.Target, target) && String.Equals(x.MethodName, methodName, StringComparison.InvariantCultureIgnoreCase)); if (!exist) { _handlers.Add(new Handler @@ -43,7 +45,7 @@ public void Add(ScriptDrivenObject target, string methodName) public void Remove(ScriptDrivenObject target, string methodName) { - _handlers.RemoveAll(x => ReferenceEquals(x.Target, target) && x.MethodName.ToLowerInvariant() == methodName.ToLowerInvariant()); + _handlers.RemoveAll(x => ReferenceEquals(x.Target, target) && String.Equals(x.MethodName, methodName, StringComparison.InvariantCultureIgnoreCase)); } public IEnumerator GetEnumerator() @@ -57,7 +59,10 @@ IEnumerator IEnumerable.GetEnumerator() } } - private Dictionary> _registeredHandlers = new Dictionary>(); + private readonly Dictionary> _registeredHandlers + = new Dictionary>(); + + private readonly object _subscriptionLock = new object(); public void AddHandler( IRuntimeContextInstance eventSource, @@ -67,15 +72,23 @@ public void AddHandler( { if (!(handlerTarget is ScriptDrivenObject handlerScript)) throw RuntimeException.InvalidArgumentType("handlerTarget"); - - if (!_registeredHandlers.TryGetValue(eventSource, out var handlers)) + + lock (_subscriptionLock) { - handlers = new Dictionary(); - handlers[eventName] = new HandlersList(); - _registeredHandlers[eventSource] = handlers; + if (!_registeredHandlers.TryGetValue(eventSource, out var handlers)) + { + handlers = new Dictionary(); + _registeredHandlers[eventSource] = handlers; + } + + if (!handlers.TryGetValue(eventName, out var handlersList)) + { + handlersList = new HandlersList(); + handlers[eventName] = handlersList; + } + + handlersList.Add(handlerScript, handlerMethod); } - - handlers[eventName].Add(handlerScript, handlerMethod); } public void RemoveHandler( @@ -87,20 +100,40 @@ public void RemoveHandler( if (!(handlerTarget is ScriptDrivenObject handlerScript)) throw RuntimeException.InvalidArgumentType("handlerTarget"); - if (_registeredHandlers.TryGetValue(eventSource, out var handlers)) + lock (_subscriptionLock) { - handlers[eventName].Remove(handlerScript, handlerMethod); + if (!_registeredHandlers.TryGetValue(eventSource, out var handlers)) + { + return; + } + + if (handlers.TryGetValue(eventName, out var handlersList)) + { + handlersList.Remove(handlerScript, handlerMethod); + } } } - public void HandleEvent(IRuntimeContextInstance eventSource, string eventName, IValue[] eventArgs) + public void HandleEvent(IRuntimeContextInstance eventSource, string eventName, IValue[] eventArgs, + IBslProcess process) { - if (!_registeredHandlers.TryGetValue(eventSource, out var handlers)) - return; - - foreach (var handler in handlers[eventName]) + HandlersList handlersLocalCopy; + + lock (_subscriptionLock) + { + + if (!_registeredHandlers.TryGetValue(eventSource, out var handlers)) + return; + + if (!handlers.TryGetValue(eventName, out handlersLocalCopy)) + { + return; + } + } + + foreach (var handler in handlersLocalCopy) { - handler.Method(eventArgs); + handler.Method(process, eventArgs); } } } diff --git a/src/ScriptEngine.HostedScript/EngineConfigProvider.cs b/src/ScriptEngine.HostedScript/EngineConfigProvider.cs deleted file mode 100644 index 1c26555ae..000000000 --- a/src/ScriptEngine.HostedScript/EngineConfigProvider.cs +++ /dev/null @@ -1,134 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace ScriptEngine.HostedScript -{ - class EngineConfigProvider - { - KeyValueConfig _currentConfig; - - readonly string _customConfigFilePath; - - public const string CONFIG_FILE_NAME = "oscript.cfg"; - public const string SYSTEM_LIB_KEY = "lib.system"; - public const string ADDITIONAL_LIB_KEY = "lib.additional"; - - public EngineConfigProvider(string customConfigFile) - { - _customConfigFilePath = customConfigFile; - ReadConfig(); - } - - public static string DefaultConfigFilePath() - { - string asmLocation; - asmLocation = System.Reflection.Assembly.GetExecutingAssembly().Location; - if (String.IsNullOrEmpty(asmLocation)) - asmLocation = System.Reflection.Assembly.GetEntryAssembly().Location; - - var assemblyPath = System.IO.Path.GetDirectoryName(asmLocation); - var configFile = System.IO.Path.Combine(assemblyPath, CONFIG_FILE_NAME); - if (System.IO.File.Exists(configFile)) - return configFile; - else - return null; - } - - private void ReadDefaultConfig() - { - var defaultCfgPath = DefaultConfigFilePath(); - if(defaultCfgPath != null) - { - var dict = ReadConfigFile(defaultCfgPath); - _currentConfig = new KeyValueConfig(dict); - } - else - { - _currentConfig = new KeyValueConfig(new Dictionary()); - } - } - - private static Dictionary ReadConfigFile(string configPath) - { - Dictionary conf = new Dictionary(); - using (var reader = new StreamReader(configPath, true)) - { - while (!reader.EndOfStream) - { - var line = reader.ReadLine(); - if (String.IsNullOrWhiteSpace(line) || line[0] == '#') - continue; - - var keyValue = line.Split(new[] { '=' }, 2); - if (keyValue.Length != 2) - continue; - - conf[keyValue[0].Trim()] = keyValue[1].Trim(); - } - } - - ExpandRelativePaths(conf, configPath); - - return conf; - } - - private static void ExpandRelativePaths(Dictionary conf, string configFile) - { - string sysDir = null; - conf.TryGetValue(SYSTEM_LIB_KEY, out sysDir); - - var confDir = System.IO.Path.GetDirectoryName(configFile); - if (sysDir != null && !System.IO.Path.IsPathRooted(sysDir)) - { - sysDir = System.IO.Path.GetFullPath(System.IO.Path.Combine(confDir, sysDir)); - conf[SYSTEM_LIB_KEY] = sysDir; - } - - string additionals; - if (conf.TryGetValue(ADDITIONAL_LIB_KEY, out additionals)) - { - var fullPaths = additionals.Split(new[]{";"}, StringSplitOptions.RemoveEmptyEntries) - .Select(x => Path.GetFullPath(Path.Combine(confDir, x))); - conf[ADDITIONAL_LIB_KEY] = String.Join(";",fullPaths); - } - } - - private void ReadCustomConfig() - { - if (!String.IsNullOrWhiteSpace(_customConfigFilePath) && File.Exists(_customConfigFilePath)) - { - var dict = ReadConfigFile(_customConfigFilePath); - _currentConfig.Merge(dict); - } - } - - private void ReadEnvironmentOverrides() - { - var env = System.Environment.GetEnvironmentVariable("OSCRIPT_CONFIG"); - if(env == null) - return; - - var paramList = new FormatParametersList(env); - _currentConfig.Merge(paramList.ToDictionary()); - } - - public KeyValueConfig ReadConfig() - { - ReadDefaultConfig(); - ReadCustomConfig(); - ReadEnvironmentOverrides(); - - return _currentConfig; - } - - } -} diff --git a/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs b/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs new file mode 100644 index 000000000..b9f3c6ff2 --- /dev/null +++ b/src/ScriptEngine.HostedScript/EnvironmentVariableConfigProvider.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Commons; +using ScriptEngine.Hosting; + +namespace ScriptEngine.HostedScript +{ + public class EnvironmentVariableConfigProvider : IConfigProvider + { + private readonly string _variableName; + + public EnvironmentVariableConfigProvider(string variableName) + { + _variableName = variableName; + } + + public string SourceId => _variableName; + + public IReadOnlyDictionary Load() + { + var envValue = Environment.GetEnvironmentVariable(_variableName); + if (string.IsNullOrEmpty(envValue)) + return new Dictionary(); + + var paramList = new FormatParametersList(envValue); + return paramList.ToDictionary(); + } + + public string ResolveRelativePath(string path) + { + return path; + } + } +} diff --git a/src/ScriptEngine.HostedScript/Extensions/CollectionExtensions.cs b/src/ScriptEngine.HostedScript/Extensions/CollectionExtensions.cs new file mode 100644 index 000000000..19f01d6df --- /dev/null +++ b/src/ScriptEngine.HostedScript/Extensions/CollectionExtensions.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; + +namespace ScriptEngine.HostedScript.Extensions +{ + public static class CollectionExtensions + { + public static void AddRange(this IList destination, IEnumerable source) + { + foreach (var item in source) + { + destination.Add(item); + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs new file mode 100644 index 000000000..185612566 --- /dev/null +++ b/src/ScriptEngine.HostedScript/Extensions/EngineBuilderExtensions.cs @@ -0,0 +1,122 @@ +/*---------------------------------------------------------- +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.IO; +using OneScript.Contexts; +using OneScript.Native.Extensions; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; + +namespace ScriptEngine.HostedScript.Extensions +{ + public static class EngineBuilderExtensions + { + public static ConfigurationProviders UseConfigFile(this ConfigurationProviders providers, string configFile, bool required = true) + { + if (File.Exists(configFile)) + { + var reader = new CfgFileConfigProvider + { + FilePath = configFile, + Required = required + }; + providers.Add(reader); + } + + return providers; + } + + public static ConfigurationProviders UseSystemConfigFile(this ConfigurationProviders providers) + { + var asmLocation = typeof(IValue).Assembly.Location; + if (string.IsNullOrEmpty(asmLocation)) + asmLocation = System.Reflection.Assembly.GetEntryAssembly()?.Location; + + var pathPrefix = (!string.IsNullOrWhiteSpace(asmLocation) ? + Path.GetDirectoryName(asmLocation) : + System.Environment.CurrentDirectory) ?? ""; + + var configFile = Path.Combine(pathPrefix, CfgFileConfigProvider.CONFIG_FILE_NAME); + + return providers.UseConfigFile(configFile); + } + + public static ConfigurationProviders UseEntrypointConfigFile(this ConfigurationProviders providers, string entryPoint) + { + var dir = Path.GetDirectoryName(entryPoint) ?? ""; + var cfgPath = Path.GetFullPath(Path.Combine(dir, CfgFileConfigProvider.CONFIG_FILE_NAME)); + if (File.Exists(cfgPath)) + { + return providers.UseConfigFile(cfgPath, false); + } + + return providers; + } + + public static ConfigurationProviders UseEnvironmentVariableConfig(this ConfigurationProviders providers, string varName) + { + var reader = new EnvironmentVariableConfigProvider(varName); + providers.Add(reader); + return providers; + } + + public static IEngineBuilder UseFileSystemLibraries(this IEngineBuilder b) + { + b.Services.RegisterSingleton(sp => + { + var libOptions = sp.Resolve(); + var searchDirs = new List(); + + var sysDir = libOptions.SystemLibraryDir; + if (sysDir == default) + { + var entrypoint = System.Reflection.Assembly.GetEntryAssembly(); + if (entrypoint == default) + entrypoint = typeof(FileSystemDependencyResolver).Assembly; + + sysDir = Path.GetDirectoryName(entrypoint.Location); + } + + if (libOptions.AdditionalLibraries != null) + { + searchDirs.AddRange(libOptions.AdditionalLibraries); + } + + var resolver = new FileSystemDependencyResolver(); + resolver.LibraryRoot = sysDir; + resolver.SearchDirectories.AddRange(searchDirs); + + return resolver; + }); + + return b; + } + + public static ExecutionContext UseTemplateFactory(this ExecutionContext env, ITemplateFactory factory) + { + var storage = new TemplateStorage(factory); + env.GlobalNamespace.InjectObject(storage); + env.GlobalInstances.RegisterInstance(storage); + return env; + } + + public static IEngineBuilder UseNativeRuntime(this IEngineBuilder builder) + { + builder.Services.Register(); + builder.Services.UseNativeRuntime(); + + return builder; + } + + public static IEngineBuilder UseEventHandlers(this IEngineBuilder builder) + { + builder.Services.RegisterSingleton(); + return builder; + } + } +} diff --git a/src/ScriptEngine.HostedScript/Extensions/NativeScriptInfoFactory.cs b/src/ScriptEngine.HostedScript/Extensions/NativeScriptInfoFactory.cs new file mode 100644 index 000000000..f7a4075ca --- /dev/null +++ b/src/ScriptEngine.HostedScript/Extensions/NativeScriptInfoFactory.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.Sources; +using OneScript.Values; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.HostedScript.Extensions +{ + public class NativeScriptInfoFactory : IScriptInformationFactory + { + public BslObjectValue GetInfo(SourceCode source) + { + return new ScriptInformationContext(source); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/FileSourceTemplate.cs b/src/ScriptEngine.HostedScript/FileSourceTemplate.cs index abbbd40ad..991c4c57d 100644 --- a/src/ScriptEngine.HostedScript/FileSourceTemplate.cs +++ b/src/ScriptEngine.HostedScript/FileSourceTemplate.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.HostedScript.Library.Binary; +using OneScript.StandardLibrary.Binary; namespace ScriptEngine.HostedScript { diff --git a/src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs b/src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs new file mode 100644 index 000000000..f5fd6b45b --- /dev/null +++ b/src/ScriptEngine.HostedScript/FileSystemDependencyResolver.cs @@ -0,0 +1,324 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using OneScript.Compilation; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Sources; + +namespace ScriptEngine.HostedScript +{ + public class FileSystemDependencyResolver : IDependencyResolver + { + public const string PREDEFINED_LOADER_FILE = "package-loader.os"; + private readonly List _libs = new List(); + private LibraryLoader _defaultLoader; + private object _defaultLoaderLocker = new object(); + private string _libraryRoot; + + #region Private classes + + private class Library + { + public string id; + public ProcessingState state; + public LibraryLoader customLoader; + public PackageInfo loadingResult; + } + + private enum ProcessingState + { + Discovered, + Processed + } + + private enum LoadStatus + { + NotFound, // Каталог библиотеки не существует + Empty, // Каталог найден, но библиотека не содержит исполняемых файлов + Success // Библиотека успешно загружена + } + + private class LoadResult + { + public PackageInfo Package { get; set; } + public LoadStatus Status { get; set; } + + public static LoadResult NotFound() => new LoadResult { Status = LoadStatus.NotFound }; + public static LoadResult Empty() => new LoadResult { Status = LoadStatus.Empty }; + public static LoadResult Success(PackageInfo package) => new LoadResult + { + Status = LoadStatus.Success, + Package = package + }; + } + + #endregion + + public FileSystemDependencyResolver() + { + } + + public IList SearchDirectories { get;} = new List(); + + public string LibraryRoot + { + get + { + if (_libraryRoot == null) + _libraryRoot = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + + return _libraryRoot; + } + set + { + if (string.IsNullOrWhiteSpace(value)) + { + _libraryRoot = null; + } + + _libraryRoot = value; + } + } + + + private ScriptingEngine Engine { get; set; } + + public void Initialize(ScriptingEngine engine) + { + Engine = engine; + } + + public PackageInfo Resolve(SourceCode module, string libraryName, IBslProcess process) + { + bool quoted = PrepareQuoted(ref libraryName); + + var result = quoted ? + LoadByRelativePath(module, libraryName, process) : + LoadByName(libraryName, process); + + if (result.Status == LoadStatus.NotFound) + { + throw new CompilerException($"Библиотека не найдена: '{libraryName}'"); + } + else if (result.Status == LoadStatus.Empty) + { + throw new CompilerException( + $"Библиотека '{libraryName}' найдена, но не содержит исполняемых файлов.\n" + + "Для получения подробной информации установите переменную окружения OS_LIBRARY_LOADER_TRACE=1"); + } + + return result.Package; + } + + private LoadResult LoadByName(string libraryName, IBslProcess process) + { + foreach (var path in SearchDirectories) + { + if(!Directory.Exists(path)) + continue; + + var libraryPath = Path.Combine(path, libraryName); + var result = LoadByPath(libraryPath, process); + + // Если библиотека найдена (успешно загружена или пуста), сразу возвращаем + // Не ищем дальше, так как это более приоритетный путь + if (result.Status != LoadStatus.NotFound) + return result; + } + + // Если в SearchDirectories ничего не нашли, проверяем rootPath + var rootPath = Path.Combine(LibraryRoot, libraryName); + return LoadByPath(rootPath, process); + } + + private LoadResult LoadByRelativePath(SourceCode module, string libraryPath, IBslProcess process) + { + string realPath; + + if (!Path.IsPathRooted(libraryPath) && module.Location != null) + { + var currentPath = module.Location; + // Загружаем относительно текущего скрипта, однако, + // если CurrentScript не файловый (TestApp или другой хост), то загружаем относительно рабочего каталога. + // немного костыльно, ага (( + // + if (!PathHasInvalidChars(currentPath)) + realPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(currentPath), libraryPath)); + else + realPath = libraryPath; + } + else + { + realPath = libraryPath; + } + + return LoadByPath(realPath, process); + } + + private LibraryLoader CreateDefaultLoader(IBslProcess process) + { + var loaderscript = Path.Combine(LibraryRoot, PREDEFINED_LOADER_FILE); + return File.Exists(loaderscript) ? + LibraryLoader.Create(Engine, loaderscript, process) + : LibraryLoader.Create(Engine, process); + } + + private LibraryLoader GetDefaultLoader(IBslProcess process) + { + if (_defaultLoader != default) + return _defaultLoader; + + lock (_defaultLoaderLocker) + { + if (_defaultLoader == default) + { + _defaultLoader = CreateDefaultLoader(process); + } + } + + return _defaultLoader; + } + + private bool PrepareQuoted(ref string value) + { + const string COMMENT = "//"; + const char QUOTE = '"'; + + bool quoted = false; + if (value.IndexOf(QUOTE)==0) + { + var secondQuote = value.Substring(1).IndexOf(QUOTE); + if (secondQuote > 0) + { + if (secondQuote+2 < value.Length) + { + var tail = value.Substring(secondQuote+2, value.Length-secondQuote-2).TrimStart(); + if (!string.IsNullOrWhiteSpace(tail) && tail.IndexOf(COMMENT, StringComparison.Ordinal) != 0) + throw new CompilerException($"Недопустимые символы после имени библиотеки: '{tail}'"); + } + value = value.Substring(1, secondQuote); + quoted = true; + } + else + { + throw new CompilerException($"Ошибка в имени библиотеки: '{value}'"); + } + } + else + { + var comment = value.IndexOf(COMMENT, StringComparison.Ordinal); + if( comment>=0 ) + { + value = value.Substring(0,comment).TrimEnd(); + } + } + + if (String.IsNullOrWhiteSpace(value)) + throw new CompilerException("Отсутствует имя библиотеки"); + + return quoted; + } + + private LoadResult LoadByPath(string libraryPath, IBslProcess process) + { + if (!Directory.Exists(libraryPath)) + return LoadResult.NotFound(); + + var package = LoadLibraryInternal(libraryPath, process); + + return package == null + ? LoadResult.Empty() + : LoadResult.Success(package); + } + + private PackageInfo LoadLibraryInternal(string libraryPath, IBslProcess process) + { + var id = GetLibraryId(libraryPath); + var existedLib = _libs.FirstOrDefault(x => x.id == id); + if(existedLib != null) + { + if (existedLib.state == ProcessingState.Discovered) + { + string libStack = ListToStringStack(_libs, id); + throw new DependencyResolveException( + new BilingualString( + $"Ошибка загрузки библиотеки {id}. Обнаружены циклические зависимости.\n", + $"Error loading library {id}. Circular dependencies found.\n") + libStack); + } + + return existedLib.loadingResult; + } + + var newLib = new Library() { id = id, state = ProcessingState.Discovered }; + int newLibIndex = _libs.Count; + + var customLoaderFile = Path.Combine(libraryPath, PREDEFINED_LOADER_FILE); + if (File.Exists(customLoaderFile)) + newLib.customLoader = LibraryLoader.Create(Engine, customLoaderFile, process); + + PackageInfo package; + try + { + _libs.Add(newLib); + package = ProcessLibrary(newLib, process); + newLib.state = ProcessingState.Processed; + newLib.loadingResult = package; + } + catch (Exception) + { + _libs.RemoveAt(newLibIndex); + throw; + } + + return package; + } + + private PackageInfo ProcessLibrary(Library lib, IBslProcess process) + { + LibraryLoader loader; + if (lib.customLoader != null) + loader = lib.customLoader; + else + loader = GetDefaultLoader(process); + + return loader.ProcessLibrary(lib.id, process); + } + + private static string ListToStringStack(IEnumerable libs, string stopToken) + { + var builder = new StringBuilder(); + string offset = ""; + foreach (var library in libs) + { + builder.Append(offset); + builder.Append("-> "); + builder.AppendLine(library.id); + offset += " "; + offset += " "; + } + + return builder.ToString(); + } + + private static string GetLibraryId(string libraryPath) + { + return Path.GetFullPath(libraryPath); + } + + private static bool PathHasInvalidChars(string path) + { + return (!string.IsNullOrEmpty(path) && path.IndexOfAny(Path.GetInvalidPathChars()) >= 0); + } + } +} diff --git a/src/ScriptEngine.HostedScript/HostedScriptEngine.cs b/src/ScriptEngine.HostedScript/HostedScriptEngine.cs index b9a016d43..6ed02ed64 100644 --- a/src/ScriptEngine.HostedScript/HostedScriptEngine.cs +++ b/src/ScriptEngine.HostedScript/HostedScriptEngine.cs @@ -1,267 +1,155 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using ScriptEngine.Environment; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.Machine; -using System.Collections.Generic; -using ScriptEngine.Compiler; -using ScriptEngine.Machine.Contexts; - - -namespace ScriptEngine.HostedScript -{ - public class HostedScriptEngine : IDisposable - { - private readonly ScriptingEngine _engine; - private readonly SystemGlobalContext _globalCtx; - private readonly RuntimeEnvironment _env; - private bool _isInitialized; - private bool _configInitialized; - private bool _librariesInitialized; - - private CodeStatProcessor _codeStat; - - public HostedScriptEngine() - { - _engine = new ScriptingEngine(); - _env = new RuntimeEnvironment(); - _engine.AttachAssembly(System.Reflection.Assembly.GetExecutingAssembly(), _env); - - _globalCtx = new SystemGlobalContext(); - _globalCtx.EngineInstance = _engine; - - _env.InjectObject(_globalCtx, false); - GlobalsManager.RegisterInstance(_globalCtx); - - InitializationCallback = (eng, env) => - { - var templateFactory = new DefaultTemplatesFactory(); - var storage = new TemplateStorage(templateFactory); - env.InjectObject(storage); - GlobalsManager.RegisterInstance(storage); - }; - - _engine.Environment = _env; - } - - public ScriptingEngine EngineInstance => _engine; - - public void InitExternalLibraries(string systemLibrary, IEnumerable searchDirs) - { - var libLoader = new LibraryResolver(_engine, _env); - _engine.DirectiveResolvers.Add(libLoader); - - libLoader.LibraryRoot = systemLibrary; - libLoader.SearchDirectories.Clear(); - if (searchDirs != null) - { - libLoader.SearchDirectories.AddRange(searchDirs); - } - - _librariesInitialized = true; - } - - public static string ConfigFileName => EngineConfigProvider.CONFIG_FILE_NAME; - - public KeyValueConfig GetWorkingConfig() - { - var cfgAccessor = GlobalsManager.GetGlobalContext(); - if (!_configInitialized) - { - cfgAccessor.Provider = new EngineConfigProvider(CustomConfig); - cfgAccessor.Refresh(); - _configInitialized = true; - } - return cfgAccessor.GetConfig(); - } - - public string CustomConfig { get; set; } - - public Action InitializationCallback { get; set; } - - public void Initialize() - { - if (!_isInitialized) - { - InitializationCallback?.Invoke(_engine, _engine.Environment); - _engine.Initialize(); - _isInitialized = true; - } - - // System language - var SystemLanguageCfg = GetWorkingConfig()["SystemLanguage"]; - - if (SystemLanguageCfg != null) - Locale.SystemLanguageISOName = SystemLanguageCfg; - else - Locale.SystemLanguageISOName = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName; - } - - private void InitLibraries(KeyValueConfig config) - { - if (_librariesInitialized) - return; - - if(config != null) - { - InitLibrariesFromConfig(config); - } - else - { - InitExternalLibraries(null, null); - } - } - - private static string ConfigFilePath() - { - return EngineConfigProvider.DefaultConfigFilePath(); - } - - private void InitLibrariesFromConfig(KeyValueConfig config) - { - string sysDir = config[EngineConfigProvider.SYSTEM_LIB_KEY]; - string additionalDirsList = config[EngineConfigProvider.ADDITIONAL_LIB_KEY]; - string[] addDirs = null; - - if(additionalDirsList != null) - { - addDirs = additionalDirsList.Split(';'); - } - - InitExternalLibraries(sysDir, addDirs); - - } - - public void AttachAssembly(System.Reflection.Assembly asm) - { - _engine.AttachAssembly(asm, _env); - } - - public void InjectGlobalProperty(string name, IValue value, bool readOnly) - { - _env.InjectGlobalProperty(value, name, readOnly); - } - - public void InjectObject(IAttachableContext obj, bool asDynamicScope) - { - _env.InjectObject(obj, asDynamicScope); - } - - public ICodeSourceFactory Loader => _engine.Loader; - - public IDebugController DebugController - { - get => _engine.DebugController; - set => _engine.DebugController = value; - } - - public CompilerService GetCompilerService() - { - InitLibraries(GetWorkingConfig()); - - var compilerSvc = _engine.GetCompilerService(); - compilerSvc.DefineVariable("ЭтотОбъект", "ThisObject", SymbolType.ContextProperty); - UserScriptContextInstance.GetOwnMethodsDefinition().ForEach(x => compilerSvc.DefineMethod(x)); - return compilerSvc; - } - - public IEnumerable GetExternalLibraries() - { - return _env.GetUserAddedScripts(); - } - - public void LoadUserScript(UserAddedScript script) - { - if (script.Type == UserAddedScriptType.Class) - { - _engine.AttachedScriptsFactory.LoadAndRegister(script.Symbol, script.Image); - } - else - { - var loaded = _engine.LoadModuleImage(script.Image); - var instance = (IValue)_engine.NewObject(loaded); - _env.InjectGlobalProperty(instance, script.Symbol, true); - } - } - - public Process CreateProcess(IHostApplication host, ICodeSource src) - { - Initialize(); - SetGlobalEnvironment(host, src); - if (_engine.DebugController != null) - { - _engine.DebugController.Init(); - _engine.DebugController.AttachToThread(); - _engine.DebugController.Wait(); - } - - var compilerSvc = GetCompilerService(); - DefineConstants(compilerSvc); - LoadedModule module; - try - { - var image = compilerSvc.Compile(src); - module = _engine.LoadModuleImage(image); - } - catch (CompilerException) - { - _engine.DebugController?.NotifyProcessExit(1); - throw; - } - return InitProcess(host, module); - } - - private void DefineConstants(CompilerService compilerSvc) - { - var definitions = GetWorkingConfig()["preprocessor.define"]?.Split(',') ?? new string[0]; - foreach (var val in definitions) - { - compilerSvc.DefinePreprocessorValue(val); - } - } - - public Process CreateProcess(IHostApplication host, ModuleImage moduleImage, ICodeSource src) - { - SetGlobalEnvironment(host, src); - var module = _engine.LoadModuleImage(moduleImage); - return InitProcess(host, module); - } - - public void SetGlobalEnvironment(IHostApplication host, ICodeSource src) - { - _globalCtx.ApplicationHost = host; - _globalCtx.CodeSource = src; - _globalCtx.InitInstance(); - } - - private Process InitProcess(IHostApplication host, LoadedModule module) - { - Initialize(); - - var process = new Process(host, module, _engine); - return process; - } - - public void EnableCodeStatistics() - { - _codeStat = new CodeStatProcessor(); - _engine.SetCodeStatisticsCollector(_codeStat); - } - - public CodeStatDataCollection GetCodeStatData() - { - return _codeStat.GetStatData(); - } - - public void Dispose() - { - _engine?.Dispose(); - _codeStat?.EndCodeStat(); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Sources; +using ScriptEngine.Machine; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Tasks; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.HostedScript +{ + public class HostedScriptEngine : IDisposable + { + private readonly ScriptingEngine _engine; + private SystemGlobalContext _globalCtx; + private readonly IRuntimeEnvironment _env; + private bool _isInitialized; + + private readonly OneScriptLibraryOptions _workingConfig; + + public HostedScriptEngine(ScriptingEngine engine) + { + _engine = engine; + _env = _engine.Environment; + _engine.AttachAssembly(typeof(HostedScriptEngine).Assembly); + _workingConfig = _engine.Services.Resolve(); + SetGlobalContexts(engine.GlobalsManager); + } + + public ScriptingEngine Engine => _engine; + + private void SetGlobalContexts(IGlobalsManager manager) + { + _globalCtx = new SystemGlobalContext(); + _globalCtx.EngineInstance = _engine; + + _env.InjectObject(_globalCtx); + manager.RegisterInstance(_globalCtx); + + var dynLoader = new DynamicLoadingFunctions(_engine); + _env.InjectObject(dynLoader); + manager.RegisterInstance(dynLoader); + + var bgTasksManager = new BackgroundTasksManager(_engine.Services.Resolve()); + _env.InjectGlobalProperty(bgTasksManager, "ФоновыеЗадания", "BackgroundJobs", true); + } + + public void Initialize() + { + if (!_isInitialized) + { + _engine.Initialize(); + _isInitialized = true; + } + + // System language + var systemLanguageCfg = _workingConfig.SystemLanguage; + + Locale.SystemLanguageISOName = systemLanguageCfg ?? System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName; + } + + public void InjectGlobalProperty(string name, string alias, IValue value, bool readOnly) + { + _env.InjectGlobalProperty(value, name, alias, readOnly); + } + + public void InjectObject(IAttachableContext obj) + { + _env.InjectObject(obj); + } + + public ScriptSourceFactory Loader => _engine.Loader; + + public ICompilerFrontend GetCompilerService() + { + var compilerSvc = _engine.GetCompilerService(); + compilerSvc.FillSymbols(typeof(UserScriptContextInstance)); + + return compilerSvc; + } + + public Process CreateProcess(IHostApplication host, SourceCode src) + { + Initialize(); + SetGlobalEnvironment(host, src); + + if (_engine.Debugger.IsEnabled) + { + _engine.Debugger.Start(); + _engine.Debugger.GetSession().WaitReadyToRun(); + } + + var compilerSvc = GetCompilerService(); + DefineConstants(compilerSvc); + IExecutableModule module; + var bslProcess = _engine.NewProcess(); + try + { + module = compilerSvc.Compile(src, bslProcess); + } + catch (CompilerException) + { + _engine.Debugger.NotifyProcessExit(1); + throw; + } + return InitProcess(bslProcess, host, module); + } + + private void DefineConstants(ICompilerFrontend compilerSvc) + { + var definitions = _workingConfig.PreprocessorDefinitions; + foreach (var val in definitions) + { + compilerSvc.PreprocessorDefinitions.Add(val); + } + + if (Utils.IsMonoRuntime) + { + compilerSvc.PreprocessorDefinitions.Add("MONO"); + } + } + + public IServiceContainer Services => _engine.Services; + + public void SetGlobalEnvironment(IHostApplication host, SourceCode src) + { + _globalCtx.ApplicationHost = host; + _globalCtx.CodeSource = src; + _globalCtx.InitInstance(); + } + + private Process InitProcess(IBslProcess bslProcess, IHostApplication host, IExecutableModule module) + { + Initialize(); + + var process = new Process(bslProcess, host, module, _engine); + return process; + } + + public void Dispose() + { + _engine?.Dispose(); + } + } +} diff --git a/src/ScriptEngine.HostedScript/IHostApplication.cs b/src/ScriptEngine.HostedScript/IHostApplication.cs index 456cc8441..c387888db 100644 --- a/src/ScriptEngine.HostedScript/IHostApplication.cs +++ b/src/ScriptEngine.HostedScript/IHostApplication.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using ScriptEngine.HostedScript.Library; +using OneScript.StandardLibrary; namespace ScriptEngine.HostedScript { @@ -13,7 +13,7 @@ public interface IHostApplication { void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary); void ShowExceptionInfo(Exception exc); - bool InputString(out string result, int maxLen); + bool InputString(out string result, string prompt, int maxLen, bool multiline); string[] GetCommandLineArguments(); } } diff --git a/src/ScriptEngine.HostedScript/ITemplateFactory.cs b/src/ScriptEngine.HostedScript/ITemplateFactory.cs index 9a0e21879..312c1a5d5 100644 --- a/src/ScriptEngine.HostedScript/ITemplateFactory.cs +++ b/src/ScriptEngine.HostedScript/ITemplateFactory.cs @@ -6,8 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; - -using ScriptEngine.HostedScript.Library.Binary; +using OneScript.StandardLibrary.Binary; namespace ScriptEngine.HostedScript { diff --git a/src/ScriptEngine.HostedScript/KeyValueConfig.cs b/src/ScriptEngine.HostedScript/KeyValueConfig.cs deleted file mode 100644 index 0b40b9e4e..000000000 --- a/src/ScriptEngine.HostedScript/KeyValueConfig.cs +++ /dev/null @@ -1,62 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; - -namespace ScriptEngine.HostedScript -{ - public class KeyValueConfig - { - private readonly Dictionary _values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - - public KeyValueConfig() - { - } - - public KeyValueConfig(Dictionary source) - { - Merge(source); - } - - public void Merge(Dictionary source) - { - foreach (var keyValue in source) - { - this[keyValue.Key] = keyValue.Value; - } - } - - public string this[string key] - { - get - { - if (String.IsNullOrWhiteSpace(key)) - throw BadKeyException(key); - - string value = null; - _values.TryGetValue(key, out value); - - return value; - - } - private set - { - if (String.IsNullOrWhiteSpace(key)) - throw BadKeyException(key); - - _values[key] = value; - } - } - - private static ArgumentException BadKeyException(string key) - { - return new ArgumentException(String.Format("wrong config key format: {0}", key)); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ArrayImpl.cs b/src/ScriptEngine.HostedScript/Library/ArrayImpl.cs deleted file mode 100644 index 99492da43..000000000 --- a/src/ScriptEngine.HostedScript/Library/ArrayImpl.cs +++ /dev/null @@ -1,255 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("Массив", "Array")] - public class ArrayImpl : AutoContext, ICollectionContext, IEnumerable - { - private readonly List _values; - - public ArrayImpl() - { - _values = new List(); - } - - public ArrayImpl(IEnumerable values) - { - _values = new List(values); - } - - public override bool IsIndexed - { - get - { - return true; - } - } - - public override IValue GetIndexedValue(IValue index) - { - if(index.DataType == DataType.Number) - return Get((int)index.AsNumber()); - - return base.GetIndexedValue(index); - } - - public override void SetIndexedValue(IValue index, IValue val) - { - if (index.DataType == DataType.Number) - Set((int)index.AsNumber(), val); - else - base.SetIndexedValue(index, val); - } - - #region ICollectionContext Members - - [ContextMethod("Количество", "Count")] - public int Count() - { - return _values.Count; - } - - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _values.Clear(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - foreach (var item in _values) - { - yield return item; - } - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - [ContextMethod("Добавить", "Add")] - public void Add(IValue value = null) - { - if (value == null) - _values.Add(ValueFactory.Create()); - else - _values.Add(value); - } - - [ContextMethod("Вставить", "Insert")] - public void Insert(int index, IValue value = null) - { - if (index < 0) - throw IndexOutOfBoundsException(); - - if (index > _values.Count) - Extend(index - _values.Count); - - if (value == null) - _values.Insert(index, ValueFactory.Create()); - else - _values.Insert(index, value); - } - - [ContextMethod("Найти", "Find")] - public IValue Find(IValue what) - { - var idx = _values.FindIndex(x => x.Equals(what)); - if(idx < 0) - { - return ValueFactory.Create(); - } - else - { - return ValueFactory.Create(idx); - } - } - - [ContextMethod("Удалить", "Delete")] - public void Remove(int index) - { - if (index < 0 || index >= _values.Count) - throw IndexOutOfBoundsException(); - - _values.RemoveAt(index); - } - - [ContextMethod("ВГраница", "UBound")] - public int UpperBound() - { - return _values.Count - 1; - } - - [ContextMethod("Получить", "Get")] - public IValue Get(int index) - { - if (index < 0 || index >= _values.Count) - throw IndexOutOfBoundsException(); - - return _values[index]; - } - - [ContextMethod("Установить", "Set")] - public void Set(int index, IValue value) - { - if (index < 0 || index >= _values.Count) - throw IndexOutOfBoundsException(); - - _values[index] = value; - } - - private void Extend(int count) - { - for (int i = 0; i < count; ++i) - { - _values.Add(ValueFactory.Create()); - } - } - - private static void FillArray(ArrayImpl currentArray, int bound) - { - for (int i = 0; i < bound; i++) - { - currentArray._values.Add(ValueFactory.Create()); - } - } - - private static IValue CloneArray(ArrayImpl cloneable) - { - ArrayImpl clone = new ArrayImpl(); - foreach (var item in cloneable._values) - { - if (item.DataType == DataType.Undefined) - clone._values.Add(ValueFactory.Create()); - else - clone._values.Add(item); - } - return clone; - } - - [ScriptConstructor] - public static ArrayImpl Constructor() - { - return new ArrayImpl(); - } - - /// - /// Позволяет задать измерения массива при его создании - /// - /// Числовые размерности массива. Например, "Массив(2,3)", создает двумерный массив 2х3. - /// - [ScriptConstructor(Name = "По количеству элементов")] - public static ArrayImpl Constructor(IValue[] dimensions) - { - if (dimensions.Length == 1 && dimensions[0].GetRawValue() is FixedArrayImpl) - { - return Constructor(dimensions[0]); - } - - ArrayImpl cloneable = null; - for (int dim = dimensions.Length - 1; dim >= 0; dim--) - { - if (dimensions[dim] == null) - throw RuntimeException.InvalidNthArgumentType(dim + 1); - - int bound = (int)dimensions[dim].AsNumber(); - if (bound <= 0) - throw RuntimeException.InvalidNthArgumentValue(dim + 1); - - var newInst = new ArrayImpl(); - FillArray(newInst, bound); - if(cloneable != null) - { - for (int i = 0; i < bound; i++) - { - newInst._values[i] = CloneArray(cloneable); - } - } - cloneable = newInst; - - } - - return cloneable; - - } - - [ScriptConstructor(Name = "На основании фиксированного массива")] - public static ArrayImpl Constructor(IValue fixedArray) - { - if (!(fixedArray.GetRawValue() is FixedArrayImpl val)) - throw RuntimeException.InvalidArgumentType(); - - return new ArrayImpl(val); - } - - private static RuntimeException IndexOutOfBoundsException() - { - return new RuntimeException("Значение индекса выходит за пределы диапазона"); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataContext.cs b/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataContext.cs deleted file mode 100644 index 78f16f829..000000000 --- a/src/ScriptEngine.HostedScript/Library/Binary/BinaryDataContext.cs +++ /dev/null @@ -1,148 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Diagnostics; -using System.IO; -using System.Text; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Binary -{ - [ContextClass("ДвоичныеДанные", "BinaryData")] - public class BinaryDataContext : AutoContext, IDisposable - { - private byte[] _buffer; - - public BinaryDataContext(string filename) - { - using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read)) - { - _buffer = new byte[fs.Length]; - fs.Read(_buffer, 0, _buffer.Length); - } - } - - public BinaryDataContext(byte[] buffer) - { - _buffer = buffer; - } - - public void Dispose() - { - _buffer = null; - } - - [ContextMethod("Размер","Size")] - public int Size() - { - return _buffer.Length; - } - - [ContextMethod("Записать","Write")] - public void Write(IValue filenameOrStream) - { - if(filenameOrStream.DataType == DataType.String) - { - var filename = filenameOrStream.AsString(); - using (var fs = new FileStream(filename, FileMode.Create, FileAccess.Write)) - { - fs.Write(_buffer, 0, _buffer.Length); - } - } - else if(filenameOrStream.AsObject() is IStreamWrapper stream) - { - stream.GetUnderlyingStream().Write(_buffer, 0, _buffer.Length); - } - else - { - throw RuntimeException.InvalidArgumentType("filenameOrStream"); - } - } - - - public byte[] Buffer => _buffer; - - public override string AsString() - { - if (_buffer.Length == 0) - return ""; - - const int LIMIT = 64; - int length = Math.Min(_buffer.Length, LIMIT); - - StringBuilder hex = new StringBuilder(length*3); - hex.AppendFormat("{0:X2}", _buffer[0]); - for (int i = 1; i < length; ++i) - { - hex.AppendFormat(" {0:X2}", _buffer[i]); - } - - if (_buffer.Length > LIMIT) - hex.Append('…'); - - return hex.ToString(); - } - - /// - /// - /// Открывает поток для чтения двоичных данных. - /// - /// - /// - /// - /// Представляет собой поток данных, который можно последовательно читать и/или в который можно последовательно писать. - /// Экземпляры объектов данного типа можно получить с помощью различных методов других объектов. - /// - [ContextMethod("ОткрытьПотокДляЧтения", "OpenStreamForRead")] - public GenericStream OpenStreamForRead() - { - var stream = new MemoryStream(_buffer, 0, _buffer.Length, false, true); - return new GenericStream(stream, true); - } - - [ScriptConstructor(Name = "На основании файла")] - public static BinaryDataContext Constructor(IValue filename) - { - return new BinaryDataContext(filename.AsString()); - } - - public override bool Equals(IValue other) - { - if (other == null) - return false; - - if (other.SystemType.ID == SystemType.ID) - { - var binData = other.GetRawValue() as BinaryDataContext; - Debug.Assert(binData != null); - - return ArraysAreEqual(_buffer, binData._buffer); - } - - return false; - } - - private static bool ArraysAreEqual(byte[] a1, byte[] a2) - { - if (a1.LongLength == a2.LongLength) - { - for (long i = 0; i < a1.LongLength; i++) - { - if (a1[i] != a2[i]) - { - return false; - } - } - return true; - } - return false; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/FileStreamsManager.cs b/src/ScriptEngine.HostedScript/Library/Binary/FileStreamsManager.cs deleted file mode 100644 index 5a41ddd02..000000000 --- a/src/ScriptEngine.HostedScript/Library/Binary/FileStreamsManager.cs +++ /dev/null @@ -1,189 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.IO; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Binary -{ - /// - /// - /// Предоставляет методы для использования в типовых сценариях работы с файлами. - /// - [ContextClass("МенеджерФайловыхПотоков", "FileStreamsManager")] - public class FileStreamsManager : AutoContext - { - - public static FileMode ConvertFileOpenModeToCLR(FileOpenModeEnum value) - { - switch (value) - { - case FileOpenModeEnum.Append: - return FileMode.Append; - case FileOpenModeEnum.Create: - return FileMode.Create; - case FileOpenModeEnum.CreateNew: - return FileMode.CreateNew; - case FileOpenModeEnum.Open: - return FileMode.Open; - case FileOpenModeEnum.OpenOrCreate: - return FileMode.OpenOrCreate; - case FileOpenModeEnum.Truncate: - return FileMode.Truncate; - - default: - throw new ArgumentOutOfRangeException(nameof(value), value, null); - } - } - - public static FileAccess ConvertFileAccessToCLR(FileAccessEnum value) - { - switch (value) - { - case FileAccessEnum.Read: - return FileAccess.Read; - case FileAccessEnum.Write: - return FileAccess.Write; - default: - return FileAccess.ReadWrite; - } - } - - public FileStreamsManager() - { - } - - /// - /// - /// Открывает файл в заданном режиме с возможностью чтения и записи. - /// Файл открывается в режиме разделяемого чтения. - /// - /// - /// - /// Имя открываемого файла. - /// - /// Режим открытия файла. - /// - /// Режим доступа к файлу. - /// - /// Размер буфера для операций с файлом. - /// - /// - /// Специализированная версия объекта Поток для работы данными, расположенными в файле на диске. Предоставляет возможность чтения из потока, записи в поток и изменения текущей позиции. - /// По умолчанию, все операции с файловым потоком являются буферизированными, размер буфера по умолчанию - 8 КБ. - /// Размер буфера можно изменить, в том числе - полностью отключить буферизацию при вызове конструктора. - /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. - /// - [ContextMethod("Открыть", "Open")] - public FileStreamContext Open(IValue fileName, IValue openingMode, IValue fileAccess = null, IValue bufferSize = null) - { - if(bufferSize == null) - return FileStreamContext.Constructor(fileName, openingMode, fileAccess); - else - return FileStreamContext.Constructor(fileName, openingMode, fileAccess, bufferSize); - } - - - /// - /// - /// Открыть существующий файл для записи в конец. Если файл не существует, то будет создан новый файл. Запись в существующий файл выполняется с конца файла. Файл открывается в режиме разделяемого чтения. - /// - /// - /// - /// Имя открываемого файла. - /// - [ContextMethod("ОткрытьДляДописывания", "OpenForAppend")] - public FileStreamContext OpenForAppend(string fileName) - { - return new FileStreamContext(fileName, FileOpenModeEnum.Append, FileAccessEnum.ReadAndWrite); - } - - - /// - /// - /// Открывает существующий файл для записи. Файл открывается в режиме разделяемого чтения. Если файл не найден, будет создан новый файл. Запись в существующий файл производится с начала файла, замещая ранее сохраненные данные. - /// - /// - /// - /// Имя открываемого файла. - - /// - /// - /// Специализированная версия объекта Поток для работы данными, расположенными в файле на диске. Предоставляет возможность чтения из потока, записи в поток и изменения текущей позиции. - /// По умолчанию, все операции с файловым потоком являются буферизированными, размер буфера по умолчанию - 8 КБ. - /// Размер буфера можно изменить, в том числе - полностью отключить буферизацию при вызове конструктора. - /// Следует учитывать, что помимо буферизации существует кэширование чтения и записи файлов в операционной системе, на которое невозможно повлиять программно. - - /// - [ContextMethod("ОткрытьДляЗаписи", "OpenForWrite")] - public FileStreamContext OpenForWrite(string fileName) - { - // TODO: Судя по описанию - открывается без обрезки (Truncate). Надо проверить в 1С. - return new FileStreamContext(fileName, FileOpenModeEnum.OpenOrCreate, FileAccessEnum.Write); - } - - - /// - /// - /// Открывает существующий файл для чтения с общим доступом на чтение. - /// - /// - /// - /// Имя открываемого файла. - /// - /// - /// - [ContextMethod("ОткрытьДляЧтения", "OpenForRead")] - public FileStreamContext OpenForRead(string fileName) - { - return new FileStreamContext(fileName, FileOpenModeEnum.Open, FileAccessEnum.Read); - } - - - /// - /// - /// Создает или перезаписывает файл и открывает поток с возможностью чтения и записи в файл. Файл открывается в режиме разделяемого чтения. - /// - /// - /// - /// Имя создаваемого файла. - /// - /// Размер буфера. - /// - /// - [ContextMethod("Создать", "Create")] - public IValue Create(string fileName, int bufferSize = 0) - { - return new FileStreamContext(fileName, new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, bufferSize == 0 ? 8192 : bufferSize)); - } - - - /// - /// НЕ РЕАЛИЗОВАН - /// Создает временный файл и открывает его в монопольном режиме с возможностью чтения и записи. Дополнительно можно установить лимит в байтах, при превышении которого будет создан файл на диске. Пока размер файла не превышает данного лимита - вся работа ведётся в памяти. - /// - /// - /// - /// Максимальный объем памяти (в байтах), при превышении которого будет создан файл на диске. - /// Значение по умолчанию: 65535. - /// - /// Размер буфера для операций с файлом (в байтах). - /// Значение по умолчанию: 8192. - /// - /// - /// - [ContextMethod("СоздатьВременныйФайл", "CreateTempFile")] - public IValue CreateTempFile(int memoryLimit = 0, int bufferSize = 0) - { - throw new NotImplementedException(); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Binary/StreamEnums.cs b/src/ScriptEngine.HostedScript/Library/Binary/StreamEnums.cs deleted file mode 100644 index 9262739e7..000000000 --- a/src/ScriptEngine.HostedScript/Library/Binary/StreamEnums.cs +++ /dev/null @@ -1,57 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.Binary -{ - [EnumerationType("РежимОткрытияФайла", "FileOpenMode")] - public enum FileOpenModeEnum - { - [EnumItem("Дописать")] - Append, - [EnumItem("Обрезать")] - Truncate, - [EnumItem("Открыть")] - Open, - [EnumItem("ОткрытьИлиСоздать")] - OpenOrCreate, - [EnumItem("Создать")] - Create, - [EnumItem("СоздатьНовый")] - CreateNew - } - - [EnumerationType("ДоступКФайлу", "FileAccess")] - public enum FileAccessEnum - { - [EnumItem("Запись")] - Write, - [EnumItem("Чтение")] - Read, - [EnumItem("ЧтениеИЗапись")] - ReadAndWrite - } - - [EnumerationType("ПозицияВПотоке", "StreamPosition")] - public enum StreamPositionEnum - { - [EnumItem("Начало")] - Begin, - [EnumItem("Конец")] - End, - [EnumItem("Текущая")] - Current - } - - [EnumerationType("ПорядокБайтов", "ByteOrder")] - public enum ByteOrderEnum - { - [EnumItem("BigEndian")] - BigEndian, - [EnumItem("LittleEndian")] - LittleEndian - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ByteOrderMarkUsageEnum.cs b/src/ScriptEngine.HostedScript/Library/ByteOrderMarkUsageEnum.cs deleted file mode 100644 index e97bec658..000000000 --- a/src/ScriptEngine.HostedScript/Library/ByteOrderMarkUsageEnum.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library -{ - [EnumerationType("ИспользованиеByteOrderMark", "ByteOrderMarkUsage")] - public enum ByteOrderMarkUsageEnum - { - [EnumItem("Авто", "Auto")] - Auto, - - [EnumItem("Использовать", "Use")] - Use, - - [EnumItem("НеИспользовать", "DontUse")] - DontUse - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ConsoleColorEnum.cs b/src/ScriptEngine.HostedScript/Library/ConsoleColorEnum.cs deleted file mode 100644 index 355e9937e..000000000 --- a/src/ScriptEngine.HostedScript/Library/ConsoleColorEnum.cs +++ /dev/null @@ -1,64 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ScriptEngine.HostedScript.Library -{ - [SystemEnum("ЦветКонсоли", "ConsoleColor")] - public class ConsoleColorEnum : EnumerationContext - { - readonly Dictionary _valuesCache = new Dictionary(); - - private ConsoleColorEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - - } - - public IValue FromNativeValue(ConsoleColor native) - { - IValue val; - if (_valuesCache.TryGetValue(native, out val)) - { - return val; - } - else - { - val = ValuesInternal.First(x => ((CLREnumValueWrapper)x).UnderlyingValue == native); - _valuesCache.Add(native, val); - } - - return val; - } - - public static ConsoleColorEnum CreateInstance() - { - ConsoleColorEnum instance; - var type = TypeManager.RegisterType("ПеречислениеЦветКонсоли", typeof(ConsoleColorEnum)); - var enumValueType = TypeManager.RegisterType("ЦветКонсоли", typeof(CLREnumValueWrapper)); - - instance = new ConsoleColorEnum(type, enumValueType); - - instance.AddValue("Белый", "White", new CLREnumValueWrapper(instance, ConsoleColor.White)); - instance.AddValue("Черный", "Black", new CLREnumValueWrapper(instance, ConsoleColor.Black)); - instance.AddValue("Синий", "Blue", new CLREnumValueWrapper(instance, ConsoleColor.Blue)); - instance.AddValue("Желтый", "Yellow", new CLREnumValueWrapper(instance, ConsoleColor.Yellow)); - instance.AddValue("Красный", "Red", new CLREnumValueWrapper(instance, ConsoleColor.Red)); - instance.AddValue("Зеленый", "Green", new CLREnumValueWrapper(instance, ConsoleColor.Green)); - instance.AddValue("Бирюза", "Cyan", new CLREnumValueWrapper(instance, ConsoleColor.Cyan)); - instance.AddValue("Малиновый", "Magenta", new CLREnumValueWrapper(instance, ConsoleColor.Magenta)); - instance.AddValue("Серый", "Gray", new CLREnumValueWrapper(instance, ConsoleColor.Gray)); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ConsoleContext.cs b/src/ScriptEngine.HostedScript/Library/ConsoleContext.cs deleted file mode 100644 index 91ffd7644..000000000 --- a/src/ScriptEngine.HostedScript/Library/ConsoleContext.cs +++ /dev/null @@ -1,189 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Класс представляет собой инструмент доступа к системной консоли. - /// Предназначен для низкоуровнего манипулирования выводом в консоль. - /// - [ContextClass("Консоль", "Console")] - public class ConsoleContext : AutoContext - { - [ContextProperty("НажатаКлавиша", "KeyPressed")] - public bool HasKey - { - get - { - return Console.KeyAvailable; - } - } - - [ContextProperty("КурсорЛево", "CursorLeft")] - public int XPos - { - get - { - return Console.CursorLeft; - } - set - { - Console.CursorLeft = Math.Min(value, Console.WindowWidth-1); - } - } - - [ContextProperty("КурсорВерх", "CursorTop")] - public int YPos - { - get - { - return Console.CursorTop; - } - set - { - Console.CursorTop = Math.Min(value, Console.WindowHeight-1); - } - } - - [ContextMethod("ПрочитатьСтроку", "ReadLine")] - public string ReadLine() - { - return Console.ReadLine(); - } - - [ContextMethod("Прочитать", "Read")] - public int ReadKey() - { - var kki = Console.ReadKey(true); - return (int)kki.Key; - } - - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - Console.Clear(); - } - - [ContextMethod("ВывестиСтроку", "WriteLine")] - public void WriteLine(string text) - { - Console.WriteLine(text); - } - - [ContextMethod("Вывести", "Write")] - public void Write(string text) - { - Console.Write(text); - } - - [ContextProperty("Ширина", "Width")] - public int Width - { - get - { - return Console.WindowWidth; - } - } - - [ContextProperty("Высота", "Height")] - public int Высота - { - get - { - return Console.WindowHeight; - } - } - - [ContextMethod("ВидимостьКурсора", "CursorVisible")] - public bool CursorVisible(bool visible) - { - bool oldVal = Console.CursorVisible; - Console.CursorVisible = visible; - return oldVal; - } - - [ContextProperty("ЦветТекста", "TextColor")] - public IValue TextColor - { - get - { - try - { - return (CLREnumValueWrapper)GlobalsManager.GetEnum().FromNativeValue(Console.ForegroundColor); - } - catch (InvalidOperationException) - { - return null; - } - } - set - { - var typed = value.GetRawValue() as CLREnumValueWrapper; - Console.ForegroundColor = typed.UnderlyingValue; - } - } - - [ContextProperty("ЦветФона", "BackgroundColor")] - public IValue BackgroundColor - { - get - { - try - { - return (CLREnumValueWrapper)GlobalsManager.GetEnum().FromNativeValue(Console.BackgroundColor); - } - catch (InvalidOperationException) - { - return null; - } - } - set - { - var typed = value.GetRawValue() as CLREnumValueWrapper; - Console.BackgroundColor = typed.UnderlyingValue; - } - } - - /// - /// Возвращает или задает кодировку консоли, используемую при чтении входных данных. - /// - /// КодировкаТекста - [ContextProperty("КодировкаВходногоПотока", "InputEncoding")] - public IValue InputEncoding - { - get - { - var encodingEnum = GlobalsManager.GetEnum(); - return encodingEnum.GetValue(Console.InputEncoding); - } - set - { - Console.InputEncoding = TextEncodingEnum.GetEncoding(value); - } - } - - /// - /// Воспроизводит звуковой сигнал. - /// - [ContextMethod("Сигнал")] - public void Beep() - { - Console.Beep(); - } - - [ScriptConstructor] - public static ConsoleContext Constructor() - { - return new ConsoleContext(); - } - } - - -} diff --git a/src/ScriptEngine.HostedScript/Library/DelegateAction.cs b/src/ScriptEngine.HostedScript/Library/DelegateAction.cs deleted file mode 100644 index 3d1b62c22..000000000 --- a/src/ScriptEngine.HostedScript/Library/DelegateAction.cs +++ /dev/null @@ -1,83 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Делегат для выполнения метода в другом объекте - /// - [ContextClass("Действие","Action")] - public class DelegateAction : ContextIValueImpl - { - private readonly Func _action; - private const string MethodName_Ru = "Выполнить"; - private const string MethodName_En = "Execute"; - - private DelegateAction(Func action) - { - _action = action; - } - - public override bool DynamicMethodSignatures => true; - - public override int FindMethod(string name) - { - if (string.Compare(name, MethodName_En, StringComparison.OrdinalIgnoreCase) == 0 - || string.Compare(name, MethodName_Ru, StringComparison.OrdinalIgnoreCase) == 0) - { - return 0; - } - - return base.FindMethod(name); - } - - public override int GetMethodsCount() - { - return 1; - } - - public override MethodInfo GetMethodInfo(int methodNumber) - { - return new MethodInfo - { - Name = MethodName_Ru, - Alias = MethodName_En, - Annotations = new AnnotationDefinition[0], - Params = new ParameterDefinition[0] - }; - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - retValue = _action(arguments); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - _action(arguments); - } - - [ScriptConstructor] - public static DelegateAction Create(IRuntimeContextInstance target, string methodName) - { - var method = target.FindMethod(methodName); - - Func action = (parameters) => - { - IValue retVal; - target.CallAsFunction(method, parameters, out retVal); - return retVal; - }; - - return new DelegateAction(action); - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/DriveInfo/DriveTypeEnum.cs b/src/ScriptEngine.HostedScript/Library/DriveInfo/DriveTypeEnum.cs deleted file mode 100644 index 9c40f7ba0..000000000 --- a/src/ScriptEngine.HostedScript/Library/DriveInfo/DriveTypeEnum.cs +++ /dev/null @@ -1,53 +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 ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.DriveInfo -{ - /// - /// Типы дисков: - /// - Диск является устройством оптических дисков, такие как компакт-ДИСК или DVD-диск. - /// - Диск является жестким диском. - /// - Диск является сетевым диском. - /// - Диск не имеет корневой каталог. - /// - Диск является диском ОЗУ. - /// - Диск является съемное запоминающее устройство, например, дисковод гибких дисков или USB-устройство флэш-памяти. - /// - Тип диска неизвестен. - /// - [SystemEnum("ТипДиска", "DriveType")] - public class DriveTypeEnum : EnumerationContext - { - - private DriveTypeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - - } - - public static DriveTypeEnum CreateInstance() - { - DriveTypeEnum instance; - var type = TypeManager.RegisterType("ПеречислениеТипДиска", typeof(DriveTypeEnum)); - var enumValueType = TypeManager.RegisterType("ТипДиска", typeof(CLREnumValueWrapper)); - - instance = new DriveTypeEnum(type, enumValueType); - - instance.AddValue("Неизвестный", "Unknown", new CLREnumValueWrapper(instance, System.IO.DriveType.Unknown)); - instance.AddValue("НеИмеетКорневойКаталог", "NoRootDirectory", new CLREnumValueWrapper(instance, System.IO.DriveType.NoRootDirectory)); - instance.AddValue("СъемноеЗапоминающееУстройство", "Removable", new CLREnumValueWrapper(instance, System.IO.DriveType.Removable)); - instance.AddValue("ЖесткийДиск", "Fixed", new CLREnumValueWrapper(instance, System.IO.DriveType.Fixed)); - instance.AddValue("СетевойДиск", "Network", new CLREnumValueWrapper(instance, System.IO.DriveType.Network)); - instance.AddValue("ОптическийДиск", "CDRom", new CLREnumValueWrapper(instance, System.IO.DriveType.CDRom)); - instance.AddValue("ДискОЗУ", "Ram", new CLREnumValueWrapper(instance, System.IO.DriveType.Ram)); - - return instance; - } - - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/EnvironmentVariableTargetEnum.cs b/src/ScriptEngine.HostedScript/Library/EnvironmentVariableTargetEnum.cs deleted file mode 100644 index 40fb7dfe1..000000000 --- a/src/ScriptEngine.HostedScript/Library/EnvironmentVariableTargetEnum.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library -{ - [EnumerationType("РасположениеПеременнойСреды", "EnvironmentVariableTarget")] - public enum EnvironmentVariableTargetEnum - { - [EnumItem("Процесс", "Process")] - Process, - - [EnumItem("Пользователь", "User")] - User, - - [EnumItem("Машина", "Machine")] - Machine - } -} diff --git a/src/ScriptEngine.HostedScript/Library/FixedArrayImpl.cs b/src/ScriptEngine.HostedScript/Library/FixedArrayImpl.cs deleted file mode 100644 index 21bc71b4f..000000000 --- a/src/ScriptEngine.HostedScript/Library/FixedArrayImpl.cs +++ /dev/null @@ -1,107 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("ФиксированныйМассив", "FixedArray")] - public class FixedArrayImpl : AutoContext, ICollectionContext, IEnumerable - { - private readonly ArrayImpl _array; - - public FixedArrayImpl(ArrayImpl source) - { - _array = new ArrayImpl(); - foreach (var Value in source) - { - _array.Add(Value); - } - } - - public override bool IsIndexed - { - get - { - return true; - } - } - - public override IValue GetIndexedValue(IValue index) - { - return _array.GetIndexedValue(index); - } - - public override void SetIndexedValue(IValue index, IValue val) - { - throw new RuntimeException("Индексированное значение доступно только для чтения"); - } - - #region ICollectionContext Members - - [ContextMethod("Количество", "Count")] - public int Count() - { - return _array.Count(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - return _array.GetEnumerator(); - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - [ContextMethod("Найти", "Find")] - public IValue Find(IValue what) - { - return _array.Find(what); - } - - [ContextMethod("ВГраница", "UBound")] - public int UpperBound() - { - return _array.UpperBound(); - } - - [ContextMethod("Получить", "Get")] - public IValue Get(int index) - { - return _array.Get(index); - } - - [ScriptConstructor(Name = "На основании обычного массива")] - public static FixedArrayImpl Constructor(IValue source) - { - var rawSource = source.GetRawValue() as ArrayImpl; - if (rawSource == null) - throw RuntimeException.InvalidArgumentType(); - - return new FixedArrayImpl(rawSource); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/FixedMapImpl.cs b/src/ScriptEngine.HostedScript/Library/FixedMapImpl.cs deleted file mode 100644 index e904cd3c9..000000000 --- a/src/ScriptEngine.HostedScript/Library/FixedMapImpl.cs +++ /dev/null @@ -1,109 +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.Collections.Generic; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("ФиксированноеСоответствие", "FixedMap")] - public class FixedMapImpl : AutoContext, ICollectionContext, IEnumerable - { - - private readonly MapImpl _map; - - public FixedMapImpl(MapImpl source) - { - _map = new MapImpl(); - foreach (KeyAndValueImpl KV in source) - { - _map.Insert(KV.Key, KV.Value); - } - } - - public override bool IsIndexed - { - get - { - return true; - } - } - - public override IValue GetIndexedValue(IValue index) - { - if(_map.ContainsKey(index)) - return _map.GetIndexedValue(index); - - throw new RuntimeException("Значение, соответствующее ключу, не задано"); - } - - public override void SetIndexedValue(IValue index, IValue val) - { - throw new RuntimeException("Индексированное значение доступно только для чтения"); - } - - public override bool IsPropReadable(int propNum) - { - return _map.IsPropReadable(propNum); - } - - public override bool IsPropWritable(int propNum) - { - return _map.IsPropWritable(propNum); - } - - #region ICollectionContext Members - - [ContextMethod("Получить", "Get")] - public IValue Retrieve(IValue key) - { - return _map.GetIndexedValue(key); - } - - [ContextMethod("Количество", "Count")] - public int Count() - { - return _map.Count(); - } - - public CollectionEnumerator GetManagedIterator() - { - return _map.GetManagedIterator(); - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - return _map.GetEnumerator(); - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - [ScriptConstructor(Name = "Из соответствия")] - public static FixedMapImpl Constructor(IValue source) - { - var rawSource = source.GetRawValue() as MapImpl; - if (rawSource == null) - throw RuntimeException.InvalidArgumentType(); - - return new FixedMapImpl(rawSource); - } - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/FixedStructureImpl.cs b/src/ScriptEngine.HostedScript/Library/FixedStructureImpl.cs deleted file mode 100644 index 45e601828..000000000 --- a/src/ScriptEngine.HostedScript/Library/FixedStructureImpl.cs +++ /dev/null @@ -1,170 +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.Collections.Generic; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("ФиксированнаяСтруктура", "FixedStructure")] - public class FixedStructureImpl : DynamicPropertiesAccessor, ICollectionContext, IEnumerable - { - private readonly StructureImpl _structure = new StructureImpl(); - - - public FixedStructureImpl(StructureImpl structure) - { - foreach (KeyAndValueImpl keyValue in structure) - _structure.Insert(keyValue.Key.AsString(), keyValue.Value); - } - - public FixedStructureImpl(string strProperties, params IValue[] values) - { - _structure = new StructureImpl(strProperties, values); - } - - [ContextMethod("Свойство", "Property")] - public bool HasProperty(string name, [ByRef] IVariable value = null) - { - return _structure.HasProperty(name, value); - } - - public override bool IsPropWritable(int propNum) - { - return false; - } - - public override IValue GetPropValue(int propNum) - { - return _structure.GetPropValue(propNum); - } - - public override void SetPropValue(int propNum, IValue newVal) - { - throw new RuntimeException("Свойство только для чтения"); - } - - public override int FindProperty(string name) - { - return _structure.FindProperty(name); - } - - - public override MethodInfo GetMethodInfo(int methodNumber) - { - return _methods.GetMethodInfo(methodNumber); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - var binding = _methods.GetMethod(methodNumber); - try - { - binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - var binding = _methods.GetMethod(methodNumber); - try - { - retValue = binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override int FindMethod(string name) - { - return _methods.FindMethod(name); - } - - #region IReflectableContext Members - - public override int GetMethodsCount() - { - return _methods.Count; - } - - #endregion - - #region ICollectionContext Members - - [ContextMethod("Количество", "Count")] - public int Count() - { - return _structure.Count(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(_structure.GetManagedIterator()); - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - return _structure.GetEnumerator(); - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return _structure.GetEnumerator(); - } - - #endregion - - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - - /// - /// Создает фиксированную структуру по исходной структуре - /// - /// Исходная структура - //[ScriptConstructor(Name = "Из структуры")] - private static FixedStructureImpl Constructor(StructureImpl structObject) - { - return new FixedStructureImpl(structObject); - } - - /// - /// Создает фиксированную структуру по структуре либо заданному перечню свойств и значений - /// - /// Структура либо строка с именами свойств, указанными через запятую. - /// Только для перечня свойств: - /// Значения свойств. Каждое значение передается, как отдельный параметр. - [ScriptConstructor(Name = "По ключам и значениям")] - public static FixedStructureImpl Constructor(IValue param1, IValue[] args) - { - var rawArgument = param1.GetRawValue(); - if (rawArgument.DataType == DataType.String) - { - return new FixedStructureImpl(param1.AsString(), args); - } - else if (rawArgument is StructureImpl) - { - return new FixedStructureImpl(rawArgument as StructureImpl); - } - - throw new RuntimeException("В качестве параметра для конструктора можно передавать только Структура или Ключи и Значения"); - } - -} -} diff --git a/src/ScriptEngine.HostedScript/Library/GlobalBinaryData.cs b/src/ScriptEngine.HostedScript/Library/GlobalBinaryData.cs deleted file mode 100644 index 4de6932e4..000000000 --- a/src/ScriptEngine.HostedScript/Library/GlobalBinaryData.cs +++ /dev/null @@ -1,239 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -using ScriptEngine.HostedScript.Library.Binary; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Глобальный контекст. Операции с двоичными данными. - /// - [GlobalContext(Category = "Процедуры и функции работы с двоичными данными")] - public sealed class GlobalBinaryData : GlobalContextBase - { - private static byte[] StringToByteArray(String hex) - { - try - { - var newHex = hex.Replace(" ", String.Empty); - int numberChars = newHex.Length; - byte[] bytes = new byte[numberChars / 2]; - for (int i = 0; i < numberChars; i += 2) - bytes[i / 2] = Convert.ToByte(newHex.Substring(i, 2), 16); - return bytes; - } - catch (FormatException) - { - throw new FormatException("Неверный формат шестнадцатеричной строки"); - } - - } - - public static IAttachableContext CreateInstance() - { - return new GlobalBinaryData(); - } - - /// - /// Объединяет несколько объектов типа ДвоичныеДанные в один. - /// - /// Массив объектов типа ДвоичныеДанные. - /// Тип: ДвоичныеДанные. - [ContextMethod("СоединитьДвоичныеДанные")] - public BinaryDataContext ConcatenateBinaryData(ArrayImpl array) - { - // Сделано на int т.к. BinaryContext.Size имеет тип int; - - using (var stream = new System.IO.MemoryStream()) - { - - foreach (var cbd in array) - { - byte[] buffer = ((BinaryDataContext) cbd.AsObject()).Buffer; - stream.Write(buffer, 0, buffer.Length); - } - - return new BinaryDataContext(stream.ToArray()); - } - } - - /// - /// Разделяет двоичные данные на части заданного размера. Размер задается в байтах. - /// - /// Объект типа ДвоичныеДанные. - /// Размер одной части данных. - /// Массив объектов типа ДвоичныеДанные. - [ContextMethod("РазделитьДвоичныеДанные")] - public ArrayImpl SplitBinaryData(BinaryDataContext data, int size) - { - // Сделано на int т.к. BinaryContext.Size имеет тип int; - ArrayImpl array = new ArrayImpl(); - - int readedBytes = 0; - - while (readedBytes < data.Buffer.Length) - { - int bytesToRead = size; - if (bytesToRead > data.Buffer.Length - readedBytes) - bytesToRead = data.Buffer.Length - readedBytes; - - byte[] buffer = new byte[bytesToRead]; - Buffer.BlockCopy(data.Buffer, readedBytes, buffer, 0, bytesToRead); - readedBytes += bytesToRead; - array.Add(new BinaryDataContext(buffer)); - } - - return array; - } - - /// - /// Преобразует строку в значение типа ДвоичныеДанные с учетом кодировки текста. - /// - /// Строка, которую требуется преобразовать в ДвоичныеДанные. - /// Кодировка текста - /// Определяет, будет ли добавлена метка порядка байт (BOM) кодировки текста в начало данных. - /// Тип: ДвоичныеДанные. - [ContextMethod("ПолучитьДвоичныеДанныеИзСтроки")] - public BinaryDataContext GetBinaryDataFromString(string str, IValue encoding = null, bool addBOM = false) - { - // Получаем кодировку - // Из синтаксис помощника если кодировка не задана используем UTF8 - - System.Text.Encoding enc = System.Text.Encoding.UTF8; - if (encoding != null) - enc = TextEncodingEnum.GetEncoding(encoding, addBOM); - - return new BinaryDataContext(enc.GetBytes(str)); - } - - // ToDo: ПолучитьБуферДвоичныхДанныхИзСтроки - - [ContextMethod("ПолучитьСтрокуИзДвоичныхДанных")] - public string GetStringFromBinaryData(BinaryDataContext data, IValue encoding = null) - { - // Получаем кодировку - // Из синтаксис помощника если кодировка не задана используем UTF8 - - System.Text.Encoding enc = System.Text.Encoding.UTF8; - if (encoding != null) - enc = TextEncodingEnum.GetEncoding(encoding); - - return enc.GetString(data.Buffer); - } - - // ToDo: ПолучитьСтрокуИзБуфераДвоичныхДанных - - - /// - /// Преобразует строку формата Base64 в двоичные данные. - /// - /// Строка в формате Base64. - /// Тип: ДвоичныеДанные. - [ContextMethod("ПолучитьДвоичныеДанныеИзBase64Строки")] - public BinaryDataContext GetBinaryDataFromBase64String(string str) - { - return new BinaryDataContext(System.Convert.FromBase64String(str)); - } - - // ToDo: ПолучитьБуферДвоичныхДанныхИзBase64Строки - - [ContextMethod("ПолучитьBase64СтрокуИзДвоичныхДанных")] - public string GetBase64StringFromBinaryData(BinaryDataContext data) - { - return System.Convert.ToBase64String(data.Buffer); - } - - // ToDo: ПолучитьBase64СтрокуИзБуфераДвоичныхДанных - - // ToDo: ПолучитьДвоичныеДанныеИзBase64ДвоичныхДанных - - // ToDo: ПолучитьБуферДвоичныхДанныхИзBase64БуфераДвоичныхДанных - - // ToDo: ПолучитьBase64ДвоичныеДанныеИзДвоичныхДанных - - // ToDo: ПолучитьBase64БуферДвоичныхДанныхИзБуфераДвоичныхДанных - - /// - /// Преобразует строку формата Base 16 (Hex) в двоичные данные. - /// - /// Строка в формате Base 16 (Hex). - /// Тип: ДвоичныеДанные. - [ContextMethod("ПолучитьДвоичныеДанныеИзHexСтроки")] - public BinaryDataContext GetBinaryDataFromHexString(string hex) - { - return new BinaryDataContext(StringToByteArray(hex)); - } - - /// - /// Преобразует строку в формате Base 16 (Hex) в буфер двоичных данных. - /// - /// Строка в формате Base 16 (Hex). - /// Тип: БуферДвоичныхДанных. - [ContextMethod("ПолучитьБуферДвоичныхДанныхИзHexСтроки")] - public BinaryDataBuffer GetBinaryDataBufferFromHexString(string hex) - { - return new BinaryDataBuffer(StringToByteArray(hex)); - } - - /// - /// Преобразует двоичные данные в строку формата Base 16 (Hex). - /// - /// Двоичные данные. - /// Тип: Строка. - [ContextMethod("ПолучитьHexСтрокуИзДвоичныхДанных")] - public string GetHexStringFromBinaryData(BinaryDataContext data) - { - return BitConverter.ToString(data.Buffer).Replace("-",""); - } - - /// - /// Преобразует буфер двоичных данных в строку формата Base 16 (Hex). - /// - /// Буфер двоичных данных. - /// Тип: Строка. - [ContextMethod("ПолучитьHexСтрокуИзБуфераДвоичныхДанных")] - public string GetHexStringFromBinaryDataBuffer(BinaryDataBuffer buffer) - { - return BitConverter.ToString(buffer.Bytes).Replace("-",""); - } - - // ToDo: ПолучитьДвоичныеДанныеИзHexДвоичныхДанных - - // ToDo: ПолучитьБуферДвоичныхДанныхИзHexБуфераДвоичныхДанных - - // ToDo: ПолучитьHexДвоичныеДанныеИзДвоичныхДанных - - // ToDo: ПолучитьHexБуферДвоичныхДанныхИзБуфераДвоичныхДанных - - /// - /// Преобразует двоичные данные в буфер двоичных данных. - /// - /// Двоичные данные. - /// Тип: БуферДвоичныхДанных. - [ContextMethod("ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных")] - public BinaryDataBuffer GetBinaryDataBufferFromBinaryData(BinaryDataContext data) - { - return new BinaryDataBuffer(data.Buffer); - } - - /// - /// Преобразует буфер двоичных данных в значение типа ДвоичныеДанные. - /// - /// Буфер двоичных данных. - /// Тип: ДвоичныеДанные. - [ContextMethod("ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных")] - public BinaryDataContext GetBinaryDataFromBinaryDataBuffer(BinaryDataBuffer buffer) - { - return new BinaryDataContext(buffer.Bytes); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/GuidWrapper.cs b/src/ScriptEngine.HostedScript/Library/GuidWrapper.cs deleted file mode 100644 index eff6f8d28..000000000 --- a/src/ScriptEngine.HostedScript/Library/GuidWrapper.cs +++ /dev/null @@ -1,107 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("УникальныйИдентификатор","UUID")] - public class GuidWrapper : IValue, IObjectWrapper - { - Guid _value; - - public GuidWrapper() - { - _value = Guid.NewGuid(); - } - - public GuidWrapper(string uuidString) - { - _value = Guid.Parse(uuidString); - } - - [ScriptConstructor] - public static GuidWrapper Create() - { - return new GuidWrapper(); - } - - [ScriptConstructor(Name = "Из строки")] - public static GuidWrapper Create(IValue uuidString) - { - return new GuidWrapper(uuidString.AsString()); - } - - public DataType DataType - { - get { return Machine.DataType.GenericValue; } - } - - public TypeDescriptor SystemType - { - get - { - return TypeManager.GetTypeByFrameworkType(typeof(GuidWrapper)); - } - } - - public decimal AsNumber() - { - throw RuntimeException.ConvertToNumberException(); - } - - public DateTime AsDate() - { - throw RuntimeException.ConvertToDateException(); - } - - public bool AsBoolean() - { - throw RuntimeException.ConvertToBooleanException(); - } - - public string AsString() - { - return _value.ToString(); - } - - public IRuntimeContextInstance AsObject() - { - throw RuntimeException.ValueIsNotObjectException(); - } - - public IValue GetRawValue() - { - return this; - } - - public int CompareTo(IValue other) - { - GuidWrapper otherUuid = other.GetRawValue() as GuidWrapper; - if (otherUuid == null) - throw RuntimeException.ComparisonNotSupportedException(); - - return _value.CompareTo(otherUuid._value); - } - - public bool Equals(IValue other) - { - GuidWrapper otherUuid = other.GetRawValue() as GuidWrapper; - if (otherUuid == null) - return false; - else - return _value.Equals(otherUuid._value); - } - - - object IObjectWrapper.UnderlyingObject - { - get { return _value; } - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Hash/HashFunctionEnum.cs b/src/ScriptEngine.HostedScript/Library/Hash/HashFunctionEnum.cs deleted file mode 100644 index 17d9297ec..000000000 --- a/src/ScriptEngine.HostedScript/Library/Hash/HashFunctionEnum.cs +++ /dev/null @@ -1,118 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System.Security.Cryptography; - - -namespace ScriptEngine.HostedScript.Library.Hash -{ - [SystemEnum("ХешФункция", "HashFunction")] - public class HashFunctionEnum : EnumerationContext - { - const string MD5 = "MD5"; - const string SHA1 = "SHA1"; - const string SHA256 = "SHA256"; - const string SHA384 = "SHA384"; - const string SHA512 = "SHA512"; - const string CRC32 = "CRC32"; - - - [EnumValue(MD5)] - public EnumerationValue Md5 - { - get - { - return this[MD5]; - } - } - - - [EnumValue(SHA1)] - public EnumerationValue Sha1 - { - get - { - return this[SHA1]; - } - } - - - [EnumValue(SHA256)] - public EnumerationValue Sha256 - { - get - { - return this[SHA256]; - } - } - - - [EnumValue(SHA384)] - public EnumerationValue Sha384 - { - get - { - return this[SHA384]; - } - } - - - [EnumValue(SHA512)] - public EnumerationValue Sha512 - { - get - { - return this[SHA512]; - } - } - - - [EnumValue(CRC32)] - public EnumerationValue Crc32 - { - get - { - return this[CRC32]; - } - } - - - - private HashFunctionEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - - } - - - public static HashFunctionEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new HashFunctionEnum(t, v)); - } - - public static HashAlgorithm GetProvider(IValue provider) - { - if (provider.DataType != DataType.GenericValue) - throw RuntimeException.InvalidArgumentType(); - - var neededProvider = provider.GetRawValue() as SelfAwareEnumValue; - if (neededProvider == null) - throw RuntimeException.InvalidArgumentType(); - - var algName = neededProvider.AsString(); - if (algName == "CRC32") - return new ScriptEngine.HostedScript.Library.Hash.Crc32(); - - var ret = HashAlgorithm.Create(algName); - if (ret == null) - throw RuntimeException.InvalidArgumentType(); - return ret; - - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Hash/HashImpl.cs b/src/ScriptEngine.HostedScript/Library/Hash/HashImpl.cs deleted file mode 100644 index 34af012fc..000000000 --- a/src/ScriptEngine.HostedScript/Library/Hash/HashImpl.cs +++ /dev/null @@ -1,149 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Text; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System.Security.Cryptography; -using System.IO; -using ScriptEngine.HostedScript.Library.Binary; - -namespace ScriptEngine.HostedScript.Library.Hash -{ - [ContextClass("ХешированиеДанных", "DataHashing")] - public class HashImpl : AutoContext, IDisposable - { - protected HashAlgorithm _provider; - protected IValue _enumValue; - protected CombinedStream _toCalculate=new CombinedStream(); - protected bool _calculated; - protected byte[] _hash; - - public HashImpl(HashAlgorithm provider, IValue enumValue) - { - _provider = provider; - _enumValue = enumValue; - _calculated = false; - } - - public byte[] InternalHash - { - get - { - if (!_calculated) - { - _hash = _provider.ComputeHash(_toCalculate); - _calculated = true; - } - return _hash; - } - } - - [ContextProperty("ХешФункция", "HashFunction")] - public IValue Extension - { - get - { - return _enumValue; - } - } - - [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); - } - } - - [ContextProperty("ХешСуммаСтрокой", "HashSumOfString")] - public string HashString - { - get - { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < InternalHash.Length; i++) - sb.Append(InternalHash[i].ToString("X2")); - return sb.ToString(); - } - } - - - [ContextMethod("Добавить", "Append")] - public void Append(IValue toAdd, uint count = 0) - { - switch (toAdd.DataType) - { - case DataType.String: - AddStream(new MemoryStream(Encoding.UTF8.GetBytes(toAdd.AsString()))); - break; - case DataType.Object when toAdd is GenericStream stream: - var length = Math.Min(count == 0 ? stream.Size() : count, stream.Size() - stream.CurrentPosition()); - var buffer = (stream.GetUnderlyingStream() as MemoryStream)?.GetBuffer(); - if (buffer == null) - throw RuntimeException.InvalidArgumentValue(); - AddStream(new MemoryStream(buffer, (int) stream.CurrentPosition(), (int) length)); - stream.Seek((int) length, StreamPositionEnum.Current); - break; - case DataType.Object when toAdd is BinaryDataContext binaryData: - AddStream(new MemoryStream(binaryData.Buffer)); - break; - default: - throw RuntimeException.InvalidArgumentType(); - } - } - - [ContextMethod("ДобавитьФайл", "AppendFile")] - public void AppendFile(string path) - { - if (!File.Exists(path)) - throw RuntimeException.InvalidArgumentType(); - AddStream(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); - } - - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _toCalculate.Close(); - _toCalculate.Dispose(); - _toCalculate = new CombinedStream(); - _calculated = false; - } - - - [ScriptConstructor(Name = "По указанной хеш-функции")] - public static HashImpl Constructor(IValue providerEnum) - { - var objectProvider = HashFunctionEnum.GetProvider(providerEnum); - return new HashImpl(objectProvider, providerEnum); - } - - public void Dispose() - { - _toCalculate.Close(); - _toCalculate.Dispose(); - } - - private void AddStream(Stream stream) - { - _toCalculate.AddStream(stream); - _toCalculate.Seek(0, SeekOrigin.Begin); - _calculated = false; - - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestBodyBinary.cs b/src/ScriptEngine.HostedScript/Library/Http/HttpRequestBodyBinary.cs deleted file mode 100644 index bbaee8669..000000000 --- a/src/ScriptEngine.HostedScript/Library/Http/HttpRequestBodyBinary.cs +++ /dev/null @@ -1,73 +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 ScriptEngine.Machine; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using ScriptEngine.HostedScript.Library.Binary; - -namespace ScriptEngine.HostedScript.Library.Http -{ - class HttpRequestBodyBinary : IHttpRequestBody - { - private readonly MemoryStream _memoryStream = new MemoryStream(); - - public HttpRequestBodyBinary() - { - } - - public HttpRequestBodyBinary(BinaryDataContext data) - { - _memoryStream.Write(data.Buffer, 0, data.Size()); - } - - public HttpRequestBodyBinary(string body, IValue encoding = null, - ByteOrderMarkUsageEnum bomUsage = ByteOrderMarkUsageEnum.Auto) - { - var utfs = new List {"utf-16", "utf-32"}; - var addBom = utfs.Contains(encoding?.AsString(), StringComparer.OrdinalIgnoreCase) && - bomUsage == ByteOrderMarkUsageEnum.Auto || bomUsage == ByteOrderMarkUsageEnum.Use; - - var encoder = encoding == null ? new UTF8Encoding(addBom) : TextEncodingEnum.GetEncoding(encoding, addBom); - - var byteArray = encoder.GetBytes(body); - _memoryStream.Write(byteArray, 0, byteArray.Length); - } - - public IValue GetAsString() - { - _memoryStream.Seek(0, SeekOrigin.Begin); - var reader = new StreamReader(_memoryStream); - return ValueFactory.Create(reader.ReadToEnd()); - } - - public IValue GetAsBinary() - { - _memoryStream.Seek(0, SeekOrigin.Begin); - return new BinaryDataContext(_memoryStream.ToArray()); - } - - public IValue GetAsFilename() - { - return ValueFactory.Create(); - } - - public Stream GetDataStream() - { - _memoryStream.Seek(0, SeekOrigin.Begin); - return _memoryStream; - } - - public void Dispose() - { - _memoryStream.Close(); - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Http/HttpResponseBody.cs b/src/ScriptEngine.HostedScript/Library/Http/HttpResponseBody.cs deleted file mode 100644 index 0dc5a0c3c..000000000 --- a/src/ScriptEngine.HostedScript/Library/Http/HttpResponseBody.cs +++ /dev/null @@ -1,227 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.IO; -using System.Net; -using System.IO.Compression; - -namespace ScriptEngine.HostedScript.Library.Http -{ - class HttpResponseBody : IDisposable - { - private const int INMEMORY_BODY_LIMIT = 1024 * 1024 * 5; // 5 Mb - private const int UNDEFINED_LENGTH = -1; - private const int CHUNK_SIZE = 0x8000; - - string _backingFileName; - bool _backFileIsTemp = false; - byte[] _inMemBody; - - private readonly bool _autoDecompress; - private long _contentSize = 0; - - public HttpResponseBody(HttpWebResponse response, string dumpToFile) - { - _autoDecompress = string.Equals(response.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase); - _contentSize = _autoDecompress ? -1 : response.ContentLength; - - if (String.IsNullOrEmpty(dumpToFile)) - { - InitInMemoryResponse(response); - } - else - { - InitFileBackedResponse(response, dumpToFile); - } - } - - private void InitInMemoryResponse(HttpWebResponse response) - { - if(_contentSize > INMEMORY_BODY_LIMIT) - { - var filename = Path.GetTempFileName(); - _backFileIsTemp = true; - InitFileBackedResponse(response, filename); - } - else - { - if(_contentSize == UNDEFINED_LENGTH) - { - ReadToStream(response); - } - else - { - ReadToArray(response); - } - } - } - - public bool AutoDecompress => _autoDecompress; - - public long ContentSize => _contentSize < 0 ? 0 : _contentSize; - - public Stream OpenReadStream() - { - if (_backingFileName != null) - { - return new FileStream(_backingFileName, FileMode.Open, FileAccess.Read); - } - else if (_inMemBody != null) - { - return new MemoryStream(_inMemBody); - } - else - throw new InvalidOperationException("No response body"); - } - - private Stream GetResponseStream(HttpWebResponse response) - { - if (_autoDecompress) - return new GZipStream(response.GetResponseStream(), CompressionMode.Decompress); - return response.GetResponseStream(); - } - - private void ReadToStream(HttpWebResponse response) - { - using (var responseStream = GetResponseStream(response)) - using(var ms = new MemoryStream()) - { - bool memStreamIsAlive = true; - - int readTotal = 0; - byte[] buffer = new byte[CHUNK_SIZE]; - while (true) - { - var bytesRead = responseStream.Read(buffer, 0, CHUNK_SIZE); - if (bytesRead == 0) - break; - - ms.Write(buffer, 0, bytesRead); - - readTotal += bytesRead; - - if(readTotal > INMEMORY_BODY_LIMIT) - { - var filename = Path.GetTempFileName(); - _backFileIsTemp = true; - _backingFileName = filename; - - ms.Position = 0; - using (var file = new FileStream(filename, FileMode.Create)) - { - StreamToStreamCopy(ms, file); - ms.Dispose(); - memStreamIsAlive = false; - StreamToStreamCopy(responseStream, file); - } - - break; - - } - } - - if(memStreamIsAlive) - { - _inMemBody = new byte[ms.Length]; - ms.Position = 0; - ms.Read(_inMemBody, 0, _inMemBody.Length); - } - - if (_autoDecompress) - _contentSize = readTotal; - } - } - - private void ReadToArray(HttpWebResponse response) - { - System.Diagnostics.Debug.Assert(_contentSize <= INMEMORY_BODY_LIMIT); - - using (var stream = GetResponseStream(response)) - { - var mustRead = (int)_contentSize; - _inMemBody = new byte[mustRead]; - int offset = 0; - - while (mustRead > 0) - { - int portion = Math.Min(CHUNK_SIZE, (int)mustRead); - var read = stream.Read(_inMemBody, offset, portion); - - if (read == 0) - break; - - mustRead -= read; - offset += read; - } - } - } - - private void InitFileBackedResponse(HttpWebResponse response, string backingFileName) - { - _backingFileName = backingFileName; - using(var responseStream = GetResponseStream(response)) - { - using(var file = new FileStream(backingFileName, FileMode.Create)) - { - StreamToStreamCopy(responseStream, file); - } - } - } - - private static void StreamToStreamCopy(Stream responseStream, Stream acceptor) - { - byte[] buffer = new byte[CHUNK_SIZE]; - while (true) - { - var bytesRead = responseStream.Read(buffer, 0, CHUNK_SIZE); - if (bytesRead == 0) - break; - - acceptor.Write(buffer, 0, bytesRead); - } - } - - private void Dispose(bool manualDispose) - { - if(manualDispose) - { - GC.SuppressFinalize(this); - _inMemBody = null; - } - - KillTemporaryFile(); - } - - private void KillTemporaryFile() - { - if(_backFileIsTemp && _backingFileName != null) - { - if(File.Exists(_backingFileName)) - { - try - { - File.Delete(_backingFileName); - } - catch - { - // нипавезло :( - } - } - } - } - - public void Dispose() - { - Dispose(true); - } - - ~HttpResponseBody() - { - Dispose(false); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Http/InternetProxyContext.cs b/src/ScriptEngine.HostedScript/Library/Http/InternetProxyContext.cs deleted file mode 100644 index bf86dd0b0..000000000 --- a/src/ScriptEngine.HostedScript/Library/Http/InternetProxyContext.cs +++ /dev/null @@ -1,185 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; - -namespace ScriptEngine.HostedScript.Library.Http -{ - /// - /// Параметры прокси-сервера для доступа в Интернет. - /// В текущей реализации поддерживается только HTTP прокси. Стандартные методы объекта ИнтернетПрокси из 1С:Предприятие для FTP и SOCKS не реализованы. - /// - [ContextClass("ИнтернетПрокси", "InternetProxy")] - public class InternetProxyContext : AutoContext - { - private ArrayImpl _bypassProxyOnAddresses; - private bool _bypassLocal; - - private Dictionary _proxies = new Dictionary(); - - private class ProxySettings - { - public string server; - public int port; - public bool useOSAuthentication; - public IWebProxy proxy; - public NetworkCredential creds; - } - - private const string PROTO_HTTP = "http"; - private const string PROTO_HTTPS = "https"; - - public InternetProxyContext(bool useDefault) - { - var settings = new ProxySettings(); - if (useDefault) - { - settings.proxy = WebRequest.GetSystemWebProxy(); - settings.creds = (NetworkCredential)System.Net.CredentialCache.DefaultCredentials; - if (settings.creds != null) - settings.proxy.Credentials = settings.creds; - else - settings.creds = new NetworkCredential(); - - _proxies[PROTO_HTTP] = settings; - _proxies[PROTO_HTTPS] = settings; - } - else - { - _bypassLocal = false; - settings.server = String.Empty; - settings.creds = new NetworkCredential(); - - _proxies[PROTO_HTTP] = settings; - _proxies[PROTO_HTTPS] = settings; - } - - _bypassProxyOnAddresses = new ArrayImpl(); - } - - public IWebProxy GetProxy(string protocol) - { - var settings = GetSettings(protocol); - IWebProxy returnProxy; - - if (settings.proxy == null) - { - if (String.IsNullOrEmpty(settings.server)) - throw new RuntimeException("Не заданы настройки прокси-сервера для протокола, используемого в запросе"); - - var wp = new WebProxy(settings.server, settings.port); - wp.Credentials = settings.creds; - wp.BypassList = _bypassProxyOnAddresses.Select(x => x.AsString()).ToArray(); - wp.BypassProxyOnLocal = _bypassLocal; - settings.proxy = wp; - returnProxy = wp; - } - else - { - returnProxy = _proxies[protocol].proxy; - } - - return returnProxy; - } - - [ContextMethod("Пользователь","User")] - public string User(string protocol) - { - return GetSettings(protocol).creds.UserName; - } - - [ContextMethod("Пароль", "Password")] - public string Password(string protocol) - { - return GetSettings(protocol).creds.Password; - } - - [ContextMethod("Сервер", "Server")] - public string Server(string protocol) - { - return GetSettings(protocol).server; - } - - [ContextMethod("Порт", "Password")] - public int Port(string protocol) - { - return GetSettings(protocol).port; - } - - [ContextMethod("Установить", "Set")] - public void Set(string protocol, string server, int port = 0, string username = "", string password = "", bool useOSAuthentication = true) - { - if(!ProtocolNameIsValid(protocol)) - throw RuntimeException.InvalidArgumentValue(); - - var settings = new ProxySettings - { - server = server, - port = port, - creds = new NetworkCredential(username, password), - useOSAuthentication = useOSAuthentication - }; - - _proxies[protocol] = settings; - } - - [ContextProperty("НеИспользоватьПроксиДляАдресов","BypassProxyOnAddresses")] - public ArrayImpl BypassProxyList - { - get - { - return _bypassProxyOnAddresses; - } - set - { - _bypassProxyOnAddresses = value; - } - } - - [ContextProperty("НеИспользоватьПроксиДляЛокальныхАдресов", "BypassProxyOnLocal")] - public bool BypassProxyOnLocal - { - get - { - return _bypassLocal; - } - set - { - _bypassLocal = value; - } - } - - private ProxySettings GetSettings(string protocol) - { - if (!ProtocolNameIsValid(protocol)) - throw RuntimeException.InvalidArgumentValue(); - - return _proxies[protocol]; - } - - private static bool ProtocolNameIsValid(string protocol) - { - return StringComparer.OrdinalIgnoreCase.Compare(protocol, PROTO_HTTP) == 0 || StringComparer.OrdinalIgnoreCase.Compare(protocol, PROTO_HTTPS) == 0; - } - - [ScriptConstructor(Name = "Формирование неинициализированного объекта")] - public static InternetProxyContext Constructor() - { - return Constructor(ValueFactory.Create(false)); - } - - [ScriptConstructor(Name = "Конструктор для системных настроек прокси")] - public static InternetProxyContext Constructor(IValue useDefault) - { - return new InternetProxyContext(useDefault.AsBoolean()); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Json/GlobalJsonFunctions.cs b/src/ScriptEngine.HostedScript/Library/Json/GlobalJsonFunctions.cs deleted file mode 100644 index f19eb2010..000000000 --- a/src/ScriptEngine.HostedScript/Library/Json/GlobalJsonFunctions.cs +++ /dev/null @@ -1,317 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using Newtonsoft.Json; -using System; - -namespace ScriptEngine.HostedScript.Library.Json -{ - - /// - /// Глобальный контекст. Операции с Json. - /// - [GlobalContext(Category = "Процедуры и функции работы с JSON")] - public sealed class GlobalJsonFunctions : GlobalContextBase - { - public static IAttachableContext CreateInstance() - { - return new GlobalJsonFunctions(); - } - - /// - /// - /// Считывает значение из JSON-текста или файла. JSON-текст должен быть корректным. - /// - /// - /// - /// Объект чтения JSON. - /// - /// Если установлено Истина, чтение объекта JSON будет выполнено в Соответствие. - /// Если установлено Ложь, объекты будут считываться в объект типа Структура. - /// Значение по умолчанию: Ложь. - /// - /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. - /// - /// Значение не обрабатывается в текущей версии. Значение по умолчанию: ISO. - /// - /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. - /// - /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. - /// - /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. - /// - /// Значение не обрабатывается в текущей версии. Значение по умолчанию: Неопределено. - /// - /// Значение не обрабатывается в текущей версии. - /// - /// - /// - [ContextMethod("ПрочитатьJSON", "ReadJSON")] - public IValue ReadJSON(JSONReader Reader, bool ReadToMap = false, IValue PropertiesWithDateValuesNames = null, IValue ExpectedDateFormat = null, string ReviverFunctionName = null, IValue ReviverFunctionModule = null, IValue ReviverFunctionAdditionalParameters = null, IValue RetriverPropertiesNames = null, int MaximumNesting = 500) - { - if (ReadToMap) - return ReadJSONInMap(Reader); - else - return ReadJSONInStruct(Reader); - } - - public IValue ReadJSONInStruct(JSONReader Reader, bool nestedArray = false) - { - if (nestedArray) - { - ArrayImpl NestedArray = new ArrayImpl(); - - while (Reader.Read()) - { - if (Reader.CurrentJsonTokenType == JsonToken.EndArray) - { - break; - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartObject) - { - NestedArray.Add(ReadJSONInStruct(Reader)); - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) - { - NestedArray.Add(ReadJSONInStruct(Reader, true)); - } - else - NestedArray.Add(Reader.CurrentValue); - } - return NestedArray; - } - - StructureImpl ResStruct = new StructureImpl(); - - while ((Reader).Read()) - { - if (Reader.CurrentJsonTokenType == JsonToken.PropertyName) - { - string PropertyName = Reader.CurrentValue.AsString(); - Reader.Read(); - - if (Reader.CurrentJsonTokenType == JsonToken.StartObject) - { - ResStruct.Insert(PropertyName, ReadJSONInStruct(Reader)); - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) - { - ResStruct.Insert(PropertyName, ReadJSONInStruct(Reader, true)); - } - else - { - ResStruct.Insert(PropertyName, Reader.CurrentValue); - } - } - else if (Reader.CurrentJsonTokenType == JsonToken.EndObject) - { - break; - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) - { - return ReadJSONInStruct(Reader, true); - } - } - return ResStruct; - } - - public IValue ReadJSONInMap(JSONReader Reader, bool nestedArray = false) - { - - if (nestedArray) - { - ArrayImpl NestedArray = new ArrayImpl(); - - while (Reader.Read()) - { - if (Reader.CurrentJsonTokenType == JsonToken.EndArray) - { - break; - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartObject) - { - NestedArray.Add(ReadJSONInMap(Reader)); - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) - { - NestedArray.Add(ReadJSONInMap(Reader, true)); - } - else - NestedArray.Add(Reader.CurrentValue); - } - return NestedArray; - } - - MapImpl ResStruct = new MapImpl(); - while ((Reader).Read()) - { - if (Reader.CurrentJsonTokenType == JsonToken.PropertyName) - { - string PropertyName = Reader.CurrentValue.AsString(); - Reader.Read(); - - if (Reader.CurrentJsonTokenType == JsonToken.StartObject) - { - ResStruct.Insert(ValueFactory.Create(PropertyName), ReadJSONInMap(Reader)); - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) - { - ResStruct.Insert(ValueFactory.Create(PropertyName), ReadJSONInMap(Reader, true)); - } - else - { - ResStruct.Insert(ValueFactory.Create(PropertyName), Reader.CurrentValue); - } - } - else if (Reader.CurrentJsonTokenType == JsonToken.EndObject) - { - break; - } - else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) - { - return ReadJSONInMap(Reader, true); - } - } - return ResStruct; - } - - /// - /// - /// Выполняет преобразование строки, прочитанной в JSON-формате, в значение типа Дата. - /// - /// - /// - /// Строка, которую требуется преобразовать в дату. - /// - /// Формат, в котором представлена дата в строке, подлежащей преобразованию. - /// - /// - /// Значения данного типа содержит дату григорианского календаря (с 01 января 0001 года) и время с точностью до секунды. - /// - [ContextMethod("ПрочитатьДатуJSON", "ReadJSONDate")] - public IValue ReadJSONDate(string String, IValue Format) - { - - var format = Format.GetRawValue() as SelfAwareEnumValue; - var JSONDateFormatEnum = GlobalsManager.GetEnum(); - DateFormatHandling dateFormatHandling = new DateFormatHandling(); - - if (format == JSONDateFormatEnum.ISO || format == null) - dateFormatHandling = DateFormatHandling.IsoDateFormat; - else if (format == JSONDateFormatEnum.Microsoft) - dateFormatHandling = DateFormatHandling.MicrosoftDateFormat; - else - throw new RuntimeException(Locale.NStr("ru='Формат даты JavaScript не поддерживается.'; en='JavaScript date format is not supported'")); - - string json = @"{""Date"":""" + String + @"""}"; - - var settings = new JsonSerializerSettings - { - DateFormatHandling = dateFormatHandling - }; - var result = JsonConvert.DeserializeObject(json, settings); - return ValueFactory.Create((DateTime)result.Date); - } - - /// - /// - /// Выполняет сериализацию Значение в формат JSON. Результат помещает в объект ЗаписьJSON. - /// Если методу требуется передать значение недопустимого типа, то можно использовать функцию преобразования значения (параметры ИмяФункцииПреобразования и МодульФункцииПреобразования). - /// - /// - /// - /// Объект, через который осуществляется запись JSON. Поток JSON должен быть подготовлен для записи значения. - /// - /// Объект записи JSON. Меняет состояние потока записи. - /// - /// В текущий версии не обрабатывается. Настройки сериализации в JSON. - /// - /// В текущий версии не обрабатывается. Значение по умолчанию: Неопределено. - /// - /// Указывает контекст, в котором реализована функция преобразования значения в значение формата JSON. - /// В текущий версии не обрабатывается. Значение по умолчанию: Неопределено. - /// - /// В текущий версии не обрабатывается. Значение по умолчанию: Неопределено. - /// - /// - [ContextMethod("ЗаписатьJSON", "WriteJSON")] - public void WriteJSON(JSONWriter Writer, IValue Value, IValue SerializationSettings = null, string ConversionFunctionName = null, IValue ConversionFunctionModule = null, IValue ConversionFunctionAdditionalParameters = null) - { - - var RawValue = Value.GetRawValue(); - - if (RawValue is ArrayImpl) - { - Writer.WriteStartArray(); - foreach (var item in (ArrayImpl)RawValue) - { - WriteJSON(Writer, item); - } - Writer.WriteEndArray(); - } - else if (RawValue is FixedArrayImpl) - { - Writer.WriteStartArray(); - foreach (var item in (FixedArrayImpl)RawValue) - { - WriteJSON(Writer, item); - } - Writer.WriteEndArray(); - } - else if (RawValue is StructureImpl) - { - Writer.WriteStartObject(); - foreach (var item in (StructureImpl)RawValue) - { - Writer.WritePropertyName(item.Key.AsString()); - WriteJSON(Writer, item.Value); - } - Writer.WriteEndObject(); - } - else if (RawValue is FixedStructureImpl) - { - Writer.WriteStartObject(); - foreach (var item in (FixedStructureImpl)RawValue) - { - Writer.WritePropertyName(item.Key.AsString()); - WriteJSON(Writer, item.Value); - } - Writer.WriteEndObject(); - } - else if (RawValue is MapImpl) - { - Writer.WriteStartObject(); - foreach (var item in (MapImpl)RawValue) - { - Writer.WritePropertyName(item.Key.AsString()); - WriteJSON(Writer, item.Value); - } - Writer.WriteEndObject(); - } - else if (RawValue is FixedMapImpl) - { - Writer.WriteStartObject(); - foreach (var item in (FixedMapImpl)RawValue) - { - Writer.WritePropertyName(item.Key.AsString()); - WriteJSON(Writer, item.Value); - } - Writer.WriteEndObject(); - } - else - Writer.WriteValue(RawValue); - } - } - - class ConvertedDate - { - public DateTime Date { get; set; } - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/Json/JSONCharactersEscapeMode.cs b/src/ScriptEngine.HostedScript/Library/Json/JSONCharactersEscapeMode.cs deleted file mode 100644 index 4a11ca6bb..000000000 --- a/src/ScriptEngine.HostedScript/Library/Json/JSONCharactersEscapeMode.cs +++ /dev/null @@ -1,54 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Json -{ - [SystemEnum("ЭкранированиеСимволовJSON", "JSONCharactersEscapeMode")] - public class JSONCharactersEscapeModeEnum : EnumerationContext - { - private JSONCharactersEscapeModeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue("Нет", "None")] - public EnumerationValue None - { - get - { - return this["Нет"]; - } - } - - [EnumValue("СимволыВнеASCII", "NotASCIISymbols")] - public EnumerationValue NotASCIISymbols - { - get - { - return this["СимволыВнеASCII"]; - } - } - - [EnumValue("СимволыВнеBMP", "SymbolsNotInBMP")] - public EnumerationValue SymbolsNotInBMP - { - get - { - return this["СимволыВнеBMP"]; - } - } - - public static JSONCharactersEscapeModeEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new JSONCharactersEscapeModeEnum(t, v)); - } - - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/Json/JSONDateFormatEnum.cs b/src/ScriptEngine.HostedScript/Library/Json/JSONDateFormatEnum.cs deleted file mode 100644 index 498af60e5..000000000 --- a/src/ScriptEngine.HostedScript/Library/Json/JSONDateFormatEnum.cs +++ /dev/null @@ -1,54 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Json -{ - [SystemEnum("ФорматДатыJSON", "JSONDateFormat")] - public class JSONDateFormatEnum : EnumerationContext - { - private JSONDateFormatEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue("ISO")] - public EnumerationValue ISO - { - get - { - return this["ISO"]; - } - } - - [EnumValue("JavaScript")] - public EnumerationValue JavaScript - { - get - { - return this["JavaScript"]; - } - } - - [EnumValue("Microsoft")] - public EnumerationValue Microsoft - { - get - { - return this["Microsoft"]; - } - } - - public static JSONDateFormatEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new JSONDateFormatEnum(t, v)); - } - - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/Json/JSONLineBreak.cs b/src/ScriptEngine.HostedScript/Library/Json/JSONLineBreak.cs deleted file mode 100644 index bec86b28a..000000000 --- a/src/ScriptEngine.HostedScript/Library/Json/JSONLineBreak.cs +++ /dev/null @@ -1,63 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Json -{ - [SystemEnum("ПереносСтрокJSON", "JSONLineBreak")] - public class JSONLineBreakEnum : EnumerationContext - { - private JSONLineBreakEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue("Unix")] - public EnumerationValue Unix - { - get - { - return this["Unix"]; - } - } - - [EnumValue("Windows")] - public EnumerationValue Windows - { - get - { - return this["Windows"]; - } - } - - [EnumValue("Авто", "Auto")] - public EnumerationValue Auto - { - get - { - return this["Авто"]; - } - } - - [EnumValue("Нет", "None")] - public EnumerationValue None - { - get - { - return this["Нет"]; - } - } - - public static JSONLineBreakEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new JSONLineBreakEnum(t, v)); - } - - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/Json/JSONReader.cs b/src/ScriptEngine.HostedScript/Library/Json/JSONReader.cs deleted file mode 100644 index c7c96acc2..000000000 --- a/src/ScriptEngine.HostedScript/Library/Json/JSONReader.cs +++ /dev/null @@ -1,360 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System.IO; -using Newtonsoft.Json; - -namespace ScriptEngine.HostedScript.Library.Json -{ - /// - /// - /// Предназначен для последовательного чтения JSON-данных из файла или строки. - /// - [ContextClass("ЧтениеJSON", "JSONReader")] - public class JSONReader : AutoContext - { - - private JsonTextReader _reader; // Объект из библиотеки Newtonsoft для работы с форматом JSON - - RuntimeException NotOpenException() - { - return new RuntimeException(Locale.NStr("ru='Источник данных JSON не открыт'; en='JSON data source is not opened'")); - } - - /// - /// - /// Возвращает true если для объекта чтения json был задан текст для парсинга. - /// - private bool IsOpen() - { - return _reader != null; - } - - public JSONReader() - { - } - - [ScriptConstructor] - public static JSONReader Constructor() - { - return new JSONReader(); - } - - /// - /// - /// Указывает на позицию, находящуюся сразу после прочитанного значения. - /// При ошибке чтение остается на позиции последнего успешно считанного символа. - /// - /// Число (Number), Неопределено (Undefined) - [ContextProperty("ТекущаяПозиция", "CurrentPosition")] - public IValue CurrentPosition - { - get - { - if (IsOpen()) - { - return ValueFactory.Create(_reader.LinePosition); - } - else - return ValueFactory.Create(); // Неопределено - } - - } - - - /// - /// - /// Указывает на позицию сразу после прочитанного значения. - /// Например, перед чтением первого элемента - 0, после чтения первого элемента -1 . - /// - /// Число (Number), Неопределено (Undefined) - [ContextProperty("ТекущаяСтрока", "CurrentLine")] - public IValue CurrentLine - { - get - { - if (IsOpen()) - { - return ValueFactory.Create(_reader.LineNumber); - } - else - return ValueFactory.Create(); // Неопределено - } - - } - - - /// - /// - /// Содержит текущее значение: - /// - /// - Число - если ТипТекущегоЗначения имеет значение Число; - /// - Строка - если ТипТекущегоЗначения имеет значение: - /// - /// - Комментарий, - /// - ИмяСвойства, - /// - Строка; - /// - Булево - если ТипТекущегоЗначения имеет значение Булево, - /// - Неопределено - если ТипТекущегоЗначения имеет значение Null. - /// Исключение генерируется в случае, если ТипТекущегоЗначения имеет одно из следующих значений: - /// - /// - НачалоМассива, - /// - КонецМассива, - /// - НачалоОбъекта, - /// - КонецОбъекта, - /// - Ничего. - /// - /// Число (Number), Строка (String), Булево (Boolean), Неопределено (Undefined) - [ContextProperty("ТекущееЗначение", "CurrentValue")] - public IValue CurrentValue - { - get - { - if (IsOpen()) - { - var type = _reader.TokenType; - - if (type == JsonToken.String || type == JsonToken.Comment || type == JsonToken.PropertyName) - return ValueFactory.Create((string)_reader.Value); - else if (type == JsonToken.Boolean) - return ValueFactory.Create((bool)_reader.Value); - else if (type == JsonToken.Integer || type == JsonToken.Float) - { - decimal d = Convert.ToDecimal(_reader.Value); - return ValueFactory.Create(d); - } - else if (type == JsonToken.Date) - { - return ValueFactory.Create((DateTime)_reader.Value); - } - else if (type == JsonToken.Null) - { - return ValueFactory.CreateNullValue(); - } - else if (type == JsonToken.Undefined) - { - return ValueFactory.Create(); - } - else - throw new RuntimeException(Locale.NStr("ru='Текущее значение JSON не может быть получено';en='Cannot get current JSON value'")); - } - else - { - throw NotOpenException(); - } - - } - } - - /// - /// - /// Тип текущего значения в документе JSON во внутреннем формате. - /// null - если чтение еще не началось или достигнут конец файла. - /// - /// CurrentJsonTokenType - public JsonToken CurrentJsonTokenType - { - get - { - if (IsOpen()) - { - return _reader.TokenType; - } - else - throw NotOpenException(); - } - } - - - /// - /// - /// Тип текущего значения в документе JSON. - /// Неопределено - если чтение еще не началось или достигнут конец файла. - /// - /// ТипЗначенияJSON (JSONValueType) - [ContextProperty("ТипТекущегоЗначения", "CurrentValueType")] - public IValue CurrentValueType - { - get - { - if (IsOpen()) - { - string JSONValueType = "None"; - - switch (_reader.TokenType) - { - case JsonToken.Null: - JSONValueType = "Null"; - break; - case JsonToken.StartObject: - JSONValueType = "ObjectStart"; - break; - case JsonToken.StartArray: - JSONValueType = "ArrayStart"; - break; - case JsonToken.PropertyName: - JSONValueType = "PropertyName"; - break; - case JsonToken.Comment: - JSONValueType = "Comment"; - break; - case JsonToken.Integer: - JSONValueType = "Number"; - break; - case JsonToken.Float: - JSONValueType = "Number"; - break; - case JsonToken.String: - JSONValueType = "String"; - break; - case JsonToken.Boolean: - JSONValueType = "Boolean"; - break; - case JsonToken.Undefined: - JSONValueType = "Null"; - break; - case JsonToken.EndObject: - JSONValueType = "ObjectEnd"; - break; - case JsonToken.EndArray: - JSONValueType = "ArrayEnd"; - break; - } - return GlobalsManager.GetEnum()[JSONValueType]; - - } - else - throw NotOpenException(); - } - - } - - /// - /// - /// Завершает чтение текста JSON из файла или строки. - /// - /// - /// - /// - [ContextMethod("Закрыть", "Close")] - public void Close() - { - - if (_reader != null) - { - _reader.Close(); - _reader = null; - } - } - - - /// - /// - /// Открывает JSON-файл для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанного файла. - /// - /// - /// - /// Имя файла, содержащего текст JSON. - /// - /// Позволяет задать кодировку входного файла. - [ContextMethod("ОткрытьФайл", "OpenFile")] - public void OpenFile(string JSONFileName, IValue encoding = null) - { - - if (IsOpen()) - Close(); - - StreamReader _fileReader; - - try - { - if (encoding != null) - _fileReader = Environment.FileOpener.OpenReader(JSONFileName, TextEncodingEnum.GetEncoding(encoding)); - else - _fileReader = Environment.FileOpener.OpenReader(JSONFileName); - } - catch (Exception e) - { - throw new RuntimeException(e.Message, e); - } - - _reader = new JsonTextReader(_fileReader); - - } - - - /// - /// Если текущее значение – начало массива или объекта, то пропускает его содержимое и конец. - /// Для остальных типов значений работает аналогично методу Прочитать(). - /// - /// - [ContextMethod("Пропустить", "Skip")] - public bool Skip() - { - - if (IsOpen()) - { - if (_reader.TokenType == JsonToken.StartArray || _reader.TokenType == JsonToken.StartObject) - { - while (_reader.Read()) - { - if (_reader.TokenType == JsonToken.EndArray || _reader.TokenType == JsonToken.EndObject) - { - return _reader.Read(); - } - } - return true; - } - else - { - if (_reader.Read()) - return _reader.Read(); - else - return false; - } - - } - else - throw NotOpenException(); - - } - - - /// - /// Выполняет чтение значения JSON. - /// - /// - [ContextMethod("Прочитать", "Read")] - public bool Read() - { - return _reader.Read(); - - } - - - /// - /// - /// Устанавливает строку, содержащую текст JSON для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанной строки. - /// - /// - /// - /// Строка, содержащая текст в формате JSON. - /// - /// - [ContextMethod("УстановитьСтроку", "SetString")] - public void SetString(string JSONString) - { - if (IsOpen()) - Close(); - - _reader = new JsonTextReader(new StringReader(JSONString)); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Json/JSONValueTypeEnum.cs b/src/ScriptEngine.HostedScript/Library/Json/JSONValueTypeEnum.cs deleted file mode 100644 index dec845f5d..000000000 --- a/src/ScriptEngine.HostedScript/Library/Json/JSONValueTypeEnum.cs +++ /dev/null @@ -1,126 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Json -{ - [SystemEnum("ТипЗначенияJSON", "JSONValueType")] - public class JSONValueTypeEnum : EnumerationContext - { - private JSONValueTypeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue("Null")] - public EnumerationValue Null - { - get - { - return this["Null"]; - } - } - - [EnumValue("Булево", "Boolean")] - public EnumerationValue Boolean - { - get - { - return this["Булево"]; - } - } - - [EnumValue("ИмяСвойства", "PropertyName")] - public EnumerationValue PropertyName - { - get - { - return this["ИмяСвойства"]; - } - } - - [EnumValue("Комментарий", "Comment")] - public EnumerationValue Comment - { - get - { - return this["Комментарий"]; - } - } - - [EnumValue("КонецМассива", "ArrayEnd")] - public EnumerationValue ArrayEnd - { - get - { - return this["КонецМассива"]; - } - } - - [EnumValue("КонецОбъекта", "ObjectEnd")] - public EnumerationValue ObjectEnd - { - get - { - return this["КонецОбъекта"]; - } - } - - [EnumValue("НачалоМассива", "ArrayStart")] - public EnumerationValue ArrayStart - { - get - { - return this["НачалоМассива"]; - } - } - - [EnumValue("НачалоОбъекта", "ObjectStart")] - public EnumerationValue ObjectStart - { - get - { - return this["НачалоОбъекта"]; - } - } - - [EnumValue("Ничего", "None")] - public EnumerationValue None - { - get - { - return this["Ничего"]; - } - } - - [EnumValue("Строка", "String")] - public EnumerationValue String - { - get - { - return this["Строка"]; - } - } - - [EnumValue("Число", "Number")] - public EnumerationValue Number - { - get - { - return this["Число"]; - } - } - - public static JSONValueTypeEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new JSONValueTypeEnum(t, v)); - } - - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/Json/JSONWriter.cs b/src/ScriptEngine.HostedScript/Library/Json/JSONWriter.cs deleted file mode 100644 index 4b723ee97..000000000 --- a/src/ScriptEngine.HostedScript/Library/Json/JSONWriter.cs +++ /dev/null @@ -1,532 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using Newtonsoft.Json; -using System.IO; -using System.Threading; -using System.Text; - -namespace ScriptEngine.HostedScript.Library.Json -{ - /// - /// - /// Предназначен для организации последовательной записи объектов и текстов JSON. - /// - [ContextClass("ЗаписьJSON", "JSONWriter")] - public class JSONWriter : AutoContext - { - private const int INDENT_SIZE = 0; - - private JSONWriterSettings _settings; - private JsonTextWriter _writer; // Объект из библиотеки Newtonsoft для работы с форматом JSON - - StringWriter _stringWriter; - - public JSONWriter() - { - - } - - private bool _escapeNonAscii; - - /// - /// - /// Возвращает true если для объекта чтения json был задан текст для парсинга. - /// - private bool IsOpen() - { - return _writer != null; - } - - /// - /// - /// Возвращает true если для объекта чтения json был задан текст для парсинга. - /// - private bool IsOpenForString() - { - return _stringWriter != null; - } - - private void SetDefaultOptions() - { - _writer.Indentation = INDENT_SIZE; - _writer.Formatting = Formatting.Indented; - _settings = new JSONWriterSettings(); - _escapeNonAscii = false; - } - - private void SetOptions(IValue settings) - { - _settings = (JSONWriterSettings)settings.GetRawValue(); - if (_settings.UseDoubleQuotes) - _writer.QuoteChar = '\"'; - else { - _writer.QuoteChar = '\''; - } - - if (_settings.PaddingSymbols != null && _settings.PaddingSymbols.Length > 0) - _writer.IndentChar = _settings.PaddingSymbols[0]; - else - _writer.IndentChar = ' '; - - if (_settings.PaddingSymbols != null && _settings.PaddingSymbols.Length > 0) - { - _writer.Indentation = 1; - } - else - { - _writer.Indentation = INDENT_SIZE; - } - _writer.Formatting = Formatting.Indented; - - if (_settings.EscapeCharacters != null) - { - var jsonCharactersEscapeMode = _settings.EscapeCharacters.GetRawValue() as SelfAwareEnumValue; - var jsonCharactersEscapeModeEnum = GlobalsManager.GetEnum(); - - if (jsonCharactersEscapeMode == jsonCharactersEscapeModeEnum.NotASCIISymbols) - { - _escapeNonAscii = true; - _writer.QuoteChar = '\"'; - _writer.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii; - } - else if (jsonCharactersEscapeMode == jsonCharactersEscapeModeEnum.SymbolsNotInBMP) - throw new NotImplementedException(); - } - } - - void WriteStringValue(string val) - { - if (_settings.EscapeCharacters != null && _escapeNonAscii) - { - StringWriter wr = new StringWriter(); - var jsonWriter = new JsonTextWriter(wr); - jsonWriter.QuoteChar = '\"'; - jsonWriter.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii; - new JsonSerializer().Serialize(jsonWriter, val); - string str = wr.ToString(); - _writer.WriteRawValue(EscapeCharacters(str.Substring(1, str.Length - 2), false)); - - } - else - _writer.WriteRawValue(EscapeCharacters(val, _settings.EscapeSlash)); - } - - string EscapeCharacters(string sval, bool EscapeSlash) - { - var sb = new StringBuilder(sval); - - int Length = sval.Length; - for (var i = 0; i < Length; i++) - { - char c = sb[i]; - if (EscapeSlash && c == '/') - { - sb.Replace("/", "\\/", i, 1); - Length++; - i++; - } - else if (_settings.EscapeAmpersand && c == '&') - { - sb.Replace("&", "\\&", i, 1); - Length++; - i++; - } - else if ((_settings.EscapeSingleQuotes || !_settings.UseDoubleQuotes) && c == '\'') - { - sb.Replace("'", "\\u0027", i, 1); - Length = Length + 5; - i = i + 5; - } - else if (_settings.EscapeAngleBrackets && c == '<') - { - sb.Replace("<", "\\u003C", i, 1); - Length = Length + 5; - i = i + 5; - } - else if (_settings.EscapeAngleBrackets && c == '>') - { - sb.Replace(">", "\\u003E", i, 1); - Length = Length + 5; - i = i + 5; - } - else if (c == '\r') - { - sb.Replace("\r", "\\r", i, 1); - Length++; - i++; - } - else if (c == '\n') - { - sb.Replace("\n", "\\n", i, 1); - Length++; - i++; - } - else if (c == '\f') - { - sb.Replace("\f", "\\f", i, 1); - Length++; - i++; - } - else if (c == '\"') - { - sb.Replace("\"", "\\\"", i, 1); - Length++; - i++; - } - else if (c == '\b') - { - sb.Replace("\b", "\\b", i, 1); - Length++; - i++; - } - else if (c == '\t') - { - sb.Replace("\t", "\\t", i, 1); - Length++; - i++; - } - else if (c == '\\') - { - sb.Replace("\\", "\\\\", i, 1); - Length++; - i++; - } - - // Спец. символы: \u0000, \u0001, \u0002, ... , \u001e, \u001f; - else if ((int)c >= 0 && (int)c <= 31) - { - string unicode = "\\u" + ((int)c).ToString("X4").ToLower(); - sb.Replace(c.ToString(), unicode, i, 1); - Length = Length + 5; - i = i + 5; - } - - } - sb.Insert(0, _writer.QuoteChar); - sb.Append(_writer.QuoteChar); - return sb.ToString(); - } - - RuntimeException NotOpenException() - { - return new RuntimeException(Locale.NStr("ru='Приемник данных JSON не открыт';en='JSON data target is not opened'")); - } - - void SetNewLineChars(TextWriter textWriter) - { - if (_settings != null) - { - if (_settings.NewLines != null) - { - var NewLines = _settings.NewLines.GetRawValue() as SelfAwareEnumValue; - var LineBreakEnum = GlobalsManager.GetEnum(); - - if (NewLines == LineBreakEnum.Unix) - textWriter.NewLine = "\n"; - else if (NewLines == LineBreakEnum.Windows) - textWriter.NewLine = "\r\n"; - else if (NewLines == LineBreakEnum.Auto) - { - if (System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == PlatformID.MacOSX) - textWriter.NewLine = "\n"; - else - textWriter.NewLine = "\r\n"; - } - else - { - textWriter.NewLine = ""; //Нет - _writer.Formatting = Formatting.None; - } - - } - - } - } - [ScriptConstructor] - public static JSONWriter Constructor() - { - return new JSONWriter(); - } - - /// - /// - /// Определяет текущие параметры записи JSON. - /// - /// ПараметрыЗаписиJSON (JSONWriterSettings) - [ContextProperty("Параметры", "Settings")] - public IValue Settings - { - get { throw new NotImplementedException(); } - - } - - - /// - /// - /// Показывает, будет ли проводиться проверка правильности структуры записываемого JSON объекта. В случае обнаружение ошибки, будет сгенерировано исключение. Например: при попытке записать значение без имени вне массива или записать окончание объекта без начала. Установка данного свойства не имеет немедленного эффекта. Установленное значение свойства будет использовано только после открытия файла или установки строки. - /// После создания объекта данное свойство имеет значение Истина. - /// - /// Булево (Boolean) - [ContextProperty("ПроверятьСтруктуру", "ValidateStructure")] - public bool ValidateStructure - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - - /// - /// - /// Завершает запись текста JSON. Если производилась запись в файл, то файл закрывается. Если производилась запись в строку, то результирующая строка будет получена в качестве возвращаемого значения метода. Если производилась запись в файл, то метод вернет пустую строку. - /// - /// - /// - /// Значения данного типа содержат строку в формате Unicode произвольной длины. - [ContextMethod("Закрыть", "Close")] - public string Close() - { - string res = ""; - - if (IsOpenForString()) - { - res = _stringWriter.ToString(); - } - - if (_writer != null) - { - _writer.Close(); - _writer = null; - } - - return res; - } - - - /// - /// - /// Выполняет запись произвольной строки в документ, при этом проверка структуры документа не выполняется. - /// Если при использовании метода свойство ПроверятьСтруктуру установлено в значение Истина, то проверка структуры продолжается на следующем элементе. - /// - /// - /// - /// Строка, записываемая в документ JSON. - [ContextMethod("ЗаписатьБезОбработки", "WriteRaw")] - public void WriteRaw(string stringValue) - { - if (!IsOpen()) - throw NotOpenException(); - - _writer.WriteRaw(stringValue); - } - - - /// - /// - /// Записывает значение свойства JSON. - /// - /// - /// - /// Записываемое значение. Типы: Строка (String), Число (Number), Булево (Boolean), Неопределено (Undefined) - /// - /// Использование экспоненциальной формы записи для числовых значений. Параметр имеет смысл только если записывается значение числового типа. - /// Значение по умолчанию: Ложь. - [ContextMethod("ЗаписатьЗначение", "WriteValue")] - public void WriteValue(IValue value, bool useFormatWithExponent = false) - { - if (!IsOpen()) - throw NotOpenException(); - - if (value.SystemType.Name == "Null") - { - _writer.WriteNull(); - return; - } - - switch (value.DataType) - { - case DataType.String: - WriteStringValue(value.AsString()); - break; - case DataType.Number: - decimal d = value.AsNumber(); - if (d == Math.Round(d)) - { - Int64 i = Convert.ToInt64(d); - if (useFormatWithExponent) - _writer.WriteRawValue(string.Format(Thread.CurrentThread.CurrentCulture, "{0:E}", i)); - else - _writer.WriteValue(i); - } - - else - { - if (useFormatWithExponent) - _writer.WriteRawValue(string.Format(string.Format(Thread.CurrentThread.CurrentCulture, "{0:E}", d))); - else - _writer.WriteValue(d); - } - - break; - case DataType.Date: - _writer.WriteValue(value.AsDate()); - break; - case DataType.Boolean: - _writer.WriteValue(value.AsBoolean()); - break; - case DataType.Undefined: - _writer.WriteNull(); - break; - default: - throw new RuntimeException("Тип переданного значения не поддерживается."); - } - } - - - /// - /// - /// Записывает имя свойства JSON. - /// - /// - /// - /// Имя свойства. - [ContextMethod("ЗаписатьИмяСвойства", "WritePropertyName")] - public void WritePropertyName(string propertyName) - { - _writer.WritePropertyName(propertyName); - } - - - /// - /// - /// Записывает конец массива JSON. - /// - /// - [ContextMethod("ЗаписатьКонецМассива", "WriteEndArray")] - public void WriteEndArray() - { - _writer.WriteEndArray(); - } - - - /// - /// - /// Записывает конец объекта JSON. - /// - /// - [ContextMethod("ЗаписатьКонецОбъекта", "WriteEndObject")] - public void WriteEndObject() - { - _writer.WriteEndObject(); - } - - - /// - /// - /// Записывает начало массива JSON. - /// - /// - [ContextMethod("ЗаписатьНачалоМассива", "WriteStartArray")] - public void WriteStartArray() - { - _writer.WriteStartArray(); - } - - - /// - /// - /// Записывает начало объекта JSON. - /// - /// - [ContextMethod("ЗаписатьНачалоОбъекта", "WriteStartObject")] - public void WriteStartObject() - { - _writer.WriteStartObject(); - } - - - /// - /// - /// Открывает файл для записи JSON. Позволяет указать тип кодировки, который будет использован для записи файла JSON, а также использование BOM. - /// - /// - /// - /// Имя файла, в который будет записываться текст JSON. - /// - /// В качестве типа кодировки может быть указана одна из возможных кодировок текста. В этом случае файл будет записан в соответствующей кодировке. Если же в качестве параметра указана пустая строка или ничего не указано, то для записи файла будет использована кодировка UTF8. - /// Поддерживаемые коды кодировок: - /// - /// Значение по умолчанию: UTF-8. - /// - /// Определяет, будет ли добавлен маркер порядка байт (BOM) к результирующему файлу JSON. - /// Внимание. Стандарт RFC7159 настоятельно рекомендует не добавлять маркер порядка байт (BOM) к документу JSON . - /// Значение по умолчанию: Ложь. - /// - /// Параметры, используемые при открытии файла для настройки записи в формате JSON. - [ContextMethod("ОткрытьФайл", "OpenFile")] - public void OpenFile(string fileName, string encoding = null, IValue addBOM = null, IValue settings = null) - { - if (IsOpen()) - Close(); - - bool bAddBOM = false; - if (addBOM != null) - bAddBOM = addBOM.AsBoolean(); - - StreamWriter streamWriter; - - try - { - if (encoding != null) - streamWriter = Environment.FileOpener.OpenWriter(fileName, TextEncodingEnum.GetEncodingByName(encoding, bAddBOM)); - else - streamWriter = Environment.FileOpener.OpenWriter(fileName, TextEncodingEnum.GetEncodingByName("UTF-8", bAddBOM)); - } - catch (Exception e) - { - throw new RuntimeException(e.Message, e); - } - - - _writer = new JsonTextWriter(streamWriter); - if (settings == null) - SetDefaultOptions(); - else - SetOptions(settings); - - SetNewLineChars(streamWriter); - } - - - /// - /// - /// Инициализирует объект для вывода результирующего JSON текста в строку. - /// - /// - /// - /// Параметры, используемые при записи объекта JSON. - /// По умолчанию, содержит ПараметрыЗаписиJSON, сгенерированные автоматически. - [ContextMethod("УстановитьСтроку", "SetString")] - public void SetString(IValue settings = null) - { - if (IsOpen()) - Close(); - _stringWriter = new StringWriter(); - _writer = new JsonTextWriter(_stringWriter); - if (settings == null) - SetDefaultOptions(); - else - SetOptions(settings); - - SetNewLineChars(_stringWriter); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/KeyAndValueImpl.cs b/src/ScriptEngine.HostedScript/Library/KeyAndValueImpl.cs deleted file mode 100644 index 2a76902d9..000000000 --- a/src/ScriptEngine.HostedScript/Library/KeyAndValueImpl.cs +++ /dev/null @@ -1,48 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("КлючИЗначение", "KeyAndValue")] - public class KeyAndValueImpl : AutoContext - { - private readonly IValue _key; - private readonly IValue _value; - - public KeyAndValueImpl(IValue key, IValue value) - { - _key = key; - _value = value; - } - - [ContextProperty("Ключ", "Key")] - public IValue Key - { - get - { - return _key; - } - } - - [ContextProperty("Значение", "Value")] - public IValue Value - { - get - { - return _value; - } - } - - public override IValue GetPropValue(int propNum) - { - return propNum == 0 ? _key : _value; - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/MapImpl.cs b/src/ScriptEngine.HostedScript/Library/MapImpl.cs deleted file mode 100644 index 22438ef81..000000000 --- a/src/ScriptEngine.HostedScript/Library/MapImpl.cs +++ /dev/null @@ -1,147 +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.Collections.Generic; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("Соответствие", "Map")] - public class MapImpl : AutoContext, ICollectionContext, IEnumerable - { - private readonly Dictionary _content = new Dictionary(new GenericIValueComparer()); - - public MapImpl() - { - } - - public MapImpl(IEnumerable source) - { - foreach (var kv in source) - { - _content.Add(kv.Key.GetRawValue(), kv.Value.GetRawValue()); - } - } - - public override bool IsIndexed - { - get - { - return true; - } - } - - public override IValue GetIndexedValue(IValue index) - { - IValue result; - if (!_content.TryGetValue(index, out result)) - { - result = ValueFactory.Create(); - } - - return result; - } - - public override void SetIndexedValue(IValue index, IValue val) - { - if (index.DataType != DataType.Undefined) - { - _content[index] = val; - } - } - - public override bool IsPropReadable(int propNum) - { - return false; - } - - public override bool IsPropWritable(int propNum) - { - return false; - } - - internal bool ContainsKey(IValue key) - { - return _content.ContainsKey(key); - } - - #region ICollectionContext Members - - [ContextMethod("Вставить", "Insert")] - public void Insert(IValue key, IValue val=null) - { - SetIndexedValue(key, val ?? ValueFactory.Create() ); - } - - [ContextMethod("Получить", "Get")] - public IValue Retrieve(IValue key) - { - return GetIndexedValue(key); - } - - [ContextMethod("Количество", "Count")] - public int Count() - { - return _content.Count; - } - - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _content.Clear(); - } - - [ContextMethod("Удалить", "Delete")] - public void Delete(IValue key) - { - _content.Remove(key); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - foreach (var item in _content) - { - yield return new KeyAndValueImpl(item.Key, item.Value); - } - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - [ScriptConstructor] - public static MapImpl Constructor() - { - return new MapImpl(); - } - - [ScriptConstructor(Name = "Из фиксированного соответствия")] - public static MapImpl Constructor(IValue source) - { - if (!(source.GetRawValue() is FixedMapImpl fix)) - throw RuntimeException.InvalidArgumentType(); - - return new MapImpl(fix); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/MessageStatusEnum.cs b/src/ScriptEngine.HostedScript/Library/MessageStatusEnum.cs deleted file mode 100644 index 13b42c15e..000000000 --- a/src/ScriptEngine.HostedScript/Library/MessageStatusEnum.cs +++ /dev/null @@ -1,32 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library -{ - [EnumerationType("СтатусСообщения", "MessageStatus")] - public enum MessageStatusEnum - { - [EnumItem("БезСтатуса", "WithoutStatus")] - WithoutStatus, - - [EnumItem("Важное", "Important")] - Important, - - [EnumItem("Внимание", "Attention")] - Attention, - - [EnumItem("Информация", "Information")] - Information, - - [EnumItem("Обычное", "Ordinary")] - Ordinary, - - [EnumItem("ОченьВажное", "VeryImportant")] - VeryImportant - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiComponent.cs b/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiComponent.cs deleted file mode 100644 index 0583eee5a..000000000 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiComponent.cs +++ /dev/null @@ -1,246 +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 ScriptEngine.Machine; -using System; -using System.Runtime.InteropServices; - -namespace ScriptEngine.HostedScript.Library.NativeApi -{ - /// - /// Экземпляр внешней компоненты Native API - /// - class NativeApiComponent : NativeApiValue, IRuntimeContextInstance, IValue - { - private readonly IHostApplication _host; - private readonly IntPtr _object; - - public override IRuntimeContextInstance AsObject() - { - return this; - } - - private enum ErrorCodes - { - ADDIN_E_NONE = 1000, - ADDIN_E_ORDINARY = 1001, - ADDIN_E_ATTENTION = 1002, - ADDIN_E_IMPORTANT = 1003, - ADDIN_E_VERY_IMPORTANT = 1004, - ADDIN_E_INFO = 1005, - ADDIN_E_FAIL = 1006, - ADDIN_E_MSGBOX_ATTENTION = 1007, - ADDIN_E_MSGBOX_INFO = 1008, - ADDIN_E_MSGBOX_FAIL = 1009, - } - - private String S(IntPtr ptr) - { - return NativeApiProxy.Str(ptr); - } - - private MessageStatusEnum Status(UInt16 wcode) - { - switch ((ErrorCodes)wcode) - { - case ErrorCodes.ADDIN_E_NONE: - return MessageStatusEnum.WithoutStatus; - case ErrorCodes.ADDIN_E_ORDINARY: - return MessageStatusEnum.Ordinary; - case ErrorCodes.ADDIN_E_IMPORTANT: - return MessageStatusEnum.Important; - case ErrorCodes.ADDIN_E_INFO: - return MessageStatusEnum.Information; - case ErrorCodes.ADDIN_E_VERY_IMPORTANT: - return MessageStatusEnum.VeryImportant; - case ErrorCodes.ADDIN_E_ATTENTION: - case ErrorCodes.ADDIN_E_MSGBOX_INFO: - case ErrorCodes.ADDIN_E_MSGBOX_FAIL: - return MessageStatusEnum.Attention; - default: - return MessageStatusEnum.Ordinary; - } - } - - public NativeApiComponent(IHostApplication host, NativeApiLibrary library, String typeName, String componentName) - { - _host = host; - _object = NativeApiProxy.GetClassObject(library.Module, componentName, - (wcode, source, descr, scode) => - { - _host.Echo("ОШИБКА: " + S(source) + " - " + S(descr), Status(wcode)); - }, - (source, message, data) => { - _host.Echo("СОБЫТИЕ: " + S(source) + " - " + S(message) + " - " + S(data)); - }, - (status) => { - _host.Echo("СТАТУС: " + S(status)); - } - ); - DefineType(TypeManager.GetTypeByName(typeName)); - } - - public void Dispose() - { - try { NativeApiProxy.DestroyObject(_object); } catch (Exception) { } - } - - public bool IsIndexed => false; - - public bool DynamicMethodSignatures => false; - - public IValue GetIndexedValue(IValue index) - { - throw new NotImplementedException(); - } - - public void SetIndexedValue(IValue index, IValue val) - { - throw new NotImplementedException(); - } - - public int FindProperty(string name) - { - var propNumber = NativeApiProxy.FindProp(_object, name); - if (propNumber < 0) - throw RuntimeException.PropNotFoundException(name); - return propNumber; - } - - public bool IsPropReadable(int propNum) - { - return NativeApiProxy.IsPropReadable(_object, propNum); - } - - public bool IsPropWritable(int propNum) - { - return NativeApiProxy.IsPropWritable(_object, propNum); - } - - public int GetPropCount() - { - return NativeApiProxy.GetNProps(_object); - } - - public string GetPropName(int propNum) - { - var name = String.Empty; - NativeApiProxy.GetPropName(_object, propNum, 0, - n => name = NativeApiProxy.Str(n) - ); - return name; - } - - public IValue GetPropValue(int propNum) - { - var result = ValueFactory.Create(); - NativeApiProxy.GetPropVal(_object, propNum, - variant => result = NativeApiVariant.GetValue(variant) - ); - return result; - } - - public void SetPropValue(int propNum, IValue newVal) - { - var variant = new NativeApiVariant(); - variant.SetValue(newVal); - NativeApiProxy.SetPropVal(_object, propNum, ref variant); - variant.Clear(); - } - - public int GetMethodsCount() - { - return NativeApiProxy.GetNMethods(_object); - } - - public int FindMethod(string name) - { - var methodNumber = NativeApiProxy.FindMethod(_object, name); - if (methodNumber < 0) - throw RuntimeException.MethodNotFoundException(name); - return methodNumber; - } - - public MethodInfo GetMethodInfo(int methodNumber) - { - if (methodNumber < 0) - throw new RuntimeException("Метод не найден"); - var name = String.Empty; - var alias = String.Empty; - NativeApiProxy.GetMethodName(_object, methodNumber, 0, - str => name = NativeApiProxy.Str(str) - ); - NativeApiProxy.GetMethodName(_object, methodNumber, 1, - str => alias = NativeApiProxy.Str(str) - ); - var paramCount = NativeApiProxy.GetNParams(_object, methodNumber); - var paramArray = new ParameterDefinition[paramCount]; - for (int i = 0; i < paramCount; i++) - NativeApiProxy.GetParamDefValue(_object, methodNumber, i, variant => - { - if (NativeApiVariant.NotEmpty(variant)) - { - paramArray[i].HasDefaultValue = true; - paramArray[i].DefaultValueIndex = ParameterDefinition.UNDEFINED_VALUE_INDEX; - } - }); - - return new MethodInfo - { - Name = name, - Alias = alias, - IsFunction = NativeApiProxy.HasRetVal(_object, methodNumber), - IsDeprecated = false, - IsExport = false, - ThrowOnUseDeprecated = false, - Params = paramArray, - }; - } - - private void SetDefValues(int methodNumber, int paramCount, IValue[] arguments) - { - for (int i = 0; i < paramCount; i++) - if (arguments[i] == null) - NativeApiProxy.GetParamDefValue(_object, methodNumber, i, - variant => arguments[i] = NativeApiVariant.GetValue(variant) - ); - } - - public void CallAsProcedure(int methodNumber, IValue[] arguments) - { - var paramArray = IntPtr.Zero; - int paramCount = NativeApiProxy.GetNParams(_object, methodNumber); - if (paramCount > 0) - paramArray = Marshal.AllocHGlobal(NativeApiVariant.Size * paramCount); - SetDefValues(methodNumber, paramCount, arguments); - NativeApiVariant.SetValue(paramArray, arguments, paramCount); - NativeApiProxy.CallAsProc(_object, methodNumber, paramArray); - NativeApiVariant.GetValue(arguments, paramArray, paramCount); - NativeApiVariant.Clear(paramArray, paramCount); - Marshal.FreeHGlobal(paramArray); - } - - public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - var paramArray = IntPtr.Zero; - int paramCount = NativeApiProxy.GetNParams(_object, methodNumber); - if (paramCount > 0) - paramArray = Marshal.AllocHGlobal(NativeApiVariant.Size * paramCount); - SetDefValues(methodNumber, paramCount, arguments); - NativeApiVariant.SetValue(paramArray, arguments, paramCount); - IValue result = retValue = ValueFactory.Create(); - bool ok = NativeApiProxy.CallAsFunc(_object, methodNumber, paramArray, - variant => result = NativeApiVariant.GetValue(variant) - ); - NativeApiVariant.GetValue(arguments, paramArray, paramCount); - NativeApiVariant.Clear(paramArray, paramCount); - Marshal.FreeHGlobal(paramArray); - if (ok) - retValue = result; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiFactory.cs b/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiFactory.cs deleted file mode 100644 index c561b412e..000000000 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiFactory.cs +++ /dev/null @@ -1,54 +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 ScriptEngine.Machine; -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; - -namespace ScriptEngine.HostedScript.Library.NativeApi -{ - /// - /// Фабрика, осуществляющая регистрацию библиотеки внешних - /// компонент Native API и создания экземпляров компонент. - /// - class NativeApiFactory : NativeApiKernel - { - public static bool Register(String filepath, String identifier) - { - if (_libraries.ContainsKey(identifier)) - return false; - var library = new NativeApiLibrary(filepath, identifier); - if (library.Loaded) - _libraries.Add(identifier, library); - return library.Loaded; - } - - private static readonly Dictionary _libraries = new Dictionary(); - - private static IHostApplication _host; - - internal static void Initialize(IHostApplication host) - { - foreach (var item in _libraries) - item.Value.Dispose(); - _libraries.Clear(); - _host = host; - } - - [Machine.Contexts.ScriptConstructor(ParametrizeWithClassName = true)] - public static IValue Constructor(String typeName) - { - var separator = new char[] { '.' }; - var names = typeName.Split(separator, StringSplitOptions.RemoveEmptyEntries); - if (names.Length == 3 && _libraries.TryGetValue(names[1], out NativeApiLibrary library)) - return library.CreateComponent(_host, typeName, names[2]); - throw new NotImplementedException(); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiKernel.cs b/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiKernel.cs deleted file mode 100644 index a7f7a9aec..000000000 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiKernel.cs +++ /dev/null @@ -1,40 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Runtime.InteropServices; - -namespace ScriptEngine.HostedScript.Library.NativeApi -{ - /// - /// Подключение DLL-файлов библиотеки внешних компонент Native API - /// - class NativeApiKernel - { - private const String KernelDll = "kernel32.dll"; - - [DllImport(KernelDll, SetLastError = true, CharSet = CharSet.Unicode)] - protected static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPWStr)] string lpLibFileName); - - [DllImport(KernelDll, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Ansi)] - protected static extern IntPtr GetProcAddress(IntPtr _module, string procName); - - [DllImport(KernelDll, SetLastError = true, CharSet = CharSet.Unicode)] - protected static extern bool FreeLibrary(IntPtr _module); - - private const String libdl = "libdl.so"; - - [DllImport(libdl)] - protected static extern IntPtr dlopen(string filename, int flags); - - [DllImport(libdl)] - protected static extern IntPtr dlsym(IntPtr handle, string symbol); - - [DllImport(libdl)] - protected static extern IntPtr dlclose(IntPtr handle); - } -} diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiLibrary.cs b/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiLibrary.cs deleted file mode 100644 index 529f3ba56..000000000 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiLibrary.cs +++ /dev/null @@ -1,82 +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 ScriptEngine.Machine; -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; - -namespace ScriptEngine.HostedScript.Library.NativeApi -{ - /// - /// Класс, ассоциированный с экземпляром библиотеки внешних компонент - /// Native API и осуществляющий непосредственное создание экземпляра компоненты. - /// - class NativeApiLibrary : NativeApiKernel - { - private delegate IntPtr GetClassNames(); - - private readonly List _components = new List(); - - private readonly String _tempfile; - - public NativeApiLibrary(String filepath, String identifier) - { - using (var stream = File.OpenRead(filepath)) - { - if (NativeApiPackage.IsZip(stream)) - { - _tempfile = Path.GetTempFileName(); - NativeApiPackage.Extract(stream, _tempfile); - Module = LoadLibrary(_tempfile); - } - else - Module = LoadLibrary(filepath); - } - if (Loaded) - RegisterComponents(identifier); - } - - public void Dispose() - { - foreach (var component in _components) - component.Dispose(); - if (Loaded && FreeLibrary(Module)) - if (!String.IsNullOrEmpty(_tempfile)) - try { File.Delete(_tempfile); } catch (Exception) { } - } - - public IntPtr Module { get; private set; } = IntPtr.Zero; - - public Boolean Loaded - { - get => Module != IntPtr.Zero; - } - - private void RegisterComponents(String identifier) - { - var funcPtr = GetProcAddress(Module, "GetClassNames"); - if (funcPtr == IntPtr.Zero) - throw new RuntimeException("В библиотеке внешних компонент не обнаружена функция: GetClassNames()"); - var namesPtr = Marshal.GetDelegateForFunctionPointer(funcPtr)(); - if (namesPtr == IntPtr.Zero) - throw new RuntimeException("Не удалось получить список компонент в составе библиотеки"); - var separator = new char[] { '|' }; - var names = NativeApiProxy.Str(namesPtr).Split(separator, StringSplitOptions.RemoveEmptyEntries); - foreach (String name in names) - TypeManager.RegisterType($"AddIn.{identifier}.{name}", typeof(NativeApiFactory)); - } - - public IValue CreateComponent(IHostApplication host, String typeName, String componentName) - { - var component = new NativeApiComponent(host, this, typeName, componentName); - _components.Add(component); - return ValueFactory.Create(component); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiProxy.cs b/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiProxy.cs deleted file mode 100644 index 401ec7b23..000000000 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiProxy.cs +++ /dev/null @@ -1,86 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Runtime.InteropServices; - -namespace ScriptEngine.HostedScript.Library.NativeApi -{ - /// - /// Трансляция вызовов C# для взаимодействия с NativeApi - /// - static class NativeApiProxy - { - private const String ProxyDll = "ScriptEngine.NativeApi.dll"; - - public delegate void PointerDelegate(IntPtr ptr); - - public delegate void ArrayDelegate(IntPtr ptr, Int32 number); - - public delegate void OnErrorDelegate(UInt16 wcode, IntPtr source, IntPtr descr, Int32 scode); - - public delegate void OnEventDelegate(IntPtr source, IntPtr message, IntPtr data); - - public delegate void OnStatusDelegate(IntPtr status); - - public static String Str(IntPtr p) - { - return p != IntPtr.Zero ? Marshal.PtrToStringUni(p) : String.Empty; - } - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern IntPtr GetClassObject(IntPtr module, string name, OnErrorDelegate onError, OnEventDelegate onEvent, OnStatusDelegate onStatus); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern void DestroyObject(IntPtr ptr); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern Int32 GetNProps(IntPtr ptr); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern Int32 FindProp(IntPtr ptr, string wsPropName); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool IsPropReadable(IntPtr ptr, Int32 lPropNum); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool IsPropWritable(IntPtr ptr, Int32 lPropNum); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern void GetPropName(IntPtr ptr, Int32 lPropNum, Int32 lPropAlias, PointerDelegate response); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern void GetPropVal(IntPtr ptr, Int32 lPropNum, PointerDelegate response); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern void SetPropVal(IntPtr ptr, Int32 lPropNum, ref NativeApiVariant value); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern Int32 GetNMethods(IntPtr ptr); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern Int32 FindMethod(IntPtr ptr, string wsMethodName); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern void GetMethodName(IntPtr ptr, Int32 lMethodNum, Int32 lMethodAlias, PointerDelegate response); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern Int32 GetNParams(IntPtr ptr, Int32 lMethodNum); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool GetParamDefValue(IntPtr ptr, Int32 lMethodNum, Int32 lParamNum, PointerDelegate response); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool HasRetVal(IntPtr ptr, Int32 lMethodNum); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool CallAsProc(IntPtr ptr, Int32 lMethodNum, IntPtr value); - - [DllImport(ProxyDll, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool CallAsFunc(IntPtr ptr, Int32 lMethodNum, IntPtr value, PointerDelegate response); - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiValue.cs b/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiValue.cs deleted file mode 100644 index 3f5c39b2d..000000000 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiValue.cs +++ /dev/null @@ -1,101 +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 ScriptEngine.Machine; -using System; - -namespace ScriptEngine.HostedScript.Library.NativeApi -{ - /// - /// Абстрактный класс, реализующий типовые методы интерфейса IValue - /// - abstract class NativeApiValue: IValue - { - private TypeDescriptor _type; - - #region IValue Members - - public DataType DataType - { - get { return Machine.DataType.Object; } - } - - protected void DefineType(TypeDescriptor type) - { - _type = type; - } - - public TypeDescriptor SystemType - { - get => _type; - } - - public decimal AsNumber() - { - throw RuntimeException.ConvertToNumberException(); - } - - public DateTime AsDate() - { - throw RuntimeException.ConvertToDateException(); - } - - public bool AsBoolean() - { - throw RuntimeException.ConvertToBooleanException(); - } - - public virtual string AsString() - { - return SystemType.Name; - } - - public IValue GetRawValue() - { - return this; - } - - public abstract IRuntimeContextInstance AsObject(); - - #endregion - - #region IComparable Members - - public int CompareTo(IValue other) - { - if (other.SystemType.Equals(this.SystemType)) - { - if (this.Equals(other)) - { - return 0; - } - else - { - throw RuntimeException.ComparisonNotSupportedException(); - } - } - else - { - return this.SystemType.ToString().CompareTo(other.SystemType.ToString()); - } - } - - #endregion - - #region IEquatable Members - - public virtual bool Equals(IValue other) - { - if (other == null) - return false; - - return other.SystemType.Equals(this.SystemType) && Object.ReferenceEquals(this.AsObject(), other.AsObject()); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiVariant.cs b/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiVariant.cs deleted file mode 100644 index e4d2e7049..000000000 --- a/src/ScriptEngine.HostedScript/Library/NativeApi/NativeApiVariant.cs +++ /dev/null @@ -1,245 +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 ScriptEngine.HostedScript.Library.Binary; -using ScriptEngine.Machine; -using System; -using System.Runtime.InteropServices; - -namespace ScriptEngine.HostedScript.Library.NativeApi -{ - /// - /// Трансляция значений между IValue и tVariant из состава Native API - /// - [StructLayout(LayoutKind.Explicit)] - public struct NativeApiVariant - { - private enum VarTypes - { - VTYPE_EMPTY = 0, - VTYPE_NULL, - VTYPE_I2, //int16_t - VTYPE_I4, //int32_t - VTYPE_R4, //float - VTYPE_R8, //double - VTYPE_DATE, //DATE (double) - VTYPE_TM, //struct tm - VTYPE_PSTR, //struct str string - VTYPE_INTERFACE, //struct iface - VTYPE_ERROR, //int32_t errCode - VTYPE_BOOL, //bool - VTYPE_VARIANT, //struct _tVariant * - VTYPE_I1, //int8_t - VTYPE_UI1, //uint8_t - VTYPE_UI2, //uint16_t - VTYPE_UI4, //uint32_t - VTYPE_I8, //int64_t - VTYPE_UI8, //uint64_t - VTYPE_INT, //int Depends on architecture - VTYPE_UINT, //unsigned int Depends on architecture - VTYPE_HRESULT, //long hRes - VTYPE_PWSTR, //struct wstr - VTYPE_BLOB, //means in struct str binary data contain - VTYPE_CLSID, //UUID - VTYPE_STR_BLOB = 0xfff, - VTYPE_VECTOR = 0x1000, - VTYPE_ARRAY = 0x2000, - VTYPE_BYREF = 0x4000, //Only with struct _tVariant * - VTYPE_RESERVED = 0x8000, - VTYPE_ILLEGAL = 0xffff, - VTYPE_ILLEGALMASKED = 0xfff, - VTYPE_TYPEMASK = 0xfff - } - - [FieldOffset(0)] private Int32 lVal; - [FieldOffset(0)] private Boolean bVal; - [FieldOffset(0)] private Double dblVal; - [FieldOffset(0)] private IntPtr pstrVal; - [FieldOffset(4)] private Int32 strLen32; - [FieldOffset(8)] private Int32 strLen64; - [FieldOffset(0)] private IntPtr pwstrVal; - [FieldOffset(4)] private Int32 wstrLen32; - [FieldOffset(8)] private Int32 wstrLen64; - [FieldOffset(44)] private UInt16 vt; - -#pragma warning disable IDE1006 // Стили именования - private Int32 strLen - { - get => IntPtr.Size == 8 ? strLen64 : strLen32; - set - { - if (IntPtr.Size == 8) - wstrLen64 = value; - else - wstrLen32 = value; - } - } - - private Int32 wstrLen - { - get => IntPtr.Size == 8 ? wstrLen64 : wstrLen32; - set - { - if (IntPtr.Size == 8) - wstrLen64 = value; - else - wstrLen32 = value; - } - } - -#pragma warning restore IDE1006 // Стили именования - - public static Int32 Size - { - get => 48; - } - - public IValue GetValue() - { - switch ((VarTypes)vt) - { - case VarTypes.VTYPE_EMPTY: return ValueFactory.Create(); - case VarTypes.VTYPE_I2: - case VarTypes.VTYPE_I4: - case VarTypes.VTYPE_ERROR: - case VarTypes.VTYPE_UI1: - return ValueFactory.Create(lVal); - case VarTypes.VTYPE_BOOL: - return ValueFactory.Create(bVal); - case VarTypes.VTYPE_R4: - case VarTypes.VTYPE_R8: - return ValueFactory.Create((Decimal)dblVal); - case VarTypes.VTYPE_PSTR: - return ValueFactory.Create(Marshal.PtrToStringAnsi(pstrVal, strLen)); - case VarTypes.VTYPE_PWSTR: - return ValueFactory.Create(Marshal.PtrToStringUni(pwstrVal, wstrLen)); - case VarTypes.VTYPE_BLOB: - byte[] buffer = new byte[strLen]; - Marshal.Copy(pstrVal, buffer, 0, strLen); - return ValueFactory.Create(new BinaryDataContext(buffer)); - default: - return ValueFactory.Create(); - } - } - - public void SetValue(IValue value) - { - switch (value.DataType) - { - case DataType.String: - String str = value.AsString(); - pwstrVal = Marshal.StringToHGlobalUni(str); - wstrLen = str.Length; - vt = (UInt16)VarTypes.VTYPE_PWSTR; - return; - case DataType.Boolean: - bVal = value.AsBoolean(); - vt = (UInt16)VarTypes.VTYPE_BOOL; - return; - case DataType.Number: - Decimal num = value.AsNumber(); - if (num % 1 == 0) - { - lVal = Convert.ToInt32(num); - vt = (UInt16)VarTypes.VTYPE_I4; - } - else - { - dblVal = Convert.ToDouble(num); - vt = (UInt16)VarTypes.VTYPE_R8; - } - return; - case DataType.Object when value.AsObject() is BinaryDataContext binaryData: - strLen = binaryData.Buffer.Length; - pstrVal = Marshal.AllocHGlobal(strLen); - Marshal.Copy(binaryData.Buffer, 0, pstrVal, strLen); - vt = (UInt16)VarTypes.VTYPE_BLOB; - return; - default: - vt = (UInt16)VarTypes.VTYPE_EMPTY; - return; - } - } - - bool IsEmpty() - { - return (VarTypes)vt == VarTypes.VTYPE_EMPTY; - } - - bool NotEmpty() - { - return (VarTypes)vt != VarTypes.VTYPE_EMPTY; - } - - static public IValue GetValue(IntPtr ptr) - { - return Marshal.PtrToStructure(ptr).GetValue(); - } - - static public void GetValue(IValue[] values, IntPtr ptr, int count) - { - for (int i = 0; i < values.Length && i < count; i++) - values[i] = GetValue(ptr + i * Size); - } - - static public void SetValue(IntPtr ptr, IValue value) - { - Marshal.PtrToStructure(ptr).SetValue(value); - } - - static public void SetValue(IntPtr ptr, IValue[] values, int count) - { - for (int i = 0; i < values.Length && i < count; i++) - { - var variant = new NativeApiVariant(); - variant.SetValue(values[i]); - Marshal.StructureToPtr(variant, ptr + i * Size, false); - } - } - - static public void Clear(IntPtr ptr) - { - Marshal.PtrToStructure(ptr).Clear(); - } - - static public bool IsEmpty(IntPtr ptr) - { - return Marshal.PtrToStructure(ptr).IsEmpty(); - } - - static public bool NotEmpty(IntPtr ptr) - { - return Marshal.PtrToStructure(ptr).NotEmpty(); - } - - static public void Clear(IntPtr ptr, int count) - { - for (int i = 0; i < count; i++) - Clear(ptr + i * Size); - } - - public void Clear() - { - switch ((VarTypes)vt) - { - case VarTypes.VTYPE_PSTR: - Marshal.FreeHGlobal(pstrVal); - pstrVal = IntPtr.Zero; - strLen = 0; - break; - case VarTypes.VTYPE_PWSTR: - Marshal.FreeHGlobal(pwstrVal); - pwstrVal = IntPtr.Zero; - wstrLen = 0; - break; - default: - break; - } - vt = (UInt16)VarTypes.VTYPE_EMPTY; - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/RandomNumberGenerator.cs b/src/ScriptEngine.HostedScript/Library/RandomNumberGenerator.cs deleted file mode 100644 index 209c22089..000000000 --- a/src/ScriptEngine.HostedScript/Library/RandomNumberGenerator.cs +++ /dev/null @@ -1,81 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("ГенераторСлучайныхЧисел", "RandomNumberGenerator")] - public class RandomNumberGenerator : AutoContext - { - private readonly Random _random; - - public RandomNumberGenerator(int seed = 0) - { - if (seed == 0) - _random = new Random(); - else - _random = new Random(seed); - } - - [ContextMethod("СлучайноеЧисло", "RandomNumber")] - public IValue RandomNumber(IValue low = null, IValue high = null) - { - long lo64 = 0, hi64 = UInt32.MaxValue; - - if (low != null) - lo64 = decimal.ToInt64(low.AsNumber()); - - if (high != null) - hi64 = decimal.ToInt64(high.AsNumber()); - - if (lo64 < 0 || lo64 > 4294967295) - throw RuntimeException.InvalidArgumentValue(); - - if (hi64 < 0 || hi64 > 4294967295) - throw RuntimeException.InvalidArgumentValue(); - - if (hi64 < lo64) - throw RuntimeException.InvalidArgumentValue(); - - // Приводим к рабочему диапазону - lo64 += Int32.MinValue; - hi64 += Int32.MinValue; - - int lo = (int)lo64, hi = (int)hi64; - - int v = _random.Next(lo, hi); - long v64 = v; - v64 -= Int32.MinValue; - - return ValueFactory.Create( v64 ); - } - - /// - /// Формирует ГСЧ с возможностью указания начального числа. - /// - /// Начальное число. Последовательность случайных чисел для одного и того же начального числа будет одинакова - /// - [ScriptConstructor(Name = "Конструктор по умолчанию")] - public static RandomNumberGenerator Constructor(IValue seed) - { - seed = seed.GetRawValue(); - if (seed.DataType == DataType.Number) - return new RandomNumberGenerator(decimal.ToInt32(seed.AsNumber())); - - return new RandomNumberGenerator(); - } - - [ScriptConstructor(Name = "Формирование неинициализированного объекта")] - public static RandomNumberGenerator Constructor() - { - return new RandomNumberGenerator(); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Reflector.cs b/src/ScriptEngine.HostedScript/Library/Reflector.cs deleted file mode 100644 index c671c8a5b..000000000 --- a/src/ScriptEngine.HostedScript/Library/Reflector.cs +++ /dev/null @@ -1,475 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.HostedScript.Library.ValueTable; -using ScriptEngine.Machine.Reflection; -using ScriptEngine.Machine.Values; -using MethodInfo = ScriptEngine.Machine.MethodInfo; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Рефлектор предназначен для получения метаданных объектов во время выполнения. - /// Как правило, рефлексия используется для проверки наличия у объекта определенных свойств/методов. - /// В OneScript рефлексию можно применять для вызова методов объектов по именам методов. - /// - [ContextClass("Рефлектор","Reflector")] - public class ReflectorContext : AutoContext - { - /// - /// Вызывает метод по его имени. - /// - /// Объект, метод которого нужно вызвать. - /// Имя метода для вызова - /// Массив аргументов, передаваемых методу. Следует учесть, что все параметры нужно передавать явно, в том числе необязательные. - /// Если вызывается функция, то возвращается ее результат. В противном случае возвращается Неопределено. - [ContextMethod("ВызватьМетод", "CallMethod")] - public IValue CallMethod(IRuntimeContextInstance target, string methodName, ArrayImpl arguments = null) - { - var methodIdx = target.FindMethod(methodName); - var methInfo = target.GetMethodInfo(methodIdx); - - var argsToPass = GetArgsToPass(arguments, methInfo); - - IValue retValue = ValueFactory.Create(); - if (methInfo.IsFunction) - { - target.CallAsFunction(methodIdx, argsToPass, out retValue); - } - else - { - target.CallAsProcedure(methodIdx, argsToPass); - } - - if (arguments != null) - { - for (int i = 0; i < argsToPass.Length; i++) - { - if (i < arguments.Count()) - { - arguments.Set(i, argsToPass[i]?.GetRawValue()); - } - } - } - - return retValue; - } - - private static IValue[] GetArgsToPass(ArrayImpl arguments, MethodInfo methInfo) - { - var argsToPass = new List(); - if (arguments != null) - { - argsToPass.AddRange(arguments); - } - - if (methInfo.ArgCount < argsToPass.Count) - throw RuntimeException.TooManyArgumentsPassed(); - - for (int i = 0; i < argsToPass.Count; i++) - { - if (!methInfo.Params[i].IsByValue) - argsToPass[i] = Variable.Create(argsToPass[i], $"reflectorArg{i}"); - } - while (argsToPass.Count < methInfo.ArgCount) - { - argsToPass.Add(null); - } - - return argsToPass.ToArray(); - } - - /// - /// Проверяет существование указанного метода у переданного объекта.. - /// - /// Объект, из которого получаем таблицу методов. - /// Имя метода для вызова - /// Истину, если метод существует, и Ложь в обратном случае. - [ContextMethod("МетодСуществует", "MethodExists")] - public bool MethodExists(IValue target, string methodName) - { - if(target.DataType == DataType.Object) - return MethodExistsForObject(target.AsObject(), methodName); - - if (target.DataType == DataType.Type) - return MethodExistsForType(target.GetRawValue() as TypeTypeValue, methodName); - - throw RuntimeException.InvalidArgumentType("target"); - } - - private static bool MethodExistsForObject(IRuntimeContextInstance target, string methodName) - { - try - { - var idx = target.FindMethod(methodName); - return idx >= 0; - } - catch (RuntimeException) - { - return false; - } - } - - private static ValueTable.ValueTable EmptyAnnotationsTable() - { - var annotationsTable = new ValueTable.ValueTable(); - annotationsTable.Columns.Add("Имя"); - annotationsTable.Columns.Add("Параметры"); - - return annotationsTable; - } - - private static ValueTable.ValueTable CreateAnnotationTable(AnnotationDefinition[] annotations) - { - var annotationsTable = EmptyAnnotationsTable(); - var annotationNameColumn = annotationsTable.Columns.FindColumnByName("Имя"); - var annotationParamsColumn = annotationsTable.Columns.FindColumnByName("Параметры"); - - foreach (var annotation in annotations) - { - var annotationRow = annotationsTable.Add(); - if (annotation.Name != null) - { - annotationRow.Set(annotationNameColumn, ValueFactory.Create(annotation.Name)); - } - if (annotation.ParamCount != 0) - { - var parametersTable = new ValueTable.ValueTable(); - var parameterNameColumn = parametersTable.Columns.Add("Имя"); - var parameterValueColumn = parametersTable.Columns.Add("Значение"); - - annotationRow.Set(annotationParamsColumn, parametersTable); - - foreach (var annotationParameter in annotation.Parameters) - { - var parameterRow = parametersTable.Add(); - if (annotationParameter.Name != null) - { - parameterRow.Set(parameterNameColumn, ValueFactory.Create(annotationParameter.Name)); - } - parameterRow.Set(parameterValueColumn, annotationParameter.RuntimeValue); - } - } - } - - return annotationsTable; - } - - private static bool MethodExistsForType(TypeTypeValue type, string methodName) - { - var clrType = GetReflectableClrType(type); - return clrType.GetMethod(methodName) != null; - } - - private static Type GetReflectableClrType(TypeTypeValue type) - { - Type clrType; - try - { - clrType = TypeManager.GetImplementingClass(type.Value.ID); - } - catch (InvalidOperationException) - { - throw NonReflectableType(); - } - - Type reflectableType; - if (clrType == typeof(AttachedScriptsFactory)) - reflectableType = ReflectUserType(type.Value.Name); - else - reflectableType = ReflectContext(clrType); - - return reflectableType; - } - - private static RuntimeException NonReflectableType() - { - return RuntimeException.InvalidArgumentValue("Тип не может быть отражен."); - } - - /// - /// Получает таблицу методов для переданного объекта.. - /// - /// Объект, из которого получаем таблицу методов. - /// Таблица значений колонками: Имя, Количество, ЭтоФункция, Аннотации - [ContextMethod("ПолучитьТаблицуМетодов", "GetMethodsTable")] - public ValueTable.ValueTable GetMethodsTable(IValue target) - { - var result = new ValueTable.ValueTable(); - if(target.DataType == DataType.Object) - FillMethodsTableForObject(target.AsObject(), result); - else if (target.DataType == DataType.Type) - FillMethodsTableForType(target.GetRawValue() as TypeTypeValue, result); - else - throw RuntimeException.InvalidArgumentType(); - - return result; - } - - private static void FillMethodsTableForObject(IRuntimeContextInstance target, ValueTable.ValueTable result) - { - FillMethodsTable(result, target.GetMethods()); - } - - private static void FillMethodsTableForType(TypeTypeValue type, ValueTable.ValueTable result) - { - var clrType = GetReflectableClrType(type); - var clrMethods = clrType.GetMethods(BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public); - FillMethodsTable(result, ConvertToOsMethods(clrMethods)); - } - - private static IEnumerable ConvertToOsMethods(IEnumerable source) - { - var dest = new List(); - foreach (var methodInfo in source) - { - var osMethod = new MethodInfo(); - osMethod.Name = methodInfo.Name; - osMethod.Alias = null; - osMethod.IsExport = methodInfo.IsPublic; - osMethod.IsFunction = methodInfo.ReturnType != typeof(void); - osMethod.Annotations = GetAnnotations(methodInfo.GetCustomAttributes()); - - var methodParameters = methodInfo.GetParameters(); - var osParams = new ParameterDefinition[methodParameters.Length]; - osMethod.Params = osParams; - for (int i = 0; i < osParams.Length; i++) - { - var parameterInfo = methodParameters[i]; - var osParam = new ParameterDefinition(); - osParam.Name = parameterInfo.Name; - osParam.IsByValue = parameterInfo.GetCustomAttribute() != null; - osParam.HasDefaultValue = parameterInfo.HasDefaultValue; - osParam.DefaultValueIndex = -1; - - // On Mono 5.20 we can't use GetCustomAttributes because it fails with InvalidCast. - // Here's a workaround with home-made attribute Type filter. - var attributes = parameterInfo.GetCustomAttributes() - .OfType(); - - osParam.Annotations = GetAnnotations(attributes); - osParams[i] = osParam; - } - dest.Add(osMethod); - } - - return dest; - } - - private static AnnotationDefinition[] GetAnnotations(IEnumerable attributes) - { - return attributes.Select(x => x.Annotation).ToArray(); - } - - private static void FillPropertiesTableForType(TypeTypeValue type, ValueTable.ValueTable result) - { - var clrType = GetReflectableClrType(type); - var nativeProps = clrType.GetProperties() - .Select(x => new - { - PropDef = x.GetCustomAttribute(), - Prop = x - }) - .Where(x=>x.PropDef != null); - - int indices = 0; - var infos = new List(); - foreach(var prop in nativeProps) - { - var info = new VariableInfo(); - info.Type = SymbolType.ContextProperty; - info.Index = indices++; - info.Identifier = prop.PropDef.GetName(); - info.Annotations = GetAnnotations(prop.Prop.GetCustomAttributes()); - infos.Add(info); - } - - if (clrType.BaseType == typeof(ScriptDrivenObject)) - { - var nativeFields = clrType.GetFields(); - foreach(var field in nativeFields) - { - var info = new VariableInfo(); - info.Type = SymbolType.ContextProperty; - info.Index = indices++; - info.Identifier = field.Name; - info.Annotations = GetAnnotations(field.GetCustomAttributes()); - infos.Add(info); - } - } - - FillPropertiesTable(result, infos); - - } - - private static void FillMethodsTable(ValueTable.ValueTable result, IEnumerable methods) - { - var nameColumn = result.Columns.Add("Имя", TypeDescription.StringType(), "Имя"); - var countColumn = result.Columns.Add("КоличествоПараметров", TypeDescription.IntegerType(), "Количество параметров"); - var isFunctionColumn = result.Columns.Add("ЭтоФункция", TypeDescription.BooleanType(), "Это функция"); - var annotationsColumn = result.Columns.Add("Аннотации", new TypeDescription(), "Аннотации"); - var paramsColumn = result.Columns.Add("Параметры", new TypeDescription(), "Параметры"); - var isExportlColumn = result.Columns.Add("Экспорт", new TypeDescription(), "Экспорт"); - - foreach (var methInfo in methods) - { - - ValueTableRow new_row = result.Add(); - new_row.Set(nameColumn, ValueFactory.Create(methInfo.Name)); - new_row.Set(countColumn, ValueFactory.Create(methInfo.ArgCount)); - new_row.Set(isFunctionColumn, ValueFactory.Create(methInfo.IsFunction)); - new_row.Set(isExportlColumn, ValueFactory.Create(methInfo.IsExport)); - - new_row.Set(annotationsColumn, methInfo.AnnotationsCount != 0 ? CreateAnnotationTable(methInfo.Annotations) : EmptyAnnotationsTable()); - - var paramTable = new ValueTable.ValueTable(); - var paramNameColumn = paramTable.Columns.Add("Имя", TypeDescription.StringType(), "Имя"); - var paramByValue = paramTable.Columns.Add("ПоЗначению", TypeDescription.BooleanType(), "По значению"); - var paramHasDefaultValue = paramTable.Columns.Add("ЕстьЗначениеПоУмолчанию", TypeDescription.BooleanType(), "Есть значение по-умолчанию"); - var paramAnnotationsColumn = paramTable.Columns.Add("Аннотации", new TypeDescription(), "Аннотации"); - - new_row.Set(paramsColumn, paramTable); - - if (methInfo.ArgCount != 0) - { - var index = 0; - foreach (var param in methInfo.Params) - { - var name = string.Format("param{0}", ++index); - var paramRow = paramTable.Add(); - paramRow.Set(paramNameColumn, ValueFactory.Create(name)); - paramRow.Set(paramByValue, ValueFactory.Create(param.IsByValue)); - paramRow.Set(paramHasDefaultValue, ValueFactory.Create(param.HasDefaultValue)); - paramRow.Set(paramAnnotationsColumn, param.AnnotationsCount != 0 ? CreateAnnotationTable(param.Annotations) : EmptyAnnotationsTable()); - } - } - } - } - - /// - /// Получает таблицу свойств для переданного объекта.. - /// - /// Объект, из которого получаем таблицу свойств. - /// Таблица значений с колонками - Имя, Аннотации - [ContextMethod("ПолучитьТаблицуСвойств", "GetPropertiesTable")] - public ValueTable.ValueTable GetPropertiesTable(IValue target) - { - ValueTable.ValueTable result = new ValueTable.ValueTable(); - - if(target.DataType == DataType.Object) - FillPropertiesTable(result, target.AsObject().GetProperties()); - else if (target.DataType == DataType.Type) - { - var type = target.GetRawValue() as TypeTypeValue; - FillPropertiesTableForType(type, result); - } - else - throw RuntimeException.InvalidArgumentType(); - - return result; - } - - /// - /// Получает свойство по его имени. - /// - /// Объект, свойство которого необходимо установить. - /// Имя свойства - /// Значение свойства - [ContextMethod("ПолучитьСвойство", "GetProperty")] - public IValue GetProperty(IRuntimeContextInstance target, string prop) - { - int propIdx; - if (target is ScriptDrivenObject script) - propIdx = script.FindAnyProperty(prop); - else - propIdx = target.FindProperty(prop); - return target.GetPropValue(propIdx); - } - - /// - /// Устанавливает свойство по его имени. - /// - /// Объект, свойство которого необходимо установить. - /// Имя свойства - /// Значение свойства. - [ContextMethod("УстановитьСвойство", "SetProperty")] - public void SetProperty(IRuntimeContextInstance target, string prop, IValue value) - { - int propIdx; - if (target is ScriptDrivenObject script) - propIdx = script.FindAnyProperty(prop); - else - propIdx = target.FindProperty(prop); - target.SetPropValue(propIdx, value); - } - - private static void FillPropertiesTable(ValueTable.ValueTable result, IEnumerable properties) - { - var nameColumn = result.Columns.Add("Имя", TypeDescription.StringType(), "Имя"); - var annotationsColumn = result.Columns.Add("Аннотации", new TypeDescription(), "Аннотации"); - var systemVarNames = new string[] { "этотобъект", "thisobject" }; - - foreach (var propInfo in properties) - { - if (systemVarNames.Contains(propInfo.Identifier.ToLower())) continue; - - ValueTableRow new_row = result.Add(); - new_row.Set(nameColumn, ValueFactory.Create(propInfo.Identifier)); - - new_row.Set(annotationsColumn, propInfo.AnnotationsCount != 0 ? CreateAnnotationTable(propInfo.Annotations) : EmptyAnnotationsTable()); - } - } - - public static Type ReflectUserType(string typeName) - { - LoadedModule module; - try - { - module = AttachedScriptsFactory.GetModuleOfType(typeName); - } - catch (KeyNotFoundException) - { - throw NonReflectableType(); - } - - var builder = new ClassBuilder(); - - return builder - .SetTypeName(typeName) - .SetModule(module) - .ExportDefaults() - .Build(); - } - - public static Type ReflectContext(Type clrType) - { - var attrib = clrType.GetCustomAttribute(); - if (attrib == null || !typeof(ContextIValueImpl).IsAssignableFrom(clrType)) - throw NonReflectableType(); - - var builderType = typeof(ClassBuilder<>).MakeGenericType(clrType); - var builder = (IReflectedClassBuilder)Activator.CreateInstance(builderType); - - return builder.SetTypeName(attrib.GetName()) - .ExportDefaults() - .Build(); - } - - [ScriptConstructor] - public static IRuntimeContextInstance CreateNew() - { - return new ReflectorContext(); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/SpecialFolderEnum.cs b/src/ScriptEngine.HostedScript/Library/SpecialFolderEnum.cs deleted file mode 100644 index 533a0878c..000000000 --- a/src/ScriptEngine.HostedScript/Library/SpecialFolderEnum.cs +++ /dev/null @@ -1,52 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -using sysFolder = System.Environment.SpecialFolder; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Системное перечисление для специальных папок. - /// - [SystemEnum("СпециальнаяПапка", "SpecialFolder")] - public class SpecialFolderEnum : EnumerationContext - { - private SpecialFolderEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - - } - - public static SpecialFolderEnum CreateInstance() - { - SpecialFolderEnum instance; - var type = TypeManager.RegisterType("ПеречислениеСпециальнаяПапка", typeof(SpecialFolderEnum)); - var enumValueType = TypeManager.RegisterType("СпециальнаяПапка", typeof(CLREnumValueWrapper)); - - instance = new SpecialFolderEnum(type, enumValueType); - - instance.AddValue("МоиДокументы", "MyDocuments", new CLREnumValueWrapper(instance, sysFolder.Personal)); - instance.AddValue("ДанныеПриложений", "ApplicationData", new CLREnumValueWrapper(instance, sysFolder.ApplicationData)); - instance.AddValue("ЛокальныйКаталогДанныхПриложений", "LocalApplicationData", new CLREnumValueWrapper(instance, sysFolder.LocalApplicationData)); - instance.AddValue("РабочийСтол", "Desktop", new CLREnumValueWrapper(instance, sysFolder.Desktop)); - instance.AddValue("КаталогРабочийСтол", "DesktopDirectory", new CLREnumValueWrapper(instance, sysFolder.DesktopDirectory)); - instance.AddValue("МояМузыка", "MyMusic", new CLREnumValueWrapper(instance, sysFolder.MyMusic)); - instance.AddValue("МоиРисунки", "MyPictures", new CLREnumValueWrapper(instance, sysFolder.MyPictures)); - instance.AddValue("Шаблоны", "Templates", new CLREnumValueWrapper(instance, sysFolder.Templates)); - instance.AddValue("МоиВидеозаписи", "MyVideos", new CLREnumValueWrapper(instance, sysFolder.MyVideos)); - instance.AddValue("ОбщиеШаблоны", "CommonTemplates", new CLREnumValueWrapper(instance, sysFolder.CommonTemplates)); - instance.AddValue("ПрофильПользователя", "UserProfile", new CLREnumValueWrapper(instance, sysFolder.UserProfile)); - instance.AddValue("ОбщийКаталогДанныхПриложения", "CommonApplicationData", new CLREnumValueWrapper(instance, sysFolder.CommonApplicationData)); - - return instance; - } - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/StringOperations.cs b/src/ScriptEngine.HostedScript/Library/StringOperations.cs deleted file mode 100644 index 3d9dcd2ac..000000000 --- a/src/ScriptEngine.HostedScript/Library/StringOperations.cs +++ /dev/null @@ -1,323 +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 ScriptEngine.Machine.Contexts; -using System; -using System.Linq; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library -{ - [GlobalContext(Category = "Операции со строками")] - public class StringOperations : GlobalContextBase - { - readonly int STRTEMPLATE_ID; - const string STRTEMPLATE_NAME_RU = "СтрШаблон"; - const string STRTEMPLATE_NAME_EN = "StrTemplate"; - - public StringOperations() - { - STRTEMPLATE_ID = this.Methods.Count; - } - - /// - /// Получает строку на языке, заданном во втором параметре (коды языков в соответствии с ISO 639-1) - /// или на текущем языке системы. - /// - /// Строка на нескольких языках - /// Код языка (если не указан, возвращает вариант для текущего языка системы, - /// если вариант не найден, то возвращает вариант для английского языка, если не задан вариант для английского языка, - /// то возвращает первый вариант из списка) - [ContextMethod("НСтр", "NStr")] - public string NStr(string src, string lang = null) - { - return Locale.NStr(src, lang); - } - - /// - /// Определяет, что строка начинается с указанной подстроки. - /// - /// Строка, начало которой проверяется на совпадение с подстрокой поиска. - /// Строка, содержащая предполагаемое начало строки. В случае если переданное значение является пустой строкой генерируется исключительная ситуация. - [ContextMethod("СтрНачинаетсяС", "StrStartsWith")] - public bool StrStartsWith(string inputString, string searchString) - { - bool result = false; - - if(!string.IsNullOrEmpty(inputString)) - { - if (!string.IsNullOrEmpty(searchString)) - { - result = inputString.StartsWith(searchString); - } - else throw new RuntimeException("Ошибка при вызове метода контекста (СтрНачинаетсяС): Недопустимое значение параметра (параметр номер '2')"); - } - - return result; - } - - /// - /// Определяет, заканчивается ли строка указанной подстрокой. - /// - /// Строка, окончание которой проверяется на совпадение с подстрокой поиска. - /// Строка, содержащая предполагаемое окончание строки. В случае если переданное значение является пустой строкой генерируется исключительная ситуация. - [ContextMethod("СтрЗаканчиваетсяНа", "StrEndsWith")] - public bool StrEndsWith(string inputString, string searchString) - { - bool result = false; - - if(!string.IsNullOrEmpty(inputString)) - { - if (!string.IsNullOrEmpty(searchString)) - { - result = inputString.EndsWith(searchString); - } - else throw new RuntimeException("Ошибка при вызове метода контекста (СтрЗаканчиваетсяНа): Недопустимое значение параметра (параметр номер '2')"); - } - - return result; - } - - /// - /// Разделяет строку на части по указанным символам-разделителям. - /// - /// Разделяемая строка. - /// Строка символов, каждый из которых является индивидуальным разделителем. - /// Указывает необходимость включать в результат пустые строки, которые могут образоваться в результате разделения исходной строки. Значение по умолчанию: Истина. - [ContextMethod("СтрРазделить", "StrSplit")] - public ArrayImpl StrSplit(string inputString, string stringDelimiter, bool? includeEmpty = true) - { - string[] arrParsed; - if (includeEmpty == null) - includeEmpty = true; - - if(!string.IsNullOrEmpty(inputString)) - { - arrParsed = inputString.Split(stringDelimiter?.ToCharArray(), (bool) includeEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries); - } - else - { - arrParsed = (bool) includeEmpty ? new string[] { string.Empty } : new string[0]; - } - return new ArrayImpl(arrParsed.Select(x => ValueFactory.Create(x))); - } - - /// - /// Соединяет массив переданных строк в одну строку с указанным разделителем - /// - /// Массив - соединяемые строки - /// Разделитель. Если не указан, строки объединяются слитно - [ContextMethod("СтрСоединить", "StrConcat")] - public string StrConcat(ArrayImpl input, string delimiter = null) - { - var strings = input.Select(x => x.AsString()); - - return String.Join(delimiter, strings); - } - - /// - /// Сравнивает строки без учета регистра. - /// - /// - /// - /// -1 первая строка больше, 1 - вторая строка больше. 0 - строки равны - [ContextMethod("СтрСравнить", "StrCompare")] - public int StrCompare(string first, string second) - { - return String.Compare(first, second, true); - } - - /// - /// Находит вхождение искомой строки как подстроки в исходной строке - /// - /// Строка, в которой ищем - /// Строка, которую надо найти - /// значение перечисления НаправлениеПоиска (с конца/с начала) - /// Начальная позиция, с которой начинать поиск - /// Указывает номер вхождения искомой подстроки в исходной строке - /// Позицию искомой строки в исходной строке. Возвращает 0, если подстрока не найдена. - [ContextMethod("СтрНайти", "StrFind")] - public int StrFind(string haystack, string needle, SearchDirection direction = SearchDirection.FromBegin, int startPos = 0, int occurance = 0) - { - int len = haystack.Length; - if (len == 0 || needle.Length == 0) - return 0; - - bool fromBegin = direction == SearchDirection.FromBegin; - - if(startPos == 0) - { - startPos = fromBegin ? 1 : len; - } - - if (startPos < 1 || startPos > len) - throw RuntimeException.InvalidArgumentValue(); - - if (occurance == 0) - occurance = 1; - - int startIndex = startPos - 1; - int foundTimes = 0; - int index = len + 1; - - if(fromBegin) - { - while(foundTimes < occurance && index >= 0) - { - index = haystack.IndexOf(needle, startIndex, StringComparison.Ordinal); - if (index >= 0) - { - startIndex = index + 1; - foundTimes++; - } - if (startIndex >= len) - break; - } - - } - else - { - while(foundTimes < occurance && index >= 0) - { - index = haystack.LastIndexOf(needle, startIndex, StringComparison.Ordinal); - if (index >= 0) - { - startIndex = index - 1; - foundTimes++; - } - if (startIndex < 0) - break; - } - - } - - if (foundTimes == occurance) - return index + 1; - else - return 0; - } - - #region IRuntimeContextInstance overrides - - public override int FindMethod(string name) - { - if (string.Compare(name, STRTEMPLATE_NAME_RU, true) == 0 || string.Compare(name, STRTEMPLATE_NAME_EN, true) == 0) - return STRTEMPLATE_ID; - else - return base.FindMethod(name); - } - - public override int GetMethodsCount() - { - return base.GetMethodsCount() + 1; - } - - private static MethodInfo CreateStrTemplateMethodInfo() - { - var strTemplateMethodInfo = new MethodInfo(); - strTemplateMethodInfo.IsFunction = true; - strTemplateMethodInfo.Name = STRTEMPLATE_NAME_RU; - strTemplateMethodInfo.Alias = STRTEMPLATE_NAME_EN; - strTemplateMethodInfo.Params = new ParameterDefinition[11]; - strTemplateMethodInfo.IsExport = true; - - strTemplateMethodInfo.Params[0] = new ParameterDefinition() - { - IsByValue = true - }; - - for (int i = 1; i < strTemplateMethodInfo.Params.Length; i++) - { - strTemplateMethodInfo.Params[i] = new ParameterDefinition() - { - IsByValue = true, - HasDefaultValue = true - }; - } - return strTemplateMethodInfo; - } - - public override MethodInfo GetMethodInfo(int methodNumber) - { - if (methodNumber == STRTEMPLATE_ID) - return CreateStrTemplateMethodInfo(); - else - return base.GetMethodInfo(methodNumber); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - if (methodNumber == STRTEMPLATE_ID) - CallStrTemplate(arguments); - else - base.CallAsProcedure(methodNumber, arguments); - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - if (methodNumber == STRTEMPLATE_ID) - retValue = CallStrTemplate(arguments); - else - base.CallAsFunction(methodNumber, arguments, out retValue); - } - - #endregion - - private IValue CallStrTemplate(IValue[] arguments) - { - var srcFormat = arguments[0].AsString(); - if (srcFormat == String.Empty) - return ValueFactory.Create(""); - - var re = new System.Text.RegularExpressions.Regex(@"(%%)|(%\d+)|(%\D)"); - int matchCount = 0; - int passedArgsCount = arguments.Skip(1).Count(x => x.DataType != DataType.NotAValidValue && x.DataType != DataType.Undefined); - var result = re.Replace(srcFormat, (m) => - { - if (m.Groups[1].Success) - return "%"; - - if(m.Groups[2].Success) - { - matchCount++; - var number = int.Parse(m.Groups[2].Value.Substring(1)); - if (number < 1 || number > 11) - throw new RuntimeException("Ошибка при вызове метода контекста (СтрШаблон): Ошибка синтаксиса шаблона в позиции " + (m.Index + 1)); - - if (arguments[number] != null && arguments[number].DataType != DataType.NotAValidValue) - return arguments[number].AsString(); - else - return ""; - } - - throw new RuntimeException("Ошибка при вызове метода контекста (СтрШаблон): Ошибка синтаксиса шаблона в позиции " + (m.Index + 1)); - - }); - - if (passedArgsCount > matchCount) - throw RuntimeException.TooManyArgumentsPassed(); - - return ValueFactory.Create(result); - } - - public static IAttachableContext CreateInstance() - { - return new StringOperations(); - } - - } - - [EnumerationType("НаправлениеПоиска", "SearchDirection")] - public enum SearchDirection - { - [EnumItem("СНачала")] - FromBegin, - [EnumItem("СКонца")] - FromEnd - } - - -} diff --git a/src/ScriptEngine.HostedScript/Library/StructureImpl.cs b/src/ScriptEngine.HostedScript/Library/StructureImpl.cs deleted file mode 100644 index 432e75c0c..000000000 --- a/src/ScriptEngine.HostedScript/Library/StructureImpl.cs +++ /dev/null @@ -1,272 +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.Collections.Generic; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("Структура", "Structure")] - public class StructureImpl : DynamicPropertiesAccessor, ICollectionContext, IEnumerable, IDebugPresentationAcceptor - { - private readonly List _values = new List(); - - public StructureImpl() - { - - } - - public StructureImpl(string strProperties, params IValue[] values) - { - var props = strProperties.Split(','); - - for (int i = 0, nprop = 0; i < props.Length; i++) - { - var prop = props[i].Trim(); - if (prop.Equals(string.Empty)) - continue; - - Insert(prop, nprop < values.Length ? values[nprop] : null); - ++nprop; - } - } - - public StructureImpl(FixedStructureImpl structure) - { - foreach (KeyAndValueImpl keyValue in structure) - { - Insert(keyValue.Key.AsString(), keyValue.Value); - } - } - - [ContextMethod("Вставить")] - public void Insert(string name, IValue val = null) - { - if (!Utils.IsValidIdentifier(name)) - throw InvalidPropertyNameException(name); - - var num = RegisterProperty(name); - if (num == _values.Count) - { - _values.Add(null); - } - - if (val == null) - { - val = ValueFactory.Create(); - } - - SetPropValue(num, val); - } - - [ContextMethod("Удалить", "Delete")] - public void Remove(string name) - { - if (!Utils.IsValidIdentifier(name)) - throw InvalidPropertyNameException(name); - - int propIndex; - try - { - propIndex = FindProperty(name); - } - catch (PropertyAccessException) - { - return; - } - - _values.RemoveAt(propIndex); - RemoveProperty(name); - ReorderPropertyNumbers(); - } - - [ContextMethod("Свойство", "Property")] - public bool HasProperty(string name, [ByRef] IVariable value = null) - { - if (!Utils.IsValidIdentifier(name)) - throw InvalidPropertyNameException(name); - - int propIndex; - try - { - propIndex = FindProperty(name); - } - catch (PropertyAccessException) - { - if(value != null) - value.Value = ValueFactory.Create(); - return false; - } - - if(value != null) - value.Value = GetPropValue(propIndex); - - return true; - } - - public override IValue GetPropValue(int propNum) - { - return _values[propNum]; - } - - public override void SetPropValue(int propNum, IValue newVal) - { - _values[propNum] = newVal; - } - - public override int GetPropCount() - { - return _values.Count; - } - - public override string GetPropName(int propNum) - { - return GetPropertyName(propNum); - } - - public override MethodInfo GetMethodInfo(int methodNumber) - { - return _methods.GetMethodInfo(methodNumber); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - var binding = _methods.GetMethod(methodNumber); - try - { - binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - var binding = _methods.GetMethod(methodNumber); - try - { - retValue = binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override int FindMethod(string name) - { - return _methods.FindMethod(name); - } - - #region IReflectableContext Members - - public override int GetMethodsCount() - { - return _methods.Count; - } - - #endregion - - #region ICollectionContext Members - - [ContextMethod("Количество", "Count")] - public int Count() - { - return _values.Count; - } - - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - ClearProperties(); - _values.Clear(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - #endregion - - #region IEnumerable Members - - public IEnumerator GetEnumerator() - { - foreach (var item in GetProperties()) - { - yield return new KeyAndValueImpl( - ValueFactory.Create(item.Key), - GetPropValue(item.Value)); - } - } - - #endregion - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - - [ScriptConstructor] - public static StructureImpl Constructor() - { - return new StructureImpl(); - } - - /// - /// Создает структуру по фиксированной структуре - /// - /// Исходная структура - //[ScriptConstructor(Name = "Из фиксированной структуры")] - private static StructureImpl Constructor(FixedStructureImpl fixedStruct) - { - return new StructureImpl(fixedStruct); - } - - /// - /// Создает структуру по заданному перечню свойств и значений - /// - /// Фиксированная структура либо строка с именами свойств, указанными через запятую. - /// Только для перечня свойств: - /// Значения свойств. Каждое значение передается, как отдельный параметр. - [ScriptConstructor(Name = "По ключам и значениям")] - public static StructureImpl Constructor(IValue param1, IValue[] args) - { - var rawArgument = param1.GetRawValue(); - if (rawArgument.DataType == DataType.String) - { - return new StructureImpl(rawArgument.AsString(), args); - } - else if (rawArgument is FixedStructureImpl) - { - return new StructureImpl(rawArgument as FixedStructureImpl); - } - - throw new RuntimeException("В качестве параметра для конструктора можно передавать только ФиксированнаяСтруктура или Ключи и Значения"); - } - - - private static RuntimeException InvalidPropertyNameException( string name ) - { - return new RuntimeException($"Задано неправильное имя атрибута структуры '{name}'"); - } - - void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) - { - visitor.ShowProperties(this); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/SystemConfigAccessor.cs b/src/ScriptEngine.HostedScript/Library/SystemConfigAccessor.cs deleted file mode 100644 index 695e6f62f..000000000 --- a/src/ScriptEngine.HostedScript/Library/SystemConfigAccessor.cs +++ /dev/null @@ -1,65 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [GlobalContext(Category = "Работа с настройками системы")] - public class SystemConfigAccessor : GlobalContextBase - { - private KeyValueConfig _config; - - internal EngineConfigProvider Provider { get; set; } - - public SystemConfigAccessor() - { - Refresh(); - } - - internal KeyValueConfig GetConfig() - { - return _config; - } - - /// - /// Метод обновляет текущие настройки значениями из файла oscript.cfg - /// - [ContextMethod("ОбновитьНастройкиСистемы", "RefreshSystemConfig")] - public void Refresh() - { - if (Provider != null) - _config = Provider.ReadConfig(); - } - - /// - /// Метод возвращает значение из файла oscript.cfg по имени настойки - /// - /// Имя настройки из файла oscript.cfg - /// Строка. Значение системной настройки. - [ContextMethod("ПолучитьЗначениеСистемнойНастройки", "GetSystemOptionValue")] - public IValue GetSystemOptionValue(string optionKey) - { - string value = null; - if (_config != null) - { - value = _config[optionKey]; - } - - if (value != null) - return ValueFactory.Create(value); - - return ValueFactory.Create(); - } - - public static IAttachableContext CreateInstance() - { - return new SystemConfigAccessor(); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/SystemEnvironmentContext.cs b/src/ScriptEngine.HostedScript/Library/SystemEnvironmentContext.cs deleted file mode 100644 index 8ec6c8de7..000000000 --- a/src/ScriptEngine.HostedScript/Library/SystemEnvironmentContext.cs +++ /dev/null @@ -1,232 +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.Collections; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Класс предоставляет информацию о системе - /// - [ContextClass("СистемнаяИнформация", "SystemInfo")] - public class SystemEnvironmentContext : AutoContext - { - /// - /// Имя машины, на которой выполняется сценарий - /// - [ContextProperty("ИмяКомпьютера", "MachineName")] - public string MachineName - { - get - { - return System.Environment.MachineName; - } - } - - /// - /// Версия операционной системы, на которой выполняется сценарий - /// - [ContextProperty("ВерсияОС", "OSVersion")] - public string OSVersion - { - get - { - return System.Environment.OSVersion.VersionString; - } - } - - /// - /// Версия OneScript, выполняющая данный сценарий - /// - [ContextProperty("Версия","Version")] - public string Version - { - get - { - return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); - } - } - - /// - /// Имя пользователя ОС с учетом домена - /// Формат строки: \\ИмяДомена\ИмяПользователя. - /// - [ContextProperty("ПользовательОС", "OSUser")] - public string OSUser - { - get - { - string DomainName = System.Environment.UserDomainName; - - if (DomainName != "") - { - return @"\\" + DomainName + @"\" + System.Environment.UserName; - } - - return System.Environment.UserName; - } - } - - /// - /// Определяет, является ли текущая операционная система 64-разрядной. - /// - [ContextProperty("Это64БитнаяОперационнаяСистема")] - public bool Is64BitOperatingSystem - { - get { return System.Environment.Is64BitOperatingSystem; } - } - - /// - /// Возвращает число процессоров. - /// 32-битовое целое число со знаком, которое возвращает количество процессоров на текущем компьютере. - /// Значение по умолчанию отсутствует. Если текущий компьютер содержит несколько групп процессоров, - /// данное свойство возвращает число логических процессоров, доступных для использования средой CLR - /// - [ContextProperty("КоличествоПроцессоров")] - public int ProcessorCount - { - get { return System.Environment.ProcessorCount; } - } - - /// - /// Возвращает количество байтов на странице памяти операционной системы - /// - [ContextProperty("РазмерСистемнойСтраницы")] - public int SystemPageSize - { - get { return System.Environment.SystemPageSize; } - } - - /// - /// Возвращает время, истекшее с момента загрузки системы (в миллисекундах). - /// - [ContextProperty("ВремяРаботыСМоментаЗагрузки")] - public long TickCount - { - get - { - var unsig = (uint)System.Environment.TickCount; - return unsig; - } - } - - /// - /// Возвращает путь для специальной папки. Поддерживаемые значения: - /// - /// * МоиДокументы / MyDocuments - /// * ДанныеПриложений / ApplicationData - /// * ЛокальныйКаталогДанныхПриложений / LocalApplicationData - /// * РабочийСтол / Desktop - /// * КаталогРабочийСтол / DesktopDirectory - /// * МояМузыка / MyMusic - /// * МоиРисунки / MyPictures - /// * Шаблоны / Templates - /// * МоиВидеозаписи / MyVideos - /// * ОбщиеШаблоны / CommonTemplates - /// * ПрофильПользователя / UserProfile - /// * ОбщийКаталогДанныхПриложения / CommonApplicationData - /// - /// Тип: СпециальнаяПапка - /// Строка - [ContextMethod("ПолучитьПутьПапки")] - public string GetFolderPath(IValue folder) - { - var typedValue = folder as CLREnumValueWrapper; - if (typedValue == null) - throw RuntimeException.InvalidArgumentType(); - - return System.Environment.GetFolderPath(typedValue.UnderlyingValue); - - } - - /// - /// Возвращает массив строк, содержащий имена логических дисков текущего компьютера. - /// - [ContextProperty("ИменаЛогическихДисков")] - public FixedArrayImpl GetLogicalDrives - { - get - { - var arr = new ArrayImpl(); - var data = System.Environment.GetLogicalDrives(); - foreach (var itm in data) - { - arr.Add(ValueFactory.Create(itm)); - } - return new FixedArrayImpl(arr); - } - } - - - /// - /// Возвращает соответствие переменных среды. Ключом является имя переменной, а значением - значение переменной - /// - /// - /// СИ = Новый СистемнаяИнформация(); - /// Для Каждого Переменная Из СИ.ПеременныеСреды() Цикл - /// Сообщить(Переменная.Ключ + " = " + Переменная.Значение); - /// КонецЦикла; - /// - /// Соответствие - [ContextMethod("ПеременныеСреды", "EnvironmentVariables")] - public MapImpl EnvironmentVariables() - { - SystemLogger.Write("WARNING! Deprecated method: 'SystemInfo.EnvironmentVariables' is deprecated, use 'EnvironmentVariables' from global context"); - var varsMap = new MapImpl(); - var allVars = System.Environment.GetEnvironmentVariables(); - foreach (DictionaryEntry item in allVars) - { - varsMap.Insert( - ValueFactory.Create((string)item.Key), - ValueFactory.Create((string)item.Value)); - } - - return varsMap; - } - - /// - /// Позволяет установить переменную среды. - /// Переменная устанавливается в области видимости процесса и очищается после его завершения. - /// - /// Имя переменной - /// Значение переменной - [ContextMethod("УстановитьПеременнуюСреды","SetEnvironmentVariable")] - public void SetEnvironmentVariable(string varName, string value) - { - SystemLogger.Write(string.Format(Locale.NStr("en='{0}';ru='{1}'"), - "WARNING! Deprecated method: \"SystemInfo.SetEnvironmentVariable\" is deprecated, use \"SetEnvironmentVariable\" from global context", - "Предупреждение! Устаревший метод: \"СистемнаяИнформация.УстановитьПеременнуюСреды\" устарел, используйте метод глобального контекста \"УстановитьПеременнуюСреды\"")); - System.Environment.SetEnvironmentVariable(varName, value); - } - - /// - /// Получить значение переменной среды. - /// - /// Имя переменной - /// Строка. Значение переменной - [ContextMethod("ПолучитьПеременнуюСреды", "GetEnvironmentVariable")] - public IValue GetEnvironmentVariable(string varName) - { - SystemLogger.Write(string.Format(Locale.NStr("en='{0}';ru='{1}'"), - "WARNING! Deprecated method: \"SystemInfo.GetEnvironmentVariable\" is deprecated, use \"GetEnvironmentVariable\" from global context", - "Предупреждение! Устаревший метод: \"СистемнаяИнформация.ПолучитьПеременнуюСреды\" устарел, используйте метод глобального контекста \"ПолучитьПеременнуюСреды\"")); - string value = System.Environment.GetEnvironmentVariable(varName); - if (value == null) - return ValueFactory.Create(); - else - return ValueFactory.Create(value); - - } - - [ScriptConstructor] - public static SystemEnvironmentContext Create() - { - return new SystemEnvironmentContext(); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/SystemGlobalContext.cs b/src/ScriptEngine.HostedScript/Library/SystemGlobalContext.cs deleted file mode 100755 index d8f99df59..000000000 --- a/src/ScriptEngine.HostedScript/Library/SystemGlobalContext.cs +++ /dev/null @@ -1,773 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using ScriptEngine.Environment; -using ScriptEngine.HostedScript.Library.Binary; -using ScriptEngine.HostedScript.Library.NativeApi; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - /// - /// Глобальный контекст. Представляет глобально доступные свойства и методы. - /// - [GlobalContext(Category="Процедуры и функции взаимодействия с системой", ManualRegistration=true)] - public class SystemGlobalContext : IAttachableContext - { - private IVariable[] _state; - private FixedArrayImpl _args; - private SymbolsContext _symbols; - private readonly DynamicPropertiesHolder _propHolder = new DynamicPropertiesHolder(); - private readonly List> _properties = new List>(); - - public SystemGlobalContext() - { - RegisterProperty("АргументыКоманднойСтроки", ()=>(IValue)CommandLineArguments); - RegisterProperty("CommandLineArguments", () => (IValue)CommandLineArguments); - - FileStreams = new FileStreamsManager(); - RegisterProperty("ФайловыеПотоки", () => FileStreams); - RegisterProperty("FileStreams", () => FileStreams); - - RegisterProperty("Символы", () => (IValue)Chars); - RegisterProperty("Chars", () => (IValue)Chars); - } - - private void RegisterProperty(string name, Func getter) - { - _propHolder.RegisterProperty(name); - _properties.Add(getter); - } - - public ScriptingEngine EngineInstance{ get; set; } - - public void InitInstance() - { - InitContextVariables(); - NativeApi.NativeApiFactory.Initialize(ApplicationHost); - } - - private void InitContextVariables() - { - _state = new IVariable[_properties.Count]; - - var propNames = _propHolder.GetProperties().OrderBy(x=>x.Value).Select(x=>x.Key).ToArray(); - for (int i = 0; i < _properties.Count; i++) - { - _state[i] = Variable.CreateContextPropertyReference(this, i, propNames[i]); - } - } - - public IHostApplication ApplicationHost { get; set; } - public ICodeSource CodeSource { get; set; } - - - /// - /// Менеджер файловых потоков. - /// - [ContextProperty("ФайловыеПотоки","FileStreams")] - public FileStreamsManager FileStreams { get; } - - /// - /// Выдает сообщение в консоль. - /// - /// Выдаваемое сообщение. - /// Статус сообщения. В зависимости от статуса изменяется цвет вывода сообщения. - [ContextMethod("Сообщить", "Message")] - public void Echo(string message, MessageStatusEnum status = MessageStatusEnum.Ordinary) - { - ApplicationHost.Echo(message ?? "", status); - } - - /// - /// Подключает сторонний файл сценария к текущей системе типов. - /// Подключенный сценарий выступает, как самостоятельный класс, создаваемый оператором Новый - /// - /// Путь к подключаемому сценарию - /// Имя типа, которое будет иметь новый класс. Экземпляры класса создаются оператором Новый. - /// ПодключитьСценарий("C:\file.os", "МойОбъект"); - /// А = Новый МойОбъект(); - [ContextMethod("ПодключитьСценарий", "AttachScript")] - public void AttachScript(string path, string typeName) - { - var compiler = EngineInstance.GetCompilerService(); - EngineInstance.AttachedScriptsFactory.AttachByPath(compiler, path, typeName); - } - - /// - /// Создает экземпляр объекта на основании стороннего файла сценария. - /// Загруженный сценарий возвращается, как самостоятельный объект. - /// Экспортные свойства и методы скрипта доступны для вызова. - /// - /// Текст сценария - /// Структура. Глобальные свойства, которые будут инжектированы в область видимости загружаемого скрипта. (Необязательный) - /// - /// Контекст = Новый Структура("ЧислоПи", 3.1415); // 4 знака хватит всем - /// ЗагрузитьСценарийИзСтроки("Сообщить(ЧислоПи);", Контекст); - [ContextMethod("ЗагрузитьСценарийИзСтроки", "LoadScriptFromString")] - public IRuntimeContextInstance LoadScriptFromString(string code, StructureImpl externalContext = null) - { - var compiler = EngineInstance.GetCompilerService(); - if(externalContext == null) - return EngineInstance.AttachedScriptsFactory.LoadFromString(compiler, code); - else - { - var extData = new ExternalContextData(); - - foreach (var item in externalContext) - { - extData.Add(item.Key.AsString(), item.Value); - } - - return EngineInstance.AttachedScriptsFactory.LoadFromString(compiler, code, extData); - - } - } - - /// - /// Создает экземпляр объекта на основании стороннего файла сценария. - /// Загруженный сценарий возвращается, как самостоятельный объект. - /// Экспортные свойства и методы скрипта доступны для вызова. - /// - /// Путь к подключаемому сценарию - /// Структура. Глобальные свойства, которые будут инжектированы в область видимости загружаемого скрипта. (Необязательный) - /// - /// Контекст = Новый Структура("ЧислоПи", 3.1415); // 4 знака хватит - /// // В коде скрипта somescript.os будет доступна глобальная переменная "ЧислоПи" - /// Объект = ЗагрузитьСценарий("somescript.os", Контекст); - [ContextMethod("ЗагрузитьСценарий", "LoadScript")] - public IRuntimeContextInstance LoadScript(string path, StructureImpl externalContext = null) - { - var compiler = EngineInstance.GetCompilerService(); - if(externalContext == null) - return EngineInstance.AttachedScriptsFactory.LoadFromPath(compiler, path); - else - { - ExternalContextData extData = new ExternalContextData(); - - foreach (var item in externalContext) - { - extData.Add(item.Key.AsString(), item.Value); - } - - return EngineInstance.AttachedScriptsFactory.LoadFromPath(compiler, path, extData); - - } - } - - /// - /// Подключает внешнюю сборку среды .NET (*.dll) и регистрирует классы 1Script, объявленные в этой сборке. - /// Публичные классы, отмеченные в dll атрибутом ContextClass, будут импортированы аналогично встроенным классам 1Script. - /// Загружаемая сборка должна ссылаться на сборку ScriptEngine.dll - /// - /// Также подключает вншение компонеты, разработанные по технологии Native API, - /// поставляемые в виде отдельных DLL или упакованные в ZIP-архив. - /// - /// - /// Путь к внешней компоненте - /// Символическое имя подключаемой внешней компоненты (только для Native API) - /// Тип подключаемой внешней компоненты (для совместимости, необязательно) - /// - /// //Подключает внешнюю сборку среды .NET (*.dll) - /// ПодключитьВнешнююКомпоненту("C:\MyAssembly.dll"); - /// КлассИзКомпоненты = Новый КлассИзКомпоненты(); // тип объявлен внутри компоненты - /// - /// //Подключает вншение компонеты Native API, упакованные в ZIP-архив - /// ПодключитьВнешнююКомпоненту("C:\AddInNative.zip", "AddInNative"); - /// ЭкземплярВнешнейКомпоненты = Новый ("AddIn.AddInNative.NativeComponent", ТипВнешнейКомпоненты.Native); - /// - /// //Подключает вншение компонеты Native API в виде отдельных DLL-файлов - /// ПодключитьВнешнююКомпоненту("C:\AddInNative.dll", "SimpleAddIn", ТипВнешнейКомпоненты.Native); - /// ЭкземплярВнешнейКомпоненты = Новый ("AddIn.SimpleAddIn.SimpleComponent"); - /// - [ContextMethod("ПодключитьВнешнююКомпоненту", "AttachAddIn")] - public bool AttachAddIn(string dllPath, string name = "", NativeApiEnums type = NativeApiEnums.OneScript) - { - if (type == NativeApiEnums.OneScript) - { - var assembly = System.Reflection.Assembly.LoadFrom(dllPath); - EngineInstance.AttachExternalAssembly(assembly, EngineInstance.Environment); - return true; - } - else { - if (!Utils.IsValidIdentifier(name)) - { - throw RuntimeException.InvalidArgumentValue(name); - } - return NativeApiFactory.Register(dllPath, name); - } - } - - /// - /// Возвращает информацию о сценарии, который был точкой входа в программу. - /// Можно выделить два вида сценариев: те, которые были подключены, как классы и те, которые запущены непосредственно. Метод СтартовыйСценарий возвращает информацию о сценарии, запущенном непосредственно. - /// Для получения информации о текущем выполняемом сценарии см. метод ТекущийСценарий() - /// - /// Объект ИнформацияОСценарии - [ContextMethod("СтартовыйСценарий", "EntryScript")] - public IRuntimeContextInstance StartupScript() - { - return new ScriptInformationContext(CodeSource); - } - - /// - /// Приостанавливает выполнение скрипта. - /// - /// Время приостановки в миллисекундах - [ContextMethod("Приостановить", "Sleep")] - public void Sleep(int delay) - { - System.Threading.Thread.Sleep(delay); - } - - /// - /// Прерывает выполнение текущего скрипта. - /// - /// Код возврата (ошибки), возвращаемый операционной системе. - [ContextMethod("ЗавершитьРаботу", "Exit")] - public void Quit(int exitCode) - { - throw new ScriptInterruptionException(exitCode); - } - - /// - /// Ввод строки пользователем. Позволяет запросить у пользователя информацию. - /// - /// Выходной параметр. Введенные данные в виде строки. - /// Максимальная длина вводимой строки. - /// Возможно указание неограниченной длины (длина=ноль), но данное поведение может не поддерживаться хост-приложением. - /// Булево. Истина, если пользователь ввел данные, Ложь, если отказался. - [ContextMethod("ВвестиСтроку", "InputString")] - public bool InputString([ByRef] IVariable resut, int len = 0) - { - string input; - bool inputIsDone; - - inputIsDone = ApplicationHost.InputString(out input, len); - - if (inputIsDone) - { - resut.Value = ValueFactory.Create(input); - return true; - } - else - return false; - } - - /// - /// Явное освобождение ресурса через интерфейс IDisposable среды CLR. - /// - /// OneScript не выполняет подсчет ссылок на объекты, а полагается на сборщик мусора CLR. - /// Это значит, что объекты автоматически не освобождаются при выходе из области видимости. - /// - /// Метод ОсвободитьОбъект можно использовать для детерминированного освобождения ресурсов. Если объект поддерживает интерфейс IDisposable, то данный метод вызовет Dispose у данного объекта. - /// - /// Как правило, интерфейс IDisposable реализуется различными ресурсами (файлами, соединениями с ИБ и т.п.) - /// - /// Объект, ресурсы которого требуется освободить. - [ContextMethod("ОсвободитьОбъект", "FreeObject")] - public void DisposeObject(IRuntimeContextInstance obj) - { - var disposable = obj as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } - } - - /// - /// OneScript не выполняет подсчет ссылок на объекты, а полагается на сборщик мусора CLR. - /// Это значит, что объекты автоматически не освобождаются при выходе из области видимости. - /// - /// С помощью данного метода можно запустить принудительную сборку мусора среды CLR. - /// Данные метод следует использовать обдуманно, поскольку вызов данного метода не гарантирует освобождение всех объектов. - /// Локальные переменные, например, до завершения текущего метода очищены не будут, - /// поскольку до завершения текущего метода CLR будет видеть, что они используются движком 1Script. - /// - /// - [ContextMethod("ВыполнитьСборкуМусора", "RunGarbageCollection")] - public void RunGarbageCollection() - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - - /// - /// Доступ к аргументам командной строки. - /// Объект АргументыКоманднойСтроки представляет собой массив в режиме "только чтение". - /// - [ContextProperty("АргументыКоманднойСтроки", "CommandLineArguments", CanWrite = false)] - public IRuntimeContextInstance CommandLineArguments - { - get - { - if (_args == null) - { - var argsArray = new ArrayImpl(); - if (ApplicationHost != null) - { - foreach (var arg in ApplicationHost.GetCommandLineArguments()) - { - argsArray.Add(ValueFactory.Create(arg)); - } - } - _args = new FixedArrayImpl(argsArray); - } - - return _args; - } - - } - - /// - /// Содержит набор системных символов. - /// - /// Набор системных символов. - [ContextProperty("Символы")] - public IRuntimeContextInstance Chars - { - get - { - if (_symbols == null) - { - _symbols = new SymbolsContext(); - } - - return _symbols; - } - } - - /// - /// Запуск приложения в операционной системе - /// - /// Командная строка запуска - /// Текущая директория запускаемого процесса (необязательно) - /// Ожидать завершения (необязательно) по умолчанию Ложь - /// Выходной параметр. Код возврата процесса. Имеет смысл только если указан параметр wait=true - [ContextMethod("ЗапуститьПриложение", "RunApp")] - public void RunApp(string cmdLine, string currentDir = null, bool wait = false, [ByRef] IVariable retCode = null) - { - var sInfo = ProcessContext.PrepareProcessStartupInfo(cmdLine, currentDir); - - var p = new System.Diagnostics.Process(); - p.StartInfo = sInfo; - p.Start(); - - if(wait) - { - p.WaitForExit(); - if(retCode != null) - retCode.Value = ValueFactory.Create(p.ExitCode); - } - - } - - /// - /// Создает процесс, которым можно манипулировать из скрипта - /// - /// Командная строка запуска - /// Текущая директория запускаемого процесса (необязательно) - /// Перехватывать стандартные потоки stdout и stderr - /// Перехватывать стандартный поток stdin - /// Кодировка стандартных потоков вывода и ошибок - /// Соответствие, где установлены значения переменных среды - [ContextMethod("СоздатьПроцесс", "CreateProcess")] - public ProcessContext CreateProcess(string cmdLine, string currentDir = null, bool redirectOutput = false, bool redirectInput = false, IValue encoding = null, MapImpl env = null) - { - return ProcessContext.Create(cmdLine, currentDir, redirectOutput, redirectInput, encoding, env); - } - - /// - /// Выполняет поиск процесса по PID среди запущенных в операционной системе - /// - /// Идентификатор процесса - /// Процесс. Если не найден - Неопределено - [ContextMethod("НайтиПроцессПоИдентификатору", "FindProcessById")] - public IValue FindProcessById(int PID) - { - System.Diagnostics.Process process; - try - { - process = System.Diagnostics.Process.GetProcessById(PID); - } - catch (ArgumentException) - { - return ValueFactory.Create(); - } - - return ValueFactory.Create(new ProcessContext(process)); - - } - - /// - /// Выполняет поиск процессов с определенным именем - /// - /// Имя процесса - /// Массив объектов Процесс. - [ContextMethod("НайтиПроцессыПоИмени", "FindProcessesByName")] - public IValue FindProcessesByName(string name) - { - var processes = System.Diagnostics.Process.GetProcessesByName(name); - var contextWrappers = processes.Select(x => new ProcessContext(x)); - - return new ArrayImpl(contextWrappers); - - } - - /// - /// Каталог исполняемых файлов OneScript - /// - /// - [ContextMethod("КаталогПрограммы","ProgramDirectory")] - public string ProgramDirectory() - { - var asm = System.Reflection.Assembly.GetExecutingAssembly(); - var filename = asm.Location; - - return System.IO.Path.GetDirectoryName(filename); - } - - [ContextMethod("КраткоеПредставлениеОшибки", "BriefErrorDescription")] - public string BriefErrorDescription(ExceptionInfoContext errInfo) - { - return errInfo.Description; - } - - [ContextMethod("ПодробноеПредставлениеОшибки", "DetailErrorDescription")] - public string DetailErrorDescription(ExceptionInfoContext errInfo) - { - return errInfo.DetailedDescription; - } - - [ContextMethod("ТекущаяУниверсальнаяДата", "CurrentUniversalDate")] - public IValue CurrentUniversalDate() - { - return ValueFactory.Create(DateTime.UtcNow); - } - - [ContextMethod("ТекущаяУниверсальнаяДатаВМиллисекундах", "CurrentUniversalDateInMilliseconds")] - public long CurrentUniversalDateInMilliseconds() - { - return DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond; - } - - /// - /// Проверяет заполненность значения по принципу, заложенному в 1С:Предприятии - /// - /// - /// - [ContextMethod("ЗначениеЗаполнено","ValueIsFilled")] - public bool ValueIsFilled(IValue inValue) - { - var value = inValue?.GetRawValue(); - if (value == null) - { - return false; - } - if (value.DataType == DataType.Undefined) - return false; - else if (value.DataType == DataType.Boolean) - return true; - else if (value.DataType == DataType.String) - return !String.IsNullOrWhiteSpace(value.AsString()); - else if (value.DataType == DataType.Number) - return value.AsNumber() != 0; - else if (value.DataType == DataType.Date) - { - var emptyDate = new DateTime(1, 1, 1, 0, 0, 0); - return value.AsDate() != emptyDate; - } - else if (value is COMWrapperContext) - { - return true; - } - else if (value is ICollectionContext) - { - var col = value as ICollectionContext; - return col.Count() != 0; - } - else if (ValueFactory.CreateNullValue().Equals(value)) - { - return false; - } - else - return true; - - } - - /// - /// Заполняет одноименные значения свойств одного объекта из другого - /// - /// Объект-приемник - /// Объект-источник - /// Заполняемые свойства (строка, через запятую) - /// Игнорируемые свойства (строка, через запятую) - [ContextMethod("ЗаполнитьЗначенияСвойств","FillPropertyValues")] - public void FillPropertyValues(IRuntimeContextInstance acceptor, IRuntimeContextInstance source, IValue filledProperties = null, IValue ignoredProperties = null) - { - string strFilled; - string strIgnored; - - if (filledProperties == null || filledProperties.DataType == DataType.Undefined) - { - strFilled = null; - } - else if (filledProperties.DataType == DataType.String) - { - strFilled = filledProperties.AsString(); - } - else - { - throw RuntimeException.InvalidArgumentType(3, nameof(filledProperties)); - } - - if (ignoredProperties == null || ignoredProperties.DataType == DataType.Undefined) - { - strIgnored = null; - } - else if (ignoredProperties.DataType == DataType.String) - { - strIgnored = ignoredProperties.AsString(); - } - else - { - throw RuntimeException.InvalidArgumentType(4, nameof(ignoredProperties)); - } - - FillPropertyValuesStr(acceptor, source, strFilled, strIgnored); - } - - public void FillPropertyValuesStr(IRuntimeContextInstance acceptor, IRuntimeContextInstance source, string filledProperties = null, string ignoredProperties = null) - { - IEnumerable sourceProperties; - - if (filledProperties == null) - { - string[] names = new string[source.GetPropCount()]; - for (int i = 0; i < names.Length; i++) - { - names[i] = source.GetPropName(i); - } - - if (ignoredProperties == null) - { - sourceProperties = names; - } - else - { - IEnumerable ignoredPropCollection = ignoredProperties.Split(',') - .Select(x => x.Trim()) - .Where(x => x.Length > 0); - - sourceProperties = names.Where(x => !ignoredPropCollection.Contains(x)); - } - } - else - { - sourceProperties = filledProperties.Split(',') - .Select(x => x.Trim()) - .Where(x => x.Length > 0); - - // Проверка существования заявленных свойств - foreach (var item in sourceProperties) - { - acceptor.FindProperty(item); // бросает PropertyAccessException если свойства нет - } - } - - - foreach (var srcProperty in sourceProperties) - { - try - { - var srcPropIdx = source.FindProperty(srcProperty); - var accPropIdx = acceptor.FindProperty(srcProperty); // бросает PropertyAccessException если свойства нет - - if (source.IsPropReadable(srcPropIdx) && acceptor.IsPropWritable(accPropIdx)) - acceptor.SetPropValue(accPropIdx, source.GetPropValue(srcPropIdx)); - - } - catch (PropertyAccessException) - { - // игнорировать свойства Источника, которых нет в Приемнике - } - } - } - - - /// - /// Получает объект класса COM по его имени или пути. Подробнее см. синтакс-помощник от 1С. - /// - /// Путь к библиотеке - /// Имя класса - /// COMОбъект - [ContextMethod("ПолучитьCOMОбъект", "GetCOMObject")] - public IValue GetCOMObject(string pathName = null, string className = null) - { - var comObject = GetCOMObjectInternal(pathName, className); - - return COMWrapperContext.Create(comObject); - } - - /// - /// Ported from Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - /// By JetBrains dotPeek decompiler - /// - private object GetCOMObjectInternal(string pathName = null, string className = null) - { - if (String.IsNullOrEmpty(className)) - { - return Marshal.BindToMoniker(pathName); - } - else if (pathName == null) - { -#if (NETSTANDARD2_0 || NETSTANDARD2_1) - throw new NotSupportedException("Getting object by classname not supported on netstandard2"); -#else - return Marshal.GetActiveObject(className); -#endif - } - else if (pathName.Length == 0) - { - return Activator.CreateInstance(System.Type.GetTypeFromProgID(className)); - } - else - { -#if (NETSTANDARD2_0 || NETSTANDARD2_1) - throw new NotSupportedException("Getting object by classname not supported on netstandard2"); -#else - var persistFile = (IPersistFile)Marshal.GetActiveObject(className); - persistFile.Load(pathName, 0); - - return (object)persistFile; -#endif - } - } - -#region IAttachableContext Members - - public void OnAttach(MachineInstance machine, - out IVariable[] variables, - out MethodInfo[] methods) - { - if (_state == null) - InitContextVariables(); - - variables = _state; - methods = new MethodInfo[_methods.Count]; - for (int i = 0; i < _methods.Count; i++) - { - methods[i] = _methods.GetMethodInfo(i); - } - } - -#endregion - -#region IRuntimeContextInstance Members - - public bool IsIndexed - { - get - { - return false; - } - } - - public bool DynamicMethodSignatures - { - get - { - return false; - } - } - - public IValue GetIndexedValue(IValue index) - { - throw new NotImplementedException(); - } - - public void SetIndexedValue(IValue index, IValue val) - { - throw new NotImplementedException(); - } - - public int FindProperty(string name) - { - return _propHolder.GetPropertyNumber(name); - } - - public bool IsPropReadable(int propNum) - { - return true; - } - - public bool IsPropWritable(int propNum) - { - return false; - } - - public IValue GetPropValue(int propNum) - { - return _properties[propNum](); - } - - public void SetPropValue(int propNum, IValue newVal) - { - throw new InvalidOperationException("global props are not writable"); - } - - public int GetPropCount() - { - return _properties.Count; - } - - public string GetPropName(int index) - { - return _propHolder.GetProperties().First(x => x.Value == index).Key; - } - - public int FindMethod(string name) - { - return _methods.FindMethod(name); - } - - public MethodInfo GetMethodInfo(int methodNumber) - { - return _methods.GetMethodInfo(methodNumber); - } - - public int GetMethodsCount() - { - return _methods.Count; - } - - public void CallAsProcedure(int methodNumber, IValue[] arguments) - { - _methods.GetMethod(methodNumber)(this, arguments); - } - - public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - retValue = _methods.GetMethod(methodNumber)(this, arguments); - } - -#endregion - - private static readonly ContextMethodsMapper _methods; - - static SystemGlobalContext() - { - _methods = new ContextMethodsMapper(); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TextEncodingEnum.cs b/src/ScriptEngine.HostedScript/Library/TextEncodingEnum.cs deleted file mode 100644 index 811a70e87..000000000 --- a/src/ScriptEngine.HostedScript/Library/TextEncodingEnum.cs +++ /dev/null @@ -1,189 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System.Text; - -namespace ScriptEngine.HostedScript.Library -{ - [SystemEnum("КодировкаТекста", "TextEncoding")] - public class TextEncodingEnum : EnumerationContext - { - private const string ENCODING_ANSI = "ANSI"; - private const string ENCODING_OEM = "OEM"; - private const string ENCODING_UTF16 = "UTF16"; - private const string ENCODING_UTF8 = "UTF8"; - private const string ENCODING_UTF8NoBOM = "UTF8NoBOM"; - private const string ENCODING_SYSTEM = "Системная"; - - private TextEncodingEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue(ENCODING_ANSI)] - public EnumerationValue Ansi - { - get - { - return this[ENCODING_ANSI]; - } - } - - [EnumValue(ENCODING_OEM)] - public EnumerationValue Oem - { - get - { - return this[ENCODING_OEM]; - } - } - - [EnumValue(ENCODING_UTF16)] - public EnumerationValue Utf16 - { - get - { - return this[ENCODING_UTF16]; - } - } - - [EnumValue(ENCODING_UTF8)] - public EnumerationValue Utf8 - { - get - { - return this[ENCODING_UTF8]; - } - } - - [EnumValue(ENCODING_UTF8NoBOM)] - public EnumerationValue Utf8NoBOM - { - get - { - return this[ENCODING_UTF8NoBOM]; - } - } - - [EnumValue(ENCODING_SYSTEM, "System")] - public EnumerationValue System - { - get - { - return this[ENCODING_SYSTEM]; - } - } - - public EnumerationValue GetValue(Encoding encoding) - { - if (encoding.Equals(Encoding.GetEncoding(866))) - return Oem; - - if (encoding.Equals(Encoding.GetEncoding(1251))) - return Ansi; - - if (encoding.Equals(new UnicodeEncoding(false, true))) - return Utf16; - - if (encoding.Equals(new UTF8Encoding(true))) - return Utf8; - - if (encoding.Equals(new UTF8Encoding(false))) - return Utf8NoBOM; - - if (encoding.Equals(Encoding.Default)) - return System; - - throw RuntimeException.InvalidArgumentValue(); - } - - public static TextEncodingEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t,v)=>new TextEncodingEnum(t,v)); - } - - public static Encoding GetEncodingByName(string encoding, bool addBOM = true) - { - Encoding enc; - if (encoding == null) - enc = new UTF8Encoding(addBOM); - else - { - switch (encoding.ToUpper()) - { - case "UTF-8": - enc = new UTF8Encoding(addBOM); - break; - case "UTF-16": - case "UTF-16LE": - // предположительно, варианты UTF16_PlatformEndian\UTF16_OppositeEndian - // зависят от платформы x86\m68k\SPARC. Пока нет понимания как корректно это обработать. - // Сейчас сделано исходя из предположения что PlatformEndian должен быть LE поскольку - // платформа x86 более широко распространена - case "UTF16_PLATFORMENDIAN": - enc = new UnicodeEncoding(false, addBOM); - break; - case "UTF-16BE": - case "UTF16_OPPOSITEENDIAN": - enc = new UnicodeEncoding(true, addBOM); - break; - case "UTF-32": - case "UTF-32LE": - case "UTF32_PLATFORMENDIAN": - enc = new UTF32Encoding(false, addBOM); - break; - case "UTF-32BE": - case "UTF32_OPPOSITEENDIAN": - enc = new UTF32Encoding(true, addBOM); - break; - default: - enc = Encoding.GetEncoding(encoding); - break; - - } - } - - return enc; - } - - public static Encoding GetEncoding(IValue encoding, bool addBOM = true) - { - if (encoding.DataType == DataType.String) - return GetEncodingByName(encoding.AsString(), addBOM); - else - { - if (encoding.DataType != DataType.GenericValue) - throw RuntimeException.InvalidArgumentType(); - - var encValue = encoding.GetRawValue() as SelfAwareEnumValue; - if (encValue == null) - throw RuntimeException.InvalidArgumentType(); - - var encodingEnum = GlobalsManager.GetEnum(); - - Encoding enc; - if (encValue == encodingEnum.Ansi) - enc = Encoding.GetEncoding(1251); - else if (encValue == encodingEnum.Oem) - enc = Encoding.GetEncoding(866); - else if (encValue == encodingEnum.Utf16) - enc = new UnicodeEncoding(false, addBOM); - else if (encValue == encodingEnum.Utf8) - enc = new UTF8Encoding(addBOM); - else if (encValue == encodingEnum.Utf8NoBOM) - enc = new UTF8Encoding(false); - else if (encValue == encodingEnum.System) - enc = Encoding.Default; - else - throw RuntimeException.InvalidArgumentValue(); - - return enc; - } - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TextWriteImpl.cs b/src/ScriptEngine.HostedScript/Library/TextWriteImpl.cs deleted file mode 100644 index 82efbfc85..000000000 --- a/src/ScriptEngine.HostedScript/Library/TextWriteImpl.cs +++ /dev/null @@ -1,150 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.IO; -using System.Text; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("ЗаписьТекста", "TextWriter")] - public class TextWriteImpl : AutoContext, IDisposable - { - StreamWriter _writer; - string _lineDelimiter = ""; - string _eolReplacement = ""; - - public TextWriteImpl() - { - - } - - public TextWriteImpl(string path, IValue encoding) - { - Open(path, encoding); - } - - public TextWriteImpl(string path, IValue encoding, bool append) - { - Open(path, encoding, null, append); - } - - /// - /// Открывает файл для записи. - /// - /// Путь к файлу - /// Кодировка (необязательный). По умолчанию используется utf-8 - /// Разделитель строк (необязательный). - /// Признак добавления в конец файла (необязательный) - /// Разделитель строк в файле (необязательный). - [ContextMethod("Открыть", "Open")] - public void Open(string path, IValue encoding = null, string lineDelimiter = null, bool append = false, string eolReplacement = null) - { - _lineDelimiter = lineDelimiter ?? "\n"; - _eolReplacement = eolReplacement ?? "\r\n"; - - Encoding enc; - if (encoding == null) - { - enc = new UTF8Encoding(true); - } - else - { - enc = TextEncodingEnum.GetEncoding(encoding); - if (enc.WebName == "utf-8" && append == true) - enc = new UTF8Encoding(false); - } - - _writer = new StreamWriter(path, append, enc); - _writer.AutoFlush = true; - } - - [ContextMethod("Закрыть","Close")] - public void Close() - { - Dispose(); - } - - /// - /// Записывает текст "как есть" - /// - /// Текст для записи - [ContextMethod("Записать", "Write")] - public void Write(string what) - { - ThrowIfNotOpened(); - - var stringToOutput = what.Replace ("\n", _eolReplacement); - - _writer.Write(stringToOutput); - } - - /// - /// Записывает текст и добавляет перевод строки - /// - /// Текст для записи - /// Разделитель строк - [ContextMethod("ЗаписатьСтроку", "WriteLine")] - public void WriteLine(string what, IValue delimiter = null) - { - ThrowIfNotOpened(); - - Write (what); - - var sDelimiter = _lineDelimiter; - if (delimiter != null && delimiter.GetRawValue ().DataType != DataType.Undefined) - sDelimiter = delimiter.GetRawValue ().AsString (); - - Write (sDelimiter); - } - - public void ThrowIfNotOpened() - { - if (_writer == null) - throw new RuntimeException("Файл не открыт"); - } - - public void Dispose() - { - if (_writer != null) - { - _writer.Dispose(); - _writer = null; - } - } - - /// - /// Создает объект с начальными значениями имени файла и кодировки. - /// - /// Имя файла - /// Кодировка в виде строки - /// Символ - разделитель строк - /// Признак добавления в конец файла (необязательный) - /// Разделитель строк в файле (необязательный). - [ScriptConstructor(Name = "По имени файла")] - public static TextWriteImpl Constructor(IValue path, IValue encoding = null, IValue lineDelimiter = null, IValue append = null, IValue eolReplacement = null) - { - bool isAppend = append != null && append.AsBoolean(); - var result = new TextWriteImpl (); - - string sLineDelimiter = lineDelimiter?.GetRawValue().AsString () ?? "\n"; - string sEolReplacement = eolReplacement?.GetRawValue().AsString () ?? "\r\n"; - - result.Open (path.AsString (), encoding, sLineDelimiter, isAppend, sEolReplacement); - - return result; - } - - [ScriptConstructor(Name = "Формирование неинициализированного объекта")] - public static TextWriteImpl Constructor() - { - return new TextWriteImpl(); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Timezones/TimeZoneConverter.cs b/src/ScriptEngine.HostedScript/Library/Timezones/TimeZoneConverter.cs deleted file mode 100644 index be0281926..000000000 --- a/src/ScriptEngine.HostedScript/Library/Timezones/TimeZoneConverter.cs +++ /dev/null @@ -1,131 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ScriptEngine.HostedScript.Library.Timezones -{ - public class TimeZoneConverter - { - private const int MAX_HOURS = 23; - private const int MAX_MINUTES = 59; - - public static TimeSpan GetTimespan(string timezone) - { - TimeSpan span; - - if (string.IsNullOrEmpty(timezone)) - span = TimeZoneInfo.Local.BaseUtcOffset; - else if (timezone.StartsWith("GMT", StringComparison.InvariantCultureIgnoreCase)) - span = TimeSpanByGMTString(timezone); - else - span = TimeZoneById(timezone).BaseUtcOffset; - - return span; - } - - public static TimeZoneInfo TimeZoneById(string id) - { - return TimeZoneInfo.FindSystemTimeZoneById(id); - } - - public static DateTime ToUniversalTime(DateTime dt, string timeZone = null) - { - var src = GetTimespan(timeZone); - var dest = TimeZoneInfo.Utc.BaseUtcOffset; - var span = dest.Subtract(src); - return dt.Add(span); - } - - public static DateTime ToLocalTime(DateTime dt, string timeZone = null) - { - var dest = GetTimespan(timeZone); - var src = TimeZoneInfo.Utc.BaseUtcOffset; - var span = dest.Subtract(src); - return dt.Add(span); - } - - public static int StandardTimeOffset(string timeZone = null, DateTime? dt = null) - { - var dest = GetTimespan(timeZone); - - if (dt == null) - dt = DateTime.UtcNow; - - var local = dt.Value.Add(dest); - - var offset = local - dt.Value; - - return (int)offset.TotalSeconds; - } - - public static IEnumerable GetAvailableTimeZones() - { - return TimeZoneInfo.GetSystemTimeZones() - .Select(x => x.Id); - } - - public static string TimeZone() - { - return TimeZoneInfo.Local.Id; - } - - public static string TimeZonePresentation(string timeZone) - { - TimeSpan offset; - - if(IsGmtString(timeZone)) - offset = TimeSpanByGMTString(timeZone); - else - offset = TimeZoneById(timeZone).BaseUtcOffset; - - var oprt = offset.Hours >= 0 ? "+" : "-"; - var result = $"GMT{oprt}{offset.ToString(@"hh\:mm")}"; - return result; - } - - private static TimeSpan TimeSpanByGMTString(string gmtString) - { - - gmtString = gmtString.ToLower(); - - var positiveOffset = gmtString.StartsWith("gmt+"); - - var arr_id = gmtString.Split( - new string[] { "+", ":", "-" }, - StringSplitOptions.None); - - int hours = 0; - int minutes = 0; - - if (arr_id.Length < 2 || arr_id.Length > 3) - throw new TimeZoneNotFoundException(); - else if (arr_id.Length == 3) - minutes = int.Parse(arr_id[2]); - - hours = int.Parse(arr_id[1]); - - if (hours > MAX_HOURS || minutes > MAX_MINUTES) - throw new TimeZoneNotFoundException(); - - if (!positiveOffset) - hours = -hours; - - var span = new TimeSpan(hours, minutes, 0); - - return span; - } - - private static bool IsGmtString(string zone) - { - return zone.StartsWith("GMT", StringComparison.InvariantCultureIgnoreCase); - } - - } - -} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/AllowedLengthEnum.cs b/src/ScriptEngine.HostedScript/Library/TypeDescription/AllowedLengthEnum.cs deleted file mode 100644 index 93d47053e..000000000 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/AllowedLengthEnum.cs +++ /dev/null @@ -1,19 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library -{ - [EnumerationType("ДопустимаяДлина", "AllowedLength")] - public enum AllowedLengthEnum - { - [EnumItem("Переменная", "Variable")] - Variable, - - [EnumItem("Фиксированная", "Fixed")] - Fixed - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/AllowedSignEnum.cs b/src/ScriptEngine.HostedScript/Library/TypeDescription/AllowedSignEnum.cs deleted file mode 100644 index c3bd4f704..000000000 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/AllowedSignEnum.cs +++ /dev/null @@ -1,19 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library -{ - [EnumerationType("ДопустимыйЗнак", "AllowedSign")] - public enum AllowedSignEnum - { - [EnumItem("Любой", "Any")] - Any, - - [EnumItem("Неотрицательный", "Nonnegative")] - Nonnegative - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/BinaryDataQualifiers.cs b/src/ScriptEngine.HostedScript/Library/TypeDescription/BinaryDataQualifiers.cs deleted file mode 100644 index c3de009c1..000000000 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/BinaryDataQualifiers.cs +++ /dev/null @@ -1,58 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("КвалификаторыДвоичныхДанных", "BinaryDataQualifiers")] - public sealed class BinaryDataQualifiers : AutoContext - { - public BinaryDataQualifiers(int length = 0, - AllowedLengthEnum allowedLength = AllowedLengthEnum.Variable) - { - Length = length; - AllowedLength = allowedLength; - } - - [ContextProperty("Длина", "Length")] - public int Length { get; } - - [ContextProperty("ДопустимаяДлина", "AllowedLength")] - public AllowedLengthEnum AllowedLength { get; } - - public override bool Equals(object obj) - { - var asThis = obj as BinaryDataQualifiers; - if (asThis == null) - return false; - - return Length == asThis.Length - && AllowedLength == asThis.AllowedLength; - } - - public override bool Equals(IValue other) - { - return object.Equals(this, other?.GetRawValue()); - } - - public override int GetHashCode() - { - return Length.GetHashCode(); - } - - [ScriptConstructor] - public static BinaryDataQualifiers Constructor(IValue length = null, - IValue allowedLength = null) - { - var paramLength = ContextValuesMarshaller.ConvertParam(length); - var paramAllowedLength = ContextValuesMarshaller.ConvertParam(allowedLength); - return new BinaryDataQualifiers(paramLength, paramAllowedLength); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/DateFractionsEnum.cs b/src/ScriptEngine.HostedScript/Library/TypeDescription/DateFractionsEnum.cs deleted file mode 100644 index 0a0294f05..000000000 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/DateFractionsEnum.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library -{ - [EnumerationType("ЧастиДаты", "DateFractions")] - public enum DateFractionsEnum - { - [EnumItem("Дата", "Date")] - Date, - - [EnumItem("ДатаВремя", "DateTime")] - DateTime, - - [EnumItem("Время", "Time")] - Time - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/NumberQualifiers.cs b/src/ScriptEngine.HostedScript/Library/TypeDescription/NumberQualifiers.cs deleted file mode 100644 index 43246cd05..000000000 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/NumberQualifiers.cs +++ /dev/null @@ -1,104 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("КвалификаторыЧисла", "NumberQualifiers")] - public sealed class NumberQualifiers : AutoContext, IValueAdjuster - { - public NumberQualifiers(int digits = 0, - int fractionDigits = 0, - AllowedSignEnum allowedSign = AllowedSignEnum.Any) - { - Digits = digits; - FractionDigits = fractionDigits; - AllowedSign = allowedSign; - } - - [ContextProperty("ДопустимыйЗнак", "AllowedSign")] - public AllowedSignEnum AllowedSign { get; } - - [ContextProperty("Разрядность", "Digits")] - public int Digits { get; } - - [ContextProperty("РазрядностьДробнойЧасти", "FractionDigits")] - public int FractionDigits { get; } - - public override bool Equals(object obj) - { - var asThis = obj as NumberQualifiers; - if (asThis == null) - return false; - - return Digits == asThis.Digits - && FractionDigits == asThis.FractionDigits - && AllowedSign == asThis.AllowedSign; - } - - public override bool Equals(IValue other) - { - return object.Equals(this, other?.GetRawValue()); - } - - public override int GetHashCode() - { - return Digits.GetHashCode(); - } - - public IValue Adjust(IValue value) - { - if (value == null) - { - // Значение по-умолчанию - return ValueFactory.Create(0); - } - // TODO: обрезать по количеству знаков - - try - { - // TODO: Вменяемое преобразование без Попытки - var numericValue = value.AsNumber(); - - if (AllowedSign == AllowedSignEnum.Nonnegative && numericValue < 0) - { - numericValue = 0; - } - - if (Digits > 0) - { - ValueFormatter.ApplyNumericSizeRestrictions(ref numericValue, Digits, FractionDigits); - } - return ValueFactory.Create(numericValue); - - } catch - { - } - - return ValueFactory.Create(0); - } - - [ScriptConstructor(Name = "На основании описания числа")] - public static NumberQualifiers Constructor(IValue digits = null, - IValue fractionDigits = null, - IValue allowedSign = null) - { - var paramDigits = ContextValuesMarshaller.ConvertParam(digits); - var paramFractionDigits = ContextValuesMarshaller.ConvertParam(fractionDigits); - - if (paramDigits < 0 || paramFractionDigits < 0) - { - throw RuntimeException.InvalidArgumentValue(); - } - - var paramAllowedSign = ContextValuesMarshaller.ConvertParam(allowedSign); - return new NumberQualifiers(paramDigits, paramFractionDigits, paramAllowedSign); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/StringQualifiers.cs b/src/ScriptEngine.HostedScript/Library/TypeDescription/StringQualifiers.cs deleted file mode 100644 index bc46eb754..000000000 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/StringQualifiers.cs +++ /dev/null @@ -1,87 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("КвалификаторыСтроки", "StringQualifiers")] - public sealed class StringQualifiers : AutoContext, IValueAdjuster - { - public StringQualifiers(int length = 0, - AllowedLengthEnum allowedLength = AllowedLengthEnum.Variable) - { - Length = length; - AllowedLength = allowedLength; - } - - [ContextProperty("Длина", "Length")] - public int Length { get; } - - [ContextProperty("ДопустимаяДлина", "AllowedLength")] - public AllowedLengthEnum AllowedLength { get; } - - public override bool Equals(object obj) - { - var asThis = obj as StringQualifiers; - if (asThis == null) - return false; - - return Length == asThis.Length - && AllowedLength == asThis.AllowedLength; - } - - public override bool Equals(IValue other) - { - return object.Equals(this, other?.GetRawValue()); - } - - public override int GetHashCode() - { - return Length.GetHashCode(); - } - - public string DefaultString() - { - if (AllowedLength == AllowedLengthEnum.Variable) - return ""; - - if (Length == 0) - return ""; - - return new string(' ', Length); - } - - public IValue Adjust(IValue value) - { - var stringValue = value?.AsString() ?? ""; - - if (Length != 0 && stringValue.Length > Length) - { - stringValue = stringValue.Substring(0, Length); - } - - if (AllowedLength == AllowedLengthEnum.Fixed && stringValue.Length < Length) - { - var tail = new string(' ', Length - stringValue.Length); - stringValue = string.Format("{0}{1}", stringValue, tail); - } - - return ValueFactory.Create(stringValue); - } - - [ScriptConstructor(Name = "На основании описания строки")] - public static StringQualifiers Constructor(IValue length = null, - IValue allowedLength = null) - { - var paramLength = ContextValuesMarshaller.ConvertParam(length); - var paramAllowedLength = ContextValuesMarshaller.ConvertParam(allowedLength); - return new StringQualifiers(paramLength, paramAllowedLength); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/TypeDescription/TypeDescription.cs b/src/ScriptEngine.HostedScript/Library/TypeDescription/TypeDescription.cs deleted file mode 100644 index c219597d1..000000000 --- a/src/ScriptEngine.HostedScript/Library/TypeDescription/TypeDescription.cs +++ /dev/null @@ -1,282 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System.Collections.Generic; -using ScriptEngine.Machine.Values; - -namespace ScriptEngine.HostedScript.Library -{ - [ContextClass("ОписаниеТипов", "TypeDescription")] - public class TypeDescription : AutoContext - { - private readonly List _types = new List(); - - public TypeDescription(IEnumerable types = null, - NumberQualifiers numberQualifiers = null, - StringQualifiers stringQualifiers = null, - DateQualifiers dateQualifiers = null, - BinaryDataQualifiers binaryDataQualifiers = null) - { - if (types != null) - { - _types.AddRange(types); - } - NumberQualifiers = numberQualifiers ?? new NumberQualifiers(); - StringQualifiers = stringQualifiers ?? new StringQualifiers(); - DateQualifiers = dateQualifiers ?? new DateQualifiers(); - BinaryDataQualifiers = binaryDataQualifiers ?? new BinaryDataQualifiers(); - } - - [ContextProperty("КвалификаторыЧисла", "NumberQualifiers")] - public NumberQualifiers NumberQualifiers { get; } - - [ContextProperty("КвалификаторыСтроки", "StringQualifiers")] - public StringQualifiers StringQualifiers { get; } - - [ContextProperty("КвалификаторыДаты", "DateQualifiers")] - public DateQualifiers DateQualifiers { get; } - - [ContextProperty("КвалификаторыДвоичныхДанных", "BinaryDataQualifiers")] - public BinaryDataQualifiers BinaryDataQualifiers { get; } - - [ContextMethod("Типы", "Types")] - public ArrayImpl Types() - { - var result = ArrayImpl.Constructor() as ArrayImpl; - - foreach (var type in _types) - { - result.Add(type); - } - - return result; - } - - [ContextMethod("СодержитТип", "ContainsType")] - public bool ContainsType(IValue type) - { - if (type is TypeTypeValue) - return _types.IndexOf(type as TypeTypeValue) != -1; - throw RuntimeException.InvalidArgumentType(nameof(type)); - } - - IValueAdjuster GetAdjusterForType(TypeTypeValue type) - { - if (type.Value.Equals(TypeDescriptor.FromDataType(DataType.Number))) - return NumberQualifiers; - - if (type.Value.Equals(TypeDescriptor.FromDataType(DataType.String))) - return StringQualifiers; - - if (type.Value.Equals(TypeDescriptor.FromDataType(DataType.Date))) - return DateQualifiers; - - if (type.Value.Equals(TypeDescriptor.FromDataType(DataType.Boolean))) - return new BooleanTypeAdjuster(); - - return null; - } - - [ContextMethod("ПривестиЗначение", "AdjustValue")] - public IValue AdjustValue(IValue value = null) - { - - if (_types.Count == 0) - { - return value ?? ValueFactory.Create(); - } - - TypeTypeValue typeToCast = null; - - if (value != null && value.DataType != DataType.Undefined) - { - var valueType = new TypeTypeValue(value.SystemType); - if (_types.Contains(valueType)) - { - // Если такой тип у нас есть - typeToCast = valueType; - } - } - - if (typeToCast == null) - { - // Если типа нет, то нужно брать значение по-умолчанию - if (_types.Count != 1) - { - // много типов - Неопределено - return ValueFactory.Create(); - } - - typeToCast = _types[0]; - } - - var adjuster = GetAdjusterForType(typeToCast); - - return adjuster?.Adjust(value) ?? ValueFactory.Create(); - } - - private static IList ConstructTypeList(IValue types) - { - var _types = new List(); - if (types == null) - return _types; - - types = types.GetRawValue(); - if (types.DataType == DataType.String) - { - var typeNames = types.AsString().Split(','); - foreach (var typeName in typeNames) - { - _types.Add(new TypeTypeValue(typeName.Trim())); - } - } else if (types is ArrayImpl) - { - foreach (var type in (types as ArrayImpl)) - { - var rawType = type.GetRawValue() as TypeTypeValue; - if (rawType == null) - return null; - - _types.Add(rawType); - } - } else - { - return null; - } - return _types; - } - - static TypeTypeValue TypeNumber() - { - return new TypeTypeValue(TypeManager.GetTypeById((int)DataType.Number)); - } - - static TypeTypeValue TypeBoolean() - { - return new TypeTypeValue(TypeManager.GetTypeById((int)DataType.Boolean)); - } - - static TypeTypeValue TypeString() - { - return new TypeTypeValue(TypeManager.GetTypeById((int)DataType.String)); - } - - public static TypeDescription StringType(int length = 0, - AllowedLengthEnum allowedLength = AllowedLengthEnum.Variable) - { - var stringQualifier = new StringQualifiers(length, allowedLength); - return new TypeDescription(new TypeTypeValue[] { TypeString() }, null, stringQualifier); - } - - public static TypeDescription IntegerType(int length = 10, - AllowedSignEnum allowedSign = AllowedSignEnum.Any) - { - var numberQualifier = new NumberQualifiers(length, 0, allowedSign); - return new TypeDescription(new TypeTypeValue[] { TypeNumber() }, numberQualifier); - } - - public static TypeDescription BooleanType() - { - return new TypeDescription(new TypeTypeValue[] { TypeBoolean() }); - } - - [ScriptConstructor] - public static TypeDescription Constructor( - IValue source = null, - IValue p1 = null, - IValue p2 = null, - IValue p3 = null, - IValue p4 = null, - IValue p5 = null, - IValue p6 = null) - { - var rawSource = source?.GetRawValue(); - - if (rawSource == null || rawSource.DataType == DataType.Undefined) - { - // первый параметр имеет право быть не задан только в таком конструкторе - return ConstructByOtherDescription(null, p1, p2, p3, p4, p5, p6); - } - - if (rawSource is TypeDescription) - { - return ConstructByOtherDescription(rawSource, p1, p2, p3, p4, p5, p6); - } - - if (rawSource.DataType == DataType.String || rawSource is ArrayImpl) - { - // TODO: проверить, что p5 и p6 не заданы - return ConstructByQualifiers(rawSource, p1, p2, p3, p4); - } - - throw RuntimeException.InvalidArgumentValue(); - } - - public static TypeDescription ConstructByQualifiers( - IValue types, - IValue numberQualifiers = null, - IValue stringQualifiers = null, - IValue dateQualifiers = null, - IValue binaryDataQualifiers = null) - { - var _types = ConstructTypeList(types); - if (_types == null) - throw RuntimeException.InvalidArgumentType(nameof(types)); - - var paramNumberQ = numberQualifiers?.GetRawValue() as NumberQualifiers; - var paramStringQ = stringQualifiers?.GetRawValue() as StringQualifiers; - var paramDateQ = dateQualifiers?.GetRawValue() as DateQualifiers; - var paramBinaryDataQ = binaryDataQualifiers?.GetRawValue() as BinaryDataQualifiers; - - return new TypeDescription(_types, paramNumberQ, paramStringQ, paramDateQ, paramBinaryDataQ); - } - - public static TypeDescription ConstructByOtherDescription( - IValue typeDescription = null, - IValue addTypes = null, - IValue removeTypes = null, - IValue numberQualifiers = null, - IValue stringQualifiers = null, - IValue dateQualifiers = null, - IValue binaryDataQualifiers = null) - { - var td = typeDescription as TypeDescription; - - var removeTypesList = ConstructTypeList(removeTypes); - if (removeTypesList == null) - throw RuntimeException.InvalidArgumentType(nameof(removeTypes)); - - - var _types = new List(); - if (td != null) - { - foreach (var ivType in td.Types()) - { - var type = ivType as TypeTypeValue; - if (removeTypesList.IndexOf(type) == -1) - { - _types.Add(type); - } - } - } - - var addTypesList = ConstructTypeList(addTypes); - if (addTypesList == null) - throw RuntimeException.InvalidArgumentType(nameof(addTypes)); - _types.AddRange(addTypesList); - - var paramNumberQ = numberQualifiers?.AsObject() as NumberQualifiers ?? td?.NumberQualifiers; - var paramStringQ = stringQualifiers?.AsObject() as StringQualifiers ?? td?.StringQualifiers; - var paramDateQ = dateQualifiers?.AsObject() as DateQualifiers ?? td?.DateQualifiers; - var paramBinaryDataQ = binaryDataQualifiers?.AsObject() as BinaryDataQualifiers ?? td?.BinaryDataQualifiers; - - return new TypeDescription(_types, paramNumberQ, paramStringQ, paramDateQ, paramBinaryDataQ); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueList/ValueListItem.cs b/src/ScriptEngine.HostedScript/Library/ValueList/ValueListItem.cs deleted file mode 100644 index 78c7d4e4e..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueList/ValueListItem.cs +++ /dev/null @@ -1,78 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; - -namespace ScriptEngine.HostedScript.Library.ValueList -{ - /// - /// Используется для доступа к свойствам и методам элемента списка значений - /// - [ContextClass("ЭлементСпискаЗначений", "ValueListItem")] - public class ValueListItem : AutoContext - { - private string _presentationHolder; - private IValue _pictureHolder; - - public ValueListItem() - { - _pictureHolder = ValueFactory.Create(); - _presentationHolder = String.Empty; - } - - [ContextProperty("Значение", "Value")] - public IValue Value - { - get; - set; - } - - [ContextProperty("Представление", "Presentation")] - public string Presentation - { - get - { - return _presentationHolder; - } - set - { - if (value == null) - _presentationHolder = String.Empty; - else - _presentationHolder = value; - } - } - - [ContextProperty("Пометка", "Check")] - public bool Check - { - get; - set; - } - - [ContextProperty("Картинка", "Picture")] - public IValue Picture - { - get - { - return _pictureHolder; - } - set - { - if(value != null) - { - _pictureHolder = value; - } - else - { - _pictureHolder = ValueFactory.Create(); - } - } - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTable/CollectionIndexes.cs b/src/ScriptEngine.HostedScript/Library/ValueTable/CollectionIndexes.cs deleted file mode 100644 index 1102c9273..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueTable/CollectionIndexes.cs +++ /dev/null @@ -1,69 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTable -{ - [ContextClass("ИндексыКоллекции", "CollectionIndexes")] - public class CollectionIndexes : AutoContext, ICollectionContext, IEnumerable - { - readonly List _indexes = new List(); - - [ContextMethod("Добавить", "Add")] - public CollectionIndex Add(string columns) - { - CollectionIndex newIndex = new CollectionIndex(); - _indexes.Add(newIndex); - return newIndex; - } - - [ContextMethod("Количество", "Count")] - public int Count() - { - return _indexes.Count(); - } - - [ContextMethod("Удалить", "Delete")] - public void Delete(IValue Index) - { - Index = Index.GetRawValue(); - if (Index is CollectionIndex) - _indexes.Remove(Index as CollectionIndex); - else - _indexes.RemoveAt(Decimal.ToInt32(Index.AsNumber())); - } - - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _indexes.Clear(); - } - - public IEnumerator GetEnumerator() - { - foreach (var item in _indexes) - { - yield return item; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTable.cs b/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTable.cs deleted file mode 100644 index aa70eda31..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTable.cs +++ /dev/null @@ -1,717 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTable -{ - /// - /// Объект для работы с данными в табличном виде. - /// Представляет из себя коллекцию строк с заранее заданной структурой. - /// - [ContextClass("ТаблицаЗначений", "ValueTable")] - public class ValueTable : AutoContext, ICollectionContext, IEnumerable - { - private readonly ValueTableColumnCollection _columns; - private readonly List _rows; - private readonly CollectionIndexes _indexes; - - public ValueTable() - { - _columns = new ValueTableColumnCollection(this); - _rows = new List(); - _indexes = new CollectionIndexes(); - } - - /// - /// Коллекция колонок - /// - /// КоллекцияКолонокТаблицыЗначений - [ContextProperty("Колонки", "Columns")] - public ValueTableColumnCollection Columns - { - get { return _columns; } - } - - /// - /// Коллекция индексов - /// - /// ИндексыКоллекции - [ContextProperty("Индексы", "Indexes")] - public CollectionIndexes Indexes - { - get { return _indexes; } - } - - /// - /// Количество строк в Таблице значений - /// - /// Число - [ContextMethod("Количество", "Count")] - public int Count() - { - return _rows.Count; - } - - /// - /// Добавляет строку в конец Таблицы значений - /// - /// СтрокаТаблицыЗначений - [ContextMethod("Добавить", "Add")] - public ValueTableRow Add() - { - ValueTableRow row = new ValueTableRow(this); - _rows.Add(row); - return row; - } - - /// - /// Вставляет строку в указанную позицию - /// - /// Число - Индекс позиции куда будет произведена вставка - /// СтрокаТаблицыЗначений - [ContextMethod("Вставить", "Insert")] - public ValueTableRow Insert(int index) - { - ValueTableRow row = new ValueTableRow(this); - _rows.Insert(index, row); - return row; - } - - /// - /// Удаляет строку - /// - /// - /// СтрокаТаблицыЗначений - Удаляемая строка - /// Число - Индекс удаляемой строки - /// - [ContextMethod("Удалить", "Delete")] - public void Delete(IValue row) - { - int index = IndexByValue(row); - - _rows.RemoveAt(index); - } - - /// - /// Загружает значения в колонку - /// - /// Массив - Значения для загрузки в колонку - /// - /// Строка - Имя колонки для загрузки - /// Число - Индекс колонки для загрузки - /// КолонкаТаблицыЗначений - Колонка для загрузки - /// - [ContextMethod("ЗагрузитьКолонку", "LoadColumn")] - public void LoadColumn(IValue values, IValue columnIndex) - { - // ValueTableColumn Column = Columns.GetColumnByIIndex(ColumnIndex); - var row_iterator = _rows.GetEnumerator(); - var array_iterator = (values as ArrayImpl).GetEnumerator(); - - while (row_iterator.MoveNext() && array_iterator.MoveNext()) - { - row_iterator.Current.Set(columnIndex, array_iterator.Current); - } - } - - /// - /// Выгружает значения колонки в новый массив - /// - /// - /// Строка - Имя колонки для выгрузки - /// Число - Индекс колонки для выгрузки - /// КолонкаТаблицыЗначений - Колонка для выгрузки - /// - /// Массив - [ContextMethod("ВыгрузитьКолонку", "UnloadColumn")] - public ArrayImpl UnloadColumn(IValue column) - { - ArrayImpl result = new ArrayImpl(); - - foreach (ValueTableRow row in _rows) - { - result.Add(row.Get(column)); - } - - return result; - } - - private List GetProcessingColumnList(string ColumnNames, bool EmptyListInCaseOfNull = false) - { - List processing_list = new List(); - if (ColumnNames != null) - { - if (ColumnNames.Trim().Length == 0) - { - // Передали пустую строку вместо списка колонок - return processing_list; - } - - foreach (string column_name in ColumnNames.Split(',')) - { - string name = column_name.Trim(); - ValueTableColumn Column = Columns.FindColumnByName(name); - - if (Column == null) - throw WrongColumnNameException(name); - - if (processing_list.Find( x=> x.Name==name ) == null) - processing_list.Add(Column); - } - } - else if (!EmptyListInCaseOfNull) - { - foreach (ValueTableColumn Column in _columns) - processing_list.Add(Column); - } - return processing_list; - } - - /// - /// Заполнить колонку/колонки указанным значением - /// - /// Произвольный - Устанавливаемое значение - /// Строка - Список имен колонок для установки значения (разделены запятыми) - [ContextMethod("ЗаполнитьЗначения", "FillValues")] - public void FillValues(IValue value, string columnNames = null) - { - List processing_list = GetProcessingColumnList(columnNames); - foreach (ValueTableRow row in _rows) - { - foreach (ValueTableColumn col in processing_list) - { - row.Set(col, value); - } - } - } - - /// - /// Получить индекс указанной строки - /// - /// СтрокаТаблицыЗначений - Строка таблицы значений, для которой необходимо определить индекс - /// Число - Индекс в коллекции, если не найдено возвращает -1 - [ContextMethod("Индекс", "IndexOf")] - public int IndexOf(IValue row) - { - row = row.GetRawValue(); - - if (row is ValueTableRow) - return _rows.IndexOf(row as ValueTableRow); - - return -1; - } - - /// - /// Сумма значений всех строк указанной колонки - /// - /// - /// Строка - Имя колонки для суммирования - /// Число - Индекс колонки для суммирования - /// КолонкаТаблицыЗначений - Колонка для суммирования - /// - /// Число - [ContextMethod("Итог", "Total")] - public IValue Total(IValue columnIndex) - { - ValueTableColumn Column = Columns.GetColumnByIIndex(columnIndex); - bool has_data = false; - decimal Result = 0; - - foreach (ValueTableRow row in _rows) - { - IValue current_value = row.Get(Column); - if (current_value.DataType == Machine.DataType.Number) - { - has_data = true; - Result += current_value.AsNumber(); - } - } - - if (has_data) - return ValueFactory.Create(Result); - - return ValueFactory.Create(); - } - - /// - /// Осуществляет поиск значения в указанных колонках - /// - /// Произвольный - Искомое значение - /// Строка - Список имен колонок для поиска значения (разделены запятыми). - /// Если параметр не указан - ищет по всем колонкам. По умолчанию: пустая строка - /// СтрокаТаблицыЗначений - если строка найдена, иначе Неопределено - [ContextMethod("Найти", "Find")] - public IValue Find(IValue value, string columnNames = null) - { - List processing_list = GetProcessingColumnList(columnNames); - foreach (ValueTableRow row in _rows) - { - foreach (ValueTableColumn col in processing_list) - { - IValue current = row.Get(col); - if (value.Equals(current)) - return row; - } - } - return ValueFactory.Create(); - } - - private bool CheckFilterCriteria(ValueTableRow Row, StructureImpl Filter) - { - foreach (KeyAndValueImpl kv in Filter) - { - ValueTableColumn Column = Columns.FindColumnByName(kv.Key.AsString()); - if (Column == null) - throw WrongColumnNameException(kv.Key.AsString()); - - IValue current = Row.Get(Column); - if (!current.Equals(kv.Value)) - return false; - } - return true; - } - - /// - /// Поиск строк по условию - /// - /// Структура - Условия поиска. Ключ - имя колонки, значение - искомое значение - /// Массив - Массив ссылок на строки, удовлетворяющих условию поиска - [ContextMethod("НайтиСтроки", "FindRows")] - public ArrayImpl FindRows(IValue filter) - { - var filterStruct = filter.GetRawValue() as StructureImpl; - - if (filterStruct == null) - throw RuntimeException.InvalidArgumentType(); - - ArrayImpl Result = new ArrayImpl(); - - foreach (ValueTableRow row in _rows) - { - if (CheckFilterCriteria(row, filterStruct)) - Result.Add(row); - } - - return Result; - } - - /// - /// Удаляет все строки. Структура колонок не меняется. - /// - [ContextMethod("Очистить", "Clear")] - public void Clear() - { - _rows.Clear(); - } - - /// - /// Получить строку по индексу - /// - /// Число - Индекс строки - /// СтрокаТаблицыЗначений - [ContextMethod("Получить", "Get")] - public ValueTableRow Get(int index) - { - if (index < 0 || index >= Count()) - throw RuntimeException.InvalidArgumentValue(); - return _rows[index]; - } - - /// - /// Сворачиваются (группируются) строки по указанным колонкам измерениям, суммируются колонки ресурсов. - /// Колонки не указанные ни в измерениях ни в ресурсах удаляются. - /// - /// Строка - Имена колонок для сворачивания (изменения), разделены запятыми - /// Строка - Имена колонок для суммирования (ресурсы), разделены запятыми - [ContextMethod("Свернуть", "GroupBy")] - public void GroupBy(string groupColumnNames, string aggregateColumnNames = null) - { - List GroupColumns = GetProcessingColumnList(groupColumnNames, true); - List AggregateColumns = GetProcessingColumnList(aggregateColumnNames, true); - - foreach (ValueTableColumn group_column in GroupColumns ) - if ( AggregateColumns.Find(x => x.Name==group_column.Name)!=null ) - throw ColumnsMixedException(group_column.Name); - - var uniqueRows = new Dictionary(new RowsByColumnsEqComparer(GroupColumns) ); - int new_idx = 0; - - foreach (ValueTableRow row in _rows) - { - if (uniqueRows.ContainsKey(row)) - { - ValueTableRow old_row = uniqueRows[row]; - - foreach (var Column in AggregateColumns) - { - IValue current = row.Get(Column); - if (current.DataType == DataType.Number) - { - decimal sum = old_row.Get(Column).AsNumber() + current.AsNumber(); - old_row.Set(Column, ValueFactory.Create(sum)); - } - } - } - else - { - ValueTableRow new_row = _rows[new_idx++]; - - foreach (var Column in GroupColumns) - new_row.Set(Column, row.Get(Column)); - - foreach (var Column in AggregateColumns) - if (row.Get(Column).DataType != DataType.Number) - new_row.Set(Column, ValueFactory.Create(0)); - else - new_row.Set(Column, row.Get(Column)); - - uniqueRows.Add(new_row, new_row); - } - } - - _rows.RemoveRange(new_idx, _rows.Count()-new_idx); - - int i = 0; - while (i < _columns.Count()) - { - ValueTableColumn Column = _columns.FindColumnByIndex(i); - if (GroupColumns.IndexOf(Column) == -1 && AggregateColumns.IndexOf(Column) == -1) - _columns.Delete(Column); - else - ++i; - } - } - - private class RowsByColumnsEqComparer : IEqualityComparer - { - private List _columns; - - public RowsByColumnsEqComparer(List columns) - { - _columns = columns; - } - - public bool Equals(ValueTableRow row1, ValueTableRow row2) - { - foreach (var column in _columns) - { - if (!row1.Get(column).Equals(row2.Get(column))) - return false; - } - return true; - } - - public int GetHashCode(ValueTableRow row) - { - int hash = 0; - foreach (var column in _columns) - hash ^= row.Get(column).AsString().GetHashCode(); - return hash; - } - } - - private int IndexByValue(IValue item) - { - item = item.GetRawValue(); - - int index; - - if (item is ValueTableRow) - { - index = IndexOf(item as ValueTableRow); - if (index == -1) - throw new RuntimeException("Строка не принадлежит таблице значений"); - } - else - { - try - { - index = decimal.ToInt32(item.AsNumber()); - } - catch (RuntimeException) - { - throw RuntimeException.InvalidArgumentType(); - } - - if (index < 0 || index >= _rows.Count()) - throw new RuntimeException("Значение индекса выходит за пределы диапазона"); - } - - return index; - } - - /// - /// Сдвигает строку на указанное количество позиций. - /// - /// - /// СтрокаТаблицыЗначений - Строка которую сдвигаем - /// Число - Индекс сдвигаемой строки - /// - /// Количество строк, на которое сдвигается строка. Если значение положительное - сдвиг вниз, иначе вверх - [ContextMethod("Сдвинуть", "Move")] - public void Move(IValue row, int offset) - { - int index_source = IndexByValue(row); - - int index_dest = index_source + offset; - - if (index_dest < 0 || index_dest >= _rows.Count()) - throw RuntimeException.InvalidNthArgumentValue(2); - - ValueTableRow tmp = _rows[index_source]; - - if (index_source < index_dest) - { - _rows.Insert(index_dest + 1, tmp); - _rows.RemoveAt(index_source); - } - else - { - _rows.RemoveAt(index_source); - _rows.Insert(index_dest, tmp); - } - } - - /// - /// Создает новую таблицу значений с указанными колонками. Данные не копируются. - /// - /// Строка - Имена колонок для копирования, разделены запятыми - /// ТаблицаЗначений - [ContextMethod("СкопироватьКолонки", "CopyColumns")] - public ValueTable CopyColumns(string columnNames = null) - { - ValueTable Result = new ValueTable(); - - List columns = GetProcessingColumnList(columnNames); - - foreach (ValueTableColumn Column in columns) - { - Result.Columns.Add(Column.Name, Column.ValueType, Column.Title, Column.Width); - } - - return Result; - } - - /// - /// Создает новую таблицу значений с указанными строками и колонками. Если передан отбор - копирует строки удовлетворяющие отбору. - /// Если не указаны строки - будут скопированы все строки. Если не указаны колонки - будут скопированы все колонки. - /// Если не указаны оба параметра - будет создана полная копия таблицы значений. - /// - /// - /// Массив - Массив строк для отбора - /// Структура - Параметры отбора. Ключ - Колонка, Значение - Значение отбора - /// - /// Строка - Имена колонок для копирования, разделены запятыми - /// ТаблицаЗначений - [ContextMethod("Скопировать", "Copy")] - public ValueTable Copy(IValue rows = null, string columnNames = null) - { - ValueTable Result = CopyColumns(columnNames); - List columns = GetProcessingColumnList(columnNames); - - IEnumerable requestedRows; - if (rows == null) - { - requestedRows = _rows; - } - else - { - if (rows.SystemType.Equals(TypeManager.GetTypeByFrameworkType(typeof(StructureImpl)))) - requestedRows = FindRows(rows).Select(x => x as ValueTableRow); - else if (rows.SystemType.Equals(TypeManager.GetTypeByFrameworkType(typeof(ArrayImpl)))) - requestedRows = GetRowsEnumByArray(rows); - else - throw RuntimeException.InvalidArgumentType(); - } - - var columnMap = new Dictionary(); - foreach (var column in columns) - { - var destinationColumn = Result.Columns.FindColumnByName(column.Name); - columnMap.Add(column, destinationColumn); - } - - foreach (var row in requestedRows) - { - ValueTableRow new_row = Result.Add(); - foreach (ValueTableColumn Column in columns) - { - new_row.Set(columnMap[Column], row.Get(Column)); - } - } - - return Result; - } - - private IEnumerable GetRowsEnumByArray(IValue Rows) - { - IEnumerable requestedRows; - var rowsArray = Rows.GetRawValue() as ArrayImpl; - if (rowsArray == null) - throw RuntimeException.InvalidArgumentType(); - - requestedRows = rowsArray.Select(x => - { - var vtr = x.GetRawValue() as ValueTableRow; - if (vtr == null || vtr.Owner() != this) - throw RuntimeException.InvalidArgumentValue(); - - return vtr; - }); - return requestedRows; - } - - private struct ValueTableSortRule - { - public ValueTableColumn Column; - public int direction; // 1 = asc, -1 = desc - } - - private List GetSortRules(string Columns) - { - - string[] a_columns = Columns.Split(','); - - List Rules = new List(); - - foreach (string column in a_columns) - { - string[] description = column.Trim().Split(' '); - if (description.Count() == 0) - throw WrongColumnNameException(); - - ValueTableSortRule Desc = new ValueTableSortRule(); - Desc.Column = this.Columns.FindColumnByName(description[0]); - - if (description.Count() > 1) - { - if (String.Compare(description[1], "DESC", true) == 0 || String.Compare(description[1], "УБЫВ", true) == 0) - Desc.direction = -1; - else - Desc.direction = 1; - } - else - Desc.direction = 1; - - Rules.Add(Desc); - } - - return Rules; - } - - private class RowComparator : IComparer - { - readonly List Rules; - - readonly GenericIValueComparer _comparer = new GenericIValueComparer(); - - public RowComparator(List Rules) - { - if (Rules.Count() == 0) - throw RuntimeException.InvalidArgumentValue(); - - this.Rules = Rules; - } - - private int OneCompare(ValueTableRow x, ValueTableRow y, ValueTableSortRule Rule) - { - IValue xValue = x.Get(Rule.Column); - IValue yValue = y.Get(Rule.Column); - - int result = _comparer.Compare(xValue, yValue) * Rule.direction; - - return result; - } - - public int Compare(ValueTableRow x, ValueTableRow y) - { - int i = 0, r; - while ((r = OneCompare(x, y, Rules[i])) == 0) - { - if (++i >= Rules.Count()) - return 0; - } - - return r; - } - } - - /// - /// Сортировать строки в таблице значений. Строки сортируются по порядку следования колонок для сортировки, с учетом варианта сортировки. - /// - /// Строка - Имена колонок для сортировки. - /// После имени колонки, через пробел, можно указать направление сортировки: "Убыв" ("Desc") - по убыванию. Возр" ("Asc") - по возрастанию - /// По умолчанию - по возрастанию. - /// - /// СравнениеЗначений - правила сравнения значений при наличии различных типов данных в колонке. - [ContextMethod("Сортировать", "Sort")] - public void Sort(string columns, IValue comparator = null) - { - _rows.Sort(new RowComparator(GetSortRules(columns))); - } - - /// - /// Не поддерживается - /// - /// - /// - [ContextMethod("ВыбратьСтроку", "ChooseRow")] - public void ChooseRow(string title = null, IValue startRow = null) - { - throw new NotSupportedException(); - } - - public IEnumerator GetEnumerator() - { - foreach (var item in _rows) - { - yield return item; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public override IValue GetIndexedValue(IValue index) - { - return Get((int)index.AsNumber()); - } - - [ScriptConstructor] - public static ValueTable Constructor() - { - return new ValueTable(); - } - - - private static RuntimeException WrongColumnNameException() - { - return new RuntimeException("Неверное имя колонки"); - } - - private static RuntimeException WrongColumnNameException(string columnName) - { - return new RuntimeException(string.Format("Неверное имя колонки '{0}'", columnName)); - } - - private static RuntimeException ColumnsMixedException(string columnName) - { - return new RuntimeException(string.Format("Колонка '{0}' не может одновременно быть колонкой группировки и колонкой суммирования", columnName)); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableColumn.cs b/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableColumn.cs deleted file mode 100644 index d7ef97b88..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableColumn.cs +++ /dev/null @@ -1,89 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - - -namespace ScriptEngine.HostedScript.Library.ValueTable -{ - /// - /// Колонка таблицы значений. - /// - [ContextClass("КолонкаТаблицыЗначений", "ValueTableColumn")] - public class ValueTableColumn : AutoContext - { - private string _title; - private string _name; - private TypeDescription _valueType; - private int _width; - private readonly WeakReference _owner; - - public ValueTableColumn(ValueTableColumnCollection Owner, string Name, string Title, TypeDescription Type, int Width) - { - _name = Name; - _title = Title; - _valueType = Type ?? new TypeDescription(); - _width = Width; - - _owner = new WeakReference(Owner); - } - - /// - /// Заголовок колонки - /// - /// Строка - [ContextProperty("Заголовок", "Title")] - public string Title - { - get { return _title ?? _name; } - set { _title = value; } - } - - /// - /// Имя колонки - /// - /// Строка - [ContextProperty("Имя", "Name")] - public string Name - { - get { return _name; } - set - { - ValueTableColumnCollection Owner = _owner.Target as ValueTableColumnCollection; - if (Owner.FindColumnByName(value) != null) - throw new RuntimeException("Неверное имя колонки!"); - - if (_title == _name) - _title = value; - - _name = value; - - } - } - /// - /// Тип значения колонки - /// - /// ОписаниеТипа - [ContextProperty("ТипЗначения", "ValueType")] - public TypeDescription ValueType - { - get { return _valueType; } - } - - /// - /// Ширина колонки - /// - /// Число - [ContextProperty("Ширина", "Width")] - public int Width - { - get { return _width; } - set { _width = value; } - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableColumnCollection.cs b/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableColumnCollection.cs deleted file mode 100644 index 34e360912..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableColumnCollection.cs +++ /dev/null @@ -1,279 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTable -{ - /// - /// Коллекция колонок таблицы значений - /// - [ContextClass("КоллекцияКолонокТаблицыЗначений", "ValueTableColumnCollection")] - public class ValueTableColumnCollection : DynamicPropertiesAccessor, ICollectionContext, IEnumerable, IDebugPresentationAcceptor - { - private readonly List _columns = new List(); - - private StringComparer NamesComparer = StringComparer.OrdinalIgnoreCase; - - private readonly ValueTable _owner; - - public ValueTableColumnCollection(ValueTable owner) - { - _owner = owner; - } - - /// - /// Добавляет колонку в таблицу значений - /// - /// Строка - Имя колонки - /// ОписаниеТипов - Тип данных колонки - /// Строка - Заголовок колонки - /// Число - Ширина колонки - /// КолонкаТаблицыЗначений - [ContextMethod("Добавить", "Add")] - public ValueTableColumn Add(string name, TypeDescription type = null, string title = null, int width = 0) - { - if (FindColumnByName(name) != null) - throw new RuntimeException("Неверное имя колонки " + name); - - var column = new ValueTableColumn(this, name, title, type, width); - _columns.Add(column); - - return column; - } - - /// - /// Вставить колонку в указанную позицию - /// - /// Число - Индекс расположения колонки - /// Строка - Имя колонки - /// ОписаниеТипов - Тип данных колонки - /// Строка - Заголовок колонки - /// Число - Ширина колонки - /// КолонкаТаблицыЗначений - [ContextMethod("Вставить", "Insert")] - public ValueTableColumn Insert(int index, string name, TypeDescription type = null, string title = null, int width = 0) - { - if (FindColumnByName(name) != null) - throw new RuntimeException("Неверное имя колонки " + name); - - ValueTableColumn column = new ValueTableColumn(this, name, title, type, width); - _columns.Insert(index, column); - - return column; - } - - /// - /// Индекс указанной колонки - /// - /// КолонкаТаблицыЗначений - Колонка, для которой определяется индекс - /// Число - [ContextMethod("Индекс", "IndexOf")] - public int IndexOf(ValueTableColumn column) - { - return _columns.IndexOf(column); - } - - /// - /// Количество колонок в таблице значений - /// - /// Число - [ContextMethod("Количество", "Count")] - public int Count() - { - return _columns.Count; - } - - /// - /// Поиск колонки по имени - /// - /// Строка - Имя колонки - /// КолонкаТаблицыЗначений - Найденная колонка таблицы значений, иначе Неопределено. - [ContextMethod("Найти", "Find")] - public IValue Find(string name) - { - ValueTableColumn Column = FindColumnByName(name); - if (Column == null) - return ValueFactory.Create(); - return Column; - } - - /// - /// Удалить колонку значений - /// - /// - /// Строка - Имя колонки для удаления - /// Число - Индекс колонки для удаления - /// КолонкаТаблицыЗначений - Колонка для удаления - /// - [ContextMethod("Удалить", "Delete")] - public void Delete(IValue column) - { - column = column.GetRawValue(); - var vtColumn = GetColumnByIIndex(column); - _owner.ForEach(x=>x.OnOwnerColumnRemoval(vtColumn)); - _columns.Remove(vtColumn); - } - - public ValueTableColumn FindColumnByName(string name) - { - return _columns.Find(column => NamesComparer.Equals(name, column.Name)); - } - - public ValueTableColumn FindColumnByIndex(int index) - { - return _columns[index]; - } - - public IEnumerator GetEnumerator() - { - foreach (var item in _columns) - { - yield return item; - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public override int FindProperty(string name) - { - int idx = _columns.FindIndex(column => NamesComparer.Equals(name, column.Name)); - if (idx == -1) - throw RuntimeException.PropNotFoundException(name); - return idx; - } - - public override int GetPropCount() - { - return _columns.Count; - } - - public override string GetPropName(int propNum) - { - return FindColumnByIndex(propNum).Name; - } - - public override IValue GetPropValue(int propNum) - { - return FindColumnByIndex(propNum); - } - - public override bool IsPropWritable(int propNum) - { - return false; - } - - public ValueTableColumn GetColumnByIIndex(IValue index) - { - if (index.DataType == DataType.String) - { - ValueTableColumn Column = FindColumnByName(index.AsString()); - if (Column == null) - throw RuntimeException.PropNotFoundException(index.AsString()); - return Column; - } - - if (index.DataType == DataType.Number) - { - int i_index = Decimal.ToInt32(index.AsNumber()); - if (i_index < 0 || i_index >= Count()) - throw RuntimeException.InvalidArgumentValue(); - - ValueTableColumn Column = FindColumnByIndex(i_index); - return Column; - } - - if (index is ValueTableColumn) - { - return index as ValueTableColumn; - } - - throw RuntimeException.InvalidArgumentType(); - } - - public int GetColumnNumericIndex(IValue index) - { - if (index.DataType == DataType.String) - { - return FindProperty(index.AsString()); - } - - if (index.DataType == DataType.Number) - { - int iIndex = Decimal.ToInt32(index.AsNumber()); - if (iIndex < 0 || iIndex >= Count()) - throw RuntimeException.InvalidArgumentValue(); - - return iIndex; - } - - var column = index.GetRawValue() as ValueTableColumn; - if (column != null) - { - return IndexOf(column); - } - - throw RuntimeException.InvalidArgumentType(); - } - - public override IValue GetIndexedValue(IValue index) - { - return GetColumnByIIndex(index); - } - - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - - public override MethodInfo GetMethodInfo(int methodNumber) - { - return _methods.GetMethodInfo(methodNumber); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - var binding = _methods.GetMethod(methodNumber); - try - { - binding(this, arguments); - } catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - var binding = _methods.GetMethod(methodNumber); - try - { - retValue = binding(this, arguments); - } catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override int FindMethod(string name) - { - return _methods.FindMethod(name); - } - - void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) - { - visitor.ShowProperties(this); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableIndex.cs b/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableIndex.cs deleted file mode 100644 index 99da495f4..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableIndex.cs +++ /dev/null @@ -1,16 +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 ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.ValueTable -{ - [ContextClass("ИндексКоллекции", "CollectionIndex")] - public class CollectionIndex : AutoContext - { - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableRow.cs b/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableRow.cs deleted file mode 100644 index 652fa3eaf..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueTable/ValueTableRow.cs +++ /dev/null @@ -1,218 +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.Collections.Generic; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTable -{ - [ContextClass("СтрокаТаблицыЗначений", "ValueTableRow")] - public class ValueTableRow : PropertyNameIndexAccessor, ICollectionContext, IEnumerable, IDebugPresentationAcceptor - { - private readonly Dictionary _data = new Dictionary(); - private readonly ValueTable _owner; - - public ValueTableRow(ValueTable owner) - { - _owner = owner; - } - - public int Count() - { - return Owner().Columns.Count(); - } - - /// - /// Владелец строки - /// - /// ТаблицаЗначений - [ContextMethod("Владелец", "Owner")] - public ValueTable Owner() - { - return _owner; - } - - private IValue TryValue(ValueTableColumn Column) - { - IValue Value; - if (_data.TryGetValue(Column, out Value)) - { - return Value; - } - return Column.ValueType.AdjustValue(); - } - - /// - /// Получает значение по индексу - /// - /// Число - Индекс колонки - /// Произвольный - Значение колонки - [ContextMethod("Получить", "Get")] - public IValue Get(int index) - { - var C = Owner().Columns.FindColumnByIndex(index); - return TryValue(C); - } - - public IValue Get(IValue index) - { - var C = Owner().Columns.GetColumnByIIndex(index); - return TryValue(C); - } - - public IValue Get(ValueTableColumn c) - { - return TryValue(c); - } - - /// - /// Установить значение - /// - /// Число - Индекс колонки - /// Произвольный - значение для установки - [ContextMethod("Установить", "Set")] - public void Set(int index, IValue value) - { - var C = Owner().Columns.FindColumnByIndex(index); - _data[C] = C.ValueType.AdjustValue(value); - } - - public void Set(IValue index, IValue value) - { - var C = Owner().Columns.GetColumnByIIndex(index); - _data[C] = C.ValueType.AdjustValue(value); - } - - public void Set(ValueTableColumn column, IValue value) - { - _data[column] = column.ValueType.AdjustValue(value); - } - - public void OnOwnerColumnRemoval(IValue column) - { - _data.Remove(column); - } - - public IEnumerator GetEnumerator() - { - foreach (ValueTableColumn item in Owner().Columns) - { - yield return TryValue(item); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public override int GetPropCount() - { - return Count(); - } - - public override string GetPropName(int propNum) - { - return Owner().Columns.GetPropName(propNum); - } - - public override int FindProperty(string name) - { - return Owner().Columns.FindProperty(name); - } - - public override bool IsPropReadable(int propNum) - { - return true; - } - - public override bool IsPropWritable(int propNum) - { - return true; - } - - public override IValue GetPropValue(int propNum) - { - ValueTableColumn C = Owner().Columns.FindColumnByIndex(propNum); - return TryValue(C); - } - - public override void SetPropValue(int propNum, IValue newVal) - { - ValueTableColumn C = Owner().Columns.FindColumnByIndex(propNum); - _data[C] = C.ValueType.AdjustValue(newVal); - } - - private ValueTableColumn GetColumnByIIndex(IValue index) - { - return Owner().Columns.GetColumnByIIndex(index); - } - - public override IValue GetIndexedValue(IValue index) - { - ValueTableColumn C = GetColumnByIIndex(index); - return TryValue(C); - } - - public override void SetIndexedValue(IValue index, IValue val) - { - var C = GetColumnByIIndex(index); - _data[C] = C.ValueType.AdjustValue(val); - } - - - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - - public override MethodInfo GetMethodInfo(int methodNumber) - { - return _methods.GetMethodInfo(methodNumber); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - var binding = _methods.GetMethod(methodNumber); - try - { - binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - var binding = _methods.GetMethod(methodNumber); - try - { - retValue = binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override int FindMethod(string name) - { - return _methods.FindMethod(name); - } - - void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) - { - visitor.ShowProperties(this); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeRow.cs b/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeRow.cs deleted file mode 100644 index e9acd4080..000000000 --- a/src/ScriptEngine.HostedScript/Library/ValueTree/ValueTreeRow.cs +++ /dev/null @@ -1,288 +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.Collections.Generic; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine; - -namespace ScriptEngine.HostedScript.Library.ValueTree -{ - /// - /// Строка дерева значений. - /// - [ContextClass("СтрокаДереваЗначений", "ValueTreeRow")] - public class ValueTreeRow : PropertyNameIndexAccessor, ICollectionContext, IEnumerable, IDebugPresentationAcceptor - { - private readonly Dictionary _data = new Dictionary(); - private readonly ValueTreeRow _parent; - private readonly ValueTree _owner; - private readonly int _level; - private readonly ValueTreeRowCollection _rows; - - public ValueTreeRow(ValueTree owner, ValueTreeRow parent, int level) - { - _owner = owner; - _parent = parent; - _level = level; - _rows = new ValueTreeRowCollection(owner, this, level + 1); - } - - public int Count() - { - return _properties.Count + _owner.Columns.Count(); - } - - [ContextProperty("Родитель", "Parent")] - public IValue Parent - { - get - { - if (_parent != null) - return _parent; - return ValueFactory.Create(); - } - } - - [ContextProperty("Строки", "Rows")] - public ValueTreeRowCollection Rows - { - get { return _rows; } - } - - /// - /// Возвращает дерево значений, в которе входит строка. - /// - /// ДеревоЗначений. Владелец строки. - [ContextMethod("Владелец", "Owner")] - public ValueTree Owner() - { - return _owner; - } - - private IValue TryValue(ValueTreeColumn column) - { - IValue value; - if (_data.TryGetValue(column, out value)) - { - return value; - } - return column.ValueType.AdjustValue(); - } - - /// - /// Получает значение по индексу. - /// - /// Число. Индекс получаемого параметра. - /// Произвольный. Получаемое значение. - [ContextMethod("Получить", "Get")] - public IValue Get(int index) - { - var column = Owner().Columns.FindColumnByIndex(index); - return TryValue(column); - } - - public IValue Get(IValue index) - { - var column = Owner().Columns.GetColumnByIIndex(index); - return TryValue(column); - } - - public IValue Get(ValueTreeColumn column) - { - return TryValue(column); - } - - /// - /// Устанавливает значение по индексу. - /// - /// Число. Индекс параметра, которому задаётся значение. - /// Произвольный. Новое значение. - [ContextMethod("Установить", "Set")] - public void Set(int index, IValue value) - { - var column = Owner().Columns.FindColumnByIndex(index); - _data[column] = column.ValueType.AdjustValue(value); - } - - public void Set(IValue index, IValue value) - { - var column = Owner().Columns.GetColumnByIIndex(index); - _data[column] = column.ValueType.AdjustValue(value); - } - - public void Set(ValueTreeColumn column, IValue value) - { - _data[column] = column.ValueType.AdjustValue(value); - } - - /// - /// Возвращает уровень вложенности строки в дереве. - /// Строки верхнего уровня имеют значение 0. - /// - /// Число. Уровень вложенности строки. - [ContextMethod("Уровень", "Level")] - public int Level() - { - return _level; - } - - public IEnumerator GetEnumerator() - { - foreach (ValueTreeColumn item in Owner().Columns) - { - yield return TryValue(item); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - private static readonly ContextPropertyMapper _properties = new ContextPropertyMapper(); - - public override int GetPropCount() - { - return Count(); - } - - public override string GetPropName(int propNum) - { - if (IsOwnProp(propNum)) - return _properties.GetProperty(propNum).Name; - return Owner().Columns.GetPropName(GetColumnIndex(propNum)); - } - - public override int FindProperty(string name) - { - if(_properties.ContainsProperty(name)) - return _properties.FindProperty(name); - - var cols = Owner().Columns; - var column = cols.FindColumnByName(name); - - if(column == null) - throw RuntimeException.PropNotFoundException(name); - - return GetColumnPropIndex(cols.IndexOf(column)); - } - - public override bool IsPropReadable(int propNum) - { - return true; - } - - public override bool IsPropWritable(int propNum) - { - return !IsOwnProp(propNum); - } - - public override IValue GetPropValue(int propNum) - { - if (IsOwnProp(propNum)) - { - var property = _properties.GetProperty(propNum); - return property.Getter(this); - } - var column = Owner().Columns.FindColumnByIndex(GetColumnIndex(propNum)); - return TryValue(column); - } - - public override void SetPropValue(int propNum, IValue newVal) - { - if (IsOwnProp(propNum)) - { - var property = _properties.GetProperty(propNum); - property.Setter(this, newVal); - return; - } - var column = Owner().Columns.FindColumnByIndex(GetColumnIndex(propNum)); - _data[column] = column.ValueType.AdjustValue(newVal); - } - - private ValueTreeColumn GetColumnByIIndex(IValue index) - { - return Owner().Columns.GetColumnByIIndex(index); - } - - public override IValue GetIndexedValue(IValue index) - { - var column = GetColumnByIIndex(index); - return TryValue(column); - } - - public override void SetIndexedValue(IValue index, IValue val) - { - var column = GetColumnByIIndex(index); - _data[GetColumnByIIndex(index)] = column.ValueType.AdjustValue(val); - } - - private static bool IsOwnProp(int propNum) - { - return _properties.Count - 1 >= propNum; - } - - private static int GetColumnPropIndex(int index) - { - return _properties.Count + index; - } - - private static int GetColumnIndex(int propIndex) - { - return propIndex - _properties.Count; - } - - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - - public override MethodInfo GetMethodInfo(int methodNumber) - { - return _methods.GetMethodInfo(methodNumber); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - var binding = _methods.GetMethod(methodNumber); - try - { - binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - var binding = _methods.GetMethod(methodNumber); - try - { - retValue = binding(this, arguments); - } - catch (System.Reflection.TargetInvocationException e) - { - throw e.InnerException; - } - } - - public override int FindMethod(string name) - { - return _methods.FindMethod(name); - } - - void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) - { - visitor.ShowProperties(this); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XDTO/XDTOSerializer.cs b/src/ScriptEngine.HostedScript/Library/XDTO/XDTOSerializer.cs deleted file mode 100644 index 766346da3..000000000 --- a/src/ScriptEngine.HostedScript/Library/XDTO/XDTOSerializer.cs +++ /dev/null @@ -1,304 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Xml; -using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine.Values; - -namespace ScriptEngine.HostedScript.Library.XDTO -{ - [ContextClass("СериализаторXDTO", "XDTOSerializer")] - public class XDTOSerializer : AutoContext - { - private static readonly XmlGlobalFunctions xmlGlobalFunctions = GlobalsManager.GetGlobalContext(); - private static readonly XmlNodeTypeEnum xmlNodeEnum = GlobalsManager.GetEnum(); - - - private XDTOSerializer() { } - - private void WriteXMLSimpleData(XmlWriterImpl xmlWriter, - string name, - IValue value, - XMLExpandedName type, - XMLTypeAssignment typeAssigment, - XMLForm form) - { - XmlNamespaceContext namespaceContext; - string xmlValue = XMLString(value); - switch (form) - { - case XMLForm.Attribute: - namespaceContext = xmlWriter.NamespaceContext; - AddNamespaceMapping(namespaceContext, xmlWriter, "", XmlSchema.Namespace); - - xmlWriter.WriteStartAttribute(name); - xmlWriter.WriteText(xmlValue); - xmlWriter.WriteEndAttribute(); - break; - - case XMLForm.Text: - xmlWriter.WriteText(xmlValue); - break; - - default: - - xmlWriter.WriteStartElement(name); - - namespaceContext = xmlWriter.NamespaceContext; - AddNamespaceMapping(namespaceContext, xmlWriter, "", XmlSchema.Namespace); - AddNamespaceMapping(namespaceContext, xmlWriter, "xsi", XmlSchema.InstanceNamespace); - - if (typeAssigment == XMLTypeAssignment.Explicit) - { - xmlWriter.WriteStartAttribute("type", XmlSchema.InstanceNamespace); - xmlWriter.WriteText(type.LocalName); - xmlWriter.WriteEndAttribute(); - } - - xmlWriter.WriteText(xmlValue); - - xmlWriter.WriteEndElement(); - break; - } - } - - private void WriteXMLUndefined(XmlWriterImpl xmlWriter, string name, XMLForm form) - { - if (form == XMLForm.Element) - { - XmlNamespaceContext namespaceContext = xmlWriter.NamespaceContext; - AddNamespaceMapping(namespaceContext, xmlWriter, "", XmlSchema.Namespace); - AddNamespaceMapping(namespaceContext, xmlWriter, "xsi", XmlSchema.InstanceNamespace); - - xmlWriter.WriteStartElement(name); - xmlWriter.WriteStartAttribute("nil", XmlSchema.InstanceNamespace); - xmlWriter.WriteText("true"); - xmlWriter.WriteEndAttribute(); - xmlWriter.WriteEndElement(); - } - } - - private void AddNamespaceMapping(XmlNamespaceContext namespaceContext, XmlWriterImpl xmlWriter, string prefix, string uri) - { - if (namespaceContext.LookupPrefix(uri) == ValueFactory.Create()) - xmlWriter.WriteNamespaceMapping(prefix, uri); - } - - #region OneScript - - #region Properties - - [ContextProperty("Фабрика", "Factory")] - public IValue Factory { get; } - - #endregion - - #region Methods - - [ContextMethod("XMLЗначение", "XMLValue")] - public IValue XMLValue(IValue givenType, string presentation) => xmlGlobalFunctions.XMLValue(givenType, presentation); - - [ContextMethod("XMLСтрока", "XMLString")] - public string XMLString(IValue value) => xmlGlobalFunctions.XMLString(value); - - //XMLТип(XMLType) - //XMLТипЗнч(XMLTypeOf) - //ВозможностьЧтенияXML(CanReadXML) - //ЗаписатьJSON(WriteJSON) - //ЗаписатьXDTO(WriteXDTO) - - [ContextMethod("ЗаписатьXML", "WriteXML")] - public void WriteXML(XmlWriterImpl xmlWriter, - IValue value, - XMLTypeAssignment typeAssigment = XMLTypeAssignment.Implicit, - XMLForm form = XMLForm.Element) - { - switch (value.DataType) - { - case DataType.Undefined: - - WriteXML(xmlWriter, value, "Undefined", typeAssigment, form); - break; - - case DataType.String: - - WriteXML(xmlWriter, value, "string", typeAssigment, form); - break; - - case DataType.Number: - - WriteXML(xmlWriter, value, "decimal", typeAssigment, form); - break; - - case DataType.Boolean: - - WriteXML(xmlWriter, value, "boolean", typeAssigment, form); - break; - - case DataType.Date: - - WriteXML(xmlWriter, value, "dateTime", typeAssigment, form); - break; - - case DataType.Object: - - IRuntimeContextInstance valueObject = value.AsObject(); - if (valueObject is IXDTOSerializableXML seriazable) - seriazable.WriteXML(xmlWriter, this); - else - throw RuntimeException.InvalidArgumentType(); - break; - - default: - throw RuntimeException.InvalidArgumentType(); - } - } - - public void WriteXML(XmlWriterImpl xmlWriter, - IValue value, - string name, - XMLTypeAssignment typeAssigment = XMLTypeAssignment.Implicit, - XMLForm form = XMLForm.Element) - { - XMLExpandedName xmlType; - switch (value.DataType) - { - case DataType.Undefined: - - WriteXMLUndefined(xmlWriter, name, form); - break; - - case DataType.String: - - xmlType = new XMLExpandedName(XmlSchema.InstanceNamespace, "string"); - WriteXMLSimpleData(xmlWriter, name, value, xmlType, typeAssigment, form); - break; - - case DataType.Number: - - xmlType = new XMLExpandedName(XmlSchema.InstanceNamespace, "decimal"); - WriteXMLSimpleData(xmlWriter, name, value, xmlType, typeAssigment, form); - break; - - case DataType.Boolean: - - xmlType = new XMLExpandedName(XmlSchema.InstanceNamespace, "boolean"); - WriteXMLSimpleData(xmlWriter, name, value, xmlType, typeAssigment, form); - break; - - case DataType.Date: - - xmlType = new XMLExpandedName(XmlSchema.InstanceNamespace, "dateTime"); - WriteXMLSimpleData(xmlWriter, name, value, xmlType, typeAssigment, form); - break; - - case DataType.Object: - - IRuntimeContextInstance valueObject = value.AsObject(); - if (valueObject is IXDTOSerializableXML seriazable) - seriazable.WriteXML(xmlWriter, this); - else - throw RuntimeException.InvalidArgumentType(); - break; - - default: - throw RuntimeException.InvalidArgumentType(); - } - } - - //ИзXMLТипа(FromXMLType) - //ПолучитьXMLТип(GetXMLType) - //ПрочитатьJSON(ReadJSON) - //ПрочитатьXDTO(ReadXDTO) - - [ContextMethod("ПрочитатьXML", "ReadXML")] - public IValue ReadXML(XmlReaderImpl xmlReader, IValue valueType = null) - { - TypeTypeValue typeValue = null; - - if (valueType is TypeTypeValue typeTypeValue) - typeValue = typeTypeValue; - - else if (xmlReader.NodeType == xmlNodeEnum.FromNativeValue(XmlNodeType.Element)) - { - IValue xsiType = xmlReader.GetAttribute(ValueFactory.Create("type"), XmlSchema.InstanceNamespace); - IValue xsiNil = xmlReader.GetAttribute(ValueFactory.Create("nil"), XmlSchema.InstanceNamespace); - - if (xsiType.DataType == DataType.String) - { - switch (xsiType.AsString()) - { - case "string": - typeValue = new TypeTypeValue("String"); - break; - - case "decimal": - typeValue = new TypeTypeValue("Number"); - break; - - case "boolean": - typeValue = new TypeTypeValue("Boolean"); - break; - - case "dateTime": - typeValue = new TypeTypeValue("Date"); - break; - - default: - break; - } - } - else if (xsiNil.DataType == DataType.String) - typeValue = new TypeTypeValue("Undefined"); - }; - - if (typeValue == null) - throw RuntimeException.InvalidArgumentValue(); - - Type implType = TypeManager.GetImplementingClass(typeValue.Value.ID); - - IValue result = ValueFactory.Create(); - - if (typeValue.Equals(new TypeTypeValue("Undefined"))) - { - result = ValueFactory.Create(); - xmlReader.Skip(); - } - else if (implType == typeof(DataType)) - { - xmlReader.Read(); - if (xmlReader.NodeType == xmlNodeEnum.FromNativeValue(XmlNodeType.Text)) - { - result = XMLValue(typeValue, xmlReader.Value); - xmlReader.Read(); - } - else - throw RuntimeException.InvalidArgumentValue(); - } - else if (typeof(IXDTOSerializableXML).IsAssignableFrom(implType)) - result = Activator.CreateInstance(implType, new object[] { xmlReader, this }) as IValue; - - xmlReader.Read(); - return result; - } - - #endregion - - #region Constructors - - [ScriptConstructor(Name = "По умолчанию")] - public static XDTOSerializer CreateInstance() => new XDTOSerializer(); - - #endregion - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XDTO/XMLForm.cs b/src/ScriptEngine.HostedScript/Library/XDTO/XMLForm.cs deleted file mode 100644 index 05ab9101a..000000000 --- a/src/ScriptEngine.HostedScript/Library/XDTO/XMLForm.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XDTO -{ - [EnumerationType("XMLForm", "ФормаXML")] - public enum XMLForm - { - [EnumItem("Element", "Элемент")] - Element, - - [EnumItem("Text", "Текст")] - Text, - - [EnumItem("Attribute", "Атрибут")] - Attribute - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XMLSchemaSet.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XMLSchemaSet.cs deleted file mode 100644 index f7cd28307..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XMLSchemaSet.cs +++ /dev/null @@ -1,121 +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.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("НаборСхемXML", "XMLSchemaSet")] - public class XMLSchemaSet : AutoContext, ICollectionContext, IEnumerable - { - private readonly XmlSchemaSet _schemaSet; - private readonly List _items; - private XMLSchemaSet() - { - _items = new List(); - _schemaSet = new XmlSchemaSet(); - _schemaSet.ValidationEventHandler += SchemaSet_ValidationError; - } - - #region OneScript - - #region Methods - - [ContextMethod("Добавить", "Add")] - public void Add(XMLSchema schema) - { - _items.Add(schema); - _schemaSet.Add(schema.SchemaObject); - } - - [ContextMethod("Количество", "Count")] - public int Count() => _schemaSet.Count; - - [ContextMethod("Получить", "Get")] - public XMLSchema Get(IValue value) - { - DataType DataType = value.DataType; - switch (DataType) - { - case DataType.String: - return _items.FirstOrDefault(x => x.TargetNamespace.Equals(value.AsString())); - - case DataType.Number: - return _items[(int)value.AsNumber()]; - - default: - throw RuntimeException.InvalidArgumentType(); - } - } - - [ContextMethod("Проверить", "Validate")] - public bool Validate() - { - _schemaSet.Compile(); - return _schemaSet.IsCompiled; - } - - [ContextMethod("Удалить", "Delete")] - public void Delete(string namespaceUri) - { - XMLSchema item = _items.Find(x => x.TargetNamespace.Equals(namespaceUri)); - if (item is XMLSchema) - { - _items.Remove(item); - _schemaSet.Remove(item.SchemaObject); - } - } - - #endregion - - #region Constructors - - [ScriptConstructor(Name = "По умолчанию")] - public static XMLSchemaSet Constructor() => new XMLSchemaSet(); - - #endregion - - #endregion - - #region ICollectionContext - - public CollectionEnumerator GetManagedIterator() => new CollectionEnumerator(GetEnumerator()); - - #endregion - - #region IEnumerable - - public IEnumerator GetEnumerator() => _items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion - - #region SchemaSetEventHandlers - - private void SchemaSet_ValidationError(object sender, ValidationEventArgs args) - { - switch (args.Severity) - { - case XmlSeverityType.Error: - SystemLogger.Write($"ERROR:{args.Message}"); - break; - - default: - SystemLogger.Write($"WARNING:{args.Message}"); - break; - } - } - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComponentFixedList.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComponentFixedList.cs deleted file mode 100644 index 3958db6f6..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSComponentFixedList.cs +++ /dev/null @@ -1,58 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections; -using System.Collections.Generic; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("ФиксированныйСписокКомпонентXS", "XSComponentFixedList")] - public class XSComponentFixedList : AutoContext, ICollectionContext, IEnumerable - { - - private readonly List _items; - - public XSComponentFixedList() => _items = new List(); - - public void Add(IXSComponent value) => _items.Add(value); - public void Remove(IXSComponent value) => _items.Remove(value); - public void Clear() => _items.Clear(); - public void RemoveAll(Predicate predicate) => _items.RemoveAll(predicate); - - #region OneScript - - #region Methods - - [ContextMethod("Количество", "Count")] - public int Count() => _items.Count; - - [ContextMethod("Получить", "Get")] - public IXSComponent Get(int index) => _items[index]; - - [ContextMethod("Содержит", "Contains")] - public bool Contains(IXSComponent value) => _items.Contains(value); - - #endregion - - #endregion - - #region ICollectionContext - - public CollectionEnumerator GetManagedIterator() => new CollectionEnumerator(GetEnumerator()); - - #endregion - - #region IEnumerable - - public IEnumerator GetEnumerator() => _items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSDisallowedSubstitutionsUnion.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSDisallowedSubstitutionsUnion.cs deleted file mode 100644 index abbad5ae6..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSDisallowedSubstitutionsUnion.cs +++ /dev/null @@ -1,51 +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.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("ОбъединениеНедопустимыхПодстановкиXS", "XSDisallowedSubstitutionsUnion")] - public class XSDisallowedSubstitutionsUnion : AutoContext - { - private ArrayImpl _values; - - private bool Contains(XmlSchemaDerivationMethod _value) - { - XSDisallowedSubstitutions _enumValue = EnumerationXSDisallowedSubstitutions.FromNativeValue(_value); - IValue _idx = _values.Find(_enumValue); - return (_idx.DataType != DataType.Undefined); - } - - internal XSDisallowedSubstitutionsUnion() => _values = ArrayImpl.Constructor(); - - #region OneScript - - #region Properties - - [ContextProperty("Все", "All")] - public bool All => Contains(XmlSchemaDerivationMethod.All); - - [ContextProperty("Ограничение", "Restriction")] - public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); - - [ContextProperty("Расширение", "Extension")] - public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); - - #endregion - - #region Methods - - [ContextMethod("Значения", "Values")] - public ArrayImpl Values() => _values; - - #endregion - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSNamedComponentMap.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSNamedComponentMap.cs deleted file mode 100644 index 7eb5ae81b..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSNamedComponentMap.cs +++ /dev/null @@ -1,84 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("КоллекцияИменованныхКомпонентXS", "XSNamedComponentMap")] - public class XSNamedComponentMap : AutoContext, ICollectionContext, IEnumerable - { - private readonly List _items; - - internal XSNamedComponentMap() => _items = new List(); - - internal void Add(IXSNamedComponent value) - { - if (string.IsNullOrWhiteSpace(value.Name)) - return; - - _items.Add(value); - } - - internal void Delete(IXSNamedComponent value) => _items.Remove(value); - - internal void Clear() => _items.Clear(); - - #region OneScript - - #region Methods - - [ContextMethod("Количество", "Count")] - public int Count() => _items.Count; - - [ContextMethod("Получить", "Get")] - public IXSNamedComponent Get(IValue value) - { - DataType DataType = value.DataType; - switch (DataType) - { - case DataType.String: - return _items.FirstOrDefault(x => x.Name.Equals(value.AsString())); - - case DataType.Number: - return _items[(int)value.AsNumber()]; - - default: - throw RuntimeException.InvalidArgumentType(); - } - } - - public IXSNamedComponent Get(string name, string namespaceURI) - { - throw new NotImplementedException(); - } - - #endregion - - #endregion - - #region ICollectionContext - - public CollectionEnumerator GetManagedIterator() => new CollectionEnumerator(GetEnumerator()); - - #endregion - - #region IEnumerable - - public IEnumerator GetEnumerator() => _items.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - #endregion - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSProhibitedSubstitutionsUnion.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSProhibitedSubstitutionsUnion.cs deleted file mode 100644 index 41e702131..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSProhibitedSubstitutionsUnion.cs +++ /dev/null @@ -1,52 +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.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("ОбъединениеЗапрещенныхПодстановокXS", "XSProhibitedSubstitutionsUnion")] - public class XSProhibitedSubstitutionsUnion : AutoContext - { - private readonly ArrayImpl _values; - - private bool Contains(XmlSchemaDerivationMethod _value) - { - XSProhibitedSubstitutions _enumValue = XSProhibitedSubstitutions.FromNativeValue(_value); - IValue _idx = _values.Find(_enumValue); - return (_idx.DataType != DataType.Undefined); - } - - internal XSProhibitedSubstitutionsUnion() => _values = ArrayImpl.Constructor(); - - #region OneScript - - #region Properties - - [ContextProperty("Все", "All")] - public bool All => Contains(XmlSchemaDerivationMethod.All); - - [ContextProperty("Ограничение", "Restriction")] - public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); - - [ContextProperty("Расширение", "Extension")] - public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); - - #endregion - - #region Methods - - [ContextMethod("Значения", "Values")] - public ArrayImpl Values() => _values; - - #endregion - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSchemaFinalUnion.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSchemaFinalUnion.cs deleted file mode 100644 index 37700baad..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSchemaFinalUnion.cs +++ /dev/null @@ -1,57 +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.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("ОбъединениеЗавершенностиСхемыXS", "XSSchemaFinalUnion")] - public class XSSchemaFinalUnion : AutoContext - { - private ArrayImpl _values; - - private bool Contains(XmlSchemaDerivationMethod _value) - { - XSSchemaFinal _enumValue = EnumerationXSSchemaFinal.FromNativeValue(_value); - IValue _idx = _values.Find(_enumValue); - return (_idx.DataType != DataType.Undefined); - } - - internal XSSchemaFinalUnion() => _values = ArrayImpl.Constructor(); - - #region OneScript - - #region Properties - - [ContextProperty("Все", "All")] - public bool All => Contains(XmlSchemaDerivationMethod.All); - - [ContextProperty("Объединение", "Union")] - public bool Union => Contains(XmlSchemaDerivationMethod.Union); - - [ContextProperty("Ограничение", "Restriction")] - public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); - - [ContextProperty("Расширение", "Extension")] - public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); - - [ContextProperty("Список", "List")] - public bool List => Contains(XmlSchemaDerivationMethod.List); - - #endregion - - #region Methods - - [ContextMethod("Значения", "Values")] - public ArrayImpl Values() => _values; - - #endregion - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSubstitutionGroupExclusionsUnion.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSubstitutionGroupExclusionsUnion.cs deleted file mode 100644 index 3f378ff58..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Collections/XSSubstitutionGroupExclusionsUnion.cs +++ /dev/null @@ -1,51 +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.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("ОбъединениеИсключенийГруппПодстановкиXS", "XSSubstitutionGroupExclusionsUnion")] - public class XSSubstitutionGroupExclusionsUnion : AutoContext - { - private readonly ArrayImpl _values; - private bool Contains(XmlSchemaDerivationMethod _value) - { - XSSubstitutionGroupExclusions _enumValue = EnumerationXSSubstitutionGroupExclusions.FromNativeValue(_value); - IValue _idx = _values.Find(_enumValue); - return (_idx.DataType != DataType.Undefined); - } - - internal XSSubstitutionGroupExclusionsUnion() => _values = ArrayImpl.Constructor(); - - #region OneScript - - #region Properties - - [ContextProperty("Все", "All")] - public bool All => Contains(XmlSchemaDerivationMethod.All); - - [ContextProperty("Ограничение", "Restriction")] - public bool Restriction => Contains(XmlSchemaDerivationMethod.Restriction); - - [ContextProperty("Расширение", "Extension")] - public bool Extension => Contains(XmlSchemaDerivationMethod.Extension); - - #endregion - - #region Methods - - [ContextMethod("Значения", "Values")] - public ArrayImpl Values() => _values; - - #endregion - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSAttributeUseCategory.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSAttributeUseCategory.cs deleted file mode 100644 index c50b1f907..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSAttributeUseCategory.cs +++ /dev/null @@ -1,27 +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.Xml.Schema; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [EnumerationType("XSAttributeUseCategory", "КатегорияИспользованияАтрибутаXS")] - public enum XSAttributeUseCategory - { - [EnumItem("EmptyRef", "ПустаяСсылка")] - EmptyRef = XmlSchemaUse.None, - - [EnumItem("Optional", "Необязательно")] - Optional = XmlSchemaUse.Optional, - - [EnumItem("Prohibited", "Запрещено")] - Prohibited = XmlSchemaUse.Prohibited, - - [EnumItem("Required", "Обязательно")] - Required = XmlSchemaUse.Required - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSComplexFinal.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSComplexFinal.cs deleted file mode 100644 index e3d2b2d36..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSComplexFinal.cs +++ /dev/null @@ -1,69 +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.Collections.Generic; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public class XSComplexFinal : EnumerationValue - { - private readonly XmlSchemaDerivationMethod _derivationMethod; - public XSComplexFinal(EnumerationContext owner, XmlSchemaDerivationMethod derivationMethod) - : base(owner) => _derivationMethod = derivationMethod; - - public static XSComplexFinal FromNativeValue(XmlSchemaDerivationMethod native) - => EnumerationXSComplexFinal.FromNativeValue(native); - - public static XmlSchemaDerivationMethod ToNativeValue(XSComplexFinal wrapper) - => wrapper._derivationMethod; - } - - [SystemEnum("ЗавершенностьСоставногоТипаXS", "XSComplexFinal")] - public class EnumerationXSComplexFinal : EnumerationContext - { - private readonly Dictionary _valuesCache; - - private EnumerationXSComplexFinal(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - _valuesCache = new Dictionary - { - { XmlSchemaDerivationMethod.All, new XSComplexFinal(this, XmlSchemaDerivationMethod.All) }, - { XmlSchemaDerivationMethod.Restriction, new XSComplexFinal(this, XmlSchemaDerivationMethod.Restriction) }, - { XmlSchemaDerivationMethod.Extension, new XSComplexFinal(this, XmlSchemaDerivationMethod.Extension) } - }; - } - internal static XSComplexFinal FromNativeValue(XmlSchemaDerivationMethod native) - { - EnumerationXSComplexFinal enumeration = GlobalsManager.GetEnum(); - enumeration._valuesCache.TryGetValue(native, out XSComplexFinal value); - return value; - } - - public static EnumerationXSComplexFinal CreateInstance() - { - TypeDescriptor type = TypeManager.RegisterType("EnumerationXSComplexFinal", typeof(EnumerationXSComplexFinal)); - TypeDescriptor enumValueType = TypeManager.RegisterType("XSComplexFinal", typeof(XSComplexFinal)); - - TypeManager.RegisterAliasFor(type, "ПеречислениеЗавершенностьСоставногоТипаXS"); - TypeManager.RegisterAliasFor(enumValueType, "ЗавершенностьСоставногоТипаXS"); - - EnumerationXSComplexFinal instance = new EnumerationXSComplexFinal(type, enumValueType); - instance.AddValue("Все", "All", new XSComplexFinal(instance, XmlSchemaDerivationMethod.All)); - instance.AddValue("Ограничение", "Restriction", new XSComplexFinal(instance, XmlSchemaDerivationMethod.Restriction)); - instance.AddValue("Расширение", "Extension", new XSComplexFinal(instance, XmlSchemaDerivationMethod.Extension)); - - return instance; - } - } - - - -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSComponentType.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSComponentType.cs deleted file mode 100644 index 8741e9c27..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSComponentType.cs +++ /dev/null @@ -1,110 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [EnumerationType("XSComponentType", "ТипКомпонентыXS")] - public enum XSComponentType - { - - [EnumItem("Annotation", "Аннотация")] - Annotation, - - [EnumItem("Include", "Включение")] - Include, - - [EnumItem("ModelGroup", "ГруппаМодели")] - ModelGroup, - - [EnumItem("Documentation", "Документация")] - Documentation, - - [EnumItem("Import", "Импорт")] - Import, - - [EnumItem("AppInfo", "ИнформацияПриложения")] - AppInfo, - - [EnumItem("AttributeUse", "ИспользованиеАтрибута")] - AttributeUse, - - [EnumItem("MaxInclusiveFacet", "МаксимальноВключающийФасет")] - MaxInclusiveFacet, - - [EnumItem("MaxExclusiveFacet", "МаксимальноИсключающийФасет")] - MaxExclusiveFacet, - - [EnumItem("Wildcard", "Маска")] - Wildcard, - - [EnumItem("MinInclusiveFacet", "МинимальноВключающийФасет")] - MinInclusiveFacet, - - [EnumItem("MinExclusiveFacet", "МинимальноИсключающийФасет")] - MinExclusiveFacet, - - [EnumItem("AttributeDeclaration", "ОбъявлениеАтрибута")] - AttributeDeclaration, - - [EnumItem("NotationDeclaration", "ОбъявлениеНотации")] - NotationDeclaration, - - [EnumItem("ElementDeclaration", "ОбъявлениеЭлемента")] - ElementDeclaration, - - [EnumItem("XPathDefinition", "ОпределениеXPath")] - XPathDefinition, - - [EnumItem("AttributeGroupDefinition", "ОпределениеГруппыАтрибутов")] - AttributeGroupDefinition, - - [EnumItem("ModelGroupDefinition", "ОпределениеГруппыМодели")] - ModelGroupDefinition, - - [EnumItem("IdentityConstraintDefinition", "ОпределениеОграниченияИдентичности")] - IdentityConstraintDefinition, - - [EnumItem("SimpleTypeDefinition", "ОпределениеПростогоТипа")] - SimpleTypeDefinition, - - [EnumItem("ComplexTypeDefinition", "ОпределениеСоставногоТипа")] - ComplexTypeDefinition, - - [EnumItem("Redefine", "Переопределение")] - Redefine, - - [EnumItem("Schema", "Схема")] - Schema, - - [EnumItem("LengthFacet", "ФасетДлины")] - LengthFacet, - - [EnumItem("FractionDigitsFacet", "ФасетКоличестваРазрядовДробнойЧасти")] - FractionDigitsFacet, - - [EnumItem("MaxLengthFacet", "ФасетМаксимальнойДлины")] - MaxLengthFacet, - - [EnumItem("MinLengthFacet", "ФасетМинимальнойДлины")] - MinLengthFacet, - - [EnumItem("PatternFacet", "ФасетОбразца")] - PatternFacet, - - [EnumItem("TotalDigitsFacet", "ФасетОбщегоКоличестваРазрядов")] - TotalDigitsFacet, - - [EnumItem("EnumerationFacet", "ФасетПеречисления")] - EnumerationFacet, - - [EnumItem("WhitespaceFacet", "ФасетПробельныхСимволов")] - WhitespaceFacet, - - [EnumItem("Particle", "Фрагмент")] - Particle - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSConstraint.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSConstraint.cs deleted file mode 100644 index 260fdc5d5..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSConstraint.cs +++ /dev/null @@ -1,28 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - /// - /// Описывает варианты ограничения значения - /// - [EnumerationType("XSConstraint", "ОграничениеЗначенияXS")] - public enum XSConstraint - { - /// - /// Используется ограничение по умолчанию - /// - [EnumItem("Default", "ПоУмолчанию")] - Default, - - /// - /// Используется фиксированное значение - /// - [EnumItem("Fixed", "Фиксированное")] - Fixed - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSContentModel.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSContentModel.cs deleted file mode 100644 index 22f52999a..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSContentModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - /// - /// Определяет вид модели содержания. - /// - [EnumerationType("XSContentModel", "МодельСодержимогоXS")] - public enum XSContentModel - { - [EnumItem("EmptyRef", "ПустаяСсылка")] - EmptyRef, - - [EnumItem("Simple", "Простая")] - Simple, - - [EnumItem("Complex", "Составная")] - Complex - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSDerivationMethod.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSDerivationMethod.cs deleted file mode 100644 index 9adae7448..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSDerivationMethod.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [EnumerationType("XSDerivationMethod", "МетодНаследованияXS")] - public enum XSDerivationMethod - { - [EnumItem("EmptyRef", "ПустаяСсылка")] - EmptyRef, - - [EnumItem("Restriction", "Ограничение")] - Restriction, - - [EnumItem("Extension", "Расширение")] - Extension - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSDisallowedSubstitutions.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSDisallowedSubstitutions.cs deleted file mode 100644 index 35d1a11eb..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSDisallowedSubstitutions.cs +++ /dev/null @@ -1,83 +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.Collections.Generic; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public class XSDisallowedSubstitutions : CLREnumValueWrapper - { - internal XSDisallowedSubstitutions(EnumerationXSDisallowedSubstitutions instance, XmlSchemaDerivationMethod realValue) - : base(instance, realValue) - { - } - - public static XSDisallowedSubstitutions FromNativeValue(XmlSchemaDerivationMethod native) - => EnumerationXSDisallowedSubstitutions.FromNativeValue(native); - - public static XmlSchemaDerivationMethod ToNativeValue(XSDisallowedSubstitutions wrapper) - => wrapper.UnderlyingValue; - } - - [SystemEnum("НедопустимыеПодстановкиXS", "XSDisallowedSubstitutions")] - public class EnumerationXSDisallowedSubstitutions : EnumerationContext - { - - private readonly Dictionary _valuesCache; - - private EnumerationXSDisallowedSubstitutions(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - _valuesCache = new Dictionary - { - { XmlSchemaDerivationMethod.All, new XSDisallowedSubstitutions(this, XmlSchemaDerivationMethod.All) }, - { XmlSchemaDerivationMethod.Restriction, new XSDisallowedSubstitutions(this, XmlSchemaDerivationMethod.Restriction) }, - { XmlSchemaDerivationMethod.Substitution, new XSDisallowedSubstitutions(this, XmlSchemaDerivationMethod.Substitution) }, - { XmlSchemaDerivationMethod.Extension, new XSDisallowedSubstitutions(this, XmlSchemaDerivationMethod.Extension) } - }; - } - - internal static XSDisallowedSubstitutions FromNativeValue(XmlSchemaDerivationMethod native) - { - - switch (native) - { - case XmlSchemaDerivationMethod.All: - case XmlSchemaDerivationMethod.Restriction: - case XmlSchemaDerivationMethod.Substitution: - case XmlSchemaDerivationMethod.Extension: - - EnumerationXSDisallowedSubstitutions enumeration = GlobalsManager.GetEnum(); - return enumeration._valuesCache[native]; - - default: - return null; - } - } - - public static EnumerationXSDisallowedSubstitutions CreateInstance() - { - - TypeDescriptor type = TypeManager.RegisterType("EnumerationXSDisallowedSubstitutions", typeof(EnumerationXSDisallowedSubstitutions)); - TypeDescriptor enumValueType = TypeManager.RegisterType("XSDisallowedSubstitutions", typeof(XSDisallowedSubstitutions)); - - TypeManager.RegisterAliasFor(type, "ПеречислениеНедопустимыеПодстановкиXS"); - TypeManager.RegisterAliasFor(enumValueType, "НедопустимыеПодстановкиXS"); - - EnumerationXSDisallowedSubstitutions instance = new EnumerationXSDisallowedSubstitutions(type, enumValueType); - - instance.AddValue("Все", "All", instance._valuesCache[XmlSchemaDerivationMethod.All]); - instance.AddValue("Ограничение", "Restriction", instance._valuesCache[XmlSchemaDerivationMethod.Restriction]); - instance.AddValue("Подстановка", "Substitution", instance._valuesCache[XmlSchemaDerivationMethod.Substitution]); - instance.AddValue("Расширение", "Extension", instance._valuesCache[XmlSchemaDerivationMethod.Extension]); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSForm.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSForm.cs deleted file mode 100644 index 71368b04e..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSForm.cs +++ /dev/null @@ -1,72 +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.Collections.Generic; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public class XSForm : CLREnumValueWrapper - { - internal XSForm(EnumerationXSForm instance, XmlSchemaForm realValue) : base(instance, realValue ) - { - } - - public static XSForm FromNativeValue(XmlSchemaForm native) => EnumerationXSForm.FromNativeValue(native); - - public static XmlSchemaForm ToNativeValue(XSForm wrapper) => wrapper.UnderlyingValue; - } - - [SystemEnum("ФормаПредставленияXS", "XSForm")] - public class EnumerationXSForm : EnumerationContext - { - private readonly Dictionary _valuesCache; - - private EnumerationXSForm(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - _valuesCache = new Dictionary - { - { XmlSchemaForm.Qualified, new XSForm(this, XmlSchemaForm.Qualified) }, - { XmlSchemaForm.Unqualified, new XSForm(this, XmlSchemaForm.Unqualified) } - }; - } - - internal static XSForm FromNativeValue(XmlSchemaForm native) - { - switch (native) - { - case XmlSchemaForm.Qualified: - case XmlSchemaForm.Unqualified: - - EnumerationXSForm enumeration = GlobalsManager.GetEnum(); - return enumeration._valuesCache[native]; - - default: - return null; - } - } - - public static EnumerationXSForm CreateInstance() - { - - TypeDescriptor type = TypeManager.RegisterType("EnumerationXSForm", typeof(EnumerationXSForm)); - TypeDescriptor enumValueType = TypeManager.RegisterType("XSForm", typeof(XSForm)); - - TypeManager.RegisterAliasFor(type, "ПеречислениеФормаПредставленияXS"); - TypeManager.RegisterAliasFor(enumValueType, "ФормаПредставленияXS"); - - EnumerationXSForm instance = new EnumerationXSForm(type, enumValueType); - - instance.AddValue("Квалифицированная", "Qualified", instance._valuesCache[XmlSchemaForm.Qualified]); - instance.AddValue("Неквалифицированная", "Unqualified", instance._valuesCache[XmlSchemaForm.Unqualified]); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs deleted file mode 100644 index a4b377c01..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSIdentityConstraintCategory.cs +++ /dev/null @@ -1,40 +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.Xml.Schema; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - /// - /// Типы ограничения идентичности. - /// - /// - [EnumerationType("XSIdentityConstraintCategory", "КатегорияОграниченияИдентичностиXS")] - public enum XSIdentityConstraintCategory - { - /// - /// Ограничение идентичности по ключу - /// - /// - [EnumItem("Key", "Ключ")] - Key, - - /// - /// Ограничение идентичности по ссылке - /// - /// - [EnumItem("KeyRef", "СсылкаНаКлюч")] - KeyRef, - - /// - /// Ограничение идентичности по опредению уникальности - /// - /// - [EnumItem("Unique", "Уникальность")] - Unique - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSNamespaceConstraintCategory.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSNamespaceConstraintCategory.cs deleted file mode 100644 index 32c3259b7..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSNamespaceConstraintCategory.cs +++ /dev/null @@ -1,25 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [EnumerationType("XSNamespaceConstraintCategory", "КатегорияОграниченияПространствИменXS")] - public enum XSNamespaceConstraintCategory - { - [EnumItem("EmptyRef", "ПустаяСсылка")] - EmptyRef, - - [EnumItem("Not", "Кроме")] - Not, - - [EnumItem("Any", "Любое")] - Any, - - [EnumItem("Set", "Набор")] - Set - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSProcessContents.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSProcessContents.cs deleted file mode 100644 index 0c29f3591..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSProcessContents.cs +++ /dev/null @@ -1,27 +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.Xml.Schema; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [EnumerationType("XSProcessContents", "ОбработкаСодержимогоXS")] - public enum XSProcessContents - { - [EnumItem("EmptyRef", "ПустаяСсылка")] - EmptyRef = XmlSchemaContentProcessing.None, - - [EnumItem("Skip", "Пропустить")] - Skip = XmlSchemaContentProcessing.Skip, - - [EnumItem("Lax", "Слабая")] - Lax = XmlSchemaContentProcessing.Lax, - - [EnumItem("Strict", "Строгая")] - Strict = XmlSchemaContentProcessing.Strict - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSProhibitedSubstitutions.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSProhibitedSubstitutions.cs deleted file mode 100644 index 84df2d313..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSProhibitedSubstitutions.cs +++ /dev/null @@ -1,81 +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.Collections.Generic; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public class XSProhibitedSubstitutions : CLREnumValueWrapper - { - internal XSProhibitedSubstitutions(EnumerationXSProhibitedSubstitutions instance, XmlSchemaDerivationMethod realValue) - : base(instance, realValue) - { - } - - public static XSProhibitedSubstitutions FromNativeValue(XmlSchemaDerivationMethod native) - => EnumerationXSProhibitedSubstitutions.FromNativeValue(native); - - public static XmlSchemaDerivationMethod ToNativeValue(XSProhibitedSubstitutions wrapper) - => wrapper.UnderlyingValue; - } - - [SystemEnum("ЗапрещенныеПодстановкиXS", "EnumerationXSProhibitedSubstitutions")] - public class EnumerationXSProhibitedSubstitutions : EnumerationContext - { - - private readonly Dictionary _valuesCache; - - private EnumerationXSProhibitedSubstitutions(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - _valuesCache = new Dictionary - { - { XmlSchemaDerivationMethod.All, new XSProhibitedSubstitutions(this, XmlSchemaDerivationMethod.All) }, - { XmlSchemaDerivationMethod.Restriction, new XSProhibitedSubstitutions(this, XmlSchemaDerivationMethod.Restriction) }, - { XmlSchemaDerivationMethod.Extension, new XSProhibitedSubstitutions(this, XmlSchemaDerivationMethod.Extension) } - }; - } - - internal static XSProhibitedSubstitutions FromNativeValue(XmlSchemaDerivationMethod native) - { - - switch (native) - { - case XmlSchemaDerivationMethod.All: - case XmlSchemaDerivationMethod.Restriction: - case XmlSchemaDerivationMethod.Extension: - - EnumerationXSProhibitedSubstitutions enumeration = GlobalsManager.GetEnum(); - return enumeration._valuesCache[native]; - - default: - return null; - } - } - - public static EnumerationXSProhibitedSubstitutions CreateInstance() - { - - TypeDescriptor type = TypeManager.RegisterType("EnumerationXSProhibitedSubstitutions", typeof(EnumerationXSProhibitedSubstitutions)); - TypeDescriptor enumValueType = TypeManager.RegisterType("XSProhibitedSubstitutions", typeof(XSProhibitedSubstitutions)); - - TypeManager.RegisterAliasFor(type, "ПеречислениеЗапрещенныеПодстановкиXS"); - TypeManager.RegisterAliasFor(enumValueType, "ЗапрещенныеПодстановкиXS"); - - EnumerationXSProhibitedSubstitutions instance = new EnumerationXSProhibitedSubstitutions(type, enumValueType); - - instance.AddValue("Все", "All", instance._valuesCache[XmlSchemaDerivationMethod.All]); - instance.AddValue("Ограничение", "Restriction", instance._valuesCache[XmlSchemaDerivationMethod.Restriction]); - instance.AddValue("Расширение", "Extension", instance._valuesCache[XmlSchemaDerivationMethod.Extension]); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSchemaFinal.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSchemaFinal.cs deleted file mode 100644 index d51ccc01c..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSchemaFinal.cs +++ /dev/null @@ -1,85 +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.Collections.Generic; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public class XSSchemaFinal : CLREnumValueWrapper - { - internal XSSchemaFinal(EnumerationXSSchemaFinal instance, XmlSchemaDerivationMethod realValue) - : base(instance, realValue) - { - } - - public static XSSchemaFinal FromNativeValue(XmlSchemaDerivationMethod native) - => EnumerationXSSchemaFinal.FromNativeValue(native); - - public static XmlSchemaDerivationMethod ToNativeValue(XSSchemaFinal wrapper) - => wrapper.UnderlyingValue; - } - - [SystemEnum("ЗавершенностьСхемыXS", "XSSchemaFinal")] - public class EnumerationXSSchemaFinal : EnumerationContext - { - - private readonly Dictionary _valuesCache; - - private EnumerationXSSchemaFinal(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - _valuesCache = new Dictionary - { - { XmlSchemaDerivationMethod.All, new XSSchemaFinal(this, XmlSchemaDerivationMethod.All) }, - { XmlSchemaDerivationMethod.Union, new XSSchemaFinal(this, XmlSchemaDerivationMethod.Union) }, - { XmlSchemaDerivationMethod.Restriction, new XSSchemaFinal(this, XmlSchemaDerivationMethod.Restriction) }, - { XmlSchemaDerivationMethod.Extension, new XSSchemaFinal(this, XmlSchemaDerivationMethod.Extension) }, - { XmlSchemaDerivationMethod.List, new XSSchemaFinal(this, XmlSchemaDerivationMethod.List) } - }; - } - - internal static XSSchemaFinal FromNativeValue(XmlSchemaDerivationMethod native) - { - switch (native) - { - case XmlSchemaDerivationMethod.All: - case XmlSchemaDerivationMethod.Union: - case XmlSchemaDerivationMethod.Restriction: - case XmlSchemaDerivationMethod.Extension: - case XmlSchemaDerivationMethod.List: - - EnumerationXSSchemaFinal enumeration = GlobalsManager.GetEnum(); - return enumeration._valuesCache[native]; - - default: - return null; - } - } - - public static EnumerationXSSchemaFinal CreateInstance() - { - - TypeDescriptor type = TypeManager.RegisterType("EnumerationXSSchemaFinal", typeof(EnumerationXSSchemaFinal)); - TypeDescriptor enumValueType = TypeManager.RegisterType("XSSchemaFinal", typeof(XSSchemaFinal)); - - TypeManager.RegisterAliasFor(type, "ПеречислениеЗавершенностьСхемыXS"); - TypeManager.RegisterAliasFor(enumValueType, "ЗавершенностьСхемыXS"); - - EnumerationXSSchemaFinal instance = new EnumerationXSSchemaFinal(type, enumValueType); - - instance.AddValue("Все", "All", instance._valuesCache[XmlSchemaDerivationMethod.All]); - instance.AddValue("Объединение", "Union", instance._valuesCache[XmlSchemaDerivationMethod.Union]); - instance.AddValue("Ограничение", "Restriction", instance._valuesCache[XmlSchemaDerivationMethod.Restriction]); - instance.AddValue("Расширение", "Extension", instance._valuesCache[XmlSchemaDerivationMethod.Extension]); - instance.AddValue("Список", "List", instance._valuesCache[XmlSchemaDerivationMethod.List]); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSimpleFinal.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSimpleFinal.cs deleted file mode 100644 index 1b45a95dc..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSimpleFinal.cs +++ /dev/null @@ -1,81 +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.Collections.Generic; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public class XSSimpleFinal : CLREnumValueWrapper - { - internal XSSimpleFinal(EnumerationXSSimpleFinal instance, XmlSchemaDerivationMethod realValue) - : base(instance, realValue) - { - } - - public static XSSimpleFinal FromNativeValue(XmlSchemaDerivationMethod native) - => EnumerationXSSimpleFinal.FromNativeValue(native); - - public static XmlSchemaDerivationMethod ToNativeValue(XSSimpleFinal wrapper) - => wrapper.UnderlyingValue; - } - - [SystemEnum("ЗавершенностьПростогоТипаXS", "XSSimpleFinal")] - public class EnumerationXSSimpleFinal : EnumerationContext - { - private readonly Dictionary _valuesCache; - - private EnumerationXSSimpleFinal(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - _valuesCache = new Dictionary - { - { XmlSchemaDerivationMethod.All, new XSSimpleFinal(this, XmlSchemaDerivationMethod.All) }, - { XmlSchemaDerivationMethod.Union, new XSSimpleFinal(this, XmlSchemaDerivationMethod.Union) }, - { XmlSchemaDerivationMethod.Restriction, new XSSimpleFinal(this, XmlSchemaDerivationMethod.Restriction) }, - { XmlSchemaDerivationMethod.List, new XSSimpleFinal(this, XmlSchemaDerivationMethod.List) } - }; - } - - public static XSSimpleFinal FromNativeValue(XmlSchemaDerivationMethod native) - { - switch (native) - { - case XmlSchemaDerivationMethod.All: - case XmlSchemaDerivationMethod.Union: - case XmlSchemaDerivationMethod.Restriction: - case XmlSchemaDerivationMethod.List: - - EnumerationXSSimpleFinal enumeration = GlobalsManager.GetEnum(); - return enumeration._valuesCache[native]; - - default: - return null; - } - } - - public static EnumerationXSSimpleFinal CreateInstance() - { - - TypeDescriptor type = TypeManager.RegisterType("EnumerationXSSimpleFinal", typeof(EnumerationXSSimpleFinal)); - TypeDescriptor enumValueType = TypeManager.RegisterType("XSSimpleFinal", typeof(XSSimpleFinal)); - - TypeManager.RegisterAliasFor(type, "ПеречислениеЗавершенностьПростогоТипаXS"); - TypeManager.RegisterAliasFor(enumValueType, "ЗавершенностьПростогоТипаXS"); - - EnumerationXSSimpleFinal instance = new EnumerationXSSimpleFinal(type, enumValueType); - - instance.AddValue("Все", "All", instance._valuesCache[XmlSchemaDerivationMethod.All]); - instance.AddValue("Объединение", "Union", instance._valuesCache[XmlSchemaDerivationMethod.Union]); - instance.AddValue("Ограничение", "Restriction", instance._valuesCache[XmlSchemaDerivationMethod.Restriction]); - instance.AddValue("Список", "List", instance._valuesCache[XmlSchemaDerivationMethod.List]); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSimpleTypeVariety.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSimpleTypeVariety.cs deleted file mode 100644 index d68ad810a..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSimpleTypeVariety.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [EnumerationType("XSSimpleTypeVariety", "ВариантПростогоТипаXS")] - public enum XSSimpleTypeVariety - { - [EnumItem("Atomic", "Атомарная")] - Atomic, - - [EnumItem("Union", "Объединение")] - Union, - - [EnumItem("List", "Список")] - List - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSubstitutionGroupExclusions.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSubstitutionGroupExclusions.cs deleted file mode 100644 index 4d8302610..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSSubstitutionGroupExclusions.cs +++ /dev/null @@ -1,81 +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.Collections.Generic; -using System.Xml.Schema; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public class XSSubstitutionGroupExclusions : CLREnumValueWrapper - { - internal XSSubstitutionGroupExclusions(EnumerationXSSubstitutionGroupExclusions instance, XmlSchemaDerivationMethod realValue) - : base(instance, realValue) - { - } - - public static XSSubstitutionGroupExclusions FromNativeValue(XmlSchemaDerivationMethod native) - => EnumerationXSSubstitutionGroupExclusions.FromNativeValue(native); - - public static XmlSchemaDerivationMethod ToNativeValue(XSSubstitutionGroupExclusions wrapper) - => wrapper.UnderlyingValue; - } - - [SystemEnum("ИсключенияГруппПодстановкиXS", "XSSubstitutionGroupExclusions")] - public class EnumerationXSSubstitutionGroupExclusions : EnumerationContext - { - - private readonly Dictionary _valuesCache; - - private EnumerationXSSubstitutionGroupExclusions(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - _valuesCache = new Dictionary - { - { XmlSchemaDerivationMethod.All, new XSSubstitutionGroupExclusions(this, XmlSchemaDerivationMethod.All) }, - { XmlSchemaDerivationMethod.Restriction, new XSSubstitutionGroupExclusions(this, XmlSchemaDerivationMethod.Restriction) }, - { XmlSchemaDerivationMethod.Extension, new XSSubstitutionGroupExclusions(this, XmlSchemaDerivationMethod.Extension) } - }; - } - - internal static XSSubstitutionGroupExclusions FromNativeValue(XmlSchemaDerivationMethod native) - { - - switch (native) - { - case XmlSchemaDerivationMethod.All: - case XmlSchemaDerivationMethod.Restriction: - case XmlSchemaDerivationMethod.Extension: - - EnumerationXSSubstitutionGroupExclusions enumeration = GlobalsManager.GetEnum(); - return enumeration._valuesCache[native]; - - default: - return null; - } - } - - public static EnumerationXSSubstitutionGroupExclusions CreateInstance() - { - - TypeDescriptor type = TypeManager.RegisterType("EnumerationXSSubstitutionGroupExclusions", typeof(EnumerationXSSubstitutionGroupExclusions)); - TypeDescriptor enumValueType = TypeManager.RegisterType("XSSubstitutionGroupExclusions", typeof(XSSubstitutionGroupExclusions)); - - TypeManager.RegisterAliasFor(type, "ПеречислениеИсключенияГруппПодстановкиXS"); - TypeManager.RegisterAliasFor(enumValueType, "ИсключенияГруппПодстановкиXS"); - - EnumerationXSSubstitutionGroupExclusions instance = new EnumerationXSSubstitutionGroupExclusions(type, enumValueType); - - instance.AddValue("Все", "All", instance._valuesCache[XmlSchemaDerivationMethod.All]); - instance.AddValue("Ограничение", "Restriction", instance._valuesCache[XmlSchemaDerivationMethod.Restriction]); - instance.AddValue("Расширение", "Extension", instance._valuesCache[XmlSchemaDerivationMethod.Extension]); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSWhitespaceHandling.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSWhitespaceHandling.cs deleted file mode 100644 index 32f89179c..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSWhitespaceHandling.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [EnumerationType("XSWhitespaceHandling", "ОбработкаПробельныхСимволовXS")] - public enum XSWhitespaceHandling - { - [EnumItem("Replace", "Заменять")] - Replace, - - [EnumItem("Collapse", "Сворачивать")] - Collapse, - - [EnumItem("Preserve", "Сохранять")] - Preserve - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSXPathVariety.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSXPathVariety.cs deleted file mode 100644 index ce48e4872..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Enumerations/XSXPathVariety.cs +++ /dev/null @@ -1,29 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - /// - /// Содержит варианты использования выражения XPath. - /// - /// - [EnumerationType("XSXPathVariety", "ВариантXPathXS")] - public enum XSXPathVariety - { - /// - /// Используется в качестве поля - /// - [EnumItem("Field", "Поле")] - Field, - - /// - /// Используется в качестве селектора - /// - [EnumItem("Selector", "Селектор")] - Selector - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSType.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSType.cs deleted file mode 100644 index 6218165f9..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Interfaces/IXSType.cs +++ /dev/null @@ -1,13 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - public interface IXSType : IXSAnnotated - { - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSComplexTypeDefinition.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSComplexTypeDefinition.cs deleted file mode 100644 index 4f6c81b87..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSComplexTypeDefinition.cs +++ /dev/null @@ -1,497 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Xml; -using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("ОпределениеСоставногоТипаXS", "XSComplexTypeDefinition")] - public class XSComplexTypeDefinition : AutoContext, IXSType, IXSNamedComponent - { - private readonly XmlSchemaComplexType _type; - private XSAnnotation _annotation; - private XSAnnotation _contentModelAnnotation; - private XSAnnotation _derivationAnnotation; - private XMLExpandedName _baseTypeName; - private IXSComponent _content; - private XSWildcard _attributeWildcard; - private XSDerivationMethod _derivationMethod; - private XSContentModel _contentModel; - - private XSComplexTypeDefinition() - { - _type = new XmlSchemaComplexType(); - Components = new XSComponentFixedList(); - Attributes = new XSComponentList(); - Attributes.Inserted += Attributes_Inserted; - Attributes.Cleared += Attributes_Cleared; - } - - internal XSComplexTypeDefinition(XmlSchemaComplexType complexType) - : this() - { - _type = complexType; - - if (_type.Annotation is XmlSchemaAnnotation annotation) - { - _annotation = XMLSchemaSerializer.CreateXSAnnotation(annotation); - _annotation.BindToContainer(RootContainer, this); - } - - if (_type.ContentModel is XmlSchemaSimpleContent simpleContent) - { - _contentModel = XSContentModel.Simple; - if (simpleContent.Content is XmlSchemaSimpleContentExtension contentExtension) - { - _derivationMethod = XSDerivationMethod.Extension; - if (contentExtension.BaseTypeName is XmlQualifiedName qualifiedName) - _baseTypeName = XMLSchemaSerializer.CreateXMLExpandedName(qualifiedName); - - if (contentExtension.AnyAttribute is XmlSchemaAnyAttribute anyAttribute) - _attributeWildcard = XMLSchemaSerializer.CreateXSWildcard(anyAttribute); - } - else if (simpleContent.Content is XmlSchemaSimpleContentRestriction contentRestriction) - { - _derivationMethod = XSDerivationMethod.Restriction; - if (contentRestriction.BaseTypeName is XmlQualifiedName qualifiedName) - _baseTypeName = XMLSchemaSerializer.CreateXMLExpandedName(qualifiedName); - - if (contentRestriction.AnyAttribute is XmlSchemaAnyAttribute anyAttribute) - _attributeWildcard = XMLSchemaSerializer.CreateXSWildcard(anyAttribute); - } - else - _derivationMethod = XSDerivationMethod.EmptyRef; - - if (_type.Particle is XmlSchemaParticle particle) - _content = XMLSchemaSerializer.CreateInstance(particle); - } - else if (_type.ContentModel is XmlSchemaComplexContent complexContent) - { - _contentModel = XSContentModel.Complex; - - if (complexContent.Content is XmlSchemaComplexContentExtension contentExtension) - { - _derivationMethod = XSDerivationMethod.Extension; - if (contentExtension.BaseTypeName is XmlQualifiedName qualifiedName) - _baseTypeName = XMLSchemaSerializer.CreateXMLExpandedName(qualifiedName); - - if (contentExtension.Particle is XmlSchemaParticle particle) - _content = XMLSchemaSerializer.CreateInstance(particle); - - if (contentExtension.AnyAttribute is XmlSchemaAnyAttribute anyAttribute) - _attributeWildcard = XMLSchemaSerializer.CreateXSWildcard(anyAttribute); - } - else if (complexContent.Content is XmlSchemaComplexContentRestriction contentRestriction) - { - _derivationMethod = XSDerivationMethod.Restriction; - if (contentRestriction.BaseTypeName is XmlQualifiedName qualifiedName) - _baseTypeName = XMLSchemaSerializer.CreateXMLExpandedName(qualifiedName); - - if (contentRestriction.Particle is XmlSchemaParticle particle) - _content = XMLSchemaSerializer.CreateInstance(particle); - - if (contentRestriction.AnyAttribute is XmlSchemaAnyAttribute anyAttribute) - _attributeWildcard = XMLSchemaSerializer.CreateXSWildcard(anyAttribute); - } - else - { - _derivationMethod = XSDerivationMethod.EmptyRef; - - if (_type.Particle is XmlSchemaParticle particle) - _content = XMLSchemaSerializer.CreateInstance(particle); - } - } - else - { - _contentModel = XSContentModel.EmptyRef; - - if (_type.Particle is XmlSchemaParticle particle) - _content = XMLSchemaSerializer.CreateInstance(particle); - } - - Attributes.Inserted -= Attributes_Inserted; - foreach (XmlSchemaObject item in _type.Attributes) - { - IXSComponent component = XMLSchemaSerializer.CreateInstance(item); - component.BindToContainer(RootContainer, this); - Attributes.Add(component); - Components.Add(component); - } - Attributes.Inserted += Attributes_Inserted; - } - - private void OnSetContentModelDerivation() - { - if (_contentModel == XSContentModel.Simple) - { - _type.ContentModel = new XmlSchemaSimpleContent(); - - if (_derivationMethod == XSDerivationMethod.Extension) - _type.ContentModel.Content = new XmlSchemaSimpleContentExtension(); - - else if (_derivationMethod == XSDerivationMethod.Restriction) - _type.ContentModel.Content = new XmlSchemaSimpleContentRestriction(); - - else - _type.ContentModel.Content = default(XmlSchemaContent); - } - else if (_contentModel == XSContentModel.Complex) - { - _type.ContentModel = new XmlSchemaComplexContent(); - - if (_derivationMethod == XSDerivationMethod.Extension) - _type.ContentModel.Content = new XmlSchemaComplexContentExtension(); - - else if (_derivationMethod == XSDerivationMethod.Restriction) - _type.ContentModel.Content = new XmlSchemaComplexContentRestriction(); - - else - _type.ContentModel.Content = default(XmlSchemaContent); - } - else - _type.ContentModel = default(XmlSchemaContentModel); - - OnSetBaseTypeName(); - OnSetAttributeWildcard(); - OnSetContentModelAnnotation(); - OnSetDerivationAnnotation(); - } - - private void OnSetBaseTypeName() - { - XmlQualifiedName baseTypeName = _baseTypeName?.NativeValue; - - if (_type.ContentModel is XmlSchemaComplexContent complexModel) - { - if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) - contentExtension.BaseTypeName = baseTypeName; - - else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) - contentRestriction.BaseTypeName = baseTypeName; - } - else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) - { - if (simpleModel.Content is XmlSchemaSimpleContentExtension contentExtension) - contentExtension.BaseTypeName = baseTypeName; - - else if (simpleModel.Content is XmlSchemaSimpleContentRestriction contentRestriction) - contentRestriction.BaseTypeName = baseTypeName; - } - } - - private void OnSetAttributeWildcard() - { - - XmlSchemaAnyAttribute anyAttribute = _attributeWildcard?.SchemaObject as XmlSchemaAnyAttribute; - - if (_type.ContentModel is XmlSchemaComplexContent complexModel) - { - if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) - contentExtension.AnyAttribute = anyAttribute; - - else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) - contentRestriction.AnyAttribute = anyAttribute; - } - else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) - { - if (simpleModel.Content is XmlSchemaSimpleContentExtension contentExtension) - contentExtension.AnyAttribute = anyAttribute; - - else if (simpleModel.Content is XmlSchemaSimpleContentRestriction contentRestriction) - contentRestriction.AnyAttribute = anyAttribute; - } - } - - private void OnSetContentModelAnnotation() - { - if (_type.ContentModel is XmlSchemaComplexContent complexModel) - XSAnnotation.SetComponentAnnotation(_contentModelAnnotation, complexModel); - - else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) - XSAnnotation.SetComponentAnnotation(_contentModelAnnotation, simpleModel); - } - - private void OnSetDerivationAnnotation() - { - if (_type.ContentModel is XmlSchemaComplexContent complexModel) - { - if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) - XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentExtension); - - else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) - XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentRestriction); - } - else if (_type.ContentModel is XmlSchemaSimpleContent simpleModel) - { - if (simpleModel.Content is XmlSchemaSimpleContentExtension contentExtension) - XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentExtension); - - else if (simpleModel.Content is XmlSchemaSimpleContentRestriction contentRestriction) - XSAnnotation.SetComponentAnnotation(_derivationAnnotation, contentRestriction); - } - } - - private void OnSetContent() - { - XmlSchemaParticle xmlParticle; - - if (_content is XSParticle particle) - xmlParticle = particle.SchemaObject; - - else if (_content is IXSFragment fragment) - xmlParticle = fragment.SchemaObject as XmlSchemaParticle; - - else if (_content is XSModelGroupDefinition groupDefinition) - xmlParticle = groupDefinition.SchemaObject as XmlSchemaGroupRef; - - else if (_content is XSModelGroup group) - xmlParticle = group.SchemaObject as XmlSchemaGroupBase; - - else - xmlParticle = null; - - if (_type.ContentModel is XmlSchemaComplexContent complexModel) - { - if (complexModel.Content is XmlSchemaComplexContentExtension contentExtension) - contentExtension.Particle = xmlParticle; - - else if (complexModel.Content is XmlSchemaComplexContentRestriction contentRestriction) - contentRestriction.Particle = xmlParticle; - } - else - _type.Particle = xmlParticle; - } - - #region OneScript - - #region Properties - - [ContextProperty("Аннотация", "Annotation")] - public XSAnnotation Annotation - { - get => _annotation; - set - { - _annotation = value; - _annotation?.BindToContainer(RootContainer, this); - XSAnnotation.SetComponentAnnotation(_annotation, _type); - } - } - - [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components { get; } - - [ContextProperty("Контейнер", "Container")] - public IXSComponent Container { get; private set; } - - [ContextProperty("КорневойКонтейнер", "RootContainer")] - public IXSComponent RootContainer { get; private set; } - - [ContextProperty("Схема", "Schema")] - public XMLSchema Schema => RootContainer.Schema; - - [ContextProperty("ТипКомпоненты", "ComponentType")] - public XSComponentType ComponentType => XSComponentType.ComplexTypeDefinition; - - [ContextProperty("ЭлементDOM", "DOMElement")] - public IValue DOMElement => ValueFactory.Create(); - - [ContextProperty("URIПространстваИмен", "NamespaceURI")] - public string URIПространстваИмен => _type.SourceUri; - - [ContextProperty("Имя", "Name")] - public string Name - { - get => _type.Name; - set => _type.Name = value; - } - - [ContextProperty("БазовыйТип", "BaseType")] - public XSComplexTypeDefinition BaseType { get; } - - [ContextProperty("КорневойТип", "RootType")] - public XSComplexTypeDefinition RootType { get; } - - [ContextProperty("Абстрактный", "Abstract")] - public bool Abstract - { - get => _type.IsAbstract; - set => _type.IsAbstract = value; - } - - [ContextProperty("АннотацияМоделиСодержимого", "ContentModelAnnotation")] - public XSAnnotation ContentModelAnnotation - { - get => _contentModelAnnotation; - set - { - _contentModelAnnotation = value; - _contentModelAnnotation?.BindToContainer(RootContainer, this); - OnSetContentModelAnnotation(); - } - } - - [ContextProperty("АннотацияНаследования", "DerivationAnnotation")] - public XSAnnotation DerivationAnnotation - { - get => _derivationAnnotation; - set - { - _derivationAnnotation = value; - _derivationAnnotation?.BindToContainer(RootContainer, this); - OnSetDerivationAnnotation(); - } - } - - [ContextProperty("Атрибуты", "Attributes")] - public XSComponentList Attributes { get; } - - [ContextProperty("Блокировка", "Block")] - public XSProhibitedSubstitutionsUnion Block { get; } - - [ContextProperty("Завершенность", "Final")] - public XSComplexFinalUnion Final { get; } - - [ContextProperty("ЗапрещенныеПодстановки", "ProhibitedSubstitutions")] - public XSProhibitedSubstitutionsUnion ProhibitedSubstitutions { get; } - - [ContextProperty("ИмяБазовогоТипа", "BaseTypeName")] - public XMLExpandedName BaseTypeName - { - get => _baseTypeName; - set - { - _baseTypeName = value; - OnSetBaseTypeName(); - } - } - - [ContextProperty("МаскаАтрибутов", "AttributeWildcard")] - public XSWildcard AttributeWildcard - { - get => _attributeWildcard; - set - { - _attributeWildcard = value; - _attributeWildcard?.BindToContainer(RootContainer, this); - OnSetAttributeWildcard(); - } - } - - [ContextProperty("МетодНаследования", "DerivationMethod")] - public XSDerivationMethod DerivationMethod - { - get => _derivationMethod; - set - { - _derivationMethod = value; - OnSetContentModelDerivation(); - } - } - - [ContextProperty("МодельСодержимого", "ContentModel")] - public XSContentModel ContentModel - { - get => _contentModel; - set - { - _contentModel = value; - OnSetContentModelDerivation(); - } - } - - //ОпределениеБазовогоТипа(BaseTypeDefinition) - - [ContextProperty("Смешанный", "Mixed")] - public bool Mixed => _type.ContentModel is XmlSchemaComplexContent complexContent ? complexContent.IsMixed : false; - - [ContextProperty("Содержимое", "Content")] - public IXSComponent Content - { - get => _content; - set - { - _content = value; - _content.BindToContainer(RootContainer, this); - OnSetContent(); - } - } - - //Фасеты(Facets) - - #endregion - - #region Methods - - [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); - - [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] - public void UpdateDOMElement() => throw new NotImplementedException(); - - [ContextMethod("Содержит", "Contains")] - public bool Contains(IXSComponent component) => Components.Contains(component); - - [ContextMethod("ЭтоОпределениеЗациклено", "IsCircular")] - public bool IsCircular() => throw new NotImplementedException(); - - [ContextMethod("ИспользованиеАтрибутов", "AttributeUses")] - public XSComponentFixedList AttributeUses() => throw new NotImplementedException(); - - #endregion - - #region Constructors - - [ScriptConstructor(Name = "По умолчанию")] - public static XSComplexTypeDefinition Constructor() => new XSComplexTypeDefinition(); - - #endregion - - #endregion - - #region IXSComponent - - public void BindToContainer(IXSComponent rootContainer, IXSComponent container) - { - RootContainer = rootContainer; - Container = container; - } - - XmlSchemaObject IXSComponent.SchemaObject => _type; - public XmlSchemaComplexType SchemaObject => _type; - - #endregion - - #region XSComponentListEvents - - private void Attributes_Inserted(object sender, XSComponentListEventArgs e) - { - IXSComponent component = e.Component; - if (!(component is IXSAttribute) && (!(component is XSAttributeGroupDefinition))) - throw RuntimeException.InvalidArgumentType(); - - component.BindToContainer(RootContainer, this); - Components.Add(component); - - _type.Attributes.Add(component.SchemaObject); - } - - private void Attributes_Cleared(object sender, EventArgs e) - { - Components.RemoveAll(x => (x is IXSAttribute)); - - _type.Attributes.Clear(); - } - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSSimpleTypeDefinition.cs b/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSSimpleTypeDefinition.cs deleted file mode 100644 index 5bf5452d3..000000000 --- a/src/ScriptEngine.HostedScript/Library/XMLSchema/Objects/XSSimpleTypeDefinition.cs +++ /dev/null @@ -1,306 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Xml; -using System.Xml.Schema; -using ScriptEngine.HostedScript.Library.Xml; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.XMLSchema -{ - [ContextClass("ОпределениеПростогоТипаXS", "XSSimpleTypeDefinition")] - public class XSSimpleTypeDefinition : AutoContext, IXSType, IXSNamedComponent - { - private readonly XmlSchemaSimpleType _type; - private XSAnnotation _annotation; - private XMLExpandedName _baseTypeName; - private XMLExpandedName _itemTypeName; - private XSSimpleTypeVariety _variety; - - private XSSimpleTypeDefinition() - { - _type = new XmlSchemaSimpleType(); - Facets = new XSComponentList(); - Facets.Inserted += Facets_Inserted; - Facets.Cleared += Facets_Cleared; - - MemberTypeDefinitions = new XSComponentList(); - MemberTypeDefinitions.Inserted += MemberTypeDefinitions_Inserted; - MemberTypeDefinitions.Cleared += MemberTypeDefinitions_Cleared; - - Components = new XSComponentFixedList(); - Variety = XSSimpleTypeVariety.Atomic; - } - - internal XSSimpleTypeDefinition(XmlSchemaSimpleType simpleType) - : this() - { - _type = simpleType; - - if (_type.Annotation is XmlSchemaAnnotation annotation) - { - _annotation = XMLSchemaSerializer.CreateXSAnnotation(annotation); - _annotation.BindToContainer(RootContainer, this); - } - - if (_type.Content is XmlSchemaSimpleTypeList typeList) - { - _variety = XSSimpleTypeVariety.List; - - if (typeList.ItemTypeName is XmlQualifiedName qualifiedName) - _itemTypeName = new XMLExpandedName(qualifiedName); - - } - else if (_type.Content is XmlSchemaSimpleTypeUnion typeUnion) - { - _variety = XSSimpleTypeVariety.Union; - - MemberTypeDefinitions.Inserted -= MemberTypeDefinitions_Inserted; - foreach (XmlSchemaObject item in typeUnion.BaseTypes) - { - IXSComponent component = XMLSchemaSerializer.CreateInstance(item); - component.BindToContainer(RootContainer, this); - MemberTypeDefinitions.Add(component); - Components.Add(component); - } - MemberTypeDefinitions.Inserted += MemberTypeDefinitions_Inserted; - } - else if (_type.Content is XmlSchemaSimpleTypeRestriction typeRestriction) - { - _variety = XSSimpleTypeVariety.Atomic; - - if (typeRestriction.BaseTypeName is XmlQualifiedName qualifiedName) - _baseTypeName = new XMLExpandedName(qualifiedName); - - Facets.Inserted -= Facets_Inserted; - foreach (XmlSchemaObject item in typeRestriction.Facets) - { - IXSComponent component = XMLSchemaSerializer.CreateInstance(item); - component.BindToContainer(RootContainer, this); - Facets.Add(component); - Components.Add(component); - } - Facets.Inserted += Facets_Inserted; - } - } - - #region OneScript - - #region Properties - - [ContextProperty("Аннотация", "Annotation")] - public XSAnnotation Annotation - { - get => _annotation; - set - { - _annotation = value; - _annotation?.BindToContainer(RootContainer, this); - XSAnnotation.SetComponentAnnotation(_annotation, _type); - } - } - - [ContextProperty("Компоненты", "Components")] - public XSComponentFixedList Components { get; } - - [ContextProperty("Контейнер", "Container")] - public IXSComponent Container { get; private set; } - - [ContextProperty("КорневойКонтейнер", "RootContainer")] - public IXSComponent RootContainer { get; private set; } - - [ContextProperty("Схема", "Schema")] - public XMLSchema Schema => RootContainer.Schema; - - [ContextProperty("ТипКомпоненты", "ComponentType")] - public XSComponentType ComponentType => XSComponentType.SimpleTypeDefinition; - - [ContextProperty("ЭлементDOM", "DOMElement")] - public IValue DOMElement => ValueFactory.Create(); - - [ContextProperty("URIПространстваИмен", "NamespaceURI")] - public string URIПространстваИмен => _type.SourceUri; - - [ContextProperty("Имя", "Name")] - public string Name - { - get => _type.Name; - set => _type.Name = value; - } - - [ContextProperty("БазовыйТип", "BaseType")] - public XSSimpleTypeDefinition BaseType { get; } - - [ContextProperty("КорневойТип", "RootType")] - public XSSimpleTypeDefinition RootType { get; } - - [ContextProperty("АннотацияВарианта", "VarietyAnnotation")] - public XSAnnotation VarietyAnnotation { get; set; } - - [ContextProperty("Вариант", "Variety")] - public XSSimpleTypeVariety Variety - { - get => _variety; - set - { - _variety = value; - - if (_variety == XSSimpleTypeVariety.List) - _type.Content = new XmlSchemaSimpleTypeList(); - - else if (_variety == XSSimpleTypeVariety.Union) - _type.Content = new XmlSchemaSimpleTypeUnion(); - - else - _type.Content = new XmlSchemaSimpleTypeRestriction(); - } - } - - [ContextProperty("Завершенность", "Final")] - public XSSimpleFinalUnion Final { get; } - - [ContextProperty("ИменаТиповОбъединения", "MemberTypeNames")] - public XMLExpandedNameList MemberTypeNames { get; } - - [ContextProperty("ИмяБазовогоТипа", "BaseTypeName")] - public XMLExpandedName BaseTypeName - { - get => _baseTypeName; - set - { - _baseTypeName = value; - if (Variety == XSSimpleTypeVariety.Atomic) - { - XmlSchemaSimpleTypeRestriction __content = _type.Content as XmlSchemaSimpleTypeRestriction; - __content.BaseTypeName = _baseTypeName.NativeValue; - } - else - throw RuntimeException.InvalidArgumentValue(); - } - } - - [ContextProperty("ИмяТипаЭлемента", "ItemTypeName")] - public XMLExpandedName ItemTypeName - { - get => _itemTypeName; - set - { - _itemTypeName = value; - if (Variety == XSSimpleTypeVariety.List) - { - XmlSchemaSimpleTypeList __content = _type.Content as XmlSchemaSimpleTypeList; - __content.ItemTypeName = _itemTypeName.NativeValue; - } - } - } - - [ContextProperty("ОпределениеБазовогоТипа", "BaseTypeDefinition")] - public XSSimpleTypeDefinition BaseTypeDefinition { get; set; } - - [ContextProperty("ОпределениеПримитивногоТипа", "PrimitiveTypeDefinition")] - public XSSimpleTypeDefinition PrimitiveTypeDefinition { get; set; } - - [ContextProperty("ОпределениеТипаЭлемента", "ItemTypeDefinition")] - public XSSimpleTypeDefinition ItemTypeDefinition { get; set; } - - [ContextProperty("ОпределенияТиповОбъединения", "MemberTypeDefinitions")] - public XSComponentList MemberTypeDefinitions { get; } - - [ContextProperty("Фасеты", "Facets")] - public XSComponentList Facets { get; } - - #endregion - - #region Methods - - [ContextMethod("КлонироватьКомпоненту", "CloneComponent")] - public IXSComponent CloneComponent(bool recursive = true) => throw new NotImplementedException(); - - [ContextMethod("ОбновитьЭлементDOM", "UpdateDOMElement")] - public void UpdateDOMElement() => throw new NotImplementedException(); - - [ContextMethod("Содержит", "Contains")] - public bool Contains(IXSComponent component) => Components.Contains(component); - - [ContextMethod("ЭтоОпределениеЗациклено", "IsCircular")] - public bool IsCircular() => throw new NotImplementedException(); - - #endregion - - #region Constructors - - [ScriptConstructor(Name = "По умолчанию")] - public static XSSimpleTypeDefinition Constructor() => new XSSimpleTypeDefinition(); - - #endregion - - #endregion - - #region IXSComponent - - public void BindToContainer(IXSComponent rootContainer, IXSComponent container) - { - RootContainer = rootContainer; - Container = container; - } - - XmlSchemaObject IXSComponent.SchemaObject => _type; - public XmlSchemaSimpleType SchemaObject => _type; - - #endregion - - #region XSComponentListEvents - - private void Facets_Inserted(object sender, XSComponentListEventArgs e) - { - IXSComponent component = e.Component; - - if (!(component is IXSFacet)) - throw RuntimeException.InvalidArgumentType(); - - component.BindToContainer(RootContainer, this); - Components.Add(component); - - if (_type.Content is XmlSchemaSimpleTypeRestriction content) - content.Facets.Add(component.SchemaObject); - } - - private void Facets_Cleared(object sender, EventArgs e) - { - Components.Clear(); - - if (_type.Content is XmlSchemaSimpleTypeRestriction content) - content.Facets.Clear(); - } - - private void MemberTypeDefinitions_Inserted(object sender, XSComponentListEventArgs e) - { - IXSComponent component = e.Component; - - if (!(component is XSSimpleTypeDefinition)) - throw RuntimeException.InvalidArgumentType(); - - component.BindToContainer(RootContainer, this); - Components.Add(component); - - if (_type.Content is XmlSchemaSimpleTypeUnion content) - content.BaseTypes.Add(component.SchemaObject); - } - - private void MemberTypeDefinitions_Cleared(object sender, EventArgs e) - { - Components.Clear(); - - if (_type.Content is XmlSchemaSimpleTypeUnion content) - content.BaseTypes.Clear(); - } - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XMLTypeAssignment.cs b/src/ScriptEngine.HostedScript/Library/Xml/XMLTypeAssignment.cs deleted file mode 100644 index b5997cd6b..000000000 --- a/src/ScriptEngine.HostedScript/Library/Xml/XMLTypeAssignment.cs +++ /dev/null @@ -1,19 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.Xml -{ - [EnumerationType("XMLTypeAssignment", "НазначениеТипаXML")] - public enum XMLTypeAssignment - { - [EnumItem("Implicit", "Неявное")] - Implicit, - - [EnumItem("Explicit", "Явное")] - Explicit - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XmlGlobalFunctions.cs b/src/ScriptEngine.HostedScript/Library/Xml/XmlGlobalFunctions.cs deleted file mode 100644 index 13d301f16..000000000 --- a/src/ScriptEngine.HostedScript/Library/Xml/XmlGlobalFunctions.cs +++ /dev/null @@ -1,92 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.Xml; - -using ScriptEngine.HostedScript.Library.Binary; - -namespace ScriptEngine.HostedScript.Library.Xml -{ - [GlobalContext(Category="Функции работы с XML")] - public class XmlGlobalFunctions : GlobalContextBase - { - [ContextMethod("XMLСтрока", "XMLString")] - public string XMLString(IValue value) - { - switch(value.DataType) - { - case DataType.Undefined: - return ""; - case DataType.Boolean: - return XmlConvert.ToString(value.AsBoolean()); - case DataType.Date: - return XmlConvert.ToString(value.AsDate(), XmlDateTimeSerializationMode.Unspecified); - case DataType.Number: - return XmlConvert.ToString(value.AsNumber()); - default: - - if(value.SystemType.Equals(TypeManager.GetTypeByFrameworkType(typeof(BinaryDataContext)))) - { - var bdc = value.GetRawValue() as BinaryDataContext; - System.Diagnostics.Debug.Assert(bdc != null); - - return Convert.ToBase64String(bdc.Buffer, Base64FormattingOptions.InsertLineBreaks); - } - else - { - return value.GetRawValue().AsString(); - } - - } - } - - [ContextMethod("XMLЗначение", "XMLValue")] - public IValue XMLValue(IValue givenType, string presentation) - { - var typeValue = TypeManager.GetTypeDescriptorFor(givenType.GetRawValue()); - - if(typeValue.Equals(TypeDescriptor.FromDataType(DataType.Boolean))) - { - return ValueFactory.Create(XmlConvert.ToBoolean(presentation)); - } - else if (typeValue.Equals(TypeDescriptor.FromDataType(DataType.Date))) - { - return ValueFactory.Create(XmlConvert.ToDateTime(presentation, XmlDateTimeSerializationMode.Unspecified)); - } - else if (typeValue.Equals(TypeDescriptor.FromDataType(DataType.Number))) - { - return ValueFactory.Create(XmlConvert.ToDecimal(presentation)); - } - else if (typeValue.Equals(TypeDescriptor.FromDataType(DataType.String))) - { - return ValueFactory.Create(presentation); - } - else if (typeValue.Equals(TypeDescriptor.FromDataType(DataType.Undefined)) && presentation == "") - { - return ValueFactory.Create(); - } - else if (typeValue.Equals(TypeManager.GetTypeByFrameworkType(typeof(BinaryDataContext)))) - { - byte[] bytes = Convert.FromBase64String(presentation); - return new BinaryDataContext(bytes); - } - else - { - throw RuntimeException.InvalidArgumentValue(); - } - - } - - public static IAttachableContext CreateInstance() - { - return new XmlGlobalFunctions(); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XmlNodeTypeEnum.cs b/src/ScriptEngine.HostedScript/Library/Xml/XmlNodeTypeEnum.cs deleted file mode 100644 index a7737ac18..000000000 --- a/src/ScriptEngine.HostedScript/Library/Xml/XmlNodeTypeEnum.cs +++ /dev/null @@ -1,72 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System.Collections.Generic; -using System.Linq; -using System.Xml; - -namespace ScriptEngine.HostedScript.Library.Xml -{ - [SystemEnum("ТипУзлаXML", "XMLNodeType")] - public class XmlNodeTypeEnum : EnumerationContext - { - readonly Dictionary _valuesCache = new Dictionary(); - - private XmlNodeTypeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - - } - - public IValue FromNativeValue(XmlNodeType native) - { - if (native == XmlNodeType.SignificantWhitespace) - native = XmlNodeType.Whitespace; - - IValue val; - if(_valuesCache.TryGetValue(native, out val)) - { - return val; - } - else - { - val = this.ValuesInternal.First(x => ((CLREnumValueWrapper)x).UnderlyingValue == native); - _valuesCache.Add(native, val); - } - - return val; - } - - public static XmlNodeTypeEnum CreateInstance() - { - XmlNodeTypeEnum instance; - var type = TypeManager.RegisterType("ПеречислениеТипУзлаXML", typeof(XmlNodeTypeEnum)); - var enumValueType = TypeManager.RegisterType("ТипУзлаXML", typeof(CLREnumValueWrapper)); - - instance = new XmlNodeTypeEnum(type, enumValueType); - - instance.AddValue("Атрибут", "Attribute", new CLREnumValueWrapper(instance, XmlNodeType.Attribute)); - instance.AddValue("ИнструкцияОбработки", "ProcessingInstruction", new CLREnumValueWrapper(instance, XmlNodeType.ProcessingInstruction)); - instance.AddValue("Комментарий", "Comment", new CLREnumValueWrapper(instance, XmlNodeType.Comment)); - instance.AddValue("КонецСущности", "EndEntity", new CLREnumValueWrapper(instance, XmlNodeType.EndEntity)); - instance.AddValue("КонецЭлемента", "EndElement", new CLREnumValueWrapper(instance, XmlNodeType.EndElement)); - instance.AddValue("НачалоЭлемента", "StartElement", new CLREnumValueWrapper(instance, XmlNodeType.Element)); - instance.AddValue("Ничего", "None", new CLREnumValueWrapper(instance, XmlNodeType.None)); - instance.AddValue("Нотация", "Notation", new CLREnumValueWrapper(instance, XmlNodeType.Notation)); - instance.AddValue("ОбъявлениеXML", "XMLDeclaration", new CLREnumValueWrapper(instance, XmlNodeType.XmlDeclaration)); - instance.AddValue("ОпределениеТипаДокумента", "DocumentTypeDefinition", new CLREnumValueWrapper(instance, XmlNodeType.DocumentType)); - instance.AddValue("ПробельныеСимволы", "Whitespace", new CLREnumValueWrapper(instance, XmlNodeType.Whitespace)); - instance.AddValue("СекцияCDATA", "CDATASection", new CLREnumValueWrapper(instance, XmlNodeType.CDATA)); - instance.AddValue("СсылкаНаСущность", "EntityReference", new CLREnumValueWrapper(instance, XmlNodeType.EntityReference)); - instance.AddValue("Сущность", "Entity", new CLREnumValueWrapper(instance, XmlNodeType.Entity)); - instance.AddValue("Текст", "Text", new CLREnumValueWrapper(instance, XmlNodeType.Text)); - - return instance; - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XmlReaderImpl.cs b/src/ScriptEngine.HostedScript/Library/Xml/XmlReaderImpl.cs deleted file mode 100644 index 15f0d431f..000000000 --- a/src/ScriptEngine.HostedScript/Library/Xml/XmlReaderImpl.cs +++ /dev/null @@ -1,523 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.IO; -using System.Xml; - -namespace ScriptEngine.HostedScript.Library.Xml -{ - [ContextClass("ЧтениеXML","XMLReader")] - public class XmlReaderImpl : AutoContext, IDisposable - { - XmlTextReader _reader; - EmptyElemCompabilityState _emptyElemReadState = EmptyElemCompabilityState.Off; - bool _attributesLoopReset = false; - - private enum EmptyElemCompabilityState - { - Off, - EmptyElementEntered, - EmptyElementRead - } - - public XmlReader GetNativeReader() - { - return _reader; - } - - [ContextMethod("ОткрытьФайл", "OpenFile")] - public void OpenFile(string path) - { - if (_reader != null) - throw new RuntimeException("Поток XML уже открыт"); - var textInput = new StreamReader(path); - InitReader(textInput); - } - - [ContextMethod("УстановитьСтроку", "SetString")] - public void SetString(string content) - { - if (_reader != null) - throw new RuntimeException("Поток XML уже открыт"); - - var textInput = new StringReader(content); - InitReader(textInput); - } - - private void InitReader(TextReader textInput) - { - _reader = new XmlTextReader(textInput); - _reader.WhitespaceHandling = WhitespaceHandling.Significant; - } - - private void CheckIfOpen() - { - if (_reader == null) - throw new RuntimeException("Файл не открыт"); - } - - #region Свойства - - [ContextProperty("URIПространстваИмен", "NamespaceURI")] - public string NamespaceURI - { - get - { - return _reader.NamespaceURI; - } - } - - [ContextProperty("Автономный", "Standalone")] - public bool Standalone - { - get - { - throw new NotSupportedException(); - } - } - - [ContextProperty("БазовыйURI", "BaseURI")] - public string BaseURI - { - get - { - return _reader.BaseURI; - } - } - - [ContextProperty("ВерсияXML", "XMLVersion")] - public string XMLVersion - { - get - { - return "1.0"; - } - } - - [ContextProperty("Значение", "Value")] - public string Value - { - get - { - return _reader.Value; - } - } - - [ContextProperty("ИмеетЗначение", "HasValue")] - public bool HasValue - { - get - { - return _reader.HasValue; - } - } - - [ContextProperty("ИмеетИмя", "HasName")] - public bool HasName - { - get - { - return _reader.LocalName != String.Empty; - } - } - - [ContextProperty("Имя", "Name")] - public string Name - { - get - { - return _reader.Name; - } - } - - [ContextProperty("ИмяНотации", "NotationName")] - public string NotationName - { - get - { - throw new NotSupportedException(); - } - } - - [ContextProperty("КодировкаXML", "XMLEncoding")] - public string XMLEncoding - { - get - { - return _reader.Encoding.WebName; - } - } - - [ContextProperty("КодировкаИсточника", "InputEncoding")] - public string InputEncoding - { - get - { - return XMLEncoding; - } - } - - private int Depth - { - get - { - if (_reader.NodeType == XmlNodeType.EndElement) - return _reader.Depth; - - if (_emptyElemReadState == EmptyElemCompabilityState.EmptyElementRead) - return _reader.Depth; - - return _reader.Depth + 1; - } - } - - [ContextProperty("КонтекстПространствИмен", "NamespaceContext")] - public XmlNamespaceContext NamespaceContext - { - get - { - return new XmlNamespaceContext(Depth, _reader.GetNamespacesInScope(XmlNamespaceScope.All)); - } - } - - [ContextProperty("ЛокальноеИмя", "LocalName")] - public string LocalName - { - get - { - return _reader.LocalName; - } - } - - [ContextProperty("Префикс", "Prefix")] - public string Prefix - { - get - { - return _reader.Prefix; - } - } - - [ContextProperty("ПубличныйИдентификатор", "PublicId")] - public string PublicId - { - get - { - throw new NotSupportedException(); - } - } - - [ContextProperty("СистемныйИдентификатор", "SystemId")] - public string SystemId - { - get - { - throw new NotSupportedException(); - } - } - - [ContextProperty("ТипУзла", "NodeType")] - public IValue NodeType - { - get - { - if (_emptyElemReadState == EmptyElemCompabilityState.EmptyElementRead) - { - return GlobalsManager.GetEnum().FromNativeValue(XmlNodeType.EndElement); - } - else - { - return GlobalsManager.GetEnum().FromNativeValue(_reader.NodeType); - } - } - } - - [ContextProperty("ЭтоАтрибутПоУмолчанию", "IsDefaultAttribute")] - public bool IsDefaultAttribute - { - get - { - return _reader.IsDefault; - } - } - - [ContextProperty("ЭтоПробельныеСимволы", "IsWhitespace")] - public bool IsWhitespace - { - get - { - throw new NotSupportedException(); - } - } - - [ContextProperty("Язык", "Lang")] - public string Lang - { - get - { - return _reader.XmlLang; - } - } - - [ContextProperty("ИгнорироватьПробелы", "IgnoreWhitespace")] - public bool IgnoreWhitespace - { - get - { - return _reader.WhitespaceHandling == WhitespaceHandling.None; - } - set - { - _reader.WhitespaceHandling = value ? WhitespaceHandling.None : WhitespaceHandling.All; - } - } - - [ContextProperty("Параметры", "Settings")] - public object Settings - { - get - { - throw new NotSupportedException(); - } - } - - [ContextProperty("ПробельныеСимволы", "Space")] - public object Space - { - get - { - throw new NotImplementedException(); - //return _reader.XmlSpace; - } - } - - [ContextProperty("ЭтоСимвольныеДанные", "IsCharacters")] - public bool IsCharacters - { - get - { - return _reader.NodeType == XmlNodeType.Text || _reader.NodeType == XmlNodeType.CDATA || _reader.NodeType == XmlNodeType.SignificantWhitespace; - } - } - #endregion - - #region Методы - [ContextMethod("URIПространстваИменАтрибута", "AttributeNamespaceURI")] - public string AttributeNamespaceURI(int index) - { - throw new NotImplementedException(); - } - - [ContextMethod("ЗначениеАтрибута", "AttributeValue")] - public IValue AttributeValue(IValue indexOrName, string URIIfGiven = null) - { - string attributeValue = null; - - if (indexOrName.DataType == DataType.Number) - { - attributeValue = _reader.GetAttribute((int)indexOrName.AsNumber()); - } - else if (indexOrName.DataType == DataType.String) - { - if (URIIfGiven == null) - attributeValue = _reader.GetAttribute(indexOrName.AsString()); - else - attributeValue = _reader.GetAttribute(indexOrName.AsString(), URIIfGiven); - } - else - { - throw RuntimeException.InvalidArgumentType(); - } - - if (attributeValue != null) - return ValueFactory.Create(attributeValue); - else - return ValueFactory.Create(); - - } - - [ContextMethod("ИмяАтрибута", "AttributeName")] - public string AttributeName(int index) - { - _reader.MoveToAttribute(index); - var name = _reader.Name; - _reader.MoveToElement(); - - return name; - } - [ContextMethod("КоличествоАтрибутов", "AttributeCount")] - public int AttributeCount() - { - return _reader.AttributeCount; - } - - [ContextMethod("ЛокальноеИмяАтрибута", "AttributeLocalName")] - public string AttributeLocalName(int index) - { - _reader.MoveToAttribute(index); - var name = _reader.LocalName; - _reader.MoveToElement(); - - return name; - } - - [ContextMethod("ПервоеОбъявление", "FirstDeclaration")] - public bool FirstDeclaration() - { - throw new NotImplementedException(); - } - - [ContextMethod("ПервыйАтрибут", "FirstAttribute")] - public bool FirstAttribute() - { - return _reader.MoveToFirstAttribute(); - } - - [ContextMethod("ПолучитьАтрибут", "GetAttribute")] - public IValue GetAttribute(IValue indexOrName, string URIIfGiven = null) - { - return AttributeValue(indexOrName, URIIfGiven); - } - - [ContextMethod("ПрефиксАтрибута", "AttributePrefix")] - public string AttributePrefix(int index) - { - _reader.MoveToAttribute(index); - var name = _reader.Prefix; - _reader.MoveToElement(); - - return name; - } - - [ContextMethod("Пропустить", "Skip")] - public void Skip() - { - if(_emptyElemReadState == EmptyElemCompabilityState.EmptyElementEntered) - { - _emptyElemReadState = EmptyElemCompabilityState.EmptyElementRead; - return; - } - - V8CompatibleSkip(); - CheckEmptyElementEntering(); - } - - private void V8CompatibleSkip() - { - if (_reader.NodeType == XmlNodeType.Element) - { - int initialDepth = _reader.Depth; - while (_reader.Read() && _reader.Depth > initialDepth) ; - System.Diagnostics.Debug.Assert(_reader.NodeType == XmlNodeType.EndElement); - } - else - { - _reader.Skip(); - } - } - - [ContextMethod("Прочитать", "Read")] - public bool Read() - { - if (_emptyElemReadState == EmptyElemCompabilityState.EmptyElementEntered) - { - _emptyElemReadState = EmptyElemCompabilityState.EmptyElementRead; - return true; - } - else - { - bool readingDone = _reader.Read(); - CheckEmptyElementEntering(); - return readingDone; - } - } - - private void CheckEmptyElementEntering() - { - _attributesLoopReset = false; - if (_reader.IsEmptyElement) - _emptyElemReadState = EmptyElemCompabilityState.EmptyElementEntered; - else - _emptyElemReadState = EmptyElemCompabilityState.Off; - } - - private bool IsEndElement() - { - var isEnd = (NodeType == GlobalsManager.GetEnum().FromNativeValue(XmlNodeType.EndElement)); - return isEnd; - } - - private bool ReadAttributeInternal() - { - if (IsEndElement() && !_attributesLoopReset) - { - _attributesLoopReset = true; - return _reader.MoveToFirstAttribute(); - } - - return _reader.MoveToNextAttribute(); - } - - [ContextMethod("ПрочитатьАтрибут", "ReadAttribute")] - public bool ReadAttribute() - { - return ReadAttributeInternal(); - } - - [ContextMethod("СледующееОбъявление", "NextDeclaration")] - public void NextDeclaration() - { - throw new NotImplementedException(); - } - - [ContextMethod("СледующийАтрибут", "NextAttribute")] - public bool NextAttribute() - { - return ReadAttributeInternal(); - } - - [ContextMethod("ТипАтрибута", "AttributeType")] - public void AttributeType() - { - throw new NotImplementedException(); - } - - [ContextMethod("Закрыть", "Close")] - public void Close() - { - Dispose(); - } - - [ContextMethod("ПерейтиКСодержимому", "MoveToContent")] - public IValue MoveToContent() - { - var nodeType = _reader.MoveToContent(); - CheckEmptyElementEntering(); - return GlobalsManager.GetEnum().FromNativeValue(nodeType); - } - - #endregion - - public void Dispose() - { - if (_reader != null) - { - _reader.Close(); - _reader = null; - } - } - - [ScriptConstructor] - public static XmlReaderImpl Create() - { - return new XmlReaderImpl(); - } - - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Xml/XmlWriterImpl.cs b/src/ScriptEngine.HostedScript/Library/Xml/XmlWriterImpl.cs deleted file mode 100644 index 8028c51a2..000000000 --- a/src/ScriptEngine.HostedScript/Library/Xml/XmlWriterImpl.cs +++ /dev/null @@ -1,361 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Xml; - -namespace ScriptEngine.HostedScript.Library.Xml -{ - [ContextClass("ЗаписьXML", "XMLWriter")] - public class XmlWriterImpl : AutoContext, IDisposable - { - private TextWriterWithSettings _internalTextWriter; - private XmlTextWriter _writer; - private XmlWriterSettingsImpl _settings = (XmlWriterSettingsImpl)XmlWriterSettingsImpl.Constructor(); - private int _depth; - private Stack> _nsmap = new Stack>(); - private StringBuilder _stringBuffer; - - private const string DEFAULT_INDENT_STRING = " "; - - public XmlWriterImpl() - { - Indent = true; - _nsmap.Push(new Dictionary()); - } - - private void EnterScope() - { - ++_depth; - var newMap = _nsmap.Peek().ToDictionary((kv) => kv.Key, (kv) => kv.Value); - _nsmap.Push(newMap); - } - - private void ExitScope() - { - _nsmap.Pop(); - --_depth; - } - - #region Properties - - [ContextProperty("Отступ","Indent")] - public bool Indent { get; set; } - - [ContextProperty("КонтекстПространствИмен", "NamespaceContext")] - public XmlNamespaceContext NamespaceContext - { - get - { - return new XmlNamespaceContext(_depth, _nsmap.Peek()); - } - } - - [ContextProperty("Параметры", "Settings")] - public XmlWriterSettingsImpl Settings - { - get { return _settings; } - } - - #endregion - - #region Methods - - [ContextMethod("ЗаписатьАтрибут","WriteAttribute")] - public void WriteAttribute(string localName, string valueOrNamespace, string value = null) - { - if(value == null) - { - _writer.WriteAttributeString(localName, valueOrNamespace); - } - else - { - _writer.WriteAttributeString(localName, valueOrNamespace, value); - } - } - - [ContextMethod("ЗаписатьБезОбработки","WriteRaw")] - public void WriteRaw(string data) - { - _writer.WriteRaw(data); - } - - [ContextMethod("ЗаписатьИнструкциюОбработки","WriteProcessingInstruction")] - public void WriteProcessingInstruction(string name, string text) - { - _writer.WriteProcessingInstruction(name, text); - } - - [ContextMethod("ЗаписатьКомментарий","WriteComment")] - public void WriteComment(string text) - { - _writer.WriteComment(text); - } - - [ContextMethod("ЗаписатьКонецАтрибута","WriteEndAttribute")] - public void WriteEndAttribute() - { - _writer.WriteEndAttribute(); - } - - [ContextMethod("ЗаписатьКонецЭлемента","WriteEndElement")] - public void WriteEndElement() - { - _internalTextWriter.TrimEndSlashes = true; - _writer.WriteEndElement(); - _internalTextWriter.TrimEndSlashes = false; - ExitScope(); - } - - [ContextMethod("ЗаписатьНачалоАтрибута","WriteStartAttribute")] - public void WriteStartAttribute(string name, string ns = null) - { - if(ns == null) - { - _writer.WriteStartAttribute(name); - } - else - { - _writer.WriteStartAttribute(name, ns); - } - - } - - [ContextMethod("ЗаписатьНачалоЭлемента","WriteStartElement")] - public void WriteStartElement(string name, string ns = null) - { - if (ns == null) - { - _writer.WriteStartElement(name); - } - else - { - _writer.WriteStartElement(name, ns); - } - EnterScope(); - } - - [ContextMethod("ЗаписатьОбъявлениеXML","WriteXMLDeclaration")] - public void WriteXMLDeclaration() - { - _writer.WriteStartDocument(); - } - - [ContextMethod("ЗаписатьСекциюCDATA","WriteCDATASection")] - public void WriteCDATASection(string data) - { - _writer.WriteCData(data); - } - - [ContextMethod("ЗаписатьСоответствиеПространстваИмен","WriteNamespaceMapping")] - public void WriteNamespaceMapping(string prefix, string uri) - { - _writer.WriteAttributeString("xmlns", prefix, null, uri); - _nsmap.Peek()[prefix] = uri; - } - - [ContextMethod("ЗаписатьСсылкуНаСущность","WriteEntityReference")] - public void WriteEntityReference(string name) - { - _writer.WriteEntityRef(name); - } - - [ContextMethod("ЗаписатьТекст","WriteText")] - public void WriteText(string text) - { - _writer.WriteString(text); - } - - [ContextMethod("ЗаписатьТекущий","WriteCurrent")] - public void WriteCurrent(XmlReaderImpl reader) - { - _writer.WriteNode(reader.GetNativeReader(), false); - } - - [ContextMethod("ЗаписатьТипДокумента","WriteDocumentType")] - public void WriteDocumentType(string name, string varArg2, string varArg3 = null, string varArg4 = null) - { - if(varArg4 != null) - { - _writer.WriteDocType(name, varArg2, varArg3, varArg4); - } - else if(varArg3 != null) - { - _writer.WriteDocType(name, null, varArg2, varArg3); - } - else - { - _writer.WriteDocType(name, null, null, varArg2); - } - } - - [ContextMethod("НайтиПрефикс","LookupPrefix")] - public IValue LookupPrefix(string uri) - { - string prefix = _writer.LookupPrefix(uri); - if (prefix == null) - return ValueFactory.Create(); - return ValueFactory.Create(prefix); - } - - [ContextMethod("Закрыть","Close")] - public IValue Close() - { - if(IsOpenForString()) - { - _writer.Flush(); - _writer.Close(); - - Dispose(); - - var result = _stringBuffer.ToString(); - _stringBuffer = null; - return ValueFactory.Create(result); - } - else - { - _writer.Flush(); - _writer.Close(); - Dispose(); - - return ValueFactory.Create(); - } - - } - - private void ApplySettings(IValue encodingOrSettings) - { - var rawEncoding = encodingOrSettings?.GetRawValue(); - if (rawEncoding is XmlWriterSettingsImpl) - { - _settings = rawEncoding as XmlWriterSettingsImpl; - } - else if ((encodingOrSettings?.DataType ?? DataType.String) == DataType.String) - { - _settings = (XmlWriterSettingsImpl) XmlWriterSettingsImpl.Constructor(encodingOrSettings, null, - ValueFactory.Create(Indent), null, ValueFactory.Create(DEFAULT_INDENT_STRING)); - } - else - { - throw RuntimeException.InvalidArgumentType(nameof(encodingOrSettings)); - } - } - - [ContextMethod("ОткрытьФайл","OpenFile")] - public void OpenFile(string path, IValue encodingOrSettings = null, IValue addBOM = null) - { - ApplySettings(encodingOrSettings); - var fs = new FileStream(path, FileMode.Create, FileAccess.Write); - var clrSettings = _settings.GetClrSettings(addBOM?.AsBoolean() ?? true); - _internalTextWriter = new TextWriterWithSettings(fs, clrSettings); - _writer = new XmlTextWriter(_internalTextWriter); - SetDefaultOptions(); - } - - [ContextMethod("УстановитьСтроку","SetString")] - public void SetString(IValue encodingOrSettings = null) - { - ApplySettings(encodingOrSettings); - _stringBuffer = new StringBuilder(); - _internalTextWriter = new TextWriterWithSettings(_stringBuffer, _settings.GetClrSettings()); - _writer = new XmlTextWriter(_internalTextWriter); - SetDefaultOptions(); - } - - private void SetDefaultOptions() - { - if (Settings.Indent) - { - _writer.IndentChar = '\xfeff'; - _writer.Formatting = Formatting.Indented; - _writer.Indentation = 1; - } - } - - #endregion - - private bool IsOpenForString() - { - return _stringBuffer != null; - } - - private sealed class TextWriterWithSettings : TextWriter - { - private readonly TextWriter _baseObject; - private readonly XmlWriterSettings _settings; - - public TextWriterWithSettings(Stream stream, XmlWriterSettings settings) - { - _baseObject = new StreamWriter(stream, settings.Encoding); - _settings = settings; - } - - public TextWriterWithSettings(StringBuilder buffer, XmlWriterSettings settings) - { - _baseObject = new StringWriter(buffer); - _settings = settings; - } - - /// - /// Признак необходимости заменять строку ` /` на `/`. - /// См. https://github.com/EvilBeaver/OneScript/issues/768#issuecomment-397848410 - /// - public bool TrimEndSlashes { get; set; } - - public override Encoding Encoding => _settings.Encoding; - - public override void Write(char value) - { - if (value == '\xfeff') - { - _baseObject.Write(_settings.IndentChars); - } - else - { - _baseObject.Write(value); - } - } - - public override void Write(string value) - { - if (value == " /" && TrimEndSlashes) - { - base.Write("/"); - return; - } - base.Write(value); - } - - public override void Close() - { - _baseObject.Close(); - base.Close(); - } - } - - public void Dispose() - { - if (_writer != null) - _writer.Close(); - - _writer = null; - } - - [ScriptConstructor] - public static XmlWriterImpl Create() - { - return new XmlWriterImpl(); - } - - public XmlWriter GetNativeWriter() => _writer; - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/FileNamesEncodingInZipFile.cs b/src/ScriptEngine.HostedScript/Library/Zip/FileNamesEncodingInZipFile.cs deleted file mode 100644 index c13ea6548..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/FileNamesEncodingInZipFile.cs +++ /dev/null @@ -1,22 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.HostedScript.Library.Zip -{ - [EnumerationType("КодировкаИменФайловВZipФайле","FileNamesEncodingInZipFile")] - public enum FileNamesEncodingInZipFile - { - [EnumItem("Авто")] - Auto, - - [EnumItem("UTF8")] - Utf8, - - [EnumItem("КодировкаОСДополнительноUTF8","OSEncodingWithUTF8")] - OsEncodingWithUtf8 - } -} \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZIPSubDirProcessingModeEnum.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZIPSubDirProcessingModeEnum.cs deleted file mode 100644 index 204a046f4..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZIPSubDirProcessingModeEnum.cs +++ /dev/null @@ -1,46 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - [SystemEnum("РежимОбработкиПодкаталоговZIP", "ZIPSubDirProcessingMode")] - public class ZIPSubDirProcessingModeEnum : EnumerationContext - { - private const string EV_DONT_RECURSE = "НеОбрабатывать"; - private const string EV_RECURSE = "ОбрабатыватьРекурсивно"; - - private ZIPSubDirProcessingModeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue(EV_DONT_RECURSE, "DontProcess")] - public EnumerationValue DontRecurse - { - get - { - return this[EV_DONT_RECURSE]; - } - } - - [EnumValue(EV_RECURSE, "ProcessRecursively")] - public EnumerationValue Recurse - { - get - { - return this[EV_RECURSE]; - } - } - - public static ZIPSubDirProcessingModeEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new ZIPSubDirProcessingModeEnum(t, v)); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipCompressionLevelEnum.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZipCompressionLevelEnum.cs deleted file mode 100644 index 53ef4bc29..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipCompressionLevelEnum.cs +++ /dev/null @@ -1,56 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - [SystemEnum("УровеньСжатияZIP", "ZIPCompressionLevel")] - public class ZipCompressionLevelEnum : EnumerationContext - { - private const string EV_MINIMAL_NAME = "Минимальный"; - private const string EV_OPTIMAL_NAME = "Оптимальный"; - private const string EV_MAXIMAL_NAME = "Максимальный"; - - private ZipCompressionLevelEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue(EV_MINIMAL_NAME, "Minimum")] - public EnumerationValue Minimal - { - get - { - return this[EV_MINIMAL_NAME]; - } - } - - [EnumValue(EV_OPTIMAL_NAME, "Optimal")] - public EnumerationValue Optimal - { - get - { - return this[EV_OPTIMAL_NAME]; - } - } - - [EnumValue(EV_MAXIMAL_NAME, "Maximum")] - public EnumerationValue Maximal - { - get - { - return this[EV_MAXIMAL_NAME]; - } - } - - public static ZipCompressionLevelEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new ZipCompressionLevelEnum(t, v)); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipCompressionMethodEnum.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZipCompressionMethodEnum.cs deleted file mode 100644 index 3239597cb..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipCompressionMethodEnum.cs +++ /dev/null @@ -1,46 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - [SystemEnum("МетодСжатияZIP", "ZIPCompressionMethod")] - public class ZipCompressionMethodEnum : EnumerationContext - { - private const string EV_COPY_NAME = "Копирование"; - private const string EV_DEFLATE_NAME = "Сжатие"; - - private ZipCompressionMethodEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue(EV_COPY_NAME, "Copy")] - public EnumerationValue Copy - { - get - { - return this[EV_COPY_NAME]; - } - } - - [EnumValue(EV_DEFLATE_NAME, "Deflate")] - public EnumerationValue Deflate - { - get - { - return this[EV_DEFLATE_NAME]; - } - } - - public static ZipCompressionMethodEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new ZipCompressionMethodEnum(t, v)); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipEncryptionMethodEnum.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZipEncryptionMethodEnum.cs deleted file mode 100644 index 859b93624..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipEncryptionMethodEnum.cs +++ /dev/null @@ -1,66 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - [SystemEnum("МетодШифрованияZIP", "ZIPEncryptionMethod")] - public class ZipEncryptionMethodEnum : EnumerationContext - { - private const string EV_AES128 = "AES128"; - private const string EV_AES192 = "AES192"; - private const string EV_AES256 = "AES256"; - private const string EV_ZIP20 = "Zip20"; - - private ZipEncryptionMethodEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue(EV_AES128)] - public EnumerationValue Aes128 - { - get - { - return this[EV_AES128]; - } - } - - [EnumValue(EV_AES192)] - public EnumerationValue Aes192 - { - get - { - return this[EV_AES192]; - } - } - - [EnumValue(EV_AES256)] - public EnumerationValue Aes256 - { - get - { - return this[EV_AES256]; - } - } - - [EnumValue(EV_ZIP20)] - public EnumerationValue Zip20 - { - get - { - return this[EV_ZIP20]; - } - } - - public static ZipEncryptionMethodEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new ZipEncryptionMethodEnum(t, v)); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipReader.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZipReader.cs deleted file mode 100644 index 575ca24b5..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipReader.cs +++ /dev/null @@ -1,160 +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 Ionic.Zip; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.Text; -using System.IO; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - /// - /// Объект чтения ZIP файлов. - /// - [ContextClass("ЧтениеZipФайла", "ZipFileReader")] - public class ZipReader : AutoContext, IDisposable - { - ZipFile _zip; - ZipFileEntriesCollection _entriesWrapper; - - public ZipReader() - { - } - - public ZipReader(string filename, string password = null) - { - Open(filename, password); - } - - private void CheckIfOpened() - { - if(_zip == null) - throw new RuntimeException("Архив не открыт"); - } - - /// - /// Открывает архив для чтения. - /// - /// Имя ZIP файла, который требуется открыть для чтения. - /// Пароль к файлу, если он зашифрован. - /// Кодировка имен файлов в архиве. - [ContextMethod("Открыть","Open")] - public void Open(string filename, string password = null, FileNamesEncodingInZipFile encoding = FileNamesEncodingInZipFile.Auto) - { - ZipFile.DefaultEncoding = Encoding.GetEncoding(866); - // fuck non-russian encodings on non-ascii files - _zip = ZipFile.Read(filename, new ReadOptions() { Encoding = ChooseEncoding(encoding) }); - _zip.Password = password; - } - - private Encoding ChooseEncoding(FileNamesEncodingInZipFile encoding) - { - if (encoding == FileNamesEncodingInZipFile.Auto || encoding == FileNamesEncodingInZipFile.OsEncodingWithUtf8) - return null; - - return Encoding.UTF8; - - } - - - /// - /// Извлечение всех файлов из архива - /// - /// Строка. Каталог в который извлекаются файлы - /// РежимВосстановленияПутейФайловZIP - [ContextMethod("ИзвлечьВсе","ExtractAll")] - public void ExtractAll(string where, SelfAwareEnumValue restorePaths = null) - { - CheckIfOpened(); - _zip.FlattenFoldersOnExtract = FlattenPathsOnExtraction(restorePaths); - _zip.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently; - _zip.ExtractAll(where); - } - - /// - /// Извлечение элемента из архива - /// - /// ЭлементZipФайла. Извлекаемый элемент. - /// Каталог, в который извлекается элемент. - /// РежимВосстановленияПутейФайлов - /// Пароль элемента (если отличается от пароля к архиву) - [ContextMethod("Извлечь", "Extract")] - public void Extract(ZipFileEntryContext entry, string destination, SelfAwareEnumValue restorePaths = null, string password = null) - { - CheckIfOpened(); - var realEntry = entry.GetZipEntry(); - _zip.FlattenFoldersOnExtract = FlattenPathsOnExtraction(restorePaths); - realEntry.Password = password; - - using (FileStream streamToExtract = new FileStream(Path.Combine(destination, entry.Name), FileMode.Create)) - { - realEntry.Extract(streamToExtract); - } - } - - /// - /// Закрыть архив и освободить объект. - /// - [ContextMethod("Закрыть", "Close")] - public void Close() - { - Dispose(); - } - - /// - /// Коллекция элементов архива. - /// - [ContextProperty("Элементы", "Elements")] - public ZipFileEntriesCollection Elements - { - get - { - CheckIfOpened(); - - if (_entriesWrapper == null) - _entriesWrapper = new ZipFileEntriesCollection(_zip.Entries); - - return _entriesWrapper; - } - } - - private static bool FlattenPathsOnExtraction(SelfAwareEnumValue restorePaths) - { - bool flattenFlag = false; - if (restorePaths != null) - { - var zipEnum = (ZipRestoreFilePathsModeEnum)restorePaths.Owner; - flattenFlag = restorePaths == zipEnum.DoNotRestore; - } - - return flattenFlag; - } - - [ScriptConstructor(Name = "Формирование неинициализированного объекта")] - public static ZipReader Construct() - { - return new ZipReader(); - } - - [ScriptConstructor(Name = "На основании имени файла")] - public static ZipReader ConstructByNameAndPassword(IValue filename, IValue password = null) - { - return new ZipReader(filename.AsString(), password?.AsString()); - } - - public void Dispose() - { - _entriesWrapper = null; - if (_zip != null) - { - _zip.Dispose(); - _zip = null; - } - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipRestoreFilePathsModeEnum.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZipRestoreFilePathsModeEnum.cs deleted file mode 100644 index 5fa69bc36..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipRestoreFilePathsModeEnum.cs +++ /dev/null @@ -1,46 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - [SystemEnum("РежимВосстановленияПутейФайловZIP", "ZIPRestoreFilePathsMode")] - public class ZipRestoreFilePathsModeEnum : EnumerationContext - { - private const string RESTORE_PATHS_NAME = "Восстанавливать"; - private const string DONT_RESTORE_PATHS_NAME = "НеВосстанавливать"; - - private ZipRestoreFilePathsModeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - } - - [EnumValue(RESTORE_PATHS_NAME, "Restore")] - public EnumerationValue Restore - { - get - { - return this[RESTORE_PATHS_NAME]; - } - } - - [EnumValue(DONT_RESTORE_PATHS_NAME, "DontRestore")] - public EnumerationValue DoNotRestore - { - get - { - return this[DONT_RESTORE_PATHS_NAME]; - } - } - - public static ZipRestoreFilePathsModeEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new ZipRestoreFilePathsModeEnum(t, v)); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipStorePathModeEnum.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZipStorePathModeEnum.cs deleted file mode 100644 index 02b750e0a..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipStorePathModeEnum.cs +++ /dev/null @@ -1,57 +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 ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - [SystemEnum("РежимСохраненияПутейZIP", "ZIPStorePathsMode")] - public class ZipStorePathModeEnum : EnumerationContext - { - const string DONT_SAVE = "НеСохранятьПути"; - const string SAVE_RELATIVE = "СохранятьОтносительныеПути"; - const string SAVE_FULL = "СохранятьПолныеПути"; - - public ZipStorePathModeEnum(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) - : base(typeRepresentation, valuesType) - { - - } - - [EnumValue(DONT_SAVE, "DontStorePath")] - public EnumerationValue DontStorePath - { - get - { - return this[DONT_SAVE]; - } - } - - [EnumValue(SAVE_RELATIVE, "StoreRelativePath")] - public EnumerationValue StoreRelativePath - { - get - { - return this[SAVE_RELATIVE]; - } - } - - [EnumValue(SAVE_FULL, "StoreFullPath")] - public EnumerationValue StoreFullPath - { - get - { - return this[SAVE_FULL]; - } - } - - public static ZipStorePathModeEnum CreateInstance() - { - return EnumContextHelper.CreateEnumInstance((t, v) => new ZipStorePathModeEnum(t, v)); - } - } -} diff --git a/src/ScriptEngine.HostedScript/Library/Zip/ZipWriter.cs b/src/ScriptEngine.HostedScript/Library/Zip/ZipWriter.cs deleted file mode 100644 index 7a94d6c35..000000000 --- a/src/ScriptEngine.HostedScript/Library/Zip/ZipWriter.cs +++ /dev/null @@ -1,392 +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 Ionic.Zip; -using Ionic.Zlib; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace ScriptEngine.HostedScript.Library.Zip -{ - /// - /// Объект записи ZIP-архивов. - /// - [ContextClass("ЗаписьZipФайла", "ZipFileWriter")] - public class ZipWriter : AutoContext, IDisposable - { - private ZipFile _zip; - private string _filename; - - public ZipWriter() - { - - } - - /// - /// Открыть архив для записи. - /// - /// Имя файла будущего архива - /// Пароль на архив - /// Комментарий к архиву - /// МетодСжатияZIP (Сжатие/Копирование) - /// УровеньСжатияZIP (Минимальный/Оптимальный/Максимальный) - /// МетодШифрованияZIP (в текущей реализации не поддерживается) - /// Кодировка имен файлов в архиве. - [ContextMethod("Открыть", "Open")] - public void Open( - string filename, - string password = null, - string comment = null, - SelfAwareEnumValue compressionMethod = null, - SelfAwareEnumValue compressionLevel = null, - SelfAwareEnumValue encryptionMethod = null, - FileNamesEncodingInZipFile encoding = FileNamesEncodingInZipFile.Auto) - { - ZipFile.DefaultEncoding = Encoding.GetEncoding(866); // fuck non-russian encodings on non-ascii files - _filename = filename; - _zip = new ZipFile(); - _zip.AlternateEncoding = Encoding.UTF8; - _zip.AlternateEncodingUsage = ChooseEncodingMode(encoding); - _zip.Password = password; - _zip.Comment = comment; - _zip.CompressionMethod = MakeZipCompressionMethod(compressionMethod); - _zip.CompressionLevel = MakeZipCompressionLevel(compressionLevel); - _zip.UseZip64WhenSaving = Zip64Option.AsNecessary; - - // Zlib падает с NullReferenceException, если задать шифрование - //_zip.Encryption = MakeZipEncryption(encryptionMethod); - } - - private ZipOption ChooseEncodingMode(FileNamesEncodingInZipFile encoding) - { - if (encoding == FileNamesEncodingInZipFile.OsEncodingWithUtf8) - return ZipOption.AsNecessary; - - return ZipOption.Always; - } - - /// - /// Записывает и закрывает файл архива. - /// - [ContextMethod("Записать", "Write")] - public void Write() - { - CheckIfOpened(); - - _zip.Save(_filename); - Dispose(true); - } - - /// - /// Добавление файла к архиву. - /// - /// Имя файла, помещаемого в архив, или маска. - /// РежимСохраненияПутейZIP (НеСохранятьПути/СохранятьОтносительныеПути/СохранятьПолныеПути) - /// РежимОбработкиПодкаталоговZIP (НеОбрабатывать/ОбрабатыватьРекурсивно) - [ContextMethod("Добавить", "Add")] - public void Add(string file, SelfAwareEnumValue storePathMode = null, SelfAwareEnumValue recurseSubdirectories = null) - { - CheckIfOpened(); - - var pathIsMasked = file.IndexOfAny(new[] { '*', '?' }) >= 0; - - var recursiveFlag = GetRecursiveFlag(recurseSubdirectories); - var searchOption = recursiveFlag ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; - - if(pathIsMasked) - { - AddFilesByMask(file, searchOption, storePathMode); - } - else if (Directory.Exists(file)) - { - AddDirectory(file, searchOption, storePathMode); - } - else if (File.Exists(file)) - { - AddSingleFile(file, storePathMode); - } - - } - - private void AddDirectory(string dir, SearchOption searchOption, SelfAwareEnumValue storePathMode) - { - string allFilesMask; - - if (System.Environment.OSVersion.Platform == PlatformID.Unix || System.Environment.OSVersion.Platform == PlatformID.MacOSX) - allFilesMask = "*"; - else - allFilesMask = "*.*"; - - var filesToAdd = Directory.EnumerateFiles(dir, allFilesMask, searchOption); - AddEnumeratedFiles(filesToAdd, GetPathForParentFolder(dir), storePathMode); - } - - private string GetPathForParentFolder(string dir) - { - var rootPath = GetRelativePath(dir, Directory.GetCurrentDirectory()); - if (rootPath == "") - rootPath = Path.Combine(Directory.GetCurrentDirectory(), dir, ".."); - else - rootPath = Directory.GetCurrentDirectory(); - return rootPath; - } - - private void AddSingleFile(string file, SelfAwareEnumValue storePathMode) - { - var storeModeEnum = GlobalsManager.GetEnum(); - if (storePathMode == null) - storePathMode = (SelfAwareEnumValue)storeModeEnum.StoreRelativePath; - - var currDir = Directory.GetCurrentDirectory(); - - string pathInArchive; - if (storePathMode == storeModeEnum.StoreFullPath) - pathInArchive = null; - else if (storePathMode == storeModeEnum.StoreRelativePath) - { - var relativePath = GetRelativePath(file, currDir); - if (relativePath == "") - pathInArchive = "."; - else - pathInArchive = Path.GetDirectoryName(relativePath); - } - else - pathInArchive = ""; - - _zip.AddFile(file, pathInArchive); - } - - private void AddFilesByMask(string file, SearchOption searchOption, SelfAwareEnumValue storePathMode) - { - // надо разделить на каталог и маску - var pathEnd = file.LastIndexOfAny(new[] { '\\', '/' }); - string path; - string mask; - IEnumerable filesToAdd; - - if (pathEnd > 1) - { - path = file.Substring(0, pathEnd); - var maskLen = file.Length - pathEnd - 1; - if (maskLen > 0) - mask = file.Substring(pathEnd + 1, maskLen); - else - { - // маска была не в конце пути - // 1С такое откидывает - return; - } - - // несуществующие пути или пути к файлам, вместо папок 1С откидывает - if (!Directory.Exists(path)) - return; - } - else if (pathEnd == 0) - { - path = ""; - mask = file.Substring(1); - } - else - { - path = ""; - mask = file; - } - - filesToAdd = Directory.EnumerateFiles(path, mask, searchOption); - var relativePath = Path.GetFullPath(path); - AddEnumeratedFiles(filesToAdd, relativePath, storePathMode); - - } - - private void AddEnumeratedFiles(IEnumerable filesToAdd, string relativePath, SelfAwareEnumValue storePathMode) - { - var storeModeEnum = GlobalsManager.GetEnum(); - if (storePathMode == null) - storePathMode = (SelfAwareEnumValue)storeModeEnum.DontStorePath; - - foreach (var item in filesToAdd) - { - string pathInArchive; - if (storePathMode == storeModeEnum.StoreRelativePath) - pathInArchive = Path.GetDirectoryName(GetRelativePath(item, relativePath)); - else if (storePathMode == storeModeEnum.StoreFullPath) - pathInArchive = null; - else - pathInArchive = ""; - - _zip.AddFile(item, pathInArchive); - } - } - - // возвращает относительный путь или "", если путь не является относительным - private string GetRelativePath(string filespec, string rootfolder) - { - var currDir = Directory.GetCurrentDirectory(); - - DirectoryInfo directory = new DirectoryInfo(Path.Combine(currDir, rootfolder)); - var folderpath = directory.FullName; - - var filepath = Path.Combine(currDir, filespec); - - if (Directory.Exists(filespec)) - { - DirectoryInfo dir = new DirectoryInfo(filepath); - filepath = dir.FullName; - } - else { - FileInfo file = new FileInfo(filepath); - filepath = file.FullName; - } - - if (!filepath.StartsWith(folderpath)) - return ""; - - var res = filepath.Substring(folderpath.Length + 1); - if (res == "") - res = "."; - return res; - } - - private static bool GetRecursiveFlag(SelfAwareEnumValue recurseSubdirectories) - { - if (recurseSubdirectories == null) - return false; - else - return recurseSubdirectories == ((ZIPSubDirProcessingModeEnum)recurseSubdirectories.Owner).Recurse; - } - - private CompressionMethod MakeZipCompressionMethod(SelfAwareEnumValue compressionMethod) - { - if (compressionMethod == null) - return CompressionMethod.Deflate; - - var owner = (ZipCompressionMethodEnum)compressionMethod.Owner; - if (compressionMethod == owner.Deflate) - return CompressionMethod.Deflate; - if (compressionMethod == owner.Copy) - return CompressionMethod.None; - - throw RuntimeException.InvalidArgumentValue(); - - } - - private CompressionLevel MakeZipCompressionLevel(SelfAwareEnumValue compressionLevel) - { - if (compressionLevel == null) - return CompressionLevel.Default; - - var owner = (ZipCompressionLevelEnum)compressionLevel.Owner; - if (compressionLevel == owner.Minimal) - return CompressionLevel.BestSpeed; - if (compressionLevel == owner.Optimal) - return CompressionLevel.Default; - if (compressionLevel == owner.Maximal) - return CompressionLevel.BestCompression; - - throw RuntimeException.InvalidArgumentValue(); - } - - private EncryptionAlgorithm MakeZipEncryption(SelfAwareEnumValue encryptionMethod) - { - if (encryptionMethod == null) - return EncryptionAlgorithm.PkzipWeak; - - var enumOwner = (ZipEncryptionMethodEnum)encryptionMethod.Owner; - - if(encryptionMethod == enumOwner.Zip20) - return EncryptionAlgorithm.PkzipWeak; - if (encryptionMethod == enumOwner.Aes128) - return EncryptionAlgorithm.WinZipAes128; - if (encryptionMethod == enumOwner.Aes256) - return EncryptionAlgorithm.WinZipAes256; - - throw RuntimeException.InvalidArgumentValue(); - - } - - private void CheckIfOpened() - { - if (_zip == null) - throw new RuntimeException("Архив не открыт"); - } - - [ScriptConstructor(Name = "Формирование неинициализированного объекта")] - public static ZipWriter Construct() - { - return new ZipWriter(); - } - - [ScriptConstructor(Name = "На основании имени файла")] - public static ZipWriter ConstructByFileOptions( - IValue filename, - IValue password = null, - IValue comment = null, - IValue compressionMethod = null, - IValue compressionLevel = null, - IValue encryptionMethod = null, - FileNamesEncodingInZipFile encoding = FileNamesEncodingInZipFile.Auto) - { - var zip = new ZipWriter(); - zip.Open(filename.AsString(), - ConvertParam(password), - ConvertParam(comment), - ConvertParam>(compressionMethod), - ConvertParam>(compressionLevel), - ConvertParam>(encryptionMethod), - encoding); - return zip; - } - - private static T ConvertParam(IValue paramSource) - { - if (paramSource == null) - return default(T); - - if (paramSource.DataType == DataType.NotAValidValue) - return default(T); - - var raw = paramSource.GetRawValue(); - if (typeof(EnumerationValue).IsAssignableFrom(typeof(T))) - { - try - { - return (T)raw; - } - catch (InvalidCastException) - { - throw RuntimeException.InvalidArgumentType(); - } - } - else - return ContextValuesMarshaller.ConvertParam(raw); - } - - #region IDisposable Members - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - if (_zip != null) - { - _zip.Dispose(); - _zip = null; - } - } - } - - #endregion - } -} diff --git a/src/ScriptEngine.HostedScript/LibraryLoader.cs b/src/ScriptEngine.HostedScript/LibraryLoader.cs index 4736d40ee..7a5cea07d 100644 --- a/src/ScriptEngine.HostedScript/LibraryLoader.cs +++ b/src/ScriptEngine.HostedScript/LibraryLoader.cs @@ -11,17 +11,24 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.IO; using System.Linq; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Values; +using ScriptEngine.Libraries; namespace ScriptEngine.HostedScript { - public class LibraryLoader : ScriptDrivenObject + public class LibraryLoader : AutoScriptDrivenObject { - private readonly RuntimeEnvironment _env; + private readonly IRuntimeEnvironment _env; + private readonly ILibraryManager _libManager; private readonly ScriptingEngine _engine; - readonly bool _customized; - - readonly List _delayLoadedScripts = new List(); + private readonly bool _customized; + private readonly Stack _librariesInProgress = new Stack(); private struct DelayLoadedScriptData { @@ -29,50 +36,56 @@ private struct DelayLoadedScriptData public string identifier; public bool asClass; } + + private class LibraryLoadingContext + { + public LibraryLoadingContext(ExternalLibraryInfo library) + { + this.Library = library; + } + + public readonly ExternalLibraryInfo Library; + public readonly List delayLoadedScripts = new List(); + } - private LibraryLoader(LoadedModule moduleHandle, RuntimeEnvironment env, ScriptingEngine engine): base(moduleHandle) + private LibraryLoader(IExecutableModule moduleHandle, + IRuntimeEnvironment env, + ILibraryManager libManager, + ScriptingEngine engine, IBslProcess process): base(moduleHandle) { _env = env; + _libManager = libManager; _engine = engine; _customized = true; - - _engine.InitializeSDO(this); + + _engine.InitializeSDO(this, process); } - private LibraryLoader(RuntimeEnvironment env, ScriptingEngine engine) + private LibraryLoader(IRuntimeEnvironment env, + ILibraryManager libManager, + ScriptingEngine engine) { _env = env; + _libManager = libManager; _engine = engine; _customized = false; } #region Static part - - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - - public static LibraryLoader Create(ScriptingEngine engine, RuntimeEnvironment env, string processingScript) + + public static LibraryLoader Create(ScriptingEngine engine, string processingScript, IBslProcess process) { - var code = engine.Loader.FromFile(processingScript); var compiler = engine.GetCompilerService(); - compiler.DefineVariable("ЭтотОбъект", "ThisObject", SymbolType.ContextProperty); + var code = engine.Loader.FromFile(processingScript); + var module = CompileModule(compiler, code, typeof(LibraryLoader), process); - for (int i = 0; i < _methods.Count; i++) - { - var mi = _methods.GetMethodInfo(i); - compiler.DefineMethod(mi); - } - - var module = compiler.Compile(code); - var loadedModule = engine.LoadModuleImage(module); - - return new LibraryLoader(loadedModule, env, engine); - + return new LibraryLoader(module, engine.Environment, engine.LibraryManager, engine, process); } - public static LibraryLoader Create(ScriptingEngine engine, RuntimeEnvironment env) + public static LibraryLoader Create(ScriptingEngine engine, IBslProcess process) { - return new LibraryLoader(env, engine); + return new LibraryLoader(engine.Environment, engine.LibraryManager, engine); } #endregion @@ -83,7 +96,7 @@ public void AddClass(string file, string className) if (!Utils.IsValidIdentifier(className)) throw RuntimeException.InvalidArgumentValue(); - _delayLoadedScripts.Add(new DelayLoadedScriptData() + _librariesInProgress.Peek().delayLoadedScripts.Add(new DelayLoadedScriptData() { path = file, identifier = className, @@ -92,12 +105,12 @@ public void AddClass(string file, string className) } [ContextMethod("ДобавитьМодуль", "AddModule")] - public void AddModule(string file, string moduleName) + public void AddModule(IBslProcess process, string file, string moduleName) { if (!Utils.IsValidIdentifier(moduleName)) throw RuntimeException.InvalidArgumentValue(); - _delayLoadedScripts.Add(new DelayLoadedScriptData() + _librariesInProgress.Peek().delayLoadedScripts.Add(new DelayLoadedScriptData() { path = file, identifier = moduleName, @@ -106,138 +119,76 @@ public void AddModule(string file, string moduleName) try { - LibraryResolver.TraceLoadLibrary( + TraceLoadLibrary( Locale.NStr($"ru = 'Загружаю модуль ={moduleName}= в область видимости из файла {file}';"+ $"en = 'Load module ={moduleName}= in to context from file {file}'") ); - _env.InjectGlobalProperty(null, moduleName, true); + + _env.InjectGlobalProperty(BslUndefinedValue.Instance, moduleName, _librariesInProgress.Peek().Library.Package); } catch (InvalidOperationException e) { // символ уже определен - throw new RuntimeException(String.Format("Невозможно загрузить модуль {0}. Такой символ уже определен.", moduleName), e); + throw new RuntimeException($"Невозможно загрузить модуль {moduleName}. Такой символ уже определен.", e); } } [ContextMethod("ЗагрузитьБиблиотеку", "LoadLibrary")] public void LoadLibrary(string dllPath) { - var assembly = System.Reflection.Assembly.LoadFrom(dllPath); + var context = new ComponentLoadingContext(dllPath); + var assembly = context.LoadFromAssemblyPath(dllPath); _engine.AttachExternalAssembly(assembly, _env); - } [ContextMethod("ДобавитьМакет", "AddTemplate")] public void AddTemplate(string file, string name, TemplateKind kind = TemplateKind.File) { - var manager = GlobalsManager.GetGlobalContext(); + var manager = _engine.GlobalsManager.GetInstance(); manager.RegisterTemplate(file, name, kind); } - protected override int GetOwnVariableCount() + public PackageInfo ProcessLibrary(string libraryPath, IBslProcess process) { - return 1; - } - - protected override int FindOwnProperty(string name) - { - if(StringComparer.OrdinalIgnoreCase.Compare(name, "ЭтотОбъект") == 0) - { - return 0; - } - if(StringComparer.OrdinalIgnoreCase.Compare(name, "ThisObject") == 0) + var package = new PackageInfo(libraryPath, Path.GetFileName(libraryPath)); + var library = new ExternalLibraryInfo(package); + _librariesInProgress.Push(new LibraryLoadingContext(library)); + try { - return 0; - } - - return base.FindOwnProperty(name); - } - - protected override string GetOwnPropName(int index) - { - if (index == 0) - return "ЭтотОбъект"; - - throw new ArgumentException(); - } - - protected override bool IsOwnPropReadable(int index) - { - return true; - } - - protected override IValue GetOwnPropValue(int index) - { - if (index == 0) - return this; - else - throw new ArgumentException(String.Format("Неверный индекс свойства {0}", index), "index"); - } - - protected override int GetOwnMethodCount() - { - return _methods.Count; - } - - protected override void UpdateState() - { - - } - - protected override int FindOwnMethod(string name) - { - return _methods.FindMethod(name); - } - - protected override MethodInfo GetOwnMethod(int index) - { - return _methods.GetMethodInfo(index); - } + bool success; + if(!_customized) + { + TraceLoadLibrary( + Locale.NStr($"ru = 'Использую НЕ кастомизированный загрузчик пакетов по умолчанию для библиотеки {libraryPath}';"+ + $"en = 'Use NOT customized package loader for library {libraryPath}'") + ); - protected override void CallOwnProcedure(int index, IValue[] arguments) - { - _methods.GetMethod(index)(this, arguments); - } + success = DefaultProcessing(libraryPath, process); + } + else + { + TraceLoadLibrary( + Locale.NStr($"ru = 'Использую КАСТОМИЗИРОВАННЫЙ загрузчик пакетов для библиотеки {libraryPath}';"+ + $"en = 'Use CUSTOMIZED package loader for library {libraryPath}'") + ); - protected override IValue CallOwnFunction(int index, IValue[] arguments) - { - return _methods.GetMethod(index)(this, arguments); - } + success = CustomizedProcessing(libraryPath, process); + } - public bool ProcessLibrary(string libraryPath) - { - bool success; - _delayLoadedScripts.Clear(); + if (!success) + return default; - if(!_customized) - { - LibraryResolver.TraceLoadLibrary( - Locale.NStr($"ru = 'Использую НЕ кастомизированный загрузчик пакетов по умолчанию для библиотеки {libraryPath}';"+ - $"en = 'Use NOT customized package loader for library {libraryPath}'") - ); - - success = DefaultProcessing(libraryPath); + CompileDelayedModules(library, process); + + return package; } - else - { - LibraryResolver.TraceLoadLibrary( - Locale.NStr($"ru = 'Использую КАСТОМИЗИРОВАННЫЙ загрузчик пакетов для библиотеки {libraryPath}';"+ - $"en = 'Use CUSTOMIZED package loader for library {libraryPath}'") - ); - - success = CustomizedProcessing(libraryPath); - } - - if (success) + finally { - var library = new ExternalLibraryDef(Path.GetFileName(libraryPath)); - CompileDelayedModules(library); + _librariesInProgress.Pop(); } - - return success; } - private bool CustomizedProcessing(string libraryPath) + private bool CustomizedProcessing(string libraryPath, IBslProcess process) { var libPathValue = ValueFactory.Create(libraryPath); var defaultLoading = Variable.Create(ValueFactory.Create(true), "$internalDefaultLoading"); @@ -246,50 +197,51 @@ private bool CustomizedProcessing(string libraryPath) int eventIdx = GetScriptMethod("ПриЗагрузкеБиблиотеки", "OnLibraryLoad"); if(eventIdx == -1) { - return DefaultProcessing(libraryPath); + return DefaultProcessing(libraryPath, process); } - CallScriptMethod(eventIdx, new[] { libPathValue, defaultLoading, cancelLoading }); + CallScriptMethod(eventIdx, new[] { libPathValue, defaultLoading, cancelLoading }, process); if (cancelLoading.AsBoolean()) // Отказ = Ложь return false; if (defaultLoading.AsBoolean()) - return DefaultProcessing(libraryPath); + return DefaultProcessing(libraryPath, process); return true; } - private bool DefaultProcessing(string libraryPath) + private bool DefaultProcessing(string libraryPath, IBslProcess process) { var files = Directory.EnumerateFiles(libraryPath, "*.os") .Select(x => new { Name = Path.GetFileNameWithoutExtension(x), Path = x }) - .Where(x => Utils.IsValidIdentifier(x.Name)); + .Where(x => Utils.IsValidIdentifier(x.Name)) + .ToList(); bool hasFiles = false; - LibraryResolver.TraceLoadLibrary( - Locale.NStr($"ru = 'Обнаружено {files.Count()} модулей в библиотеке {libraryPath}';"+ - $"en = 'Found {files.Count()} modules in library {libraryPath}'") + TraceLoadLibrary( + Locale.NStr($"ru = 'Обнаружено {files.Count} модулей в библиотеке {libraryPath}';"+ + $"en = 'Found {files.Count} modules in library {libraryPath}'") ); foreach (var file in files) { - LibraryResolver.TraceLoadLibrary( + TraceLoadLibrary( Locale.NStr($"ru = 'Загружаю модуль библиотеки из {file.Path}';"+ $"en = 'Load library module from {file.Path}'") ); hasFiles = true; - AddModule(file.Path, file.Name); + AddModule(process, file.Path, file.Name); } return hasFiles; } - private void CompileDelayedModules(ExternalLibraryDef library) + private void CompileDelayedModules(ExternalLibraryInfo library, IBslProcess process) { - foreach (var scriptFile in _delayLoadedScripts) + foreach (var scriptFile in _librariesInProgress.Peek().delayLoadedScripts) { if (scriptFile.asClass) { @@ -297,34 +249,45 @@ private void CompileDelayedModules(ExternalLibraryDef library) } else { - var module = library.AddModule(scriptFile.identifier, scriptFile.path); + library.AddModule(scriptFile.identifier, scriptFile.path); } } - library.Modules.ForEach(moduleFile => + foreach (var moduleFile in library.Modules) { - var image = CompileFile(moduleFile.FilePath); - moduleFile.Image = image; - }); + moduleFile.Module = CompileFile(moduleFile.FilePath, library.Package.Id, process); + } - library.Classes.ForEach(classFile => + foreach (var classFile in library.Classes) { - var image = CompileFile(classFile.FilePath); - _engine.AttachedScriptsFactory.LoadAndRegister(classFile.Symbol, image); - classFile.Image = image; - }); + var module = CompileFile(classFile.FilePath, library.Package.Id, process); + _engine.AttachedScriptsFactory.RegisterTypeModule(classFile.Symbol, module); + classFile.Module = module; + } - _env.InitExternalLibrary(_engine, library); + _libManager.InitExternalLibrary(_engine, library, process); } - private ModuleImage CompileFile(string path) + private IExecutableModule CompileFile(string path, string ownerPackageId, IBslProcess process) { var compiler = _engine.GetCompilerService(); - var source = _engine.Loader.FromFile(path); - var module = _engine.AttachedScriptsFactory.CompileModuleFromSource(compiler, source, null); + var source = _engine.Loader.FromFile(path, ownerPackageId); + var module = _engine.AttachedScriptsFactory.CompileModuleFromSource(compiler, source, null, process); return module; } + + private static Lazy TraceEnabled = + new Lazy(() => + System.Environment.GetEnvironmentVariable("OS_LIBRARY_LOADER_TRACE") == "1" || + System.Environment.GetEnvironmentVariable("OS_LRE_TRACE") == "1"); // для обратной совместимости + + public static void TraceLoadLibrary(string message) + { + if (TraceEnabled.Value) { + SystemLogger.Write("LRE: " + message); + } + } } } diff --git a/src/ScriptEngine.HostedScript/LibraryResolver.cs b/src/ScriptEngine.HostedScript/LibraryResolver.cs deleted file mode 100644 index 890e9a877..000000000 --- a/src/ScriptEngine.HostedScript/LibraryResolver.cs +++ /dev/null @@ -1,376 +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 ScriptEngine.Compiler; -using ScriptEngine.Environment; -using ScriptEngine.Machine; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace ScriptEngine.HostedScript -{ - public class LibraryResolver : IDirectiveResolver - { - private const string USE_DIRECTIVE_RU = "использовать"; - private const string USE_DIRECTIVE_EN = "use"; - private const string PREDEFINED_LOADER_FILE = "package-loader.os"; - - private readonly RuntimeEnvironment _env; - private readonly ScriptingEngine _engine; - private readonly List _libs; - private LibraryLoader _defaultLoader; - private string _libraryRoot; - - #region Private classes - - private class Library - { - public string id; - public ProcessingState state; - public LibraryLoader customLoader; - } - - private enum ProcessingState - { - Discovered, - Processed - } - - #endregion - - public LibraryResolver(ScriptingEngine engine, RuntimeEnvironment env) - { - _env = env; - _engine = engine; - _libs = new List(); - - this.SearchDirectories = new List(); - } - - public string LibraryRoot - { - get - { - if (_libraryRoot == null) - _libraryRoot = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); - - return _libraryRoot; - } - - set - { - _libraryRoot = value; - } - } - - public List SearchDirectories { get; private set; } - - //TODO: Тут совсем ужасно спроектировано взаимодействие слоев и передача контекста - // нужно снова заняться версией 2.0 (( - private readonly Stack _compiledSourcesStack = new Stack(); - - public ICodeSource Source - { - get - { - if (_compiledSourcesStack.Count == 0) - return null; - - return _compiledSourcesStack.Peek(); - } - set - { - if(value == null) - { - if (_compiledSourcesStack.Count > 0) - _compiledSourcesStack.Pop(); - } - else - { - _compiledSourcesStack.Push(value); - } - } - } - - private LibraryLoader DefaultLoader - { - get - { - if (_defaultLoader == null) - CreateDefaultLoader(); - - return _defaultLoader; - - } - set { _defaultLoader = value; } - } - - private void CreateDefaultLoader() - { - - var loaderscript = Path.Combine(LibraryRoot, PREDEFINED_LOADER_FILE); - TraceLoadLibrary( - Locale.NStr($"ru = 'Путь поиска package-loader - {loaderscript}';"+ - $"en = 'Package-loader path search - {loaderscript}'") - ); - - if (File.Exists(loaderscript)) - { - TraceLoadLibrary( - Locale.NStr($"ru = 'Загружен package-loader по адресу {loaderscript}';"+ - $"en = 'Load package-loader from {loaderscript}'") - ); - _defaultLoader = LibraryLoader.Create(_engine, _env, loaderscript); - } - else - { - TraceLoadLibrary( - Locale.NStr($"ru = 'Загружен package-loader по умолчанию';"+ - $"en = 'Default package-loader is used'") - ); - _defaultLoader = LibraryLoader.Create(_engine, _env); - } - } - - public bool Resolve(string directive, string value, bool codeEntered) - { - if (codeEntered) { - return false; - } - - if (DirectiveSupported(directive)) - { - LoadLibrary(value); - return true; - } - else - return false; - } - - private bool DirectiveSupported(string directive) - { - return StringComparer.InvariantCultureIgnoreCase.Compare(directive, USE_DIRECTIVE_RU) == 0 - || StringComparer.InvariantCultureIgnoreCase.Compare(directive, USE_DIRECTIVE_EN) == 0; - } - - private void LoadLibrary(string value) - { - bool quoted = PrepareQuoted(ref value); - bool loaded; - if (quoted) - loaded = LoadByRelativePath(value); - else - loaded = LoadByName(value); - - if(!loaded) - throw new CompilerException(String.Format("Библиотека не найдена: '{0}'", value)); - - } - - private bool LoadByRelativePath(string libraryPath) - { - string realPath; - - if (!Path.IsPathRooted(libraryPath) && Source != null) - { - var currentPath = Source.SourceDescription; - // Загружаем относительно текущего скрипта, однако, - // если CurrentScript не файловый (TestApp или другой хост), то загружаем относительно рабочего каталога. - // немного костыльно, ага (( - // - if (!PathHasInvalidChars(currentPath)) - realPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(currentPath), libraryPath)); - else - realPath = libraryPath; - } - else - { - realPath = libraryPath; - } - - return LoadByPath(realPath); - } - - private static bool PathHasInvalidChars(string path) - { - - return (!string.IsNullOrEmpty(path) && path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) >= 0); - } - - private bool PrepareQuoted(ref string value) - { - const string COMMENT = "//"; - const char QUOTE = '"'; - - bool quoted = false; - if (value.IndexOf(QUOTE)==0) - { - var secondQuote = value.Substring(1).IndexOf(QUOTE); - if (secondQuote > 0) - { - if (secondQuote+2 < value.Length) - { - var tail = value.Substring(secondQuote+2, value.Length-secondQuote-2).TrimStart(); - if (!String.IsNullOrWhiteSpace(tail) && tail.IndexOf(COMMENT) != 0) - throw new CompilerException($"Недопустимые символы после имени библиотеки: '{tail}'"); - } - value = value.Substring(1, secondQuote); - quoted = true; - } - else - { - throw new CompilerException($"Ошибка в имени библиотеки: '{value}'"); - } - } - else - { - var comment = value.IndexOf(COMMENT); - if( comment>=0 ) - { - value = value.Substring(0,comment).TrimEnd(); - } - } - - if (String.IsNullOrWhiteSpace(value)) - throw new CompilerException("Отсутствует имя библиотеки"); - - return quoted; - } - - private bool LoadByPath(string libraryPath) - { - if (Directory.Exists(libraryPath)) - { - TraceLoadLibrary( - Locale.NStr($"ru = 'Загружаю библиотеку по пути {libraryPath}';"+ - $"en = 'Load library from path {libraryPath}'") - ); - return LoadLibraryInternal(libraryPath); - } - - return false; - } - - private bool LoadByName(string value) - { - foreach (var path in SearchDirectories) - { - if(!Directory.Exists(path)) - continue; - - var libraryPath = Path.Combine(path, value); - if (LoadByPath(libraryPath)) - return true; - } - - var rootPath = Path.Combine(LibraryRoot, value); - if (LoadByPath(rootPath)) - return true; - - return false; - } - - private bool LoadLibraryInternal(string libraryPath) - { - var id = GetLibraryId(libraryPath); - var existedLib = _libs.FirstOrDefault(x => x.id == id); - if(existedLib != null) - { - if (existedLib.state == ProcessingState.Discovered) - { - string libStack = listToStringStack(_libs, id); - throw new RuntimeException($"Ошибка загрузки библиотеки {id}. Обнаружены циклические зависимости.\n" + - $"{libStack}"); - } - TraceLoadLibrary( - Locale.NStr($"ru = 'Использую уже загруженную библиотеку {existedLib.id}';"+ - $"en = 'Use allready loaded library {existedLib.id}'") - ); - return true; - } - - var newLib = new Library() { id = id, state = ProcessingState.Discovered }; - bool hasFiles; - int newLibIndex = _libs.Count; - - var customLoaderFile = Path.Combine(libraryPath, PREDEFINED_LOADER_FILE); - if (File.Exists(customLoaderFile)) - newLib.customLoader = LibraryLoader.Create(_engine, _env, customLoaderFile); - - try - { - _libs.Add(newLib); - TraceLoadLibrary( - Locale.NStr($"ru = 'Начинаю процессинг {newLib.id}';"+ - $"en = 'Start processing {newLib.id}'") - ); - - hasFiles = ProcessLibrary(newLib); - newLib.state = ProcessingState.Processed; - } - catch (Exception) - { - _libs.RemoveAt(newLibIndex); - throw; - } - - TraceLoadLibrary( - Locale.NStr($"ru = 'Библиотека {newLib.id} будет загружена - {hasFiles}';"+ - $"en = 'Library {newLib.id} will be loaded - {hasFiles}'") - ); - - return hasFiles; - } - - private string listToStringStack(List libs, string stopToken) - { - var builder = new StringBuilder(); - string offset = ""; - foreach (var library in libs) - { - builder.Append(offset); - builder.Append("-> "); - builder.AppendLine(library.id); - offset += " "; - if (library.id == stopToken) - { - break; - } - } - - return builder.ToString(); - } - - private string GetLibraryId(string libraryPath) - { - return Path.GetFullPath(libraryPath); - } - - private bool ProcessLibrary(Library lib) - { - LibraryLoader loader; - if (lib.customLoader != null) - loader = lib.customLoader; - else - loader = this.DefaultLoader; - - return loader.ProcessLibrary(lib.id); - } - - public static void TraceLoadLibrary(string message) - { - //OS_LRE_TRACE - по аналогии с Package loader OSLIB_LOADER_TRACE - var isTrace = System.Environment.GetEnvironmentVariable("OS_LRE_TRACE"); - if (isTrace == "1") { - SystemLogger.Write("LRE: " + message); - } - } - - } -} diff --git a/src/ScriptEngine.HostedScript/OneScriptLibraryOptions.cs b/src/ScriptEngine.HostedScript/OneScriptLibraryOptions.cs new file mode 100644 index 000000000..f407c1cc0 --- /dev/null +++ b/src/ScriptEngine.HostedScript/OneScriptLibraryOptions.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +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 ScriptEngine.Hosting; + +namespace ScriptEngine.HostedScript +{ + public class OneScriptLibraryOptions : OneScriptCoreOptions + { + public const string SYSTEM_LIBRARY_DIR = "lib.system"; + public const string ADDITIONAL_LIBRARIES = "lib.additional"; + + public OneScriptLibraryOptions(KeyValueConfig config) : base(config) + { + SystemLibraryDir = config.GetEntry(SYSTEM_LIBRARY_DIR)?.ResolvePath(); + AdditionalLibraries = config.GetEntry(ADDITIONAL_LIBRARIES)?.ResolvePathList(';').ToList(); + } + + public string SystemLibraryDir { get; set; } + + public IEnumerable AdditionalLibraries { get; set; } + } +} diff --git a/src/ScriptEngine.HostedScript/Process.cs b/src/ScriptEngine.HostedScript/Process.cs index 99d2f02df..39a36d266 100644 --- a/src/ScriptEngine.HostedScript/Process.cs +++ b/src/ScriptEngine.HostedScript/Process.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; +using OneScript.Execution; using ScriptEngine.Machine; namespace ScriptEngine.HostedScript @@ -14,13 +15,19 @@ public class Process ScriptingEngine _engine; readonly IHostApplication _host; - readonly LoadedModule _module; + readonly IExecutableModule _module; + private IBslProcess _bslProcess; - internal Process(IHostApplication host, LoadedModule src, ScriptingEngine runtime) + internal Process( + IBslProcess process, + IHostApplication host, + IExecutableModule src, + ScriptingEngine runtime) { _host = host; _engine = runtime; _module = src; + _bslProcess = process; } public int Start() @@ -29,9 +36,7 @@ public int Start() try { - MachineInstance.Current.EventProcessor = new DefaultEventProcessor(); - _engine.UpdateContexts(); - _engine.NewObject(_module); + _engine.NewObject(_module, _bslProcess); exitCode = 0; } catch (ScriptInterruptionException e) @@ -45,7 +50,7 @@ public int Start() } finally { - _engine.DebugController?.NotifyProcessExit(exitCode); + _engine.Debugger.NotifyProcessExit(exitCode); _engine.Dispose(); _engine = null; } diff --git a/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj b/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj index 9e0b95113..4d5769efc 100644 --- a/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj +++ b/src/ScriptEngine.HostedScript/ScriptEngine.HostedScript.csproj @@ -1,42 +1,37 @@ - - - - - net452;netstandard2.0;netstandard2.1 - Debug;Release - AnyCPU - - - - - - - - 1C (BSL) language runtime - - - - PreserveNewest - - - - OneScript.StandardLibrary - OneScript Main Client Libraries - Standard class libraries for 1Script - collections, network, filesystem etc... - OneScript OpenSource Community 2015 - BSL 1C 1Script OneScript - - - bin\Release\net452\ScriptEngine.HostedScript.xml - 1701;1702;1705;1591 - - - - - - - - - - + + + + + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug + AnyCPU + + + + + + + + 1C (BSL) language runtime + + + + PreserveNewest + + + + OneScript.Hosting + OneScript Main Client Libraries + Standard class libraries for 1Script - collections, network, filesystem etc... + OneScript OpenSource Community 2015 + BSL 1C 1Script OneScript + + + bin\Release\net452\ScriptEngine.HostedScript.xml + 1701;1702;1705;1591 + + + true + false + \ No newline at end of file diff --git a/src/ScriptEngine.HostedScript/SystemConfigAccessor.cs b/src/ScriptEngine.HostedScript/SystemConfigAccessor.cs new file mode 100644 index 000000000..996ee71fa --- /dev/null +++ b/src/ScriptEngine.HostedScript/SystemConfigAccessor.cs @@ -0,0 +1,54 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.HostedScript.Library +{ + [GlobalContext(Category = "Работа с настройками системы")] + public class SystemConfigAccessor : GlobalContextBase + { + private readonly EngineConfiguration _activeConfig; + + public SystemConfigAccessor(EngineConfiguration activeConfig) + { + _activeConfig = activeConfig; + } + + /// + /// Метод обновляет текущие настройки значениями из файла oscript.cfg + /// + [ContextMethod("ОбновитьНастройкиСистемы", "RefreshSystemConfig")] + public void Refresh() + { + _activeConfig.Reload(); + } + + /// + /// Метод возвращает значение из файла oscript.cfg по имени настойки + /// + /// Имя настройки из файла oscript.cfg + /// Строка. Значение системной настройки. + [ContextMethod("ПолучитьЗначениеСистемнойНастройки", "GetSystemOptionValue")] + public IValue GetSystemOptionValue(string optionKey) + { + var cfg = _activeConfig.GetConfig(); + + var value = cfg[optionKey]; + + return value != null ? ValueFactory.Create(value) : ValueFactory.Create(); + } + + public static IAttachableContext CreateInstance(EngineConfiguration configHolder) + { + return new SystemConfigAccessor(configHolder); + } + } +} diff --git a/src/ScriptEngine.HostedScript/SystemGlobalContext.cs b/src/ScriptEngine.HostedScript/SystemGlobalContext.cs new file mode 100644 index 000000000..192c53cf5 --- /dev/null +++ b/src/ScriptEngine.HostedScript/SystemGlobalContext.cs @@ -0,0 +1,281 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Sources; +using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Collections; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.HostedScript +{ + /// + /// Глобальный контекст. Представляет глобально доступные свойства и методы. + /// + [GlobalContext(Category="Процедуры и функции взаимодействия с системой", ManualRegistration=true)] + public class SystemGlobalContext : IAttachableContext + { + private static readonly ContextMethodsMapper _methods + = new ContextMethodsMapper(); + + private static readonly ContextPropertyMapper _properties = + new ContextPropertyMapper(); + + private IVariable[] _state; + private FixedArrayImpl _args; + + public ScriptingEngine EngineInstance{ get; set; } + + public void InitInstance() + { + InitContextVariables(); + } + + private void InitContextVariables() + { + _state = new IVariable[_properties.Count]; + + for (int i = 0; i < _properties.Count; i++) + { + _state[i] = Variable.CreateContextPropertyReference(this, i, _properties.GetProperty(i).Name); + } + } + + IVariable IAttachableContext.GetVariable(int index) => _state[index]; + + BslMethodInfo IAttachableContext.GetMethod(int index) => _methods.GetRuntimeMethod(index); + + public IHostApplication ApplicationHost { get; set; } + public SourceCode CodeSource { get; set; } + + /// + /// Выдает сообщение в консоль. + /// + /// Выдаваемое сообщение. + /// Статус сообщения. В зависимости от статуса изменяется цвет вывода сообщения. + [ContextMethod("Сообщить", "Message")] + public void Echo(string message, MessageStatusEnum status = MessageStatusEnum.Ordinary) + { + ApplicationHost.Echo(message ?? "", status); + } + + /// + /// Возвращает информацию о сценарии, который был точкой входа в программу. + /// Можно выделить два вида сценариев: те, которые были подключены, как классы и те, которые запущены непосредственно. Метод СтартовыйСценарий возвращает информацию о сценарии, запущенном непосредственно. + /// Для получения информации о текущем выполняемом сценарии см. метод ТекущийСценарий() + /// + /// Объект ИнформацияОСценарии + [ContextMethod("СтартовыйСценарий", "EntryScript")] + public IRuntimeContextInstance StartupScript() + { + return new ScriptInformationContext(CodeSource); + } + + /// + /// Прерывает выполнение текущего скрипта. + /// + /// Код возврата (ошибки), возвращаемый операционной системе. + [ContextMethod("ЗавершитьРаботу", "Exit")] + public void Quit(int exitCode) + { + throw new ScriptInterruptionException(exitCode); + } + + /// + /// Ввод строки пользователем. Позволяет запросить у пользователя информацию. + /// + /// Выходной параметр. Введенные данные в виде строки. + /// Строка, выводимая в качестве подсказки. Необязательный, по умолчанию - пустая строка. + /// Максимальная длина вводимой строки. Необязательный, по умолчанию - 0 (неограниченная длина). + /// Указание неограниченной длины может не поддерживаться хост-приложением. + /// Булево, определяет режим ввода многострочного текста. Необязательный, по умолчанию - Ложь. + /// Булево. Истина, если пользователь ввел данные, Ложь, если отказался. + [ContextMethod("ВвестиСтроку", "InputString")] + public bool InputString([ByRef] IVariable resut, string prompt = null, int? len = null, bool? multiline = null) + { + string input; + bool inputIsDone; + + string strPrompt = null; + int length = 0; + bool flagML = false; + + if (prompt != null) + { + strPrompt = prompt; + } + + if (len != null) + { + length = (int)len; + } + + if (multiline != null) + { + flagML = (bool)multiline; + } + + inputIsDone = ApplicationHost.InputString(out input, strPrompt, length, flagML); + + if (inputIsDone) + { + resut.Value = ValueFactory.Create(input); + return true; + } + else + return false; + } + + /// + /// Доступ к аргументам командной строки. + /// Объект АргументыКоманднойСтроки представляет собой массив в режиме "только чтение". + /// + [ContextProperty("АргументыКоманднойСтроки", "CommandLineArguments", CanWrite = false)] + public FixedArrayImpl CommandLineArguments + { + get + { + if (_args == null) + { + var argsArray = new ArrayImpl(); + if (ApplicationHost != null) + { + foreach (var arg in ApplicationHost.GetCommandLineArguments()) + { + argsArray.Add(ValueFactory.Create(arg)); + } + } + _args = new FixedArrayImpl(argsArray); + } + + return _args; + } + + } + + /// + /// Каталог исполняемых файлов OneScript + /// + /// + [DeprecatedName("ProgramDirectory")] + [ContextMethod("КаталогПрограммы","BinDir")] + public string ProgramDirectory() + { + var asm = System.Reflection.Assembly.GetExecutingAssembly(); + var filename = asm.Location; + + return System.IO.Path.GetDirectoryName(filename); + } + +#region IAttachableContext Members + +int IAttachableContext.VariablesCount => _properties.Count; + + int IAttachableContext.MethodsCount => _methods.Count; + +#endregion + +#region IRuntimeContextInstance Members + + public bool IsIndexed + { + get + { + return false; + } + } + + public bool DynamicMethodSignatures + { + get + { + return false; + } + } + + public IValue GetIndexedValue(IValue index) + { + throw new NotImplementedException(); + } + + public void SetIndexedValue(IValue index, IValue val) + { + throw new NotImplementedException(); + } + + public int GetPropertyNumber(string name) + { + return _properties.FindProperty(name); + } + + public bool IsPropReadable(int propNum) + { + return _properties.GetProperty(propNum).CanRead; + } + + public bool IsPropWritable(int propNum) + { + return _properties.GetProperty(propNum).CanWrite; + } + + public IValue GetPropValue(int propNum) + { + return _properties.GetProperty(propNum).Getter(this); + } + + public void SetPropValue(int propNum, IValue newVal) + { + throw new InvalidOperationException("global props are not writable"); + } + + public int GetPropCount() + { + return _properties.Count; + } + + public string GetPropName(int index) + { + return _properties.GetProperty(index).Name; + } + + public int GetMethodNumber(string name) + { + return _methods.FindMethod(name); + } + + public virtual BslMethodInfo GetMethodInfo(int methodNumber) + { + return _methods.GetRuntimeMethod(methodNumber); + } + + public BslPropertyInfo GetPropertyInfo(int propertyNumber) + { + return _properties.GetProperty(propertyNumber).PropertyInfo; + } + + public int GetMethodsCount() + { + return _methods.Count; + } + + public void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + _methods.GetCallableDelegate(methodNumber)(this, arguments, process); + } + + public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + retValue = _methods.GetCallableDelegate(methodNumber)(this, arguments, process); + } + +#endregion + + } +} diff --git a/src/ScriptEngine.HostedScript/TemplateStorage.cs b/src/ScriptEngine.HostedScript/TemplateStorage.cs index 0694980a8..ea5b61859 100644 --- a/src/ScriptEngine.HostedScript/TemplateStorage.cs +++ b/src/ScriptEngine.HostedScript/TemplateStorage.cs @@ -7,6 +7,9 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Contexts.Enums; +using OneScript.Exceptions; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; @@ -83,9 +86,9 @@ public void Dispose() [EnumerationType("ТипМакета", "TemplateKind")] public enum TemplateKind { - [EnumItem("Файл")] + [EnumValue("Файл", "File")] File, - [EnumItem("ДвоичныеДанные")] + [EnumValue("ДвоичныеДанные", "BinaryData")] BinaryData } diff --git a/src/ScriptEngine.HostedScript/oscript.cfg b/src/ScriptEngine.HostedScript/oscript.cfg index b4616b1a9..1ddb2504f 100644 --- a/src/ScriptEngine.HostedScript/oscript.cfg +++ b/src/ScriptEngine.HostedScript/oscript.cfg @@ -3,6 +3,14 @@ # Корневой каталог системных библиотек lib.system = ../lib +# Режим проверки явных импортов библиотек через директиву #Использовать +# Возможные значения: +# on - ошибка компиляции при использовании библиотеки без явного импорта +# off - проверка отключена +# warn - предупреждение в лог при использовании библиотеки без явного импорта, компиляция будет успешной +# dev - ошибка компиляции только при включенном отладчике, иначе аналогично режиму off +#lang.explicitImports=dev + #Дополнительные каталоги поиска библиотек #lib.additional = C:\somedir;C:somedir2; diff --git a/src/ScriptEngine.NativeApi/.gitignore b/src/ScriptEngine.NativeApi/.gitignore index d1f5d6f2f..46cf50baa 100644 --- a/src/ScriptEngine.NativeApi/.gitignore +++ b/src/ScriptEngine.NativeApi/.gitignore @@ -1,5 +1,5 @@ -/build64 -/build32 +/build64* +/build32* /build /bind /bin diff --git a/src/ScriptEngine.NativeApi/CMakeLists.txt b/src/ScriptEngine.NativeApi/CMakeLists.txt index 0d4c99620..50d45b1d0 100644 --- a/src/ScriptEngine.NativeApi/CMakeLists.txt +++ b/src/ScriptEngine.NativeApi/CMakeLists.txt @@ -12,11 +12,16 @@ target_compile_definitions(${TARGET} PRIVATE UNICODE _UNICODE) target_include_directories(${TARGET} PRIVATE include) if (UNIX) - if (TARGET_ARCH STREQUAL "x86") + if (TARGET_PLATFORM_32) + set(MySuffix "32") + set(CMAKE_LIBRARY_PATH "/usr/lib/i386-linux-gnu" CACHE PATH "") set(CMAKE_CXX_FLAGS "-m32 ${CMAKE_CXX_FLAGS}") else(UNIX) + set(MySuffix "64") set(CMAKE_CXX_FLAGS "-m64 ${CMAKE_CXX_FLAGS}") endif () + set_target_properties(${TARGET} PROPERTIES PREFIX "") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s") else(UNIX) if (NOT MSVC) message(FATAL_ERROR "Must be compiled with MSVC on Windows") @@ -26,3 +31,10 @@ else(UNIX) set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE) target_compile_definitions(${TARGET} PRIVATE _WINDOWS) endif(UNIX) + +set_target_properties(${TARGET} PROPERTIES + OUTPUT_NAME ${PROJECT_NAME}${MySuffix} + POSITION_INDEPENDENT_CODE ON + CXX_STANDARD_REQUIRED ON + CXX_STANDARD 17) + diff --git a/src/ScriptEngine.NativeApi/DetectUnionOffset.cpp b/src/ScriptEngine.NativeApi/DetectUnionOffset.cpp deleted file mode 100644 index 66f1f06e8..000000000 --- a/src/ScriptEngine.NativeApi/DetectUnionOffset.cpp +++ /dev/null @@ -1,32 +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/. -----------------------------------------------------------*/ - -#include - -#include "include/types.h" -#include "include/ComponentBase.h" -#include "include/AddInDefBase.h" -#include "include/IMemoryManager.h" -#include -#include - -int main() -{ - tVariant variant; - printf("offsetof(struct tVariant, lVal) is %d\n", (int)offsetof(struct tVariant, lVal)); - printf("offsetof(struct tVariant, bVal) is %d\n", (int)offsetof(struct tVariant, bVal)); - printf("offsetof(struct tVariant, dblVal) is %d\n", (int)offsetof(struct tVariant, dblVal)); - printf("offsetof(struct tVariant, date) is %d\n", (int)offsetof(struct tVariant, date)); - printf("offsetof(struct tVariant, tmVal) is %d\n", (int)offsetof(struct tVariant, tmVal)); - printf("offsetof(struct tVariant, pstrVal) is %d\n", (int)offsetof(struct tVariant, pstrVal)); - printf("offsetof(struct tVariant, strLen) is %d\n", (int)offsetof(struct tVariant, strLen)); - printf("offsetof(struct tVariant, pwstrVal) is %d\n", (int)offsetof(struct tVariant, pwstrVal)); - printf("offsetof(struct tVariant, wstrLen) is %d\n", (int)offsetof(struct tVariant, wstrLen)); - printf("offsetof(struct tVariant, vt) is %d\n", (int)offsetof(struct tVariant, vt)); - printf("sizeof(struct tVariant) is %d\n", (int)sizeof(tVariant)); - return 0; -} diff --git a/src/ScriptEngine.NativeApi/NativeApiProxy.cpp b/src/ScriptEngine.NativeApi/NativeApiProxy.cpp index ebf898f4d..0a60a4101 100644 --- a/src/ScriptEngine.NativeApi/NativeApiProxy.cpp +++ b/src/ScriptEngine.NativeApi/NativeApiProxy.cpp @@ -5,9 +5,13 @@ was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -#ifdef _WINDOWS +#include "include/types.h" +#include "include/ComponentBase.h" +#include "include/AddInDefBase.h" +#include "include/IMemoryManager.h" +#include "NativeInterface.h" -#include +#ifdef _WINDOWS BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { @@ -25,11 +29,10 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser #define DllExport extern "C" __declspec(dllexport) -#else +#else//_WINDOWS #define DllExport extern "C" -#include #include #endif//_WINDOWS @@ -38,27 +41,39 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReser #define EMPTY_DEF -#include "include/types.h" -#include "include/ComponentBase.h" -#include "include/AddInDefBase.h" -#include "include/IMemoryManager.h" -#include "NativeInterface.h" - typedef void(_stdcall* StringFuncRespond) (const WCHAR_T* s); typedef void(_stdcall* VariantFuncRespond) (const tVariant* variant); +static bool AllocMemory(void** pMemory, unsigned long ulCountByte) { +#ifdef _WINDOWS + return *pMemory = LocalAlloc(LMEM_FIXED, ulCountByte); +#else + return *pMemory = calloc(1, ulCountByte); +#endif//_WINDOWS +} + +void ADDIN_API FreeMemory(void** pMemory) { +#ifdef _WINDOWS + LocalFree(*pMemory); + *pMemory = nullptr; +#else + free(*pMemory); + *pMemory = nullptr; +#endif//_WINDOWS +} + class ProxyComponent : public IMemoryManager { private: IComponentBase* pComponent = nullptr; NativeInterface mInterface; public: ProxyComponent( - IComponentBase* pComponent, + IComponentBase* pComponent, ErrorFuncRespond onError, EventFuncRespond onEvent, StatusFuncRespond onStatus - ): - pComponent(pComponent), + ) : + pComponent(pComponent), mInterface(onError, onEvent, onStatus) { pComponent->setMemManager(this); @@ -69,20 +84,10 @@ class ProxyComponent : public IMemoryManager { delete pComponent; } virtual bool ADDIN_API AllocMemory(void** pMemory, unsigned long ulCountByte) override { - #ifdef _WINDOWS - return *pMemory = LocalAlloc(LMEM_FIXED, ulCountByte); - #else - return *pMemory = calloc(1, ulCountByte); - #endif//_WINDOWS + return ::AllocMemory(pMemory, ulCountByte); } virtual void ADDIN_API FreeMemory(void** pMemory) override { - #ifdef _WINDOWS - LocalFree(*pMemory); - *pMemory = nullptr; - #else - free(*pMemory); - *pMemory = nullptr; - #endif//_WINDOWS + if (*pMemory) ::FreeMemory(pMemory); } IComponentBase& Component() { return *pComponent; @@ -94,22 +99,35 @@ static void ClearVariant(tVariant& variant) switch (variant.vt) { case VTYPE_BLOB: case VTYPE_PSTR: - free(variant.pstrVal); - variant.pstrVal = nullptr; + FreeMemory((void**)&variant.pstrVal); variant.strLen = 0; break; case VTYPE_PWSTR: - free(variant.pwstrVal); - variant.pwstrVal = nullptr; + FreeMemory((void**)&variant.pwstrVal); variant.wstrLen = 0; break; } variant.vt = VTYPE_EMPTY; } +DllExport tVariant* CreateVariant(int32_t lSizeArray) +{ + if (lSizeArray <= 0) return nullptr; + void* ptr = nullptr; + ::AllocMemory(&ptr, sizeof(tVariant) * lSizeArray); + return (tVariant*)ptr; +} + +DllExport void FreeVariant(tVariant* variant) +{ + if (variant == nullptr) return; + ::ClearVariant(*variant); + ::FreeMemory((void**)&variant); +} + DllExport ProxyComponent* GetClassObject( - HMODULE hModule, - const WCHAR_T* wsName, + HMODULE hModule, + const WCHAR_T* wsName, ErrorFuncRespond onError = nullptr, EventFuncRespond onEvent = nullptr, StatusFuncRespond onStatus = nullptr @@ -167,6 +185,106 @@ DllExport bool SetPropVal(ProxyComponent* proxy, int32_t lPropNum, tVariant* var return ok; } +DllExport void SetVariantEmpty(tVariant* variant, int32_t number) +{ + tVariant* v = variant + number; + TV_VT(v) = VTYPE_EMPTY; +} + +DllExport void SetVariantBool(tVariant* variant, int32_t number, bool value) +{ + tVariant* v = variant + number; + TV_BOOL(v) = value; + TV_VT(v) = VTYPE_BOOL; +} + +DllExport void SetVariantReal(tVariant* variant, int32_t number, double value) +{ + tVariant* v = variant + number; + TV_R8(v) = value; + TV_VT(v) = VTYPE_R8; +} + +DllExport void SetVariantInt(tVariant* variant, int32_t number, int32_t value) +{ + tVariant* v = variant + number; + TV_I4(v) = value; + TV_VT(v) = VTYPE_I4; +} + +DllExport void SetVariantStr(tVariant* variant, int32_t number, const WCHAR_T* value, int32_t length) +{ + tVariant* v = variant + number; + unsigned long size = sizeof(WCHAR_T) * (length + 1); + if (::AllocMemory((void**)&v->pwstrVal, size)) { + memcpy(v->pwstrVal, value, size); + v->wstrLen = length; + while (v->wstrLen && v->pwstrVal[v->wstrLen - 1] == 0) v->wstrLen--; + TV_VT(v) = VTYPE_PWSTR; + } +} + +DllExport void SetVariantBlob(tVariant* variant, int32_t number, const char* value, int32_t length) +{ + tVariant* v = variant + number; + if (::AllocMemory((void**)&v->pstrVal, length)) { + memcpy(v->pstrVal, value, length); + v->strLen = length; + TV_VT(v) = VTYPE_BLOB; + } +} + +typedef void(_stdcall* TSetVariantEmpty)(tVariant*, int32_t); +typedef void(_stdcall* TSetVariantBool)(tVariant*, int32_t, bool); +typedef void(_stdcall* TSetVariantReal)(tVariant*, int32_t, double); +typedef void(_stdcall* TSetVariantInt)(tVariant*, int32_t, int32_t); +typedef void(_stdcall* TSetVariantBlob)(tVariant*, int32_t, void*, int32_t); + +DllExport void GetVariant(tVariant* variant, int32_t number + , TSetVariantEmpty e + , TSetVariantBool b + , TSetVariantInt i + , TSetVariantReal r + , TSetVariantBlob s + , TSetVariantBlob x +) +{ + if (variant == nullptr) return; + switch (variant->vt) { + case VTYPE_EMPTY: + e(variant, number); + break; + case VTYPE_I2: + case VTYPE_I4: + case VTYPE_ERROR: + case VTYPE_UI1: + i(variant, number, variant->lVal); + break; + case VTYPE_BOOL: + b(variant, number, variant->bVal); + break; + case VTYPE_R4: + case VTYPE_R8: + r(variant, number, variant->dblVal); + break; + case VTYPE_DATE: + case VTYPE_TM: + e(variant, number); + break; + case VTYPE_PSTR: + e(variant, number); + break; + case VTYPE_PWSTR: + s(variant, number, variant->pwstrVal, variant->strLen); + break; + case VTYPE_BLOB: + x(variant, number, variant->pstrVal, variant->strLen); + break; + default: + e(variant, number); + } +} + DllExport bool IsPropReadable(ProxyComponent* proxy, int32_t lPropNum) { CHECK_PROXY(false); @@ -208,7 +326,16 @@ DllExport int32_t GetNParams(ProxyComponent* proxy, int32_t lMethodNum) return (int32_t)proxy->Component().GetNParams(lMethodNum); } -DllExport bool ADDIN_API GetParamDefValue(ProxyComponent* proxy, int32_t lMethodNum, int32_t lParamNum, VariantFuncRespond respond) +DllExport bool HasParamDefValue(ProxyComponent* proxy, int32_t lMethodNum, int32_t lParamNum) +{ + CHECK_PROXY(false); + tVariant variant = { 0 }; + bool result = proxy->Component().GetParamDefValue(lMethodNum, lParamNum, &variant) && variant.vt != VTYPE_EMPTY; + ClearVariant(variant); + return result; +} + +DllExport bool GetParamDefValue(ProxyComponent* proxy, int32_t lMethodNum, int32_t lParamNum, VariantFuncRespond respond) { CHECK_PROXY(false); tVariant variant = { 0 }; @@ -224,7 +351,7 @@ DllExport bool HasRetVal(ProxyComponent* proxy, int32_t lMethodNum) return proxy->Component().HasRetVal(lMethodNum); } -DllExport bool ADDIN_API CallAsProc(ProxyComponent* proxy, int32_t lMethodNum, tVariant* paParams) +DllExport bool CallAsProc(ProxyComponent* proxy, int32_t lMethodNum, tVariant* paParams) { CHECK_PROXY(false); auto lSizeArray = GetNParams(proxy, lMethodNum); @@ -232,7 +359,7 @@ DllExport bool ADDIN_API CallAsProc(ProxyComponent* proxy, int32_t lMethodNum, t return ok; } -DllExport bool ADDIN_API CallAsFunc(ProxyComponent* proxy, int32_t lMethodNum, tVariant* paParams, VariantFuncRespond respond) +DllExport bool CallAsFunc(ProxyComponent* proxy, int32_t lMethodNum, tVariant* paParams, VariantFuncRespond respond) { CHECK_PROXY(false); tVariant variant = { 0 }; diff --git a/src/ScriptEngine.NativeApi/ScriptEngine.NativeApi.vcxproj b/src/ScriptEngine.NativeApi/ScriptEngine.NativeApi.vcxproj index 6aed7bf82..7d282cc88 100644 --- a/src/ScriptEngine.NativeApi/ScriptEngine.NativeApi.vcxproj +++ b/src/ScriptEngine.NativeApi/ScriptEngine.NativeApi.vcxproj @@ -1,10 +1,15 @@ + Debug Win32 + + LinuxDebug + AnyCPU + Release Win32 @@ -71,22 +76,25 @@ - + + true - $(ProjectDir)bin\x86\$(Configuration)\net452 - + + false - $(ProjectDir)bin\x86\$(Configuration)\net452 - - true - $(ProjectDir)\bin\$(Configuration)\net452 + + + $(ProjectDir)bin\$(Configuration)\x86\ + $(ProjectName)32 - - false - $(ProjectDir)\bin\$(Configuration)\net452 + + + $(ProjectDir)bin\$(Configuration)\x64\ + $(ProjectName)64 + Level3 @@ -96,6 +104,7 @@ NotUsing + stdcpp17 Windows @@ -114,6 +123,7 @@ NotUsing + stdcpp17 Windows @@ -132,6 +142,7 @@ NotUsing + stdcpp17 Windows @@ -150,6 +161,7 @@ NotUsing + stdcpp17 Windows @@ -172,4 +184,4 @@ - \ No newline at end of file + diff --git a/src/ScriptEngine.NativeApi/build.bat b/src/ScriptEngine.NativeApi/build.bat new file mode 100644 index 000000000..a962b6ae5 --- /dev/null +++ b/src/ScriptEngine.NativeApi/build.bat @@ -0,0 +1,11 @@ +cmake -E make_directory build32w +cd build32w +cmake .. -A Win32 -DMySuffix=32 +cmake --build . --config Release +cd .. + +cmake -E make_directory build64w +cd build64w +cmake .. -A x64 -DMySuffix=64 +cmake --build . --config Release +cd .. \ No newline at end of file diff --git a/src/ScriptEngine.NativeApi/build.sh b/src/ScriptEngine.NativeApi/build.sh new file mode 100755 index 000000000..a2aaacc80 --- /dev/null +++ b/src/ScriptEngine.NativeApi/build.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +cmake -E make_directory build32 +cd build32 +cmake -D CMAKE_BUILD_TYPE:STRING=Release -D TARGET_PLATFORM_32:BOOL=ON --build .. +cmake --build . +cp *.so .. +cd .. + +cmake -E make_directory build64 +cd build64 +cmake -D CMAKE_BUILD_TYPE:STRING=Release -D TARGET_PLATFORM_32:BOOL=OFF --build .. +cmake --build . +chmod +x *.so +cp *.so .. +cd .. diff --git a/src/ScriptEngine.Snegopat/AssemblyInfo.cpp b/src/ScriptEngine.Snegopat/AssemblyInfo.cpp deleted file mode 100644 index 437db2d42..000000000 --- a/src/ScriptEngine.Snegopat/AssemblyInfo.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "stdafx.h" - -using namespace System; -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; -using namespace System::Security::Permissions; - -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// -[assembly:AssemblyTitleAttribute("ScriptEngineSnegopat")]; -[assembly:AssemblyDescriptionAttribute("")]; -[assembly:AssemblyConfigurationAttribute("")]; -[assembly:AssemblyCompanyAttribute("")]; -[assembly:AssemblyProductAttribute("ScriptEngineSnegopat")]; -[assembly:AssemblyCopyrightAttribute("Copyright BeaverSoft(c) 2014")]; -[assembly:AssemblyTrademarkAttribute("")]; -[assembly:AssemblyCultureAttribute("")]; - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the value or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly:AssemblyVersionAttribute("1.0.11.36")]; - -[assembly:ComVisible(false)]; - -[assembly:CLSCompliantAttribute(true)]; - -[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; diff --git a/src/ScriptEngine.Snegopat/CriticalResourceLoader.cpp b/src/ScriptEngine.Snegopat/CriticalResourceLoader.cpp deleted file mode 100644 index 0ef5d2eba..000000000 --- a/src/ScriptEngine.Snegopat/CriticalResourceLoader.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "stdafx.h" -#include "CriticalResourceLoader.h" - -CriticalResourceLoader::CriticalResourceLoader(HMODULE mod) -{ - m_module = mod; - m_modulePath = new WCHAR[MAX_PATH+1]; - memset(m_modulePath, 0, (MAX_PATH+1) * sizeof(WCHAR)); - GetModuleFileName(mod, m_modulePath, MAX_PATH); - - System::AppDomain::CurrentDomain->AssemblyResolve += - gcnew System::ResolveEventHandler(this, &CriticalResourceLoader::DependencyHandler); - -} - -Reflection::Assembly^ CriticalResourceLoader::DependencyHandler(Object^ sender, ResolveEventArgs^ args) -{ - System::String^ str = args->Name; - if(str->IndexOf(L"ScriptEngine",0) >= 0) - { - - System::IntPtr^ ptr = gcnew System::IntPtr(m_modulePath); - System::String^ pathBuild = System::Runtime::InteropServices::Marshal::PtrToStringUni(*ptr, MAX_PATH); - - int idx = pathBuild->LastIndexOf('\\'); - System::String^ dir = pathBuild->Substring(0, idx + 1); - - idx = str->IndexOf(','); - System::String^ dll = str->Substring(0, idx) + ".dll"; - pathBuild = System::IO::Path::Combine(dir, dll); - - return System::Reflection::Assembly::LoadFrom(pathBuild); - - } - else - { - return nullptr; - } -} - -CriticalResourceLoader::~CriticalResourceLoader(void) -{ - delete[] m_modulePath; -} - -bool CriticalResourceLoader::PrepareTypeInfo() -{ - return true; -} - -IUnknown* CriticalResourceLoader::GetLoader(IDispatch* pDesigner) -{ - IAddinLoader* loader = new IAddinLoaderImpl(pDesigner); - return loader; -} \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/CriticalResourceLoader.h b/src/ScriptEngine.Snegopat/CriticalResourceLoader.h deleted file mode 100644 index 34e302cf8..000000000 --- a/src/ScriptEngine.Snegopat/CriticalResourceLoader.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "StdAfx.h" -#include -#include "IAddinImpl.h" -#include "IAddinLoaderImpl.h" - -using namespace System; - -ref class CriticalResourceLoader -{ -private: - HMODULE m_module; - WCHAR* m_modulePath; - - Reflection::Assembly^ DependencyHandler(Object^ sender, ResolveEventArgs^ args); - -public: - CriticalResourceLoader(HMODULE); - bool PrepareTypeInfo(); - IUnknown* GetLoader(IDispatch* pDesigner); - ~CriticalResourceLoader(void); -}; - diff --git a/src/ScriptEngine.Snegopat/DispatchHelpers.cpp b/src/ScriptEngine.Snegopat/DispatchHelpers.cpp deleted file mode 100644 index 01f9f3702..000000000 --- a/src/ScriptEngine.Snegopat/DispatchHelpers.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include "Stdafx.h" -#include "DispatchHelpers.h" - -#pragma unmanaged - -LPCTSTR getNextVarType(LPCTSTR pszFmt, VARTYPE FAR* pvt) -{ - *pvt = 0; - if (*pszFmt == '&') - { - *pvt = VT_BYREF; - pszFmt++; - if (!*pszFmt) - return NULL; - } - switch(*pszFmt) - { - case 'b': - *pvt |= VT_BOOL; - break; - case 'i': - *pvt |= VT_I2; - break; - case 'I': - *pvt |= VT_I4; - break; - case 'r': - *pvt |= VT_R4; - break; - case 'R': - *pvt |= VT_R8; - break; - case 'c': - *pvt |= VT_CY; - break; - case 's': - *pvt |= VT_BSTR; - break; - case 'e': - *pvt |= VT_ERROR; - break; - case 'd': - *pvt |= VT_DATE; - break; - case 'v': - *pvt |= VT_VARIANT; - break; - case 'U': - *pvt |= VT_UNKNOWN; - break; - case 'D': - *pvt |= VT_DISPATCH; - break; - case '\0': - return NULL; // End of Format string - default: - return NULL; - } - return ++pszFmt; -} - -HRESULT countArgsInFormat(LPCTSTR pszFmt, UINT FAR *pn) -{ - *pn = 0; - - if(pszFmt == NULL) - return NOERROR; - - while (*pszFmt) - { - if (*pszFmt == '&') - pszFmt++; - - switch(*pszFmt) - { - case 'b': - case 'i': - case 'I': - case 'r': - case 'R': - case 'c': - case 's': - case 'e': - case 'd': - case 'v': - case 'D': - case 'U': - ++*pn; - pszFmt++; - break; - case '\0': - default: - return ResultFromScode(E_INVALIDARG); - } - } - return NOERROR; -} - -HRESULT invoke(LPDISPATCH pdisp, - WORD wFlags, - LPVARIANT pvRet, - EXCEPINFO FAR* pexcepinfo, - UINT FAR* pnArgErr, - LPOLESTR pszName, - LPCTSTR pszFmt, - ...) -{ - va_list argList; - va_start(argList, pszFmt); - DISPID dispid; - HRESULT hr; - VARIANTARG* pvarg = NULL; - - if (pdisp == NULL) - return ResultFromScode(E_INVALIDARG); - - // Get DISPID of property/method - hr = pdisp->GetIDsOfNames(IID_NULL, &pszName, 1, LOCALE_USER_DEFAULT, &dispid); - if(FAILED(hr)) - { - return hr; - } - - DISPPARAMS dispparams; - memset(&dispparams, 0, sizeof dispparams); - - // determine number of arguments - if (pszFmt != NULL) - countArgsInFormat(pszFmt, &dispparams.cArgs); - - // Property puts have a named argument that represents the value that the property is - // being assigned. - DISPID dispidNamed = DISPID_PROPERTYPUT; - if (wFlags & DISPATCH_PROPERTYPUT) - { - if (dispparams.cArgs == 0) - return ResultFromScode(E_INVALIDARG); - dispparams.cNamedArgs = 1; - dispparams.rgdispidNamedArgs = &dispidNamed; - } - - if (dispparams.cArgs != 0) - { - // allocate memory for all VARIANTARG parameters - pvarg = new VARIANTARG[dispparams.cArgs]; - if(pvarg == NULL) - return ResultFromScode(E_OUTOFMEMORY); - dispparams.rgvarg = pvarg; - memset(pvarg, 0, sizeof(VARIANTARG) * dispparams.cArgs); - - // get ready to walk vararg list - LPCTSTR psz = pszFmt; - pvarg += dispparams.cArgs - 1; // params go in opposite order - - while (psz = getNextVarType(psz, &pvarg->vt)) - { - if (pvarg < dispparams.rgvarg) - { - hr = ResultFromScode(E_INVALIDARG); - goto cleanup; - } - switch (pvarg->vt) - { - case VT_I2: - V_I2(pvarg) = va_arg(argList, short); - break; - case VT_I4: - V_I4(pvarg) = va_arg(argList, long); - break; - case VT_R4: - V_R4(pvarg) = va_arg(argList, float); - break; - case VT_DATE: - case VT_R8: - V_R8(pvarg) = va_arg(argList, double); - break; - case VT_CY: - V_CY(pvarg) = va_arg(argList, CY); - break; - case VT_BSTR: - V_BSTR(pvarg) = SysAllocString(va_arg(argList, OLECHAR FAR*)); - if (pvarg->bstrVal == NULL) - { - hr = ResultFromScode(E_OUTOFMEMORY); - pvarg->vt = VT_EMPTY; - goto cleanup; - } - break; - case VT_DISPATCH: - V_DISPATCH(pvarg) = va_arg(argList, LPDISPATCH); - break; - case VT_ERROR: - V_ERROR(pvarg) = va_arg(argList, SCODE); - break; - case VT_BOOL: - V_BOOL(pvarg) = va_arg(argList, BOOL) ? -1 : 0; - break; - case VT_VARIANT: - *pvarg = va_arg(argList, VARIANTARG); - break; - case VT_UNKNOWN: - V_UNKNOWN(pvarg) = va_arg(argList, LPUNKNOWN); - break; - - case VT_I2|VT_BYREF: - V_I2REF(pvarg) = va_arg(argList, short FAR*); - break; - case VT_I4|VT_BYREF: - V_I4REF(pvarg) = va_arg(argList, long FAR*); - break; - case VT_R4|VT_BYREF: - V_R4REF(pvarg) = va_arg(argList, float FAR*); - break; - case VT_R8|VT_BYREF: - V_R8REF(pvarg) = va_arg(argList, double FAR*); - break; - case VT_DATE|VT_BYREF: - V_DATEREF(pvarg) = va_arg(argList, DATE FAR*); - break; - case VT_CY|VT_BYREF: - V_CYREF(pvarg) = va_arg(argList, CY FAR*); - break; - case VT_BSTR|VT_BYREF: - V_BSTRREF(pvarg) = va_arg(argList, BSTR FAR*); - break; - case VT_DISPATCH|VT_BYREF: - V_DISPATCHREF(pvarg) = va_arg(argList, LPDISPATCH FAR*); - break; - case VT_ERROR|VT_BYREF: - V_ERRORREF(pvarg) = va_arg(argList, SCODE FAR*); - break; - case VT_BOOL|VT_BYREF: - { - BOOL FAR* pbool = va_arg(argList, BOOL FAR*); - *pbool = 0; - V_BOOLREF(pvarg) = (VARIANT_BOOL FAR*)pbool; - } - break; - case VT_VARIANT|VT_BYREF: - V_VARIANTREF(pvarg) = va_arg(argList, VARIANTARG FAR*); - break; - case VT_UNKNOWN|VT_BYREF: - V_UNKNOWNREF(pvarg) = va_arg(argList, LPUNKNOWN FAR*); - break; - - default: - { - hr = ResultFromScode(E_INVALIDARG); - goto cleanup; - } - break; - } - - --pvarg; // get ready to fill next argument - } //while - } //if - - // Initialize return variant, in case caller forgot. Caller can pass NULL if return - // value is not expected. - if (pvRet) - VariantInit(pvRet); - // make the call - try { - hr = pdisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, wFlags, - &dispparams, pvRet, pexcepinfo, pnArgErr); - } - catch (...) { - hr = E_FAIL; - } - -cleanup: - // cleanup any arguments that need cleanup - if (dispparams.cArgs != 0) - { - VARIANTARG FAR* pvarg = dispparams.rgvarg; - UINT cArgs = dispparams.cArgs; - - while (cArgs--) - { - switch (pvarg->vt) - { - case VT_BSTR: - VariantClear(pvarg); - break; - } - ++pvarg; - } - } -// delete dispparams.rgvarg; - va_end(argList); - return hr; -} \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/DispatchHelpers.h b/src/ScriptEngine.Snegopat/DispatchHelpers.h deleted file mode 100644 index ee929580f..000000000 --- a/src/ScriptEngine.Snegopat/DispatchHelpers.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -HRESULT invoke(LPDISPATCH pdisp, - WORD wFlags, - LPVARIANT pvRet, - ::EXCEPINFO FAR* pexcepinfo, - UINT FAR* pnArgErr, - LPOLESTR pszName, - LPCTSTR pszFmt, - ...); - -LPCTSTR getNextVarType(LPCTSTR pszFmt, VARTYPE FAR* pvt); - -HRESULT countArgsInFormat(LPCTSTR pszFmt, UINT FAR *pn); \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/EventCallableSDO.cpp b/src/ScriptEngine.Snegopat/EventCallableSDO.cpp deleted file mode 100644 index 57283744f..000000000 --- a/src/ScriptEngine.Snegopat/EventCallableSDO.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "stdafx.h" -#include "EventCallableSDO.h" - -EventCallableSDO::EventCallableSDO(ScriptDrivenObject^ instance, LoadedModuleHandle module) - : ReflectableSDO(instance, module) -{ -} - -Object^ EventCallableSDO::InvokeInternal(String^ name, - System::Reflection::BindingFlags invokeAttr, - Binder^ binder, - Object^ target, - array^ args, - System::Globalization::CultureInfo^ culture) -{ - array^ passedArgs = gcnew array(args->Length); - array^ wrappers = gcnew array(args->Length); - - try - { - Object^ result; - - for (int i = 0; i < args->Length; i++) - { - IParamsWrapper^ wrapper = dynamic_cast(args[i]); - IValue^ argValue; - if(wrapper != nullptr) - { - wrappers[i] = wrapper; - argValue = COMWrapperContext::CreateIValue(wrapper->val); - } - else - { - argValue = COMWrapperContext::CreateIValue(args[i]); - } - - passedArgs[i] = Variable::CreateReference(Variable::Create(argValue)); - - } - - result = ReflectableSDO::InvokeInternal(name, invokeAttr, binder, target, passedArgs, culture); - - for (int i = 0; i < args->Length; i++) - { - IParamsWrapper^ wrapper = wrappers[i]; - Object^ argValue = COMWrapperContext::MarshalIValue(safe_cast(passedArgs[i])); - if(wrapper != nullptr) - { - wrapper->val = argValue; - } - else - { - args[i] = passedArgs[i]; - } - } - - return result; - - } - catch(Exception^ exc) - { - auto buf = stringBuf(exc->ToString()); - MessageBox(0, buf, L"Error", MB_ICONERROR); - delete[] buf; - - throw; - - } - finally - { - delete[] passedArgs; - delete[] wrappers; - } -} \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/EventCallableSDO.h b/src/ScriptEngine.Snegopat/EventCallableSDO.h deleted file mode 100644 index 7cef91312..000000000 --- a/src/ScriptEngine.Snegopat/EventCallableSDO.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include "MarshalingHelpers.h" -#include "SnegAPIDefinitions.h" - -using namespace System; -using namespace System::Reflection; -using namespace ScriptEngine; -using namespace ScriptEngine::Environment; -using namespace ScriptEngine::Machine; -using namespace ScriptEngine::Machine::Contexts; - -public ref class EventCallableSDO : public ReflectableSDO -{ -public: - EventCallableSDO(ScriptDrivenObject^ instance, LoadedModuleHandle module); - -protected: - - virtual Object^ InvokeInternal(String^ name, - System::Reflection::BindingFlags invokeAttr, - Binder^ binder, - Object^ target, - array^ args, - System::Globalization::CultureInfo^ culture) override; -}; - diff --git a/src/ScriptEngine.Snegopat/IAddinImpl.cpp b/src/ScriptEngine.Snegopat/IAddinImpl.cpp deleted file mode 100644 index 2209b6b79..000000000 --- a/src/ScriptEngine.Snegopat/IAddinImpl.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "stdafx.h" -#include "IAddinImpl.h" -#include "MarshalingHelpers.h" -#include "DispatchHelpers.h" -#include - -IAddinImpl::IAddinImpl(ScriptEngine::Machine::Contexts::ScriptDrivenObject^ innerObject) : RefCountable() -{ - m_innerObject = innerObject; -} - -IAddinImpl::~IAddinImpl(void) -{ - m_innerObject = nullptr; -} - -//IUnknown interface -#pragma region IUnknown implementation - -HRESULT __stdcall IAddinImpl::QueryInterface( - REFIID riid , - void **ppObj) -{ - if (riid == IID_IUnknown) - { - *ppObj = static_cast(this); - AddRef(); - return S_OK; - } - if (riid == IID_IAddinMacroses) - { - *ppObj = static_cast(this); - AddRef(); - return S_OK; - } - else - { - *ppObj = NULL ; - return E_NOINTERFACE ; - } -} - -ULONG __stdcall IAddinImpl::AddRef() -{ - return RefCountable::AddRef(); -} - -ULONG __stdcall IAddinImpl::Release() -{ - return RefCountable::Release(); -} - -#pragma endregion - -HRESULT STDMETHODCALLTYPE IAddinImpl::macroses(SAFEARRAY **result) -{ - String^ prefix = L"_"; - - array^ macrosArray = m_innerObject->GetExportedMethods(); - int macroCount = 0; - for each (String^ name in macrosArray) - { - if(name->StartsWith(prefix)) - { - macroCount++; - } - } - SAFEARRAYBOUND Bound[1]; - Bound[0].lLbound = 0; - Bound[0].cElements = macroCount; - *result = SafeArrayCreate(VT_VARIANT, 1, Bound); - LONG idx[1]; - - for (int i = 0, j = 0; i < macrosArray->Length; i++) - { - if(macrosArray[i]->StartsWith(prefix) && macrosArray[i]->Length > prefix->Length) - { - WCHAR* buf = stringBuf(macrosArray[i]->Substring(prefix->Length)); - BSTR allocString = SysAllocString(buf); - delete[] buf; - - VARIANT val; - V_VT(&val) = VT_BSTR; - V_BSTR(&val) = allocString; - - idx[0] = j++; - HRESULT hr = SafeArrayPutElement(*result, idx, &val); - } - } - - return S_OK; - -} - -HRESULT STDMETHODCALLTYPE IAddinImpl::invokeMacros( - BSTR MacrosName, - VARIANT *result) -{ - String^ prefix = L"_"; - System::String^ strMacro = prefix + (gcnew System::String(MacrosName)); - - try - { - int mNum = m_innerObject->FindMethod(strMacro); - auto mInfo = m_innerObject->GetMethodInfo(mNum); - // - m_innerObject->CallAsProcedure(mNum, gcnew array(0)); - } - catch(System::Exception^ e) - { - auto buf = stringBuf(e->ToString()); - MessageBox(0, buf, L"Error", MB_OK); - delete[] buf; - } - - return S_OK; - -} - -void IAddinImpl::OnZeroCount() -{ - m_innerObject = nullptr; -} diff --git a/src/ScriptEngine.Snegopat/IAddinImpl.h b/src/ScriptEngine.Snegopat/IAddinImpl.h deleted file mode 100644 index eada97833..000000000 --- a/src/ScriptEngine.Snegopat/IAddinImpl.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include -#include "Snegopat_h.h" -#include "RefCountable.h" -#include - -using namespace System; -using namespace ScriptEngine; - -class IAddinImpl : - public RefCountable, - public IAddinMacroses -{ -private: - gcroot m_innerObject; - - BSTR m_uniqueName; - BSTR m_displayName; - BSTR m_fullPath; - -public: - - IAddinImpl(Machine::Contexts::ScriptDrivenObject^ innerObject); - - void SetNames(BSTR uniqueName, BSTR displayName, BSTR fullPath) - { - m_uniqueName = uniqueName; - m_displayName = displayName; - m_fullPath = fullPath; - } - - ScriptEngine::Machine::Contexts::ScriptDrivenObject^ - GetManagedInstance() - { - return m_innerObject; - } - - //IUnknown interface - virtual HRESULT __stdcall QueryInterface( - REFIID riid, - void **ppObj); - virtual ULONG __stdcall AddRef(); - virtual ULONG __stdcall Release(); - - // IAddIn interface - - virtual HRESULT STDMETHODCALLTYPE macroses(SAFEARRAY **result); - - virtual HRESULT STDMETHODCALLTYPE invokeMacros(BSTR MacrosName, VARIANT *result); - - virtual void OnZeroCount(); - - ~IAddinImpl(void); - -}; - diff --git a/src/ScriptEngine.Snegopat/IAddinLoaderImpl.cpp b/src/ScriptEngine.Snegopat/IAddinLoaderImpl.cpp deleted file mode 100644 index ff16b68d0..000000000 --- a/src/ScriptEngine.Snegopat/IAddinLoaderImpl.cpp +++ /dev/null @@ -1,434 +0,0 @@ -#include "stdafx.h" -#include "IAddinLoaderImpl.h" -#include "Snegopat_i.c" -#include "MarshalingHelpers.h" -#include "DispatchHelpers.h" -#include "ScriptDrivenAddin.h" -#include -#include - -IAddinLoaderImpl::IAddinLoaderImpl(IDispatch* pDesigner) : RefCountable() -{ - m_pDesigner = pDesigner; - m_pDesigner->AddRef(); - m_engine = gcnew ScriptEngine::ScriptingEngine(); - - ScriptEngine::RuntimeEnvironment^ env = gcnew ScriptEngine::RuntimeEnvironment(); - - IntPtr handle = IntPtr(pDesigner); - Object^ managedObject = System::Runtime::InteropServices::Marshal::GetObjectForIUnknown(handle); - IRuntimeContextInstance^ designerWrapper = Contexts::COMWrapperContext::Create(managedObject); - - env->InjectGlobalProperty((IValue^)designerWrapper, L"Designer", true); - env->InjectGlobalProperty((IValue^)designerWrapper, L"", true); - - SnegopatAttachedContext^ importedProperties = gcnew SnegopatAttachedContext(designerWrapper); - TypeManager::NewInstanceHandler = importedProperties->GetType(); - env->InjectObject(importedProperties, true); - - LibraryAttachedContext^ stdLib = gcnew LibraryAttachedContext(m_engine); - env->InjectObject(stdLib); - m_engine->Environment = env; - m_engine->Initialize(); - -} - -IAddinLoaderImpl::~IAddinLoaderImpl(void) -{ - if(nullptr != (ScriptEngine::ScriptingEngine^)m_engine) - { - m_engine = nullptr; - m_pDesigner = NULL; - } -} - -void IAddinLoaderImpl::OnZeroCount() -{ - m_pDesigner->Release(); -} - -ScriptDrivenAddin^ IAddinLoaderImpl::LoadFromScriptFile(String^ path, addinNames* names) -{ - String^ strDisplayName = nullptr; - String^ strUniqueName = nullptr; - - System::IO::StreamReader^ rd = nullptr; - - rd = ScriptEngine::Environment::FileOpener::OpenReader(path); - ScriptDrivenAddin^ scriptObject = nullptr; - - try - { - Char ch = rd->Peek(); - while(ch > -1 && ch == '$') - { - String^ macro = rd->ReadLine(); - if(macro->Length > 0) - { - array^ parts = macro->Split(gcnew array(2){' ', '\t'}, 2); - parts[0] = parts[0]->Trim(); - parts[1] = parts[1]->Trim(); - if(parts->Length < 2) - { - continue; - } - - if(parts[0] == "$uname") - { - strUniqueName = parts[1]; - } - else if(parts[0] == "$dname") - { - strDisplayName = parts[1]; - } - } - ch = rd->Peek(); - } - - if(!rd->EndOfStream) - { - if(strDisplayName == nullptr) - strDisplayName = System::IO::Path::GetFileNameWithoutExtension(path); - if(strUniqueName == nullptr) - strUniqueName = System::IO::Path::GetFileNameWithoutExtension(path); - - names->displayName = stringToBSTR(strDisplayName); - names->uniqueName = stringToBSTR(strUniqueName); - - String^ code = rd->ReadToEnd(); - ICodeSource^ src = m_engine->Loader->FromString(code); - CompilerService^ compiler = m_engine->GetCompilerService(); - - String^ thisName = L""; - compiler->DefineVariable(thisName, SymbolType::ContextProperty); - ScriptEngine::Environment::ScriptModuleHandle mHandle = compiler->CreateModule(src); - LoadedModuleHandle mh = m_engine->LoadModuleImage(mHandle); - - scriptObject = gcnew ScriptDrivenAddin(mh); - scriptObject->AddProperty(thisName, scriptObject); - scriptObject->InitOwnData(); - - } - } - finally - { - delete rd; - } - - return scriptObject; -} - -ScriptDrivenAddin^ IAddinLoaderImpl::LoadFromDialog(String^ path, addinNames* names) -{ - IDispatch* files = NULL; - IDispatch* storage = NULL; - IDispatch* intFile = NULL; - - BSTR tmp_bstr = NULL; - - HRESULT hr; - - VARIANT retVal; - hr = invoke(m_pDesigner, DISPATCH_PROPERTYGET, &retVal, NULL, NULL, L"v8files", NULL); - if(SUCCEEDED(hr)) - files = V_DISPATCH(&retVal); - else - return nullptr; - - String^ uri = L"file://" + path; - tmp_bstr = stringToBSTR(uri); - try - { - hr = invoke(files, DISPATCH_METHOD, &retVal, NULL, NULL, L"open", L"si", tmp_bstr, 8); - - if(SUCCEEDED(hr)) - intFile = V_DISPATCH(&retVal); - else - return nullptr; - - hr = invoke(files, DISPATCH_METHOD, &retVal, NULL, NULL, L"attachStorage", L"U", intFile); - - if(SUCCEEDED(hr)) - storage = V_DISPATCH(&retVal); - else - return nullptr; - - hr = invoke(storage, DISPATCH_METHOD, &retVal, NULL, NULL, L"open", L"si", L"module", 8); - - IDispatch* moduleFile = NULL; - if(SUCCEEDED(hr)) - moduleFile = V_DISPATCH(&retVal); - else - return nullptr; - - hr = invoke(moduleFile, DISPATCH_METHOD, &retVal, NULL, NULL, L"getString", L"i", 2); - moduleFile->Release(); - storage->Release(); - storage = NULL; - invoke(files, DISPATCH_METHOD, NULL, NULL, NULL, L"close", L"U", intFile); - intFile->Release(); - intFile = NULL; - - String^ code; - if(SUCCEEDED(hr)) - { - tmp_bstr = V_BSTR(&retVal); - } - else - return nullptr; - - if(tmp_bstr[0] == 65279) // . - tmp_bstr[0] = ' '; - - code = gcnew String(tmp_bstr); - - ICodeSource^ src = m_engine->Loader->FromString(code); - CompilerService^ compiler = m_engine->GetCompilerService(); - String^ thisProp = L""; - String^ formProp = L""; - - compiler->DefineVariable(thisProp, SymbolType::ContextProperty); - compiler->DefineVariable(formProp, SymbolType::ContextProperty); - LoadedModuleHandle mh = m_engine->LoadModuleImage(compiler->CreateModule(src)); - - ScriptDrivenAddin^ scriptObject = gcnew ScriptDrivenAddin(mh); - - SysFreeString(tmp_bstr); - tmp_bstr = stringToBSTR(path); - - Object^ managedDesigner = Marshal::GetObjectForIUnknown(IntPtr(m_pDesigner)); - array^ args = gcnew array(2) - { - gcnew String(tmp_bstr), - scriptObject->UnderlyingObject - }; - - Object^ form = nullptr; - try - { - form = managedDesigner->GetType()->InvokeMember("loadScriptForm", - System::Reflection::BindingFlags::InvokeMethod, - nullptr, - managedDesigner, - args); - } - finally - { - Marshal::ReleaseComObject(managedDesigner); - } - - scriptObject->AddProperty(thisProp, scriptObject); - scriptObject->AddProperty(formProp, COMWrapperContext::Create(form)); - scriptObject->InitOwnData(); - - String^ title = safe_cast(form->GetType()->InvokeMember(L"", System::Reflection::BindingFlags::GetProperty, nullptr, form, nullptr)); - if(String::IsNullOrWhiteSpace(title)) - { - names->displayName = stringToBSTR(System::IO::Path::GetFileNameWithoutExtension(path)); - names->uniqueName = stringToBSTR(System::IO::Path::GetFileNameWithoutExtension(path)); - } - else - { - auto split = title->Split('/'); - if(split->Length > 1) - { - names->uniqueName = stringToBSTR(split[0]); - names->displayName = stringToBSTR(split[1]); - array^ titleArgs = gcnew array(1); - titleArgs[0] = split[1]; - form->GetType()->InvokeMember(L"", System::Reflection::BindingFlags::SetProperty, nullptr, form, titleArgs); - } - else - { - names->displayName = stringToBSTR(title); - names->uniqueName = stringToBSTR(System::IO::Path::GetFileNameWithoutExtension(path)); - } - } - - return scriptObject; - - } - finally - { - if(storage != NULL) - storage->Release(); - if(intFile != NULL) - intFile->Release(); - - files->Release(); - - if(tmp_bstr != NULL) - SysFreeString(tmp_bstr); - } - -} - -//IUnknown interface -#pragma region IUnknown implementation - -HRESULT __stdcall IAddinLoaderImpl::QueryInterface( - REFIID riid , - void **ppObj) -{ - if(riid == IID_IAddinLoader) - { - *ppObj = static_cast(this); - AddRef(); - return S_OK; - } - else if (riid == IID_IUnknown) - { - *ppObj = static_cast(this); - AddRef(); - return S_OK; - } - else - { - *ppObj = NULL ; - return E_NOINTERFACE ; - } -} - -ULONG __stdcall IAddinLoaderImpl::AddRef() -{ - return RefCountable::AddRef(); -} - -ULONG __stdcall IAddinLoaderImpl::Release() -{ - return RefCountable::Release(); -} - -#pragma endregion - -#pragma region IAddinLoader implementation - -HRESULT __stdcall IAddinLoaderImpl::proto( - BSTR *result) -{ - *result = SysAllocString(L"1clang"); - return S_OK; -} - -HRESULT __stdcall IAddinLoaderImpl::load( - BSTR uri, - BSTR *fullPath, - BSTR *uniqueName, - BSTR *displayName, - IUnknown **result) -{ - String^ strDisplayName = nullptr; - String^ strUniqueName = nullptr; - - HRESULT res = E_FAIL; - - std::wstring wsUri = uri; - int pos = wsUri.find_first_of(':', 0); - if(pos != std::wstring::npos) - { - String^ path = gcnew String(wsUri.substr(pos+1).c_str()); - String^ protocol = gcnew String(wsUri.substr(0, pos+1).c_str()); - path = System::IO::Path::GetFullPath(path); - String^ extension = System::IO::Path::GetExtension(path)->ToLower(); - ScriptDrivenAddin^ scriptObject; - addinNames names; - try - { - if(extension == ".os" || extension == ".1scr") - { - scriptObject = LoadFromScriptFile(path, &names); - } - else if(extension == ".osf" || extension == ".ssf") - { - scriptObject = LoadFromDialog(path, &names); - res = S_OK; - } - else - { - return E_FAIL; - } - - if(scriptObject == nullptr) - return E_FAIL; - - *displayName = names.displayName; - *uniqueName = names.uniqueName; - *fullPath = stringToBSTR(protocol+path); - - IAddinImpl* snegopatAddin = new IAddinImpl(scriptObject); - m_engine->InitializeSDO(scriptObject); - - snegopatAddin->SetNames(*uniqueName, *displayName, *fullPath); - snegopatAddin->QueryInterface(IID_IUnknown, (void**)result); - - res = S_OK; - } - catch(Exception^ e) - { - WCHAR* msg = stringBuf(e->Message); - MessageBox(0, msg, L"Load error", MB_ICONERROR); - delete[] msg; - - res = E_FAIL; - } - - } - - return res; - -} - -HRESULT __stdcall IAddinLoaderImpl::canUnload( - BSTR fullPath, - IUnknown *addin, - VARIANT_BOOL *result) -{ - *result = VARIANT_TRUE; - return S_OK; -} - -HRESULT __stdcall IAddinLoaderImpl::unload( - BSTR fullPath, - IUnknown *addin, - VARIANT_BOOL *result) -{ - *result = VARIANT_TRUE; - return S_OK; -} - -HRESULT __stdcall IAddinLoaderImpl::loadCommandName( - BSTR *result) -{ - *result = SysAllocString(L" 1|1clang"); - return S_OK; -} - -HRESULT __stdcall IAddinLoaderImpl::selectLoadURI( - BSTR *result) -{ - OPENFILENAME ofn; - const int PREFIX_LEN = 7; - const int BUFFER_SIZE = PREFIX_LEN + MAX_PATH + 1; - WCHAR pUri[BUFFER_SIZE]; - memset(pUri, 0, (BUFFER_SIZE) * sizeof(WCHAR)); - wcsncat_s(pUri, L"1clang:", PREFIX_LEN); - WCHAR* file = pUri + PREFIX_LEN; - - memset(&ofn,0,sizeof(OPENFILENAME)); - ofn.lStructSize = sizeof(OPENFILENAME); - ofn.lpstrFilter = L"/ OneScript\0*.1scr;*.os;*.osf\0 1 (*.os; *.1scr)\0*.os;*.1scr\0 1 (*.osf; *.ssf)\0*.osf;*.ssf\0 \0*.*\0\0"; - ofn.lpstrFile = file; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_EXPLORER|OFN_FILEMUSTEXIST; - if(GetOpenFileName(&ofn)) - { - *result = SysAllocStringLen(pUri, MAX_PATH); - return S_OK; - } - else - { - return E_ABORT; - } -} - -#pragma endregion diff --git a/src/ScriptEngine.Snegopat/IAddinLoaderImpl.h b/src/ScriptEngine.Snegopat/IAddinLoaderImpl.h deleted file mode 100644 index b8611f3cc..000000000 --- a/src/ScriptEngine.Snegopat/IAddinLoaderImpl.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "Stdafx.h" -#include "Snegopat_h.h" -#include "RefCountable.h" -#include "IAddinImpl.h" -#include "SnegopatAttachedContext.h" -#include "LibraryAttachedContext.h" -#include "ScriptDrivenAddin.h" - -#include - -using namespace System; -using namespace ScriptEngine; -using namespace ScriptEngine::Machine; -using namespace ScriptEngine::Compiler; -using namespace ScriptEngine::Environment; -using namespace System::Runtime::InteropServices; - -class IAddinLoaderImpl : - public RefCountable, - public IAddinLoader -{ -private: - - IDispatch* m_pDesigner; - gcroot m_engine; - - struct addinNames - { - BSTR uniqueName; - BSTR displayName; - }; - - ScriptDrivenAddin^ LoadFromScriptFile(String^ path, addinNames* names); - ScriptDrivenAddin^ LoadFromDialog(String^ path, addinNames* names); - -protected: - virtual void OnZeroCount(); - -public: - IAddinLoaderImpl(IDispatch* pDesigner); - - //IUnknown interface - virtual HRESULT __stdcall QueryInterface( - REFIID riid, - void **ppObj); - virtual ULONG __stdcall AddRef(); - virtual ULONG __stdcall Release(); - - virtual HRESULT __stdcall proto( - BSTR *result); - - virtual HRESULT __stdcall load( - BSTR uri, - BSTR *fullPath, - BSTR *uniqueName, - BSTR *displayName, - IUnknown **result); - - virtual HRESULT __stdcall canUnload( - BSTR fullPath, - IUnknown *addin, - VARIANT_BOOL *result); - - virtual HRESULT __stdcall unload( - BSTR fullPath, - IUnknown *addin, - VARIANT_BOOL *result); - - virtual HRESULT __stdcall loadCommandName( - BSTR *result); - - virtual HRESULT __stdcall selectLoadURI( - BSTR *result); - - virtual ~IAddinLoaderImpl(void); -}; - diff --git a/src/ScriptEngine.Snegopat/LibraryAttachedContext.cpp b/src/ScriptEngine.Snegopat/LibraryAttachedContext.cpp deleted file mode 100644 index e74d14f7a..000000000 --- a/src/ScriptEngine.Snegopat/LibraryAttachedContext.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "stdafx.h" -#include "LibraryAttachedContext.h" - - -LibraryAttachedContext::LibraryAttachedContext(ScriptingEngine^ engine) -{ - m_engine = engine; - m_methodIndexes = gcnew Dictionary(); - m_methods = gcnew array(1); - - ScriptEngine::Machine::MethodInfo miDispose; - miDispose.Name = L""; - miDispose.Alias = L"DisposeObject"; - miDispose.Params = gcnew array(1); - miDispose.Params[0].IsByValue = true; - - m_methods[0] = miDispose; -} - -void LibraryAttachedContext::OnAttach(MachineInstance^ machine, - [Out] cli::array^% variables, - [Out] cli::array^% methods, - [Out] IRuntimeContextInstance^% instance) -{ - instance = this; - methods = m_methods; - variables = gcnew array(0); -} - -IEnumerable^ LibraryAttachedContext::GetProperties() -{ - return gcnew array(0); -} - -IEnumerable^ LibraryAttachedContext::GetMethods() -{ - return m_methods; -} - -int LibraryAttachedContext::FindProperty(String^ name) -{ - throw RuntimeException::PropNotFoundException(name); -} - -bool LibraryAttachedContext::IsPropReadable(int propNum) -{ - return false; -} - -bool LibraryAttachedContext::IsPropWritable(int propNum) -{ - return false; -} - -IValue^ LibraryAttachedContext::GetPropValue(int propNum) -{ - throw gcnew NotImplementedException(); -} - -void LibraryAttachedContext::SetPropValue(int propNum, IValue^ val) -{ - throw gcnew NotImplementedException(); -} - -int LibraryAttachedContext::FindMethod(String^ mName) -{ - int index; - if (m_methodIndexes->TryGetValue(mName, index)) - { - return index; - } - - throw RuntimeException::MethodNotFoundException(mName); -} - -ScriptEngine::Machine::MethodInfo LibraryAttachedContext::GetMethodInfo(int mNum) -{ - return m_methods[mNum]; -} - -void LibraryAttachedContext::CallAsProcedure(int mNum, array^ args) -{ - switch (mNum) - { - case 0: - DisposeObject(args[0]); - break; - } -} - -void LibraryAttachedContext::CallAsFunction(int mNum, array^ args, [Out] IValue^% retVal) -{ - throw gcnew NotImplementedException(); -} - -void LibraryAttachedContext::DisposeObject(IValue^ object) -{ - auto ptr = dynamic_cast(object); - if (ptr != nullptr) - { - delete ptr; - } -} diff --git a/src/ScriptEngine.Snegopat/LibraryAttachedContext.h b/src/ScriptEngine.Snegopat/LibraryAttachedContext.h deleted file mode 100644 index b7c5e143f..000000000 --- a/src/ScriptEngine.Snegopat/LibraryAttachedContext.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "ScriptDrivenAddin.h" - -using namespace System; -using namespace System::Collections::Generic; -using namespace ScriptEngine; -using namespace ScriptEngine::Machine; -using namespace ScriptEngine::Machine::Contexts; -using namespace System::Runtime::InteropServices; - -ref class LibraryAttachedContext : public IAttachableContext, public IRuntimeContextInstance -{ -private: - - ScriptingEngine^ m_engine; - array^ m_methods; - Dictionary^ m_methodIndexes; - - void DisposeObject(IValue^ object); - -public: - LibraryAttachedContext(ScriptingEngine^ engine); - - virtual void OnAttach(MachineInstance^ machine, - [Out] cli::array^% variables, - [Out] cli::array^% methods, - [Out] IRuntimeContextInstance^% instance); - - virtual IEnumerable^ GetProperties(); - virtual IEnumerable^ GetMethods(); - - property bool IsIndexed - { - virtual bool get() { return false; } - }; - - property bool DynamicMethodSignatures - { - virtual bool get() { return false; } - }; - - virtual IValue^ GetIndexedValue(IValue^ index){ return nullptr; } - virtual void SetIndexedValue(IValue^ index, IValue^ val){} - - virtual int FindProperty(String^ name); - virtual bool IsPropReadable(int propNum); - virtual bool IsPropWritable(int propNum); - virtual IValue^ GetPropValue(int propNum); - virtual void SetPropValue(int propNum, IValue^ val); - virtual int FindMethod(String^ mName); - virtual ScriptEngine::Machine::MethodInfo GetMethodInfo(int mNum); - virtual void CallAsProcedure(int mNum, array^ args); - virtual void CallAsFunction(int mNum, array^ args, [Out] IValue^% retVal); - -}; - diff --git a/src/ScriptEngine.Snegopat/MarshalingHelpers.cpp b/src/ScriptEngine.Snegopat/MarshalingHelpers.cpp deleted file mode 100644 index 139a27070..000000000 --- a/src/ScriptEngine.Snegopat/MarshalingHelpers.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "Stdafx.h" -#include "MarshalingHelpers.h" - -WCHAR* stringBuf(System::String^ str) -{ - int len = str->Length; - WCHAR* buf = new WCHAR[len+1]; - memset(buf, 0, (len+1) * sizeof(WCHAR)); - for(int i = 0; i < len; i++) - { - buf[i] = str[i]; - } - - return buf; -} - -BSTR stringToBSTR(System::String^ str) -{ - WCHAR* buf = stringBuf(str); - BSTR ret = SysAllocString(buf); - delete[] buf; - - return ret; -} \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/MarshalingHelpers.h b/src/ScriptEngine.Snegopat/MarshalingHelpers.h deleted file mode 100644 index aab091c29..000000000 --- a/src/ScriptEngine.Snegopat/MarshalingHelpers.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include - -WCHAR* stringBuf(System::String^ str); - -BSTR stringToBSTR(System::String^ str); diff --git a/src/ScriptEngine.Snegopat/ReadMe.txt b/src/ScriptEngine.Snegopat/ReadMe.txt deleted file mode 100644 index e36360214..000000000 --- a/src/ScriptEngine.Snegopat/ReadMe.txt +++ /dev/null @@ -1,38 +0,0 @@ -======================================================================== - DYNAMIC LINK LIBRARY : ScriptEngine.Snegopat Project Overview -======================================================================== - -AppWizard has created this ScriptEngine.Snegopat DLL for you. - -This file contains a summary of what you will find in each of the files that -make up your ScriptEngine.Snegopat application. - -ScriptEngine.Snegopat.vcxproj - This is the main project file for VC++ projects generated using an Application Wizard. - It contains information about the version of Visual C++ that generated the file, and - information about the platforms, configurations, and project features selected with the - Application Wizard. - -ScriptEngine.Snegopat.vcxproj.filters - This is the filters file for VC++ projects generated using an Application Wizard. - It contains information about the association between the files in your project - and the filters. This association is used in the IDE to show grouping of files with - similar extensions under a specific node (for e.g. ".cpp" files are associated with the - "Source Files" filter). - -ScriptEngine.Snegopat.cpp - This is the main DLL source file. - -ScriptEngine.Snegopat.h - This file contains a class declaration. - -AssemblyInfo.cpp - Contains custom attributes for modifying assembly metadata. - -///////////////////////////////////////////////////////////////////////////// -Other notes: - -AppWizard uses "TODO:" to indicate parts of the source code you -should add to or customize. - -///////////////////////////////////////////////////////////////////////////// diff --git a/src/ScriptEngine.Snegopat/RefCountable.cpp b/src/ScriptEngine.Snegopat/RefCountable.cpp deleted file mode 100644 index 9d52ab72b..000000000 --- a/src/ScriptEngine.Snegopat/RefCountable.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "stdafx.h" -#include "RefCountable.h" - -RefCountable::RefCountable(void) -{ - m_refCount = 0; -} - -ULONG __stdcall RefCountable::AddRef() -{ - return InterlockedIncrement(&m_refCount) ; -} - -ULONG __stdcall RefCountable::Release() -{ - long nRefCount = 0; - nRefCount = InterlockedDecrement(&m_refCount) ; - if (nRefCount == 0) - { - OnZeroCount(); - delete this; - } - - return nRefCount; -} - -void RefCountable::OnZeroCount() -{ -} - -RefCountable::~RefCountable(void) -{ -} diff --git a/src/ScriptEngine.Snegopat/RefCountable.h b/src/ScriptEngine.Snegopat/RefCountable.h deleted file mode 100644 index 09d78de03..000000000 --- a/src/ScriptEngine.Snegopat/RefCountable.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "Snegopat_h.h" - -class RefCountable -{ -private: - ULONG m_refCount; - -protected: - - virtual void OnZeroCount(); - -public: - - RefCountable(void); - virtual ~RefCountable(void); - - virtual ULONG __stdcall AddRef(); - virtual ULONG __stdcall Release(); - -}; - diff --git a/src/ScriptEngine.Snegopat/ScriptDrivenAddin.cpp b/src/ScriptEngine.Snegopat/ScriptDrivenAddin.cpp deleted file mode 100644 index 2fe0e44cf..000000000 --- a/src/ScriptEngine.Snegopat/ScriptDrivenAddin.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "stdafx.h" -#include "ScriptDrivenAddin.h" - - -ScriptDrivenAddin::ScriptDrivenAddin(LoadedModuleHandle module) - : ScriptDrivenObject(module, true) -{ - m_marshalledReference = gcnew EventCallableSDO(this, module); - m_valuesByIndex = gcnew List(); - m_names = gcnew Dictionary(StringComparer::InvariantCultureIgnoreCase); -} - -ScriptDrivenAddin::~ScriptDrivenAddin() -{ - if(m_valuesByIndex != nullptr) - { - for (int i = 0; i < m_valuesByIndex->Count; i++) - { - if(Marshal::IsComObject(m_valuesByIndex[i])) - { - Marshal::ReleaseComObject(m_valuesByIndex[i]); - } - } - m_valuesByIndex->Clear(); - m_names->Clear(); - m_valuesByIndex = nullptr; - } -} - -Object^ ScriptDrivenAddin::UnderlyingObject::get() -{ - return m_marshalledReference; -} - -void ScriptDrivenAddin::AddProperty(String^ name, IValue^ value) -{ - int newIndex = m_valuesByIndex->Count; - m_valuesByIndex->Add(value); - m_names->Add(name, newIndex); -} - -int ScriptDrivenAddin::FindOwnProperty(String^ name) -{ - int index = -1; - if(m_names->TryGetValue(name, index)) - { - return index; - } - else - { - return -1; - } -} - -IValue^ ScriptDrivenAddin::GetOwnPropValue(int index) -{ - return m_valuesByIndex[index]; -} \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/ScriptDrivenAddin.h b/src/ScriptEngine.Snegopat/ScriptDrivenAddin.h deleted file mode 100644 index 6ff638b23..000000000 --- a/src/ScriptEngine.Snegopat/ScriptDrivenAddin.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "EventCallableSDO.h" - -using namespace System; -using namespace System::Collections::Generic; -using namespace ScriptEngine; -using namespace ScriptEngine::Machine; -using namespace ScriptEngine::Machine::Contexts; - -ref class ScriptDrivenAddin : public ScriptDrivenObject, public IObjectWrapper -{ -private: - - EventCallableSDO^ m_marshalledReference; - List^ m_valuesByIndex; - Dictionary^ m_names; - -public: - ScriptDrivenAddin(LoadedModuleHandle module); - virtual ~ScriptDrivenAddin(); - - virtual property Object^ UnderlyingObject - { - Object^ get(); - } - - void AddProperty(String^ name, IValue^ value); - -protected: - - virtual int GetOwnMethodCount() override - { - return 0; - } - - virtual int GetOwnVariableCount() override - { - return m_valuesByIndex->Count; - } - - virtual int FindOwnProperty(String^ name) override; - virtual bool IsOwnPropReadable(int index) override - { - return true; - } - virtual bool IsOwnPropWritable(int index) override - { - return false; - } - virtual IValue^ GetOwnPropValue(int index) override; - - virtual void UpdateState() override { }; - -}; - diff --git a/src/ScriptEngine.Snegopat/ScriptEngine.Snegopat.vcxproj b/src/ScriptEngine.Snegopat/ScriptEngine.Snegopat.vcxproj deleted file mode 100644 index 8e81360b1..000000000 --- a/src/ScriptEngine.Snegopat/ScriptEngine.Snegopat.vcxproj +++ /dev/null @@ -1,187 +0,0 @@ - - - - - Debug - Win32 - - - ReleaseNoSnegopat - Win32 - - - Release - Win32 - - - - {618F7216-6576-410D-818C-C79BB6BEEA0C} - v4.0 - ManagedCProj - ScriptEngineSnegopat - 8.1 - - - - DynamicLibrary - true - v120 - true - Unicode - - - DynamicLibrary - false - v120 - true - Unicode - - - DynamicLibrary - false - v120 - true - Unicode - - - - - - - - - - - - - - - - true - - - false - - - false - - - - Level3 - Disabled - WIN32;_DEBUG;%(PreprocessorDefinitions) - Use - - - true - %(AdditionalDependencies) - - - - - Level3 - WIN32;NDEBUG;%(PreprocessorDefinitions) - Use - - - true - %(AdditionalDependencies) - - - - - Level3 - WIN32;NDEBUG;%(PreprocessorDefinitions) - Use - - - true - %(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - NotUsing - false - false - NotUsing - NotUsing - - - - CompileAsCpp - NotUsing - true - true - NotUsing - NotUsing - CompileAsCpp - CompileAsCpp - - - Create - Create - Create - - - - - - - - - - - - - - $(ProjectDir)$(ProjectName).tlb - $(ProjectDir)$(ProjectName).tlb - $(ProjectDir)$(ProjectName).tlb - - - - - - - - {f062d1d9-d307-492a-a56b-ff3c55f8f6c0} - - - - - - \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/ScriptEngine.Snegopat.vcxproj.filters b/src/ScriptEngine.Snegopat/ScriptEngine.Snegopat.vcxproj.filters deleted file mode 100644 index fc3c59bbf..000000000 --- a/src/ScriptEngine.Snegopat/ScriptEngine.Snegopat.vcxproj.filters +++ /dev/null @@ -1,131 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Header Files - - - Source Files - - - - - - - - Resource Files - - - - - Resource Files - - - - - Source Files - - - - - Resource Files - - - \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/SnegAPIDefinitions.h b/src/ScriptEngine.Snegopat/SnegAPIDefinitions.h deleted file mode 100644 index 74dac6d4a..000000000 --- a/src/ScriptEngine.Snegopat/SnegAPIDefinitions.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "Stdafx.h" - -using namespace System; -using namespace System::Runtime::InteropServices; - -[ComVisible(true)] -[Guid("F80C31B9-1D9F-4438-BEF3-10D829664EFD")] -[InterfaceType(ComInterfaceType::InterfaceIsDual)] -interface class IParamsWrapper -{ -public: - - [DispId(0x60020000)] - property Object^ val - { - Object^ get(); - void set(Object^ value); - } - - /*[DispId(0x60020000), propget, helpstring("")] - HRESULT val([out, retval] VARIANT* pVal); - [id(0x60020000), propput, helpstring("")] - HRESULT val([in] VARIANT pVal);*/ -}; \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/Snegopat.idl b/src/ScriptEngine.Snegopat/Snegopat.idl deleted file mode 100644 index 8d27c74ce..000000000 --- a/src/ScriptEngine.Snegopat/Snegopat.idl +++ /dev/null @@ -1,66 +0,0 @@ -import "oaidl.idl"; -import "ocidl.idl"; - -interface IAddin; - -[ - odl, - uuid(2BEEF9E6-AF34-4593-9E73-3D07EAA4CF0D), - version(1.0), - helpstring(" "), - nonextensible -] -interface IAddinLoader : IUnknown { - [helpstring(" ")] - HRESULT _stdcall proto([out, retval] BSTR* result); - [helpstring(" ")] - HRESULT _stdcall load( - [in] BSTR uri, - [out] BSTR* fullPath, - [out] BSTR* uniqueName, - [out] BSTR* displayName, - [out, retval] IUnknown** result); - [helpstring(" ")] - HRESULT _stdcall canUnload( - [in] BSTR fullPath, - [in] IUnknown* addin, - [out, retval] VARIANT_BOOL* result); - [helpstring(" ")] - HRESULT _stdcall unload( - [in] BSTR fullPath, - [in] IUnknown* addin, - [out, retval] VARIANT_BOOL* result); - [helpstring(" ")] - HRESULT _stdcall loadCommandName([out, retval] BSTR* result); - [helpstring(" ")] - HRESULT _stdcall selectLoadURI([out, retval] BSTR* result); -}; - -[ - odl, - uuid(0CCEA8E6-D187-4B84-B74D-85079F8E2008), - version(1.0), - helpstring(" "), - nonextensible -] -interface IAddinMacroses : IUnknown { - [helpstring(" ")] - HRESULT _stdcall macroses([out, retval] SAFEARRAY(VARIANT)* result); - [helpstring(" ")] - HRESULT _stdcall invokeMacros( - [in] BSTR MacrosName, - [out, retval] VARIANT* result); -}; - -[ -uuid(64CE2CE5-A523-40A4-8527-824715AFE929), -helpstring("1Script loader for Snegopat") -] -library OneScriptSnegopat -{ - importlib("stdole32.tlb"); - importlib("stdole2.tlb"); - - interface IAddin; - interface IAddinGroup; -}; \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/SnegopatAttachedContext.cpp b/src/ScriptEngine.Snegopat/SnegopatAttachedContext.cpp deleted file mode 100644 index 73ff3da02..000000000 --- a/src/ScriptEngine.Snegopat/SnegopatAttachedContext.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "stdafx.h" -#include "SnegopatAttachedContext.h" - -#define ALIASED_METHOD(name, alias) InsertMethod((name)); MethodAlias((name), (alias)) - -SnegopatAttachedContext::SnegopatAttachedContext(IRuntimeContextInstance^ Designer) -{ - m_DesignerWrapper = Designer; - m_varList = gcnew List(); - //m_nameList = gcnew List(); - m_methods = gcnew List(); - m_propDispIdMap = gcnew Dictionary(); - m_methDispIdMap = gcnew Dictionary(); - m_propIndexes = gcnew Dictionary(StringComparer::InvariantCultureIgnoreCase); - m_methIndexes = gcnew Dictionary(StringComparer::InvariantCultureIgnoreCase); - - s_instance = this; - - InsertProperty("addins"); - InsertProperty("cmdTrace"); - InsertProperty("events"); - InsertProperty("profileRoot"); - InsertProperty("snegopat"); - InsertProperty("hotkeys"); - InsertProperty("windows"); - InsertProperty("metadata"); - InsertProperty("v8files"); - InsertProperty("ownerName"); - InsertProperty("v8debug"); - InsertProperty("sVersion"); - InsertProperty("v8Version"); - - ALIASED_METHOD("v8New", ""); - ALIASED_METHOD("Message", ""); - ALIASED_METHOD("MessageBox", ""); - ALIASED_METHOD("globalContext",""); - ALIASED_METHOD("getCmdState", ""); - ALIASED_METHOD("saveProfile", ""); - ALIASED_METHOD("createTimer", ""); - ALIASED_METHOD("killTimer", ""); - ALIASED_METHOD("toV8Value", "1"); - ALIASED_METHOD("loadResourceString", ""); - ALIASED_METHOD("loadScriptForm", ""); - ALIASED_METHOD("designScriptForm", ""); - ALIASED_METHOD("sendCommand", ""); - - MethodAlias("MessageBox", ""); - -} - -void SnegopatAttachedContext::OnAttach(MachineInstance^ machine, - [Out] cli::array^% variables, - [Out] cli::array^% methods, - [Out] IRuntimeContextInstance^% instance) -{ - instance = this; - methods = m_methods->ToArray(); - variables = m_varList->ToArray(); -} - -IEnumerable^ SnegopatAttachedContext::GetProperties() -{ - array^ arr = gcnew array( m_propIndexes->Count); - int i = 0; - for each (KeyValuePair kv in m_propIndexes) - { - VariableInfo vi; - - vi.Identifier = kv.Key; - vi.Type = SymbolType::ContextProperty; - arr[i++] = vi; - } - - return arr; - -} - -IEnumerable^ SnegopatAttachedContext::GetMethods() -{ - array^ arr = gcnew array(m_methods->Count); - for (int i = 0; i < m_methods->Count; i++) - { - arr[i] = m_methods[i]; - } - - return arr; -} - -void SnegopatAttachedContext::InsertProperty(String^ name) -{ - int index = m_propIndexes->Count; - int propNum = m_DesignerWrapper->FindProperty(name); - m_propDispIdMap->Add(index, propNum); - m_varList->Add(Variable::CreateContextPropertyReference(this, index)); - m_propIndexes->Add(name, index); -} - -void SnegopatAttachedContext::PropertyAlias(String^ name, String^ alias) -{ - int index = m_propIndexes[name]; - m_propIndexes->Add(alias, index); -} - -void SnegopatAttachedContext::InsertMethod(String^ name) -{ - int index = m_methods->Count; - int mNum = m_DesignerWrapper->FindMethod(name); - MethodInfo mi = m_DesignerWrapper->GetMethodInfo(mNum); - mi.Name = name; - m_methods->Add(mi); - m_methDispIdMap->Add(index, mNum); - m_methIndexes->Add(name, index); -} - -void SnegopatAttachedContext::MethodAlias(String^ name, String^ alias) -{ - int index = m_methIndexes[name]; - m_methIndexes->Add(alias, index); - MethodInfo mi = m_methods[index]; - mi.Alias = alias; - m_methods[index] = mi; - -} - -int SnegopatAttachedContext::FindProperty(String^ name) -{ - int index; - if(m_propIndexes->TryGetValue(name, index)) - { - return index; - } - - throw RuntimeException::PropNotFoundException(name); -} - -bool SnegopatAttachedContext::IsPropReadable(int propNum) -{ - int dispId = m_propDispIdMap[propNum]; - return m_DesignerWrapper->IsPropReadable(dispId); -} - -bool SnegopatAttachedContext::IsPropWritable(int propNum) -{ - int dispId = m_propDispIdMap[propNum]; - return m_DesignerWrapper->IsPropWritable(dispId); -} - -IValue^ SnegopatAttachedContext::GetPropValue(int propNum) -{ - int dispId = m_propDispIdMap[propNum]; - return m_DesignerWrapper->GetPropValue(dispId); -} - -void SnegopatAttachedContext::SetPropValue(int propNum, IValue^ val) -{ - int dispId = m_propDispIdMap[propNum]; - m_DesignerWrapper->SetPropValue(dispId, val); -} - -int SnegopatAttachedContext::FindMethod(String^ mName) -{ - int index; - if(m_methIndexes->TryGetValue(mName, index)) - { - return index; - } - - throw RuntimeException::MethodNotFoundException(mName); -} - -MethodInfo SnegopatAttachedContext::GetMethodInfo(int mNum) -{ - int dispId = m_methDispIdMap[mNum]; - return m_DesignerWrapper->GetMethodInfo(dispId); -} - -void SnegopatAttachedContext::CallAsProcedure(int mNum, array^ args) -{ - int dispId = m_methDispIdMap[mNum]; - m_DesignerWrapper->CallAsProcedure(dispId, args); -} - -void SnegopatAttachedContext::CallAsFunction(int mNum, array^ args, [Out] IValue^% retVal) -{ - int dispId = m_methDispIdMap[mNum]; - m_DesignerWrapper->CallAsFunction(dispId, args, retVal); -} - -IRuntimeContextInstance^ SnegopatAttachedContext::CreateV8New(String^ className, array^ args) -{ - int v8new = s_instance->m_DesignerWrapper->FindMethod("v8new"); - IValue^ result; - array^ realArgs = gcnew array(args->Length + 1); - realArgs[0] = ValueFactory::Create(className); - args->CopyTo(realArgs, 1); - s_instance->m_DesignerWrapper->CallAsFunction(v8new, realArgs, result); - - return safe_cast(result); -} \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/SnegopatAttachedContext.h b/src/ScriptEngine.Snegopat/SnegopatAttachedContext.h deleted file mode 100644 index 7a0e1e387..000000000 --- a/src/ScriptEngine.Snegopat/SnegopatAttachedContext.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once -#include -#include - -using namespace System; -using namespace System::Collections::Generic; -using namespace ScriptEngine; -using namespace ScriptEngine::Machine; -using namespace ScriptEngine::Compiler; -using namespace ScriptEngine::Environment; -using namespace System::Runtime::InteropServices; - -/* Defines global context for snegopat script -*/ -ref class SnegopatAttachedContext : public IAttachableContext, public IRuntimeContextInstance -{ -private: - - IRuntimeContextInstance^ m_DesignerWrapper; - List^ m_varList; - List^ m_nameList; - List^ m_methods; - Dictionary^ m_propDispIdMap; - Dictionary^ m_methDispIdMap; - Dictionary^ m_propIndexes; - Dictionary^ m_methIndexes; - - void InsertProperty(String^ name); - void PropertyAlias(String^ name, String^ alias); - void InsertMethod(String^ name); - void MethodAlias(String^ name, String^ alias); - - static SnegopatAttachedContext^ s_instance; - -public: - SnegopatAttachedContext(IRuntimeContextInstance^ Designer); - - virtual void OnAttach(MachineInstance^ machine, - [Out] cli::array^% variables, - [Out] cli::array^% methods, - [Out] IRuntimeContextInstance^% instance); - - virtual IEnumerable^ GetProperties(); - virtual IEnumerable^ GetMethods(); - - property bool IsIndexed - { - virtual bool get() { return false; } - }; - - property bool DynamicMethodSignatures - { - virtual bool get() { return true; } - }; - - virtual IValue^ GetIndexedValue(IValue^ index){ return nullptr; } - virtual void SetIndexedValue(IValue^ index, IValue^ val){} - - virtual int FindProperty(String^ name); - virtual bool IsPropReadable(int propNum); - virtual bool IsPropWritable(int propNum); - virtual IValue^ GetPropValue(int propNum); - virtual void SetPropValue(int propNum, IValue^ val); - virtual int FindMethod(String^ mName); - virtual ScriptEngine::Machine::MethodInfo GetMethodInfo(int mNum); - virtual void CallAsProcedure(int mNum, array^ args); - virtual void CallAsFunction(int mNum, array^ args, [Out] IValue^% retVal); - - [ScriptEngine::Machine::Contexts::ScriptConstructor(ParametrizeWithClassName = true)] - static IRuntimeContextInstance^ CreateV8New(String^ className, array^ args); - - static void SetInstance(SnegopatAttachedContext^ instance) - { - s_instance = instance; - } - -}; - diff --git a/src/ScriptEngine.Snegopat/Stdafx.cpp b/src/ScriptEngine.Snegopat/Stdafx.cpp deleted file mode 100644 index 82b291a4a..000000000 --- a/src/ScriptEngine.Snegopat/Stdafx.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// stdafx.cpp : source file that includes just the standard includes -// ScriptEngine.Snegopat.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" diff --git a/src/ScriptEngine.Snegopat/Stdafx.h b/src/ScriptEngine.Snegopat/Stdafx.h deleted file mode 100644 index 3cc4c24ef..000000000 --- a/src/ScriptEngine.Snegopat/Stdafx.h +++ /dev/null @@ -1,7 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, -// but are changed infrequently - -#pragma once - - diff --git a/src/ScriptEngine.Snegopat/app.ico b/src/ScriptEngine.Snegopat/app.ico deleted file mode 100644 index 301942f60..000000000 Binary files a/src/ScriptEngine.Snegopat/app.ico and /dev/null differ diff --git a/src/ScriptEngine.Snegopat/app.rc b/src/ScriptEngine.Snegopat/app.rc deleted file mode 100644 index ead98eeac..000000000 Binary files a/src/ScriptEngine.Snegopat/app.rc and /dev/null differ diff --git a/src/ScriptEngine.Snegopat/clrfactory.cpp b/src/ScriptEngine.Snegopat/clrfactory.cpp deleted file mode 100644 index 52db46b4d..000000000 --- a/src/ScriptEngine.Snegopat/clrfactory.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "StdAfx.h" -#include -#include - -#include "CriticalResourceLoader.h" - -gcroot g_ResLoader; - -void InitLibrary(HMODULE module) -{ - g_ResLoader = gcnew CriticalResourceLoader(module); -} - -IUnknown* GetLoader(IDispatch* pDesigner) -{ - return g_ResLoader->GetLoader(pDesigner); -} - -bool PrepareTypeInfo() -{ - return g_ResLoader->PrepareTypeInfo(); -} \ No newline at end of file diff --git a/src/ScriptEngine.Snegopat/resource.h b/src/ScriptEngine.Snegopat/resource.h deleted file mode 100644 index 52f16bdcf..000000000 Binary files a/src/ScriptEngine.Snegopat/resource.h and /dev/null differ diff --git a/src/ScriptEngine.Snegopat/snegopat.cpp b/src/ScriptEngine.Snegopat/snegopat.cpp deleted file mode 100644 index 3935c966a..000000000 --- a/src/ScriptEngine.Snegopat/snegopat.cpp +++ /dev/null @@ -1,60 +0,0 @@ - -#include "Stdafx.h" -#include -#include -#include "DispatchHelpers.h" - -void InitLibrary(HMODULE module); -IUnknown* GetLoader(IDispatch*); -bool PrepareTypeInfo(); - -HMODULE g_CurrentModule; - -BOOL WINAPI DllMain( - HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved -) -{ - if(fdwReason == DLL_PROCESS_ATTACH) - { - g_CurrentModule = hinstDLL; - } - - return TRUE; -} - -extern "C" void __declspec(dllexport) addinInfo(BSTR* uniqueName, BSTR* displayName) -{ - *uniqueName = SysAllocString(L"1ScriptLoader"); - *displayName = SysAllocString(L" 1"); - -} - -extern "C" void __declspec(dllexport) initAddin(IDispatch* pDesigner) -{ -#ifdef _DEBUG - MessageBox(0, L"attach debugger and press ok", L"Debug message", MB_OK); -#endif - - InitLibrary(g_CurrentModule); - if(!PrepareTypeInfo()) - { - return; - } - - HRESULT hr; - VARIANT addins; - addins.vt = VT_DISPATCH; - VariantInit(&addins); - hr = invoke(pDesigner, DISPATCH_PROPERTYGET, &addins, NULL, NULL, L"addins", NULL); - if(FAILED(hr)) - { - return; - } - - IDispatch* addinsObj = V_DISPATCH(&addins); - - IUnknown* loader = GetLoader(pDesigner); - hr = invoke(addinsObj, DISPATCH_METHOD, NULL, NULL, NULL, L"registerLoader", L"U", loader); -} diff --git a/src/ScriptEngine/BslProcess.cs b/src/ScriptEngine/BslProcess.cs new file mode 100644 index 000000000..65b8c9de2 --- /dev/null +++ b/src/ScriptEngine/BslProcess.cs @@ -0,0 +1,64 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace ScriptEngine +{ + internal class BslProcess : IBslProcess + { + private readonly IExecutorProvider[] _executorProviders; + private readonly IDictionary _bslExecutorsByModule; + + private bool _isRunning; + + public BslProcess(int id, ExecutionContext context, IEnumerable executorProviders) + { + _executorProviders = executorProviders.ToArray(); + _bslExecutorsByModule = + _executorProviders.ToDictionary(item => item.SupportedModuleType, item => item.GetInvokeDelegate()); + + VirtualThreadId = id; + Services = context.Services.CreateScope(); + } + + public IServiceContainer Services { get; } + + public int VirtualThreadId { get; } + + public BslValue Run(BslObjectValue target, IExecutableModule module, BslScriptMethodInfo method, IValue[] arguments) + { + var notifyExecutors = !_isRunning; + if (notifyExecutors) + { + Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this)); + } + + _isRunning = true; + + try + { + return _bslExecutorsByModule[module.GetType()](this, target, module, method, arguments); + } + finally + { + if (notifyExecutors) + { + Array.ForEach(_executorProviders, e => e.AfterProcessExit(this)); + _isRunning = false; + } + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/BslProcessFactory.cs b/src/ScriptEngine/BslProcessFactory.cs new file mode 100644 index 000000000..8d063ab74 --- /dev/null +++ b/src/ScriptEngine/BslProcessFactory.cs @@ -0,0 +1,37 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Threading; +using OneScript.DependencyInjection; +using OneScript.Execution; +using ExecutionContext = ScriptEngine.Machine.ExecutionContext; + +namespace ScriptEngine +{ + /// + /// Типовая фабрика bsl-процессов на базе диспетчера исполнителей + /// + public class BslProcessFactory : IBslProcessFactory + { + private readonly IServiceContainer _services; + private int _threadIdCounter = 0; + + public BslProcessFactory(IServiceContainer services) + { + _services = services; + } + + public IBslProcess NewProcess() + { + // Создаем новый контекст со всеми зависимостями + var context = _services.Resolve(); + var executors = _services.ResolveEnumerable(); + + return new BslProcess(Interlocked.Increment(ref _threadIdCounter), context, executors); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Compiler/ApplicationDump.cs b/src/ScriptEngine/Compiler/ApplicationDump.cs index dbbfe5243..6f807ca3e 100644 --- a/src/ScriptEngine/Compiler/ApplicationDump.cs +++ b/src/ScriptEngine/Compiler/ApplicationDump.cs @@ -5,6 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using ScriptEngine.Libraries; using System; namespace ScriptEngine.Compiler diff --git a/src/ScriptEngine/Compiler/CodeGenerationFlags.cs b/src/ScriptEngine/Compiler/CodeGenerationFlags.cs new file mode 100644 index 000000000..cdf67541e --- /dev/null +++ b/src/ScriptEngine/Compiler/CodeGenerationFlags.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +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 ScriptEngine.Compiler +{ + [Flags] + public enum CodeGenerationFlags + { + Always = 0, + CodeStatistics = 1, + DebugCode = 2, + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs new file mode 100644 index 000000000..28b7b4af7 --- /dev/null +++ b/src/ScriptEngine/Compiler/CodeGeneratorPrivateTypes.cs @@ -0,0 +1,46 @@ +/*---------------------------------------------------------- +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 OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace ScriptEngine.Compiler +{ + public partial class StackMachineCodeGenerator + { + private const int DUMMY_ADDRESS = -1; + + private struct ForwardedMethodDecl + { + public string identifier; + public BslSyntaxNode factArguments; + public bool asFunction; + public CodeRange location; + public int commandIndex; + } + + private class NestedLoopInfo + { + private NestedLoopInfo(){} + + public static NestedLoopInfo New() + { + return new NestedLoopInfo() + { + startPoint = DUMMY_ADDRESS, + breakStatements = new List(), + tryNesting = 0 + }; + } + + public int startPoint; + public List breakStatements; + public int tryNesting; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Compiler/CompiledCodeIndexer.cs b/src/ScriptEngine/Compiler/CompiledCodeIndexer.cs deleted file mode 100644 index 75a46f99e..000000000 --- a/src/ScriptEngine/Compiler/CompiledCodeIndexer.cs +++ /dev/null @@ -1,19 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using OneScript.Language.LexicalAnalysis; - -namespace ScriptEngine.Compiler -{ - class CompiledCodeIndexer : ISourceCodeIndexer - { - public string GetCodeLine(int index) - { - return "<исходный код недоступен>"; - } - } -} diff --git a/src/ScriptEngine/Compiler/Compiler.cs b/src/ScriptEngine/Compiler/Compiler.cs deleted file mode 100644 index 00296c8f7..000000000 --- a/src/ScriptEngine/Compiler/Compiler.cs +++ /dev/null @@ -1,2075 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using OneScript.Language; -using OneScript.Language.LexicalAnalysis; -using ScriptEngine.Machine; - -namespace ScriptEngine.Compiler -{ - [Flags] - public enum CodeGenerationFlags - { - Always, - CodeStatistics, - DebugCode - } - - class Compiler { - public const string BODY_METHOD_NAME = "$entry"; - - private const int DUMMY_ADDRESS = -1; - private static readonly Dictionary _tokenToOpCode; - - private ILexemGenerator _lexer; - private ICompilerContext _ctx; - private ModuleImage _module; - private Lexem _lastExtractedLexem; - private bool _inMethodScope = false; - private bool _isMethodsDefined = false; - private bool _isStatementsDefined = false; - private bool _isFunctionProcessed = false; - private bool _isCodeEntered = false; - - private readonly Stack _tokenStack = new Stack(); - private readonly Stack _nestedLoops = new Stack(); - private readonly List _forwardedMethods = new List(); - private readonly List _annotations = new List(); - - private struct ForwardedMethodDecl - { - public string identifier; - public bool[] factArguments; - public bool asFunction; - public int codeLine; - public int commandIndex; - } - - private class NestedLoopInfo - { - private NestedLoopInfo(){} - - public static NestedLoopInfo New() - { - return new NestedLoopInfo() - { - startPoint = DUMMY_ADDRESS, - breakStatements = new List(), - tryNesting = 0 - }; - } - - public int startPoint; - public List breakStatements; - public int tryNesting; - } - - public CompilerDirectiveHandler DirectiveHandler { get; set; } - - public CodeGenerationFlags ProduceExtraCode { get; set; } - - public ModuleImage Compile(ILexemGenerator lexer, ICompilerContext context) - { - _module = new ModuleImage(); - _ctx = context; - _lexer = lexer; - - BuildModule(); - CheckForwardedDeclarations(); - - _module.LoadAddress = _ctx.TopIndex(); - return _module; - } - - public ModuleImage CompileExpression(ILexemGenerator lexer, ICompilerContext context) - { - _module = new ModuleImage(); - _ctx = context; - _lexer = lexer; - NextToken(); - BuildExpressionUpTo(Token.EndOfText); - - _module.LoadAddress = _ctx.TopIndex(); - return _module; - } - - public ModuleImage CompileExecBatch(ILexemGenerator lexer, ICompilerContext context) - { - _module = new ModuleImage(); - _ctx = context; - _lexer = lexer; - NextToken(); - PushStructureToken(Token.EndOfText); - BuildModuleBody(); - - _module.LoadAddress = _ctx.TopIndex(); - return _module; - } - - private AnnotationParameter BuildAnnotationParameter() - { - // id | id = value | value - var result = new AnnotationParameter(); - if (_lastExtractedLexem.Type == LexemType.Identifier) - { - result.Name = _lastExtractedLexem.Content; - NextToken(); - if (_lastExtractedLexem.Token != Token.Equal) - { - result.ValueIndex = AnnotationParameter.UNDEFINED_VALUE_INDEX; - return result; - } - NextToken(); - } - - var cDef = CreateConstDefinition(ref _lastExtractedLexem); - result.ValueIndex = GetConstNumber(ref cDef); - - NextToken(); - - return result; - } - - private IList BuildAnnotationParameters() - { - var parameters = new List(); - while (_lastExtractedLexem.Token != Token.EndOfText) - { - parameters.Add(BuildAnnotationParameter()); - if (_lastExtractedLexem.Token == Token.Comma) - { - NextToken(); - continue; - } - if (_lastExtractedLexem.Token == Token.ClosePar) - { - NextToken(); - break; - } - throw CompilerException.UnexpectedOperation(); - } - return parameters; - } - - private void BuildAnnotations() - { - while (_lastExtractedLexem.Type == LexemType.Annotation) - { - var annotation = new AnnotationDefinition() {Name = _lastExtractedLexem.Content}; - - NextToken(); - if (_lastExtractedLexem.Token == Token.OpenPar) - { - NextToken(); - annotation.Parameters = BuildAnnotationParameters().ToArray(); - } - - _annotations.Add(annotation); - } - } - - private AnnotationDefinition[] ExtractAnnotations() - { - var result = _annotations.ToArray(); - _annotations.Clear(); - return result; - } - - private void BuildModule() - { - NextToken(); - - if (_lastExtractedLexem.Type == LexemType.EndOfText) - { - return; - } - - try - { - DispatchModuleBuild(); - } - catch (CompilerException exc) - { - if(exc.LineNumber == CodePositionInfo.OUT_OF_TEXT) - AppendCodeInfo(_lexer.GetCodePosition(), exc); - throw; - } - } - - private void CheckForwardedDeclarations() - { - if (_forwardedMethods.Count > 0) - { - foreach (var item in _forwardedMethods) - { - SymbolBinding methN; - try - { - methN = _ctx.GetMethod(item.identifier); - } - catch (CompilerException exc) - { - AppendCodeInfo(exc, item.codeLine, CodePositionInfo.OUT_OF_TEXT); - throw; - } - - var scope = _ctx.GetScope(methN.ContextIndex); - - var methInfo = scope.GetMethod(methN.CodeIndex); - Debug.Assert(StringComparer.OrdinalIgnoreCase.Compare(methInfo.Name, item.identifier) == 0); - if (item.asFunction && !methInfo.IsFunction) - { - var exc = CompilerException.UseProcAsFunction(); - AppendCodeInfo(exc, item.codeLine, CodePositionInfo.OUT_OF_TEXT); - throw exc; - } - - try - { - CheckFactArguments(methInfo, item.factArguments); - } - catch (CompilerException exc) - { - AppendCodeInfo(exc, item.codeLine, CodePositionInfo.OUT_OF_TEXT); - throw; - } - - CorrectCommandArgument(item.commandIndex, GetMethodRefNumber(ref methN)); - } - } - } - - private void AppendCodeInfo(CodePositionInfo info, CompilerException exc) - { - CompilerException.AppendCodeInfo(exc, info); - } - - private void AppendCodeInfo(CompilerException exc, int line, int column) - { - var info = _lexer.GetCodePosition(); - info.LineNumber = line; - info.ColumnNumber = column; - info.Code = _lexer.Iterator.GetCodeLine(line); - - CompilerException.AppendCodeInfo(exc, info); - } - - private void DispatchModuleBuild() - { - HandleImportDirectives(); - while (_lastExtractedLexem.Type != LexemType.EndOfText) - { - if (_lastExtractedLexem.Type == LexemType.Identifier) - { - if (_lastExtractedLexem.Token == Token.VarDef) - { - _isCodeEntered = true; - BuildVariableDefinitions(); - } - else if (_lastExtractedLexem.Token == Token.Procedure || _lastExtractedLexem.Token == Token.Function) - { - _isCodeEntered = true; - _isMethodsDefined = true; - BuildSingleMethod(); - } - else - { - _isCodeEntered = true; - BuildModuleBody(); - } - } - else if (_lastExtractedLexem.Type == LexemType.EndOperator) - { - _isCodeEntered = true; - BuildModuleBody(); - } - else if (_lastExtractedLexem.Type == LexemType.Annotation) - { - BuildAnnotations(); - } - else if (_lastExtractedLexem.Type == LexemType.PreprocessorDirective) - { - throw CompilerException.IllegalDirective(_lastExtractedLexem.Content); - } - else - { - throw CompilerException.UnexpectedOperation(); - } - } - } - - private void UpdateCompositeContext() - { - var modCtx = _ctx as ModuleCompilerContext; - if (modCtx != null) - modCtx.Update(); - } - - private void BuildVariableDefinitions() - { - while (_lastExtractedLexem.Token == Token.VarDef) - { - NextToken(); - while (true) - { - - if (IsUserSymbol(ref _lastExtractedLexem)) - { - var symbolicName = _lastExtractedLexem.Content; - var annotations = ExtractAnnotations(); - var definition = _ctx.DefineVariable(symbolicName); - if (_inMethodScope) - { - if (_isStatementsDefined) - { - throw CompilerException.LateVarDefinition(); - } - } - else - { - if (_isMethodsDefined) - { - throw CompilerException.LateVarDefinition(); - } - - _module.VariableRefs.Add(definition); - _module.Variables.Add(new VariableInfo() - { - Identifier = symbolicName, - Annotations = annotations, - CanGet = true, - CanSet = true, - Index = definition.CodeIndex - }); - } - NextToken(); - if (_lastExtractedLexem.Token == Token.Export) - { - _module.ExportedProperties.Add(new ExportedSymbol() - { - SymbolicName = symbolicName, - Index = definition.CodeIndex - }); - NextToken(); - } - if (_lastExtractedLexem.Token == Token.Comma) - { - NextToken(); - continue; - } - if (_lastExtractedLexem.Token != Token.Semicolon) - { - throw CompilerException.SemicolonExpected(); - } - NextToken(); - break; - } - else - { - throw CompilerException.IdentifierExpected(); - } - } - } - } - - private void BuildModuleBody() - { - _isFunctionProcessed = false; - var entry = _module.Code.Count; - _ctx.PushScope(new SymbolScope()); - - try - { - BuildCodeBatch(Token.EndOfText); - } - catch - { - _ctx.PopScope(); - throw; - } - - var localCtx = _ctx.PopScope(); - - var topIdx = _ctx.TopIndex(); - - if (entry != _module.Code.Count) - { - var bodyMethod = new MethodInfo(); - bodyMethod.Name = BODY_METHOD_NAME; - var descriptor = new MethodDescriptor(); - descriptor.EntryPoint = entry; - descriptor.Signature = bodyMethod; - FillVariablesFrame(ref descriptor, localCtx); - - var entryRefNumber = _module.MethodRefs.Count; - var bodyBinding = new SymbolBinding() - { - ContextIndex = topIdx, - CodeIndex = _module.Methods.Count - }; - _module.Methods.Add(descriptor); - _module.MethodRefs.Add(bodyBinding); - _module.EntryMethodIndex = entryRefNumber; - } - } - - private static void FillVariablesFrame(ref MethodDescriptor descriptor, SymbolScope localCtx) - { - descriptor.Variables = new VariablesFrame(); - - for (int i = 0; i < localCtx.VariableCount; i++) - { - descriptor.Variables.Add(localCtx.GetVariable(i)); - } - } - - private void HandleDirective() - { - var directive = _lastExtractedLexem.Content; - var value = _lexer.Iterator.ReadToLineEnd(); - - if (DirectiveHandler == null || !DirectiveHandler(directive, value, _isCodeEntered)) - throw CompilerException.UnknownDirective(directive, value); - } - - private void HandleImportDirectives() - { - while (_lastExtractedLexem.Type == LexemType.PreprocessorDirective) - { - HandleDirective(); - UpdateCompositeContext(); // костыль для #330 - - _lastExtractedLexem = _lexer.NextLexem(); - } - } - - private void BuildSingleMethod() - { - var entryPoint = _module.Code.Count; - AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics); - - if (_lastExtractedLexem.Token == Token.Procedure) - { - _isFunctionProcessed = false; - } - else if (_lastExtractedLexem.Token == Token.Function) - { - _isFunctionProcessed = true; - } - else - { - throw CompilerException.UnexpectedOperation(); - } - NextToken(); - - #region Method signature - // сигнатура - if (!IsUserSymbol(ref _lastExtractedLexem)) - { - throw CompilerException.IdentifierExpected(); - } - - int definitionLine = _lexer.CurrentLine; - int definitionColumn = _lexer.CurrentColumn; - MethodInfo method = new MethodInfo(); - method.Name = _lastExtractedLexem.Content; - method.IsFunction = _isFunctionProcessed; - method.Annotations = ExtractAnnotations(); - - NextToken(); - if (_lastExtractedLexem.Token != Token.OpenPar) - { - throw CompilerException.TokenExpected(Token.OpenPar); - } - #endregion - - #region Parameters list - var paramsList = new List(); - var methodCtx = new SymbolScope(); - - BuildMethodParametersList(paramsList, methodCtx); - - method.Params = paramsList.ToArray(); - - #endregion - - bool isExportedMethod = false; - if (_lastExtractedLexem.Token == Token.Export) - { - isExportedMethod = true; - NextToken(); - } - - method.IsExport = isExportedMethod; - - #region Body - // тело - - try - { - _ctx.PushScope(methodCtx); - DispatchMethodBody(); - } - finally - { - _ctx.PopScope(); - } - - var descriptor = new MethodDescriptor(); - descriptor.EntryPoint = entryPoint; - descriptor.Signature = method; - FillVariablesFrame(ref descriptor, methodCtx); - - SymbolBinding binding; - try - { - binding = _ctx.DefineMethod(method); - } - catch (CompilerException) - { - var exc = new CompilerException("Метод с таким именем уже определен: " + method.Name); - AppendCodeInfo(exc, definitionLine, definitionColumn); - throw exc; - } - _module.MethodRefs.Add(binding); - _module.Methods.Add(descriptor); - - // TODO: deprecate? - if (isExportedMethod) - { - _module.ExportedMethods.Add(new ExportedSymbol() - { - SymbolicName = method.Name, - Index = binding.CodeIndex - }); - } - - #endregion - - NextToken(); - } - - private void BuildMethodParametersList(List paramsList, SymbolScope methodCtx) - { - NextToken(); // ( - while (_lastExtractedLexem.Token != Token.ClosePar) - { - // [Знач] Идентификатор[= Литерал][,[Знач] Идентификатор[= Литерал]] - var param = BuildParameterDefinition(); - - if (_lastExtractedLexem.Token == Token.Comma) - { - paramsList.Add(param); - methodCtx.DefineVariable(param.Name); - NextToken(); - - if (_lastExtractedLexem.Token == Token.ClosePar) // сразу после запятой не можем выйти из цикла, т.к. ждем в этом месте параметр - { - throw CompilerException.IdentifierExpected(); - } - - continue; - } - - paramsList.Add(param); - methodCtx.DefineVariable(param.Name); - } - - NextToken(); // ) - } - - private ParameterDefinition BuildParameterDefinition() - { - var param = new ParameterDefinition(); - - if (_lastExtractedLexem.Type == LexemType.Annotation) - { - BuildAnnotations(); - param.Annotations = ExtractAnnotations(); - } - - if (_lastExtractedLexem.Token == Token.ByValParam) - { - param.IsByValue = true; - NextToken(); - if (IsUserSymbol(ref _lastExtractedLexem)) - { - param.Name = _lastExtractedLexem.Content; - } - else - { - throw CompilerException.IdentifierExpected(); - } - } - else if (IsUserSymbol(ref _lastExtractedLexem)) - { - param.IsByValue = false; - param.Name = _lastExtractedLexem.Content; - } - else - { - throw CompilerException.IdentifierExpected(); - } - - NextToken(); - if (_lastExtractedLexem.Token == Token.Equal) - { - param.HasDefaultValue = true; - param.DefaultValueIndex = BuildDefaultParameterValue(); - } - - return param; - } - - private int BuildDefaultParameterValue() - { - NextToken(); - - bool hasSign = false; - bool signIsMinus = _lastExtractedLexem.Token == Token.Minus; - if (signIsMinus || _lastExtractedLexem.Token == Token.Plus) - { - hasSign = true; - NextToken(); - } - - if (IsLiteral(ref _lastExtractedLexem)) - { - var cd = CreateConstDefinition(ref _lastExtractedLexem); - if (hasSign) - { - if (_lastExtractedLexem.Type == LexemType.NumberLiteral && signIsMinus) - { - cd.Presentation = '-' + cd.Presentation; - } - else if (_lastExtractedLexem.Type == LexemType.StringLiteral - || _lastExtractedLexem.Type == LexemType.DateLiteral) - { - throw CompilerException.NumberExpected(); - } - } - - NextToken(); - return GetConstNumber(ref cd); - } - else - { - throw CompilerException.LiteralExpected(); - } - } - - private void DispatchMethodBody() - { - _inMethodScope = true; - BuildVariableDefinitions(); - _isStatementsDefined = true; - - var codeStart = _module.Code.Count; - - BuildCodeBatch(_isFunctionProcessed? Token.EndFunction : Token.EndProcedure); - - if (_isFunctionProcessed) - { - var undefConst = new ConstDefinition() - { - Type = DataType.Undefined, - Presentation = "Неопределено" - }; - - AddCommand(OperationCode.PushConst, GetConstNumber(ref undefConst)); - } - - var codeEnd = _module.Code.Count; - - if (_lastExtractedLexem.Token == Token.EndProcedure - || _lastExtractedLexem.Token == Token.EndFunction) - { - AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics|CodeGenerationFlags.DebugCode); - } - - AddCommand(OperationCode.Return); - - { - // заменим Return на Jmp <сюда> - for (var i = codeStart; i < codeEnd; i++) - { - if (_module.Code[i].Code == OperationCode.Return) - { - _module.Code[i] = new Command() { Code = OperationCode.Jmp, Argument = codeEnd }; - } - } - } - - _isStatementsDefined = false; - _inMethodScope = false; - } - - private void BuildCodeBatch(params Token[] endTokens) - { - PushStructureToken(endTokens); - - while (true) - { - if (endTokens.Contains(_lastExtractedLexem.Token)) - { - break; - } - - if (_lastExtractedLexem.Token == Token.Semicolon) - { - NextToken(); - continue; - } - - if (_lastExtractedLexem.Type == LexemType.PreprocessorDirective) - { - throw CompilerException.IllegalDirective(_lastExtractedLexem.Content); - } - - if (_lastExtractedLexem.Type != LexemType.Identifier && _lastExtractedLexem.Token != Token.EndOfText) - { - throw CompilerException.UnexpectedOperation(); - } - - if (_lastExtractedLexem.Token == Token.NotAToken) - { - BuildSimpleStatement(); - } - else - { - BuildComplexStructureStatement(); - } - - if (_lastExtractedLexem.Token != Token.Semicolon) - { - if (endTokens.Contains(_lastExtractedLexem.Token) || LanguageDef.IsEndOfBlockToken(_lastExtractedLexem.Token)) - { - break; - } - throw CompilerException.SemicolonExpected(); - } - NextToken(); - } - PopStructureToken(); - } - - private void BuildComplexStructureStatement() - { - switch (_lastExtractedLexem.Token) - { - case Token.If: - BuildIfStatement(); - break; - case Token.For: - BuildForStatement(); - break; - case Token.While: - BuildWhileStatement(); - break; - case Token.Break: - BuildBreakStatement(); - break; - case Token.Continue: - BuildContinueStatement(); - break; - case Token.Return: - BuildReturnStatement(); - break; - case Token.Try: - BuildTryExceptStatement(); - break; - case Token.RaiseException: - BuildRaiseExceptionStatement(); - break; - case Token.Execute: - BuildExecuteStatement(); - break; - case Token.AddHandler: - case Token.RemoveHandler: - BuildEventHandlerOperation(_lastExtractedLexem.Token); - break; - default: - var expected = PopStructureToken(); - throw CompilerException.TokenExpected(expected); - } - } - - private void BuildIfStatement() - { - AddLineNumber(_lexer.CurrentLine); - - var exitIndices = new List(); - NextToken(); - BuildExpressionUpTo(Token.Then); - var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - - NextToken(); - BuildCodeBatch(Token.Else, Token.ElseIf, Token.EndIf); - exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); - - bool hasAlternativeBranches = false; - - while (_lastExtractedLexem.Token == Token.ElseIf) - { - CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); - AddLineNumber(_lastExtractedLexem.LineNumber); - - NextToken(); - BuildExpressionUpTo(Token.Then); - jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - - NextToken(); - BuildCodeBatch(Token.Else, Token.ElseIf, Token.EndIf); - exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); - } - - if (_lastExtractedLexem.Token == Token.Else) - { - hasAlternativeBranches = true; - CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); - AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics); - - NextToken(); - BuildCodeBatch(Token.EndIf); - } - - int exitIndex = AddLineNumber(_lastExtractedLexem.LineNumber); - - if (!hasAlternativeBranches) - { - CorrectCommandArgument(jumpFalseIndex, exitIndex); - } - - foreach (var indexToWrite in exitIndices) - { - CorrectCommandArgument(indexToWrite, exitIndex); - } - NextToken(); - } - - private void BuildForStatement() - { - AddLineNumber(_lexer.CurrentLine); - - NextToken(); - if (_lastExtractedLexem.Token == Token.Each) - { - BuildForEachStatement(); - } - else if (_lastExtractedLexem.Type == LexemType.Identifier) - { - BuildCountableForStatement(); - } - else - { - throw CompilerException.IdentifierExpected(); - } - } - - private void BuildForEachStatement() - { - Debug.Assert(_lastExtractedLexem.Token == Token.Each); - NextToken(); - - if (!IsUserSymbol(ref _lastExtractedLexem)) - throw CompilerException.IdentifierExpected(); - - var identifier = _lastExtractedLexem.Content; - NextToken(); - if(_lastExtractedLexem.Token != Token.In) - throw CompilerException.TokenExpected(Token.In); - - NextToken(); - BuildExpressionUpTo(Token.Loop); - AddCommand(OperationCode.PushIterator); - var loopBegin = AddLineNumber(_lastExtractedLexem.LineNumber); - AddCommand(OperationCode.IteratorNext); - var condition = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - BuildLoadVariable(identifier); - - var loopRecord = NestedLoopInfo.New(); - loopRecord.startPoint = loopBegin; - _nestedLoops.Push(loopRecord); - - NextToken(); - BuildCodeBatch(Token.EndLoop); - - if (_lastExtractedLexem.Token == Token.EndLoop) - { - AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics | CodeGenerationFlags.DebugCode); - } - - AddCommand(OperationCode.Jmp, loopBegin); - - var indexLoopEnd = AddCommand(OperationCode.StopIterator); - CorrectCommandArgument(condition, indexLoopEnd); - CorrectBreakStatements(_nestedLoops.Pop(), indexLoopEnd); - NextToken(); - } - - private void BuildCountableForStatement() - { - string counter = _lastExtractedLexem.Content; - NextToken(); - if (_lastExtractedLexem.Token != Token.Equal) - { - throw CompilerException.TokenExpected(Token.Equal); - } - NextToken(); - BuildExpressionUpTo(Token.To); - BuildLoadVariable(counter); - NextToken(); - BuildExpressionUpTo(Token.Loop); - AddCommand(OperationCode.MakeRawValue); - AddCommand(OperationCode.PushTmp); - - var jmpIndex = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); - var indexLoopBegin = AddLineNumber(_lastExtractedLexem.LineNumber); - - // increment - BuildPushVariable(counter); - AddCommand(OperationCode.Inc); - BuildLoadVariable(counter); - - var counterIndex = BuildPushVariable(counter); - CorrectCommandArgument(jmpIndex, counterIndex); - var conditionIndex = AddCommand(OperationCode.JmpCounter, DUMMY_ADDRESS); - - var loopRecord = NestedLoopInfo.New(); - loopRecord.startPoint = indexLoopBegin; - _nestedLoops.Push(loopRecord); - - NextToken(); - BuildCodeBatch(Token.EndLoop); - - if (_lastExtractedLexem.Token == Token.EndLoop) - { - AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics | CodeGenerationFlags.DebugCode); - } - - // jmp to start - AddCommand(OperationCode.Jmp, indexLoopBegin); - - var indexLoopEnd = AddCommand(OperationCode.PopTmp, 1); - CorrectCommandArgument(conditionIndex, indexLoopEnd); - CorrectBreakStatements(_nestedLoops.Pop(), indexLoopEnd); - NextToken(); - } - - private void BuildWhileStatement() - { - AddLineNumber(_lexer.CurrentLine); - - NextToken(); - var conditionIndex = _module.Code.Count; - var loopRecord = NestedLoopInfo.New(); - loopRecord.startPoint = conditionIndex; - _nestedLoops.Push(loopRecord); - BuildExpressionUpTo(Token.Loop); - - var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - NextToken(); - BuildCodeBatch(Token.EndLoop); - - if (_lastExtractedLexem.Token == Token.EndLoop) - { - AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics | CodeGenerationFlags.DebugCode); - } - - AddCommand(OperationCode.Jmp, conditionIndex); - - var endLoop = AddCommand(OperationCode.Nop); - CorrectCommandArgument(jumpFalseIndex, endLoop); - CorrectBreakStatements(_nestedLoops.Pop(), endLoop); - - NextToken(); - } - - private void BuildBreakStatement() - { - if (_nestedLoops.Count == 0) - { - throw CompilerException.BreakOutsideOfLoop(); - } - AddLineNumber(_lexer.CurrentLine); - - var loopInfo = _nestedLoops.Peek(); - ExitTryBlocks(); - var idx = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); - loopInfo.breakStatements.Add(idx); - NextToken(); - } - - private void ExitTryBlocks() - { - var tryBlocks = _nestedLoops.Peek().tryNesting; - if (tryBlocks > 0) - AddCommand(OperationCode.ExitTry, tryBlocks); - } - - private void PushTryNesting() - { - if (_nestedLoops.Count > 0) - { - _nestedLoops.Peek().tryNesting++; - } - } - - private void PopTryNesting() - { - if (_nestedLoops.Count > 0) - { - _nestedLoops.Peek().tryNesting--; - } - } - - private void BuildContinueStatement() - { - if (_nestedLoops.Count == 0) - { - throw CompilerException.ContinueOutsideOfLoop(); - } - - AddLineNumber(_lexer.CurrentLine); - - var loopInfo = _nestedLoops.Peek(); - ExitTryBlocks(); - AddCommand(OperationCode.Jmp, loopInfo.startPoint); - NextToken(); - } - - private void BuildReturnStatement() - { - AddLineNumber(_lexer.CurrentLine); - - if (_isFunctionProcessed) - { - NextToken(); - if (_lastExtractedLexem.Token == Token.Semicolon - || LanguageDef.IsEndOfBlockToken(_lastExtractedLexem.Token)) - { - throw CompilerException.FuncEmptyReturnValue(); - } - BuildExpression(Token.Semicolon); - AddCommand(OperationCode.MakeRawValue); - } - else if (_inMethodScope) - { - NextToken(); - if (_lastExtractedLexem.Token != Token.Semicolon - && !LanguageDef.IsEndOfBlockToken(_lastExtractedLexem.Token)) - { - throw CompilerException.ProcReturnsAValue(); - } - } - else - { - throw CompilerException.ReturnOutsideOfMethod(); - } - - AddCommand(OperationCode.Return); - } - - private void BuildTryExceptStatement() - { - AddLineNumber(_lexer.CurrentLine, CodeGenerationFlags.CodeStatistics); - - var beginTryIndex = AddCommand(OperationCode.BeginTry, DUMMY_ADDRESS); - NextToken(); - PushTryNesting(); - BuildCodeBatch(Token.Exception); - PopTryNesting(); - var jmpIndex = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); - - Debug.Assert(_lastExtractedLexem.Token == Token.Exception); - - var beginHandler = AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics); - - CorrectCommandArgument(beginTryIndex, beginHandler); - - NextToken(); - BuildCodeBatch(Token.EndTry); - - var endIndex = AddLineNumber(_lastExtractedLexem.LineNumber, CodeGenerationFlags.CodeStatistics | CodeGenerationFlags.DebugCode); - AddCommand(OperationCode.EndTry); - CorrectCommandArgument(jmpIndex, endIndex); - - NextToken(); - } - - private void BuildRaiseExceptionStatement() - { - AddLineNumber(_lexer.CurrentLine); - - NextToken(); - if (_lastExtractedLexem.Token == Token.Semicolon) - { - if (_tokenStack.Any(x => x.Contains(Token.EndTry))) - { - AddCommand(OperationCode.RaiseException, -1); - } - else - { - throw CompilerException.MismatchedRaiseException(); - } - } - else - { - BuildExpression(Token.Semicolon); - AddCommand(OperationCode.RaiseException); - } - } - - private void BuildExecuteStatement() - { - AddLineNumber(_lexer.CurrentLine); - NextToken(); - - BuildExpression(Token.Semicolon); - AddCommand(OperationCode.Execute); - } - - private void BuildEventHandlerOperation(Token token) - { - NextToken(); - if(!IsUserSymbol(ref _lastExtractedLexem)) - throw CompilerException.IdentifierExpected(); - - BuildExpression(Token.Comma); - var lastCommand = _module.Code[_module.Code.Count - 1]; - if (lastCommand.Code != OperationCode.ResolveProp) - { - throw new CompilerException(Locale.NStr("ru = 'Ожидается имя события'; en = 'Event name expected'")); - } - - _module.Code[_module.Code.Count - 1] = new Command - { - Code = OperationCode.PushConst, - Argument = lastCommand.Argument - }; - - NextToken(); - BuildExpression(Token.Semicolon); - - lastCommand = _module.Code[_module.Code.Count - 1]; - if (lastCommand.Code != OperationCode.ResolveProp) - { - throw new CompilerException(Locale.NStr("ru = 'Ожидается имя обработчика события'; en = 'Event handler name expected'")); - } - - _module.Code[_module.Code.Count - 1] = new Command - { - Code = OperationCode.PushConst, - Argument = lastCommand.Argument - }; - - AddCommand(TokenToOperationCode(token)); - } - - private void CorrectCommandArgument(int index, int newArgument) - { - var cmd = _module.Code[index]; - cmd.Argument = newArgument; - _module.Code[index] = cmd; - } - - private void CorrectBreakStatements(NestedLoopInfo nestedLoopInfo, int endLoopIndex) - { - foreach (var breakCmdIndex in nestedLoopInfo.breakStatements) - { - CorrectCommandArgument(breakCmdIndex, endLoopIndex); - } - } - - private void BuildSimpleStatement() - { - var identifier = _lastExtractedLexem.Content; - - NextToken(); - AddLineNumber(_lexer.CurrentLine); - switch (_lastExtractedLexem.Token) - { - case Token.Equal: - NextToken(); - BuildExpression(Token.Semicolon); - BuildLoadVariable(identifier); - break; - case Token.OpenPar: - ProcessCallOnLeftHand(identifier); - break; - case Token.Dot: - case Token.OpenBracket: - // access chain - BuildPushVariable(identifier); - BuildAccessChainLeftHand(); - break; - default: - throw CompilerException.UnexpectedOperation(); - } - } - - private void ProcessCallOnLeftHand(string identifier) - { - var args = PushMethodArgumentsBeforeCall(); - if(IsContinuationToken(ref _lastExtractedLexem)) - { - BuildMethodCall(identifier, args, true); - BuildAccessChainLeftHand(); - } - else - { - BuildMethodCall(identifier, args, false); - } - } - - private void BuildAccessChainLeftHand() - { - string ident; - BuildContinuationLeftHand(out ident); - - if (ident == null) - { - // это присваивание - if (_lastExtractedLexem.Token != Token.Equal) - throw CompilerException.UnexpectedOperation(); - - NextToken(); // перешли к выражению - BuildExpression(Token.Semicolon); - AddCommand(OperationCode.AssignRef); - } - else - { - // это вызов - Debug.Assert(_lastExtractedLexem.Token == Token.OpenPar); - PushMethodArgumentsBeforeCall(); - var cDef = new ConstDefinition(); - cDef.Type = DataType.String; - cDef.Presentation = ident; - int lastIdentifierConst = GetConstNumber(ref cDef); - - if (IsContinuationToken(ref _lastExtractedLexem)) - { - AddCommand(OperationCode.ResolveMethodFunc, lastIdentifierConst); - BuildAccessChainLeftHand(); - } - else - { - AddCommand(OperationCode.ResolveMethodProc, lastIdentifierConst); - } - } - } - - private bool IsContinuationToken(ref Lexem lex) - { - return lex.Token == Token.Dot || lex.Token == Token.OpenBracket; - } - - private void BuildExpressionUpTo(Token stopToken) - { - BuildPrimaryNode(); - BuildOperation(0); - if (_lastExtractedLexem.Token == stopToken) - return; - - if (_lastExtractedLexem.Token == Token.EndOfText) - throw CompilerException.UnexpectedEndOfText(); - else - throw CompilerException.ExpressionSyntax(); - } - - private void BuildExpression(Token stopToken) - { - BuildPrimaryNode(); - BuildOperation(0); - if (_lastExtractedLexem.Token == stopToken) - return; - - var endTokens = _tokenStack.Peek(); - if (endTokens.Contains(_lastExtractedLexem.Token)) - return; - - if (_lastExtractedLexem.Token == Token.EndOfText) - throw CompilerException.UnexpectedEndOfText(); - else - throw CompilerException.ExpressionSyntax(); - } - - private void BuildOperation(int acceptablePriority) - { - var currentOp = _lastExtractedLexem.Token; - var opPriority = GetBinaryPriority(currentOp); - while (LanguageDef.IsBinaryOperator(currentOp) && opPriority >= acceptablePriority) - { - bool isLogical = LanguageDef.IsLogicalBinaryOperator(currentOp); - int logicalCmdIndex = DUMMY_ADDRESS; - - if (isLogical) - { - logicalCmdIndex = AddCommand(TokenToOperationCode(currentOp)); - } - - NextToken(); - BuildPrimaryNode(); - - var newOp = _lastExtractedLexem.Token; - int newPriority = GetBinaryPriority(newOp); - - if (newPriority > opPriority) - { - BuildOperation(newPriority); - } - - if (isLogical) - { - currentOp = _lastExtractedLexem.Token; - newPriority = GetBinaryPriority(currentOp); - if(opPriority < newPriority) - { - BuildOperation(newPriority); - } - - AddCommand(OperationCode.MakeBool); - CorrectCommandArgument(logicalCmdIndex, _module.Code.Count - 1); - } - else - { - var opCode = TokenToOperationCode(currentOp); - AddCommand(opCode); - } - - currentOp = _lastExtractedLexem.Token; - opPriority = GetBinaryPriority(currentOp); - } - } - - private static OperationCode TokenToOperationCode(Token stackOp) - { - OperationCode opCode; - switch (stackOp) - { - case Token.Equal: - opCode = OperationCode.Equals; - break; - case Token.NotEqual: - opCode = OperationCode.NotEqual; - break; - case Token.Plus: - opCode = OperationCode.Add; - break; - case Token.Minus: - opCode = OperationCode.Sub; - break; - case Token.Multiply: - opCode = OperationCode.Mul; - break; - case Token.Division: - opCode = OperationCode.Div; - break; - case Token.Modulo: - opCode = OperationCode.Mod; - break; - case Token.UnaryPlus: - opCode = OperationCode.Number; - break; - case Token.UnaryMinus: - opCode = OperationCode.Neg; - break; - case Token.And: - opCode = OperationCode.And; - break; - case Token.Or: - opCode = OperationCode.Or; - break; - case Token.Not: - opCode = OperationCode.Not; - break; - case Token.LessThan: - opCode = OperationCode.Less; - break; - case Token.LessOrEqual: - opCode = OperationCode.LessOrEqual; - break; - case Token.MoreThan: - opCode = OperationCode.Greater; - break; - case Token.MoreOrEqual: - opCode = OperationCode.GreaterOrEqual; - break; - case Token.AddHandler: - opCode = OperationCode.AddHandler; - break; - case Token.RemoveHandler: - opCode = OperationCode.RemoveHandler; - break; - default: - throw new NotSupportedException(); - } - return opCode; - } - - private static int GetBinaryPriority(Token newOp) - { - int newPriority; - if (LanguageDef.IsBinaryOperator(newOp)) - newPriority = LanguageDef.GetPriority(newOp); - else - newPriority = -1; - - return newPriority; - } - - private void BuildPrimaryNode() - { - if (LanguageDef.IsLiteral(ref _lastExtractedLexem)) - { - BuildPushConstant(); - NextToken(); - } - else if (LanguageDef.IsUserSymbol(ref _lastExtractedLexem)) - { - ProcessPrimaryIdentifier(); - } - else if (_lastExtractedLexem.Token == Token.Plus) - { - ProcessPrimaryUnaryPlus(); - } - else if (_lastExtractedLexem.Token == Token.Minus) - { - ProcessPrimaryUnaryMinus(); - } - else if (_lastExtractedLexem.Token == Token.Not) - { - ProcessUnaryBoolean(); - } - else if (_lastExtractedLexem.Token == Token.OpenPar) - { - ProcessSubexpression(); - BuildContinuationRightHand(); - } - else if(_lastExtractedLexem.Token == Token.NewObject) - { - BuildNewObjectCreation(); - BuildContinuationRightHand(); - } - else if (LanguageDef.IsBuiltInFunction(_lastExtractedLexem.Token)) - { - BuildBuiltinFunction(); - BuildContinuationRightHand(); - } - else if (_lastExtractedLexem.Token == Token.Question) - { - BuildQuestionOperator(); - BuildContinuationRightHand(); - } - else - { - throw CompilerException.ExpressionSyntax(); - } - } - private void ProcessPrimaryIdentifier() - { - var identifier = _lastExtractedLexem.Content; - var lineNumber = _lastExtractedLexem.LineNumber; - NextToken(); - if (IsContinuationToken(ref _lastExtractedLexem)) - { - BuildPushVariable(identifier); - BuildContinuationRightHand(); - } - else if (_lastExtractedLexem.Token == Token.OpenPar) - { - BuildFunctionCall(identifier, lineNumber); - BuildContinuationRightHand(); - } - else - { - BuildPushVariable(identifier); - } - } - - private bool LastExtractedIsPimary() - { - return LanguageDef.IsLiteral(ref _lastExtractedLexem) - || LanguageDef.IsIdentifier(ref _lastExtractedLexem) - || _lastExtractedLexem.Token == Token.OpenPar; - } - - private void ProcessPrimaryUnaryMinus() - { - NextToken(); - if (!LastExtractedIsPimary()) - { - throw CompilerException.ExpressionExpected(); - } - - BuildPrimaryNode(); - AddCommand(OperationCode.Neg); - } - - private void ProcessPrimaryUnaryPlus() - { - NextToken(); - if (!LastExtractedIsPimary()) - { - throw CompilerException.ExpressionExpected(); - } - - BuildPrimaryNode(); - AddCommand(OperationCode.Number); - } - - private void ProcessSubexpression() - { - NextToken(); // съели открывающую скобку - BuildPrimaryNode(); - BuildOperation(0); - - if (_lastExtractedLexem.Token != Token.ClosePar) - throw CompilerException.TokenExpected(")"); - - NextToken(); // съели закрывающую скобку - } - - private void ProcessUnaryBoolean() - { - NextToken(); - BuildPrimaryNode(); - BuildOperation(GetBinaryPriority(Token.Not)); - AddCommand(OperationCode.Not); - } - - private void BuildContinuationRightHand() - { - string dummy; - BuildContinuationInternal(false, out dummy); - } - - private void BuildContinuationLeftHand(out string lastIdentifier) - { - BuildContinuationInternal(true, out lastIdentifier); - } - - private void BuildContinuationInternal(bool interruptOnCall, out string lastIdentifier) - { - lastIdentifier = null; - while (true) - { - if (_lastExtractedLexem.Token == Token.Dot) - { - NextToken(); - if (!IsValidPropertyName(ref _lastExtractedLexem)) - throw CompilerException.IdentifierExpected(); - - string identifier = _lastExtractedLexem.Content; - NextToken(); - if (_lastExtractedLexem.Token == Token.OpenPar) - { - if (interruptOnCall) - { - lastIdentifier = identifier; - return; - } - else - { - var args = BuildArgumentList(); - var cDef = new ConstDefinition(); - cDef.Type = DataType.String; - cDef.Presentation = identifier; - int lastIdentifierConst = GetConstNumber(ref cDef); - AddCommand(OperationCode.ArgNum, args.Length); - AddCommand(OperationCode.ResolveMethodFunc, lastIdentifierConst); - } - } - else - { - ResolveProperty(identifier); - } - } - else if (_lastExtractedLexem.Token == Token.OpenBracket) - { - NextToken(); - if (_lastExtractedLexem.Token == Token.CloseBracket) - throw CompilerException.ExpressionExpected(); - - BuildExpressionUpTo(Token.CloseBracket); - Debug.Assert(_lastExtractedLexem.Token == Token.CloseBracket); - NextToken(); - - AddCommand(OperationCode.PushIndexed); - } - else - { - break; - } - } - } - - private bool IsValidPropertyName(ref Lexem lex) - { - return LanguageDef.IsIdentifier(ref lex) - || lex.Type == LexemType.BooleanLiteral - || lex.Type == LexemType.NullLiteral - || lex.Type == LexemType.UndefinedLiteral - || lex.Token == Token.And - || lex.Token == Token.Or - || lex.Token == Token.Not; - } - - private void ResolveProperty(string identifier) - { - var cDef = new ConstDefinition(); - cDef.Type = DataType.String; - cDef.Presentation = identifier; - var identifierConstIndex = GetConstNumber(ref cDef); - AddCommand(OperationCode.ResolveProp, identifierConstIndex); - } - - private void BuildQuestionOperator() - { - Debug.Assert(_lastExtractedLexem.Token == Token.Question); - NextToken(); - if (_lastExtractedLexem.Token != Token.OpenPar) - throw CompilerException.UnexpectedOperation(); - - NextToken(); - BuildExpressionUpTo(Token.Comma); - - AddCommand(OperationCode.MakeBool); - var addrOfCondition = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); - - NextToken(); - BuildExpressionUpTo(Token.Comma); // построили true-part - - var endOfTruePart = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); // уход в конец оператора - - CorrectCommandArgument(addrOfCondition, _module.Code.Count); // отметили, куда переходить по false - NextToken(); - BuildExpressionUpTo(Token.ClosePar); // построили false-part - - CorrectCommandArgument(endOfTruePart, _module.Code.Count); - - NextToken(); - } - - private bool[] BuildArgumentList() - { - Debug.Assert(_lastExtractedLexem.Token == Token.OpenPar); - - List arguments = new List(); - - PushStructureToken(Token.ClosePar); - try - { - NextToken(); // съели открывающую скобку - while (_lastExtractedLexem.Token != Token.ClosePar) - { - PushPassedArgument(arguments); - } - - if (_lastExtractedLexem.Token != Token.ClosePar) - throw CompilerException.TokenExpected(")"); - - NextToken(); // съели закрывающую скобку - } - finally - { - PopStructureToken(); - } - - return arguments.ToArray(); - } - - private void PushPassedArgument(IList arguments) - { - if (_lastExtractedLexem.Token == Token.Comma) - { - AddCommand(OperationCode.PushDefaultArg); - arguments.Add(false); - NextToken(); - if (_lastExtractedLexem.Token == Token.ClosePar) - { - AddCommand(OperationCode.PushDefaultArg); - arguments.Add(false); - } - } - else if (_lastExtractedLexem.Token != Token.ClosePar) - { - BuildExpression(Token.Comma); - arguments.Add(true); - if (_lastExtractedLexem.Token == Token.Comma) - { - NextToken(); - if (_lastExtractedLexem.Token == Token.ClosePar) - { - AddCommand(OperationCode.PushDefaultArg); - arguments.Add(false); - } - } - } - } - - private void BuildLoadVariable(string identifier) - { - var hasVar = _ctx.TryGetVariable(identifier, out var varBinding); - if (hasVar) - { - if (varBinding.binding.ContextIndex == _ctx.TopIndex()) - { - AddCommand(OperationCode.LoadLoc, varBinding.binding.CodeIndex); - } - else - { - var num = GetVariableRefNumber(ref varBinding.binding); - AddCommand(OperationCode.LoadVar, num); - } - } - else - { - // can create variable - var binding = _ctx.DefineVariable(identifier); - AddCommand(OperationCode.LoadLoc, binding.CodeIndex); - } - } - - private void BuildFunctionCall(string identifier, int callLineNumber) - { - bool[] args = PushMethodArgumentsBeforeCall(); - AddLineNumber(callLineNumber, CodeGenerationFlags.CodeStatistics); - BuildMethodCall(identifier, args, true); - AddLineNumber(callLineNumber, CodeGenerationFlags.DebugCode); - } - - private bool[] PushMethodArgumentsBeforeCall() - { - var argsPassed = BuildArgumentList(); - AddCommand(OperationCode.ArgNum, argsPassed.Length); - return argsPassed; - } - - private void BuildMethodCall(string identifier, bool[] argsPassed, bool asFunction) - { - var hasMethod = _ctx.TryGetMethod(identifier, out var methBinding); - if (hasMethod) - { - var scope = _ctx.GetScope(methBinding.ContextIndex); - - // dynamic scope checks signatures only at runtime - if (!scope.IsDynamicScope) - { - var methInfo = scope.GetMethod(methBinding.CodeIndex); - if (asFunction && !methInfo.IsFunction) - { - throw CompilerException.UseProcAsFunction(); - } - CheckFactArguments(methInfo, argsPassed); - } - - if (asFunction) - AddCommand(OperationCode.CallFunc, GetMethodRefNumber(ref methBinding)); - else - AddCommand(OperationCode.CallProc, GetMethodRefNumber(ref methBinding)); - } - else - { - // can be defined later - var forwarded = new ForwardedMethodDecl(); - forwarded.identifier = identifier; - forwarded.asFunction = asFunction; - forwarded.codeLine = _lexer.CurrentLine; - forwarded.factArguments = argsPassed; - - var opCode = asFunction ? OperationCode.CallFunc : OperationCode.CallProc; - forwarded.commandIndex = AddCommand(opCode, DUMMY_ADDRESS); - _forwardedMethods.Add(forwarded); - } - } - - private void CheckFactArguments(MethodInfo methInfo, bool[] argsPassed) - { - CheckFactArguments(methInfo.Params, argsPassed); - } - - private void CheckFactArguments(ParameterDefinition[] parameters, bool[] argsPassed) - { - if (argsPassed.Length > parameters.Length) - { - throw CompilerException.TooManyArgumentsPassed(); - } - - if (parameters.Skip(argsPassed.Length).Any(param => !param.HasDefaultValue)) - { - throw CompilerException.TooFewArgumentsPassed(); - } - } - - private void BuildNewObjectCreation() - { - NextToken(); - if (_lastExtractedLexem.Token == Token.OpenPar) - { - // создание по строковому имени класса - NewObjectDynamicConstructor(); - } - else if (IsUserSymbol(ref _lastExtractedLexem) || _lastExtractedLexem.Token == Token.ExceptionInfo) - { - NewObjectStaticConstructor(); - } - else - { - throw CompilerException.IdentifierExpected(); - } - } - - private void NewObjectDynamicConstructor() - { - var argsPassed = BuildArgumentList(); - if (argsPassed.Length == 0) - throw CompilerException.ExpressionExpected(); - - AddCommand(OperationCode.NewInstance, argsPassed.Length-1); - } - - private void NewObjectStaticConstructor() - { - var name = _lastExtractedLexem.Content; - var cDef = new ConstDefinition() - { - Type = DataType.String, - Presentation = name - }; - - AddCommand(OperationCode.PushConst, GetConstNumber(ref cDef)); - - NextToken(); - bool[] argsPassed; - if (_lastExtractedLexem.Token == Token.OpenPar) - { - // Отрабатываем только в тех случаях, если явно указана скобка. - // В остальных случаях дальнейшую обработку отдаём наружу - argsPassed = BuildArgumentList(); - } - else - { - argsPassed = new bool[0]; - } - - AddCommand(OperationCode.NewInstance, argsPassed.Length); - } - - private void BuildPushConstant() - { - var cDef = CreateConstDefinition(ref _lastExtractedLexem); - var num = GetConstNumber(ref cDef); - AddCommand(OperationCode.PushConst, num); - } - - private int BuildPushVariable(string identifier) - { - var varNum = _ctx.GetVariable(identifier); - if (varNum.type == SymbolType.ContextProperty) - { - return PushReference(varNum.binding); - } - else - { - return PushSimpleVariable(varNum.binding); - } - } - - private void BuildBuiltinFunction() - { - OperationCode funcId = BuiltInFunctionCode(_lastExtractedLexem.Token); - NextToken(); - if(_lastExtractedLexem.Token != Token.OpenPar) - { - throw CompilerException.TokenExpected(Token.OpenPar); - } - - var passedArgs = BuildArgumentList(); - if (funcId == OperationCode.Min || funcId == OperationCode.Max) - { - if (passedArgs.Length == 0) - throw CompilerException.TooFewArgumentsPassed(); - } - else - { - var parameters = BuiltinFunctions.ParametersInfo(funcId); - CheckFactArguments(parameters, passedArgs); - } - - AddCommand(funcId, passedArgs.Length); - } - - #region Helper methods - - private static bool IsUserSymbol(ref Lexem lex) - { - return LanguageDef.IsUserSymbol(ref lex); - } - - private static bool IsLiteral(ref Lexem lex) - { - return LanguageDef.IsLiteral(ref lex); - } - - private static OperationCode BuiltInFunctionCode(Token token) - { - return _tokenToOpCode[token]; - } - - private static ConstDefinition CreateConstDefinition(ref Lexem lex) - { - DataType constType = DataType.Undefined; - switch (lex.Type) - { - case LexemType.BooleanLiteral: - constType = DataType.Boolean; - break; - case LexemType.DateLiteral: - constType = DataType.Date; - break; - case LexemType.NumberLiteral: - constType = DataType.Number; - break; - case LexemType.StringLiteral: - constType = DataType.String; - break; - case LexemType.NullLiteral: - constType = DataType.GenericValue; - break; - } - - ConstDefinition cDef = new ConstDefinition() - { - Type = constType, - Presentation = lex.Content - }; - return cDef; - } - - private int GetConstNumber(ref ConstDefinition cDef) - { - var idx = _module.Constants.IndexOf(cDef); - if (idx < 0) - { - idx = _module.Constants.Count; - _module.Constants.Add(cDef); - } - return idx; - } - - private int GetMethodRefNumber(ref SymbolBinding methodBinding) - { - var idx = _module.MethodRefs.IndexOf(methodBinding); - if (idx < 0) - { - idx = _module.MethodRefs.Count; - _module.MethodRefs.Add(methodBinding); - } - return idx; - } - - private int GetVariableRefNumber(ref SymbolBinding binding) - { - var idx = _module.VariableRefs.IndexOf(binding); - if (idx < 0) - { - idx = _module.VariableRefs.Count; - _module.VariableRefs.Add(binding); - } - - return idx; - } - - private void NextToken() - { - if (_lastExtractedLexem.Token != Token.EndOfText) - { - _lastExtractedLexem = _lexer.NextLexem(); - } - else - { - throw CompilerException.UnexpectedEndOfText(); - } - } - - private void PushStructureToken(params Token[] tok) - { - _tokenStack.Push(tok); - } - - private Token[] PopStructureToken() - { - var tok = _tokenStack.Pop(); - return tok; - } - - private int AddCommand(OperationCode code, int arg = 0) - { - var addr = _module.Code.Count; - _module.Code.Add(new Command() { Code = code, Argument = arg }); - return addr; - } - - private int AddLineNumber(int linenum, CodeGenerationFlags emitConditions = CodeGenerationFlags.Always) - { - var addr = _module.Code.Count; - bool emit = emitConditions == CodeGenerationFlags.Always || ExtraCodeConditionsMet(emitConditions); - if (emit) - { - _module.Code.Add(new Command() { Code = OperationCode.LineNum, Argument = linenum }); - } - return addr; - } - - private bool ExtraCodeConditionsMet(CodeGenerationFlags emitConditions) - { - return (((int)ProduceExtraCode) & (int)emitConditions) != 0; - } - - private int PushSimpleVariable(SymbolBinding binding) - { - if (binding.ContextIndex == _ctx.TopIndex()) - { - return AddCommand(OperationCode.PushLoc, binding.CodeIndex); - } - else - { - var idx = GetVariableRefNumber(ref binding); - return AddCommand(OperationCode.PushVar, idx); - } - } - - private int PushReference(SymbolBinding binding) - { - var idx = GetVariableRefNumber(ref binding); - - return AddCommand(OperationCode.PushRef, idx); - } - - #endregion - - static Compiler() - { - _tokenToOpCode = new Dictionary(); - - var tokens = LanguageDef.BuiltInFunctions(); - var opCodes = BuiltinFunctions.GetOperationCodes(); - - Debug.Assert(tokens.Length == opCodes.Length); - for (int i = 0; i < tokens.Length; i++) - { - _tokenToOpCode.Add(tokens[i], opCodes[i]); - } - } - } - - public delegate bool CompilerDirectiveHandler(string directive, string value, bool codeEntered); -} diff --git a/src/ScriptEngine/Compiler/CompilerBackendSelector.cs b/src/ScriptEngine/Compiler/CompilerBackendSelector.cs new file mode 100644 index 000000000..85d8198c8 --- /dev/null +++ b/src/ScriptEngine/Compiler/CompilerBackendSelector.cs @@ -0,0 +1,63 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using OneScript.Compilation; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Native.Compiler; + +namespace ScriptEngine.Compiler +{ + public class CompilerBackendSelector : BslSyntaxWalker + { + private bool _isNative; + + public CompilerBackendSelector(OneScriptCoreOptions options) + { + _isNative = options.UseNativeAsDefaultRuntime; + } + + public Func StackBackendInitializer { get; set; } + + public Func NativeBackendInitializer { get; set; } + + public ICompilerBackend Select(ModuleNode ast) + { + VisitModule(ast); + if (_isNative) + { + return NativeBackendInitializer?.Invoke(); + } + + return StackBackendInitializer?.Invoke(); + } + + protected override void VisitModuleAnnotation(AnnotationNode node) + { + if (string.Equals(node.Name, NativeRuntimeAnnotationHandler.NativeDirectiveName, + StringComparison.CurrentCultureIgnoreCase)) + { + _isNative = true; + } + else if (string.Equals(node.Name, NativeRuntimeAnnotationHandler.StackRuntimeDirectiveName, + StringComparison.CurrentCultureIgnoreCase)) + { + _isNative = false; + } + } + + protected override void VisitModule(ModuleNode node) + { + foreach (var child in node.Children.Where(x => x.Kind == NodeKind.Annotation)) + { + VisitModuleAnnotation((AnnotationNode)child); + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Compiler/CompilerContext.cs b/src/ScriptEngine/Compiler/CompilerContext.cs deleted file mode 100644 index c00174c7a..000000000 --- a/src/ScriptEngine/Compiler/CompilerContext.cs +++ /dev/null @@ -1,226 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using ScriptEngine.Machine; - -namespace ScriptEngine.Compiler -{ - class CompilerContext : ICompilerContext - { - readonly List _scopeStack = new List(); - - public void PushScope(SymbolScope scope) - { - _scopeStack.Add(scope); - } - - public SymbolScope PopScope() - { - var idx = _scopeStack.Count - 1; - if (idx >= 0) - { - var retVal = _scopeStack[idx]; - _scopeStack.RemoveAt(idx); - return retVal; - } - else - { - throw new InvalidOperationException("No scopes defined"); - } - } - - public SymbolScope Peek() - { - var idx = _scopeStack.Count - 1; - if (idx >= 0) - { - return _scopeStack[idx]; - } - else - { - throw new InvalidOperationException("No scopes defined"); - } - } - - private SymbolBinding GetSymbol(string symbol, Func extract) - { - if (TryGetSymbol(symbol, extract, out var result)) - { - return result; - } - - throw new SymbolNotFoundException(symbol); - } - - private bool TryGetSymbol(string symbol, Func extract, out SymbolBinding result) - { - for (int i = _scopeStack.Count - 1; i >= 0; i--) - { - var number = extract(_scopeStack[i]); - if (number < 0) - continue; - - result = new SymbolBinding(); - result.CodeIndex = number; - result.ContextIndex = i; - return true; - - } - - result = default(SymbolBinding); - return false; - } - - private bool HasSymbol(Func definitionCheck) - { - for (int i = _scopeStack.Count - 1; i >= 0; i--) - { - var isDefined = definitionCheck(_scopeStack[i]); - if (isDefined) - return true; - } - - return false; - } - - public VariableBinding GetVariable(string name) - { - var sb = GetSymbol(name, x => ExtractVariableIndex(name, x)); - return new VariableBinding() - { - type = _scopeStack[sb.ContextIndex].GetVariable(sb.CodeIndex).Type, - binding = sb - }; - } - - public SymbolBinding GetMethod(string name) - { - return GetSymbol(name, x => ExtractMethodIndex(name, x)); - } - - public bool TryGetMethod(string name, out SymbolBinding result) - { - return TryGetSymbol(name, x => ExtractMethodIndex(name, x), out result); - } - - public bool TryGetVariable(string name, out VariableBinding vb) - { - var hasSymbol = TryGetSymbol(name, x => ExtractVariableIndex(name, x), out var sb); - if (!hasSymbol) - { - vb = default(VariableBinding); - return false; - } - - vb = new VariableBinding() - { - type = _scopeStack[sb.ContextIndex].GetVariable(sb.CodeIndex).Type, - binding = sb - }; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ExtractVariableIndex(string name, SymbolScope scope) - { - if (scope.IsVarDefined(name)) - { - return scope.GetVariableNumber(name); - } - else - return -1; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int ExtractMethodIndex(string name, SymbolScope scope) - { - if (scope.IsMethodDefined(name)) - { - return scope.GetMethodNumber(name); - } - else - return -1; - } - - public SymbolScope GetScope(int scopeIndex) - { - return _scopeStack[scopeIndex]; - } - - public int ScopeIndex(SymbolScope scope) - { - return _scopeStack.IndexOf(scope); - } - - public SymbolBinding DefineMethod(MethodInfo method) - { - if (_scopeStack.Count > 0) - { - if (!HasSymbol(x => x.IsMethodDefined(method.Name))) - { - var idx = TopIndex(); - var num = _scopeStack[TopIndex()].DefineMethod(method); - return new SymbolBinding() - { - ContextIndex = idx, - CodeIndex = num - }; - } - else - throw new CompilerException("Symbol already defined"); - } - - throw new InvalidOperationException("Scopes are not defined"); - } - - public SymbolBinding DefineVariable(string name, string alias = null) - { - if (_scopeStack.Count > 0) - { - var idx = TopIndex(); - var scope = GetScope(idx); - if (!scope.IsVarDefined(name)) - { - var num = scope.DefineVariable(name, alias); - return new SymbolBinding() - { - ContextIndex = idx, - CodeIndex = num - }; - } - else - throw new CompilerException("Symbol already defined"); - } - - throw new InvalidOperationException("Scopes are not defined"); - } - - public SymbolBinding DefineProperty(string name, string alias = null) - { - if (_scopeStack.Count > 0) - { - var idx = TopIndex(); - var num = _scopeStack[idx].DefineVariable(name, alias, SymbolType.ContextProperty); - return new SymbolBinding() - { - ContextIndex = idx, - CodeIndex = num - }; - } - - throw new InvalidOperationException("Scopes are not defined"); - } - - public int TopIndex() - { - return _scopeStack.Count - 1; - } - - } -} diff --git a/src/ScriptEngine/Compiler/CompilerErrors.cs b/src/ScriptEngine/Compiler/CompilerErrors.cs new file mode 100644 index 000000000..9651e20ad --- /dev/null +++ b/src/ScriptEngine/Compiler/CompilerErrors.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +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.Runtime.CompilerServices; +using OneScript.Language; +using OneScript.Localization; + +namespace ScriptEngine.Compiler +{ + public static class CompilerErrors + { + public static CodeError UseProcAsFunction() => + Create("Использование процедуры, как функции", "Procedure called as function"); + + public static CodeError TooFewArgumentsPassed() => + Create("Недостаточно фактических параметров", "Too many actual parameters"); + + public static CodeError TooManyArgumentsPassed() => + Create("Слишком много фактических параметров", "Too many actual parameters"); + + public static CodeError MissedArgument() => + Create("Пропущен обязательный параметр", "Missed mandatory parameter"); + + public static CodeError MissedImport(string symbol, string libName) => + Create($"Свойство {symbol} принадлежит пакету {libName}, который не импортирован в данном модуле", + $"Property {symbol} belongs to package {libName} which is not imported in this module"); + + private static CodeError Create(string ru, string en, [CallerMemberName] string errorId = default) + { + return new CodeError + { + ErrorId = errorId, + Description = BilingualString.Localize(ru, en) + }; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Compiler/CompilerExceptions.cs b/src/ScriptEngine/Compiler/CompilerExceptions.cs deleted file mode 100644 index 0f38e1677..000000000 --- a/src/ScriptEngine/Compiler/CompilerExceptions.cs +++ /dev/null @@ -1,161 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Linq; -using OneScript.Language; -using OneScript.Language.LexicalAnalysis; - -namespace ScriptEngine.Compiler -{ - public class CompilerException : ScriptException - { - public CompilerException(string msg) - : base(new CodePositionInfo(), msg) - { - - } - - internal static CompilerException AppendCodeInfo(CompilerException exc, CodePositionInfo codePosInfo) - { - exc.LineNumber = codePosInfo.LineNumber; - exc.ColumnNumber = codePosInfo.ColumnNumber; - exc.Code = codePosInfo.Code; - exc.ModuleName = codePosInfo.ModuleName; - - return exc; - } - - internal static CompilerException UnexpectedOperation() - { - return new CompilerException(Locale.NStr("ru='Неизвестная операция';en='Unknown operation'")); - } - - internal static CompilerException IdentifierExpected() - { - return new CompilerException(Locale.NStr("ru='Ожидается идентификатор';en='Identifier expecting'")); - } - - internal static CompilerException SemicolonExpected() - { - return new CompilerException(Locale.NStr("ru='Ожидается символ ; (точка с запятой)';en='Expecting \";\"'")); - } - - internal static CompilerException LateVarDefinition() - { - return new CompilerException(Locale.NStr("ru='Объявления переменных должны быть расположены в начале модуля, процедуры или функции';" - + "en='Variable declarations must be placed at beginning of module, procedure, or function'")); - } - - internal static CompilerException TokenExpected(params Token[] expected) - { - var names = expected.Select(x => Enum.GetName(typeof(Token), x)); - return new CompilerException(Locale.NStr("ru='Ожидается символ: ';en='Expecting symbol: '") + String.Join("/", names)); - } - - internal static CompilerException TokenExpected(string tokens) - { - return new CompilerException(Locale.NStr("ru='Ожидается символ: ';en='Expecting symbol: '") + tokens); - } - - internal static CompilerException ExpressionSyntax() - { - return new CompilerException(Locale.NStr("ru='Ошибка в выражении';en='Expression syntax error'")); - } - - internal static CompilerException UseProcAsFunction() - { - return new CompilerException(Locale.NStr("ru='Использование процедуры, как функции';en='Procedure called as function'")); - } - - internal static CompilerException TooFewArgumentsPassed() - { - return new CompilerException(Locale.NStr("ru='Недостаточно фактических параметров';en='Not enough actual parameters'")); - } - - internal static CompilerException TooManyArgumentsPassed() - { - return new CompilerException(Locale.NStr("ru='Слишком много фактических параметров'; en='Too many actual parameters'")); - } - - internal static CompilerException InternalCompilerError(string reason) - { - return new CompilerException(Locale.NStr("ru='Внутренняя ошибка компилятора:';en='Internal compiler error:'") + reason); - } - - internal static CompilerException UnexpectedEndOfText() - { - return new CompilerException(Locale.NStr("ru='Обнаружено логическое завершение текста модуля';en='Logical end of module source text encountered'")); - } - - internal static CompilerException BreakOutsideOfLoop() - { - return new CompilerException(Locale.NStr("ru='Оператор \"Прервать\" может использоваться только внутри цикла';en='Break operator may be used only within loop'")); - } - - internal static CompilerException ContinueOutsideOfLoop() - { - return new CompilerException(Locale.NStr("ru='Оператор \"Продолжить\" может использоваться только внутри цикла';en='Continue operator may be used only within loop'")); - } - - internal static CompilerException ReturnOutsideOfMethod() - { - return new CompilerException(Locale.NStr("ru='Оператор \"Возврат\" может использоваться только внутри метода';en='Return operator may not be used outside procedure or function'")); - } - - internal static CompilerException ProcReturnsAValue() - { - return new CompilerException(Locale.NStr("ru='Процедуры не могут возвращать значение';en='Procedures cannot return value'")); - } - - internal static CompilerException FuncEmptyReturnValue() - { - return new CompilerException(Locale.NStr("ru='Функция должна возвращать значение';en='Function should return a value'")); - } - - internal static CompilerException MismatchedRaiseException() - { - return new CompilerException(Locale.NStr("ru='Оператор \"ВызватьИсключение\" без параметров может использоваться только в блоке \"Исключение\"';en='Raise operator may be used without arguments only when handling exception'")); - } - - public static CompilerException ExpressionExpected() - { - return new CompilerException(Locale.NStr("ru='Ожидается выражение';en='Expression expected'")); - } - - public static CompilerException LiteralExpected() - { - return new CompilerException(Locale.NStr("ru='Ожидается константа';en='Constant expected'")); - } - - public static CompilerException NumberExpected() - { - return new CompilerException(Locale.NStr("ru='Ожидается числовая константа';en='Numeric constant expected'")); - } - - public static CompilerException IllegalDirective(string name) - { - return new CompilerException(Locale.NStr("ru='Недопустимая директива:';en='Illegal directive'")+name); - } - - public static CompilerException UnknownDirective(string name, string arg) - { - return new CompilerException(Locale.NStr("ru='Неизвестная директива:';en='Unknown directive'") + $"{name} ({arg})"); - } - - } - - public class ExtraClosedParenthesis : CompilerException - { - internal ExtraClosedParenthesis(CodePositionInfo codePosInfo) : base(Locale.NStr("ru='Ожидается символ: (';en='Expecting symbol: ('")) - { - this.LineNumber = codePosInfo.LineNumber; - this.Code = codePosInfo.Code; - } - } - -} diff --git a/src/ScriptEngine/Compiler/CompilerFrontend.cs b/src/ScriptEngine/Compiler/CompilerFrontend.cs new file mode 100644 index 000000000..2125cc694 --- /dev/null +++ b/src/ScriptEngine/Compiler/CompilerFrontend.cs @@ -0,0 +1,113 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Machine.Debugger; + +namespace ScriptEngine.Compiler +{ + /// + /// Часть компилятора, независимая от рантайма (нативного или стекового) + /// Запускает компиляцию модуля, строит AST и делегирует построение кода в бэкенд компилятора под конкретный рантайм. + /// + public class CompilerFrontend : CompilerFrontendBase + { + private readonly IDependencyResolver _dependencyResolver; + private readonly PredefinedInterfaceResolver _interfaceResolver; + private readonly OneScriptCoreOptions _options; + private readonly CompilerBackendSelector _backendSelector; + + public CompilerFrontend( + PreprocessorHandlers handlers, + IErrorSink errorSink, + IServiceContainer services, + IDependencyResolver dependencyResolver, + PredefinedInterfaceResolver interfaceResolver, + OneScriptCoreOptions options) : base(handlers, errorSink, services) + { + _dependencyResolver = dependencyResolver; + _interfaceResolver = interfaceResolver; + _options = options; + + _backendSelector = services.Resolve(); + + _backendSelector.NativeBackendInitializer = NativeInitializer; + _backendSelector.StackBackendInitializer = StackInitializer; + } + + private ICompilerBackend StackInitializer() + { + var actualBehavior = _options.ExplicitImports; + if (_options.ExplicitImports == ExplicitImportsBehavior.Development) + { + var dbg = Services.TryResolve(); + if (dbg == null || dbg.IsEnabled == false) + { + actualBehavior = ExplicitImportsBehavior.Disabled; + } + else + { + actualBehavior = ExplicitImportsBehavior.Warn; + } + } + + var backend = new DefaultCompilerBackend(ErrorSink, actualBehavior); + SetDefaultOptions(backend); + backend.DependencyResolver = _dependencyResolver; + + return backend; + } + + private ICompilerBackend NativeInitializer() + { + var backend = new NativeCompilerBackend(ErrorSink, Services); + SetDefaultOptions(backend); + + return backend; + } + + private void SetDefaultOptions(ICompilerBackend backend) + { + backend.GenerateCodeStat = GenerateCodeStat; + backend.GenerateDebugCode = GenerateDebugCode; + } + + protected override IExecutableModule CompileInternal(SymbolTable symbols, ModuleNode parsedModule, Type classType, IBslProcess process) + { + var backend = _backendSelector.Select(parsedModule); + backend.Symbols = symbols; + + var module = backend.Compile(parsedModule, classType, process); + + _interfaceResolver.Resolve(module); + + return module; + } + + protected override IExecutableModule CompileExpressionInternal(SymbolTable symbols, ModuleNode parsedModule) + { + var backend = _backendSelector.Select(parsedModule); + backend.Symbols = symbols; + return backend.Compile(parsedModule, typeof(UserScriptContextInstance), ForbiddenBslProcess.Instance); + } + + protected override IExecutableModule CompileBatchInternal(SymbolTable symbols, ModuleNode parsedModule) + { + var backend = _backendSelector.Select(parsedModule); + backend.Symbols = symbols; + return backend.Compile(parsedModule, typeof(UserScriptContextInstance), ForbiddenBslProcess.Instance); + } + } +} diff --git a/src/ScriptEngine/Compiler/DefaultCompilerBackend.cs b/src/ScriptEngine/Compiler/DefaultCompilerBackend.cs new file mode 100644 index 000000000..b56db0805 --- /dev/null +++ b/src/ScriptEngine/Compiler/DefaultCompilerBackend.cs @@ -0,0 +1,52 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace ScriptEngine.Compiler +{ + public class DefaultCompilerBackend : ICompilerBackend + { + private readonly StackMachineCodeGenerator _codeGen; + + public DefaultCompilerBackend(IErrorSink errorSink, ExplicitImportsBehavior importsOption) + { + _codeGen = new StackMachineCodeGenerator(errorSink, importsOption); + } + + public IDependencyResolver DependencyResolver { get; set; } + + public bool GenerateDebugCode { get; set; } + + public bool GenerateCodeStat { get; set; } + + public SymbolTable Symbols { get; set; } + + public IExecutableModule Compile(ModuleNode parsedModule, Type classType, IBslProcess process) + { + _codeGen.ProduceExtraCode = GetCodeFlags(); + _codeGen.DependencyResolver = DependencyResolver; + return _codeGen.CreateModule(parsedModule, parsedModule.Source, Symbols, process); + } + + private CodeGenerationFlags GetCodeFlags() + { + CodeGenerationFlags cs = CodeGenerationFlags.Always; + if (GenerateDebugCode) + cs |= CodeGenerationFlags.DebugCode; + + if (GenerateCodeStat) + cs |= CodeGenerationFlags.CodeStatistics; + + return cs; + } + } +} diff --git a/src/ScriptEngine/Compiler/DirectiveMultiResolver.cs b/src/ScriptEngine/Compiler/DirectiveMultiResolver.cs deleted file mode 100644 index 9925fe8f0..000000000 --- a/src/ScriptEngine/Compiler/DirectiveMultiResolver.cs +++ /dev/null @@ -1,51 +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.Collections.Generic; -using ScriptEngine.Environment; - -namespace ScriptEngine.Compiler -{ - internal class DirectiveMultiResolver : List, IDirectiveResolver - { - - public ICodeSource Source - { - get - { - foreach (var resolver in this) - { - var result = resolver.Source; - if (result != null) - { - return result; - } - } - - return null; - } - set - { - foreach (var resolver in this) - { - resolver.Source = value; - } - } - } - - public bool Resolve(string directive, string value, bool codeEntered) - { - foreach (var resolver in this) - { - if (resolver.Resolve(directive, value, codeEntered)) - return true; - } - return false; - } - } -} - diff --git a/src/ScriptEngine/Compiler/EvalCompiler.cs b/src/ScriptEngine/Compiler/EvalCompiler.cs new file mode 100644 index 000000000..7f30f1202 --- /dev/null +++ b/src/ScriptEngine/Compiler/EvalCompiler.cs @@ -0,0 +1,53 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace ScriptEngine.Compiler +{ + /// + /// Компилятор вычислимых выражений + /// + public class EvalCompiler : CompilerFrontendBase + { + private readonly DefaultCompilerBackend _backend; + + public EvalCompiler( + IErrorSink errorSink, + IServiceContainer services) : base(new PreprocessorHandlers(), errorSink, services) + { + _backend = new DefaultCompilerBackend(errorSink, ExplicitImportsBehavior.Disabled); + } + + protected override IExecutableModule CompileInternal(SymbolTable symbols, ModuleNode parsedModule, Type classType, IBslProcess process) + { + throw new NotSupportedException(); + } + + protected override IExecutableModule CompileExpressionInternal(SymbolTable symbols, ModuleNode parsedModule) + { + _backend.Symbols = symbols; + _backend.GenerateDebugCode = false; + _backend.GenerateCodeStat = false; + return _backend.Compile(parsedModule, default, ForbiddenBslProcess.Instance); + } + + protected override IExecutableModule CompileBatchInternal(SymbolTable symbols, ModuleNode parsedModule) + { + _backend.Symbols = symbols; + _backend.GenerateDebugCode = false; + _backend.GenerateCodeStat = false; + return _backend.Compile(parsedModule, default, ForbiddenBslProcess.Instance); + } + } +} diff --git a/src/ScriptEngine/Compiler/ICompilerContext.cs b/src/ScriptEngine/Compiler/ICompilerContext.cs deleted file mode 100644 index 4fe5755e2..000000000 --- a/src/ScriptEngine/Compiler/ICompilerContext.cs +++ /dev/null @@ -1,28 +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 ScriptEngine.Machine; - -namespace ScriptEngine.Compiler -{ - interface ICompilerContext - { - SymbolBinding DefineMethod(ScriptEngine.Machine.MethodInfo method); - SymbolBinding DefineProperty(string name, string alias = null); - SymbolBinding DefineVariable(string name, string alias = null); - SymbolBinding GetMethod(string name); - SymbolScope GetScope(int scopeIndex); - VariableBinding GetVariable(string name); - bool TryGetVariable(string name, out VariableBinding binding); - bool TryGetMethod(string name, out SymbolBinding binding); - SymbolScope Peek(); - SymbolScope PopScope(); - void PushScope(SymbolScope scope); - int ScopeIndex(SymbolScope scope); - int TopIndex(); - } -} diff --git a/src/ScriptEngine/Compiler/IDirectiveResolver.cs b/src/ScriptEngine/Compiler/IDirectiveResolver.cs index f07a69fd3..380a9edcb 100644 --- a/src/ScriptEngine/Compiler/IDirectiveResolver.cs +++ b/src/ScriptEngine/Compiler/IDirectiveResolver.cs @@ -5,10 +5,12 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Environment; +using System; +using OneScript.Language.Sources; namespace ScriptEngine.Compiler { + [Obsolete] public interface IDirectiveResolver { ICodeSource Source { get; set; } diff --git a/src/ScriptEngine/Compiler/ModuleCompilerContext.cs b/src/ScriptEngine/Compiler/ModuleCompilerContext.cs deleted file mode 100644 index b68921f1e..000000000 --- a/src/ScriptEngine/Compiler/ModuleCompilerContext.cs +++ /dev/null @@ -1,164 +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 ScriptEngine.Machine; - -namespace ScriptEngine.Compiler -{ - class ModuleCompilerContext : ICompilerContext - { - private readonly CompilerContext _outerCtx; - private readonly CompilerContext _moduleCtx; - private int OUTER_CTX_SIZE; - private int _localScopesCount = 0; - - public ModuleCompilerContext(CompilerContext outerContext) - { - _outerCtx = outerContext; - _moduleCtx = new CompilerContext(); - Update(); - } - - #region ICompilerContext Members - - public SymbolBinding DefineMethod(MethodInfo method) - { - var sb = _moduleCtx.DefineMethod(method); - ShiftIndex(ref sb); - - return sb; - } - - public SymbolBinding DefineProperty(string name, string alias = null) - { - var sb = _moduleCtx.DefineProperty(name, alias); - ShiftIndex(ref sb); - - return sb; - } - - public SymbolBinding DefineVariable(string name, string alias = null) - { - var sb = _moduleCtx.DefineVariable(name, alias); - ShiftIndex(ref sb); - - return sb; - } - - public SymbolBinding GetMethod(string name) - { - if(!_moduleCtx.TryGetMethod(name, out var sb)) - return _outerCtx.GetMethod(name); - - ShiftIndex(ref sb); - return sb; - } - - public bool TryGetMethod(string name, out SymbolBinding binding) - { - if (!_moduleCtx.TryGetMethod(name, out binding)) - return _outerCtx.TryGetMethod(name, out binding); - - ShiftIndex(ref binding); - return true; - } - - public SymbolScope GetScope(int scopeIndex) - { - if (scopeIndex < OUTER_CTX_SIZE) - { - return _outerCtx.GetScope(scopeIndex); - } - else - { - return _moduleCtx.GetScope(scopeIndex - OUTER_CTX_SIZE); - } - } - - public VariableBinding GetVariable(string name) - { - if (!_moduleCtx.TryGetVariable(name, out var vb)) - return _outerCtx.GetVariable(name); - - ShiftIndex(ref vb.binding); - return vb; - - } - - public bool TryGetVariable(string name, out VariableBinding binding) - { - if (!_moduleCtx.TryGetVariable(name, out binding)) - return _outerCtx.TryGetVariable(name, out binding); - - ShiftIndex(ref binding.binding); - return true; - - } - - public SymbolScope Peek() - { - if (_localScopesCount > 0) - return _moduleCtx.Peek(); - else - return _outerCtx.Peek(); - } - - public SymbolScope PopScope() - { - var scope = _moduleCtx.PopScope(); - _localScopesCount--; - - return scope; - - } - - public void PushScope(SymbolScope scope) - { - _moduleCtx.PushScope(scope); - _localScopesCount++; - } - - public int ScopeIndex(SymbolScope scope) - { - int idx = _moduleCtx.ScopeIndex(scope); - if (idx >= 0) - { - return idx + OUTER_CTX_SIZE; - } - else - { - idx = _outerCtx.ScopeIndex(scope); - } - - return idx; - } - - public int TopIndex() - { - if (_localScopesCount > 0) - { - return _moduleCtx.TopIndex() + OUTER_CTX_SIZE; - } - else - { - return _outerCtx.TopIndex(); - } - } - - #endregion - - private void ShiftIndex(ref SymbolBinding symbolBinding) - { - symbolBinding.ContextIndex += OUTER_CTX_SIZE; - } - - internal void Update() - { - OUTER_CTX_SIZE = _outerCtx.TopIndex() + 1; - } - } -} diff --git a/src/ScriptEngine/Compiler/ModuleDumpWriter.cs b/src/ScriptEngine/Compiler/ModuleDumpWriter.cs new file mode 100644 index 000000000..198b13e62 --- /dev/null +++ b/src/ScriptEngine/Compiler/ModuleDumpWriter.cs @@ -0,0 +1,229 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Native.Compiler; +using OneScript.Native.Runtime; +using OneScript.Sources; +using ScriptEngine.Machine; + +namespace ScriptEngine.Compiler +{ + public class ModuleDumpWriter + { + readonly ICompilerFrontend _compiler; + private readonly IBslProcess _process; + + public ModuleDumpWriter(ICompilerFrontend compilerService, IBslProcess process) + { + _compiler = compilerService; + _process = process; + } + + public void Write(TextWriter output, SourceCode source) + { + var compilerResult = _compiler.Compile(source, _process); + if (compilerResult is StackRuntimeModule stackModule) + { + var module = stackModule; + WriteImage(output, module); + } + else + { + WriteNativeModule(output, (DynamicModule)compilerResult); + } + + } + + private void WriteNativeModule(TextWriter output, DynamicModule compilerResult) + { + foreach (var attribute in compilerResult.ModuleAttributes) + { + output.WriteLine($"[{attribute.Name}]"); + } + output.WriteLine("class DynamicModule"); + output.WriteLine("{"); + output.WriteLine(".fields"); + foreach (var field in compilerResult.Fields) + { + PrintField(output, field); + } + + output.WriteLine(".properties"); + foreach (var prop in compilerResult.Properties) + { + PrintProperty(output, prop); + } + + output.WriteLine(".methods"); + foreach (var method in compilerResult.Methods) + { + PrintMethod(output, (BslNativeMethodInfo)method); + } + + output.WriteLine("}"); + } + + private void PrintProperty(TextWriter output, BslPropertyInfo prop) + { + output.WriteLine(); + foreach (var attribute in prop.GetAnnotations()) + { + output.WriteLine($"[{attribute.Name}]"); + } + output.WriteLine($"{prop.PropertyType} {prop.Name}"); + } + + private void PrintMethod(TextWriter output, BslNativeMethodInfo method) + { + output.WriteLine(); + foreach (var attribute in method.GetAnnotations()) + { + output.WriteLine($"[{attribute.Name}]"); + } + + var propertyInfo = typeof(Expression).GetProperty("DebugView", BindingFlags.Instance | BindingFlags.NonPublic); + output.WriteLine(propertyInfo.GetValue(method.Implementation) as string); + } + + private void PrintField(TextWriter output, BslFieldInfo field) + { + output.WriteLine(); + foreach (var attribute in field.GetAnnotations()) + { + output.WriteLine($"[{attribute.Name}]"); + } + output.WriteLine($"{field.FieldType} {field.Name}"); + } + + public void Write(TextWriter output, StackRuntimeModule module) + { + WriteImage(output, module); + } + + private void WriteImage(TextWriter output, StackRuntimeModule module) + { + output.WriteLine(".variableFrame:"); + module.Fields + .Cast() + .OrderBy(x=>x.DispatchId) + .ForEach(x=>output.WriteLine($"{x.DispatchId}:{x.Name}{(x.IsPublic ? " export" : "")}")); + + output.WriteLine(".constants"); + for (int i = 0; i < module.Constants.Count; i++) + { + var item = module.Constants[i]; + output.WriteLine( + $"{i,-3}:type: {item.SystemType.Alias}, val: {item}"); + } + output.WriteLine(".identifiers"); + for (int i = 0; i < module.Identifiers.Count; i++) + { + var item = module.Identifiers[i]; + output.WriteLine( + $"{i,-3}: {item}"); + } + output.WriteLine(".code"); + for (int i = 0; i < module.Code.Count; i++) + { + var item = module.Code[i]; + output.WriteLine( + $"{i,-3}:({Enum.GetName(typeof(OperationCode), item.Code),-10}{item.Argument,3})"); + } + output.WriteLine(".procedures"); + foreach (var item in module.Methods.Cast()) + { + WriteMethodDefinition(output, item.GetRuntimeMethod()); + } + output.WriteLine(".varmap"); + WriteSymbolMap(output, module.VariableRefs); + output.WriteLine(".procmap"); + WriteSymbolMap(output, module.MethodRefs); + output.WriteLine(".moduleEntry"); + output.WriteLine(module.EntryMethodIndex.ToString()); + } + + private void WriteSymbolMap(TextWriter output, IList map) + { + for (int i = 0; i < map.Count; i++) + { + var item = map[i]; + var targetInfo = item.Kind switch + { + ScopeBindingKind.Static => item.Target?.GetType().Name ?? "null", + ScopeBindingKind.ThisScope => "this", + ScopeBindingKind.FrameScope => $"frame[{item.ScopeIndex}]", + _ => "unknown" + }; + output.Write(string.Format("{0,-3}:({1},{2},{3})\n", i, item.Kind, targetInfo, item.MemberNumber)); + } + } + + private void WriteAnnotationsList(TextWriter output, AnnotationDefinition[] annotations) + { + output.WriteLine(".annotations ["); + foreach (var annotation in annotations) + { + output.Write(" {0}", annotation.Name); + if (annotation.ParamCount != 0) + { + var delimiter = ": "; + foreach (var parameter in annotation.Parameters) + { + output.Write(delimiter); + output.Write(parameter); + delimiter = ", "; + } + } + output.WriteLine(""); + } + output.WriteLine("]"); + } + + private void WriteMethodDefinition(TextWriter output, MachineMethod item) + { + output.Write(item.Signature.IsFunction ? "Func " : "Proc "); + output.Write( + $"{item.Signature.Name} entryPoint: {item.EntryPoint}, frameSize:{item.LocalVariables.Length}\n"); + if (item.Signature.AnnotationsCount != 0) + { + WriteAnnotationsList(output, item.Signature.Annotations); + } + output.Write(".args {0}\n", item.Signature.ArgCount); + if (item.Signature.Params != null) + { + for (int i = 0; i < item.Signature.Params.Length; i++) + { + output.Write("{0,-3}: ByVal={1}", i, item.Signature.Params[i].IsByValue); + if (item.Signature.Params[i].HasDefaultValue) + { + output.Write(" defValue: {0}\n", item.Signature.Params[i].DefaultValueIndex); + } + else + { + output.WriteLine(); + } + } + } + output.WriteLine(".variables ["); + for (int i = 0; i < item.LocalVariables.Length; i++) + { + output.WriteLine($" {i}:{item.LocalVariables[i]}"); + } + output.WriteLine("]"); + } + } +} diff --git a/src/ScriptEngine/Compiler/ModuleWriter.cs b/src/ScriptEngine/Compiler/ModuleWriter.cs deleted file mode 100644 index 4d1863996..000000000 --- a/src/ScriptEngine/Compiler/ModuleWriter.cs +++ /dev/null @@ -1,149 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.IO; -using ScriptEngine.Environment; -using ScriptEngine.Machine; - -namespace ScriptEngine.Compiler -{ - public class ModuleWriter - { - readonly CompilerService _compiler; - - public ModuleWriter(CompilerService compilerService) - { - _compiler = compilerService; - } - - public void Write(TextWriter output, ICodeSource source) - { - var module = _compiler.Compile(source); - - WriteImage(output, module); - - } - - public void Write(TextWriter output, ModuleImage module) - { - WriteImage(output, module); - } - - private void WriteImage(TextWriter output, ModuleImage module) - { - output.WriteLine(".loadAt: {0}", module.LoadAddress); - output.WriteLine(".variableFrame:"); - module.Variables.ForEach(x=>output.WriteLine(" " + x)); - - output.WriteLine(".constants"); - for (int i = 0; i < module.Constants.Count; i++) - { - var item = module.Constants[i]; - output.WriteLine( - String.Format("{0,-3}:type: {1}, val: {2}", - i, - Enum.GetName(typeof(DataType), item.Type), - item.Presentation)); - } - output.WriteLine(".code"); - for (int i = 0; i < module.Code.Count; i++) - { - var item = module.Code[i]; - output.WriteLine( - String.Format("{0,-3}:({1,-10}{2,3})", - i, - Enum.GetName(typeof(OperationCode), item.Code), - item.Argument)); - } - output.WriteLine(".procedures"); - foreach (var item in module.Methods) - { - WriteMethodDefinition(output, item); - } - output.WriteLine(".varmap"); - WriteSymbolMap(output, module.VariableRefs); - output.WriteLine(".procmap"); - WriteSymbolMap(output, module.MethodRefs); - output.WriteLine(".moduleEntry"); - output.WriteLine(module.EntryMethodIndex.ToString()); - output.WriteLine(".exports"); - WriteExports(output, module.ExportedProperties); - WriteExports(output, module.ExportedMethods); - } - - private void WriteSymbolMap(TextWriter output, IList map) - { - for (int i = 0; i < map.Count; i++) - { - var item = map[i]; - output.Write(string.Format("{0,-3}:({1},{2})\n", i, item.ContextIndex, item.CodeIndex)); - } - } - - private void WriteAnnotationsList(TextWriter output, AnnotationDefinition[] annotations) - { - output.WriteLine(".annotations ["); - foreach (var annotation in annotations) - { - output.Write(" {0}", annotation.Name); - if (annotation.ParamCount != 0) - { - var delimiter = ": "; - foreach (var parameter in annotation.Parameters) - { - output.Write(delimiter); - output.Write(parameter); - delimiter = ", "; - } - } - output.WriteLine(""); - } - output.WriteLine("]"); - } - - private void WriteMethodDefinition(TextWriter output, MethodDescriptor item) - { - output.Write(item.Signature.IsFunction ? "Func " : "Proc "); - output.Write(string.Format("{0} entryPoint: {1}, frameSize:{2}\n", - item.Signature.Name, - item.EntryPoint, - item.Variables.Count)); - if (item.Signature.AnnotationsCount != 0) - { - WriteAnnotationsList(output, item.Signature.Annotations); - } - output.Write(".args {0}\n", item.Signature.ArgCount); - if (item.Signature.Params != null) - { - for (int i = 0; i < item.Signature.Params.Length; i++) - { - output.Write("{0,-3}: ByVal={1}", i, item.Signature.Params[i].IsByValue); - if (item.Signature.Params[i].HasDefaultValue) - { - output.Write(" defValue: {0}\n", item.Signature.Params[i].DefaultValueIndex); - } - else - { - output.WriteLine(); - } - } - } - output.WriteLine(".variables ["); - item.Variables.ForEach(x=>output.WriteLine(" " + x)); - output.WriteLine("]"); - } - - private void WriteExports(TextWriter output, IList exports) - { - for (int i = 0; i < exports.Count; i++) - { - output.WriteLine(String.Format("{0}:{1,-3}", exports[i].SymbolicName, exports[i].Index)); - } - } - } -} diff --git a/src/ScriptEngine/Compiler/NativeCompilerBackend.cs b/src/ScriptEngine/Compiler/NativeCompilerBackend.cs new file mode 100644 index 000000000..2399851cd --- /dev/null +++ b/src/ScriptEngine/Compiler/NativeCompilerBackend.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Native.Compiler; + +namespace ScriptEngine.Compiler +{ + public class NativeCompilerBackend : ICompilerBackend + { + private readonly ModuleCompiler _codeGen; + + public NativeCompilerBackend(IErrorSink errorSink, IServiceContainer services) + { + _codeGen = new ModuleCompiler(errorSink, services, services.Resolve()); + } + + public bool GenerateDebugCode { get; set; } + + public bool GenerateCodeStat { get; set; } + + public SymbolTable Symbols { get; set; } + + public IExecutableModule Compile(ModuleNode parsedModule, Type classType, IBslProcess process) + { + return _codeGen.Compile(parsedModule.Source, parsedModule, Symbols, process); + } + } +} diff --git a/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs new file mode 100644 index 000000000..3edae66b7 --- /dev/null +++ b/src/ScriptEngine/Compiler/StackMachineCodeGenerator.cs @@ -0,0 +1,1445 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.Extensions; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Localization; +using OneScript.Sources; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace ScriptEngine.Compiler +{ + public partial class StackMachineCodeGenerator : BslSyntaxWalker + { + private readonly IErrorSink _errorSink; + private readonly ExplicitImportsBehavior _importsOption; + private readonly StackRuntimeModule _module; + private SourceCode _sourceCode; + private SymbolTable _ctx; + private List _constMap = new List(); + private HashSet _localImports = new HashSet(); + + private readonly List _forwardedMethods = new List(); + private readonly Stack _nestedLoops = new Stack(); + + private IBslProcess _compilerProcess; + + private HashSet _reportedOldProperties = new HashSet(); + + public StackMachineCodeGenerator(IErrorSink errorSink, ExplicitImportsBehavior importsOption) + { + _errorSink = errorSink; + _importsOption = importsOption; + _module = new StackRuntimeModule(typeof(IRuntimeContextInstance)); + } + + public CodeGenerationFlags ProduceExtraCode { get; set; } + + public IDependencyResolver DependencyResolver { get; set; } + + public StackRuntimeModule CreateModule(ModuleNode moduleNode, SourceCode source, SymbolTable context, + IBslProcess process) + { + if (moduleNode.Kind != NodeKind.Module) + { + throw new ArgumentException($"Node must be a Module node"); + } + + _ctx = context; + _sourceCode = source; + _compilerProcess = process; + + return CreateImageInternal(moduleNode); + } + + private StackRuntimeModule CreateImageInternal(ModuleNode moduleNode) + { + VisitModule(moduleNode); + CheckForwardedDeclarations(); + + _module.Source = _sourceCode; + + return _module; + } + + protected override void VisitModuleAnnotation(AnnotationNode node) + { + if (node.Kind == NodeKind.Import) + HandleImportClause(node); + + var classAnnotation = new BslAnnotationAttribute(node.Name); + classAnnotation.SetParameters(GetAnnotationParameters(node)); + _module.ModuleAttributes.Add(classAnnotation); + } + + private void HandleImportClause(AnnotationNode node) + { + if(DependencyResolver == default) + return; + + var libName = node.Children + .Cast() + .First() + .Value + .Content; + + try + { + var resolvedLib = DependencyResolver.Resolve(_sourceCode, libName, _compilerProcess); + if (resolvedLib != null && _importsOption != ExplicitImportsBehavior.Disabled) + { + _localImports.Add(resolvedLib.Id); + } + } + catch (DependencyResolveException e) + { + var error = new CodeError + { + Description = e.Message, + Position = MakeCodePosition(node.Location), + ErrorId = nameof(CompilerException) + }; + AddError(error); + } + } + + private void CheckForwardedDeclarations() + { + if (_forwardedMethods.Count != 0) + { + foreach (var item in _forwardedMethods) + { + if (!_ctx.TryFindMethodBinding(item.identifier, out var methN)) + { + AddError(LocalizedErrors.SymbolNotFound(item.identifier), item.location); + continue; + } + + var scope = _ctx.GetScope(methN.ScopeNumber); + + var methInfo = scope.Methods[methN.MemberNumber].Method; + Debug.Assert(StringComparer.OrdinalIgnoreCase.Compare(methInfo.Name, item.identifier) == 0); + if (item.asFunction && !methInfo.IsFunction()) + { + AddError( + CompilerErrors.UseProcAsFunction(), + item.location); + continue; + } + + CheckFactArguments(methInfo.GetParameters(), item.factArguments); + CorrectCommandArgument(item.commandIndex, GetMethodRefNumber(methN)); + } + } + } + + protected override void VisitModuleVariable(VariableDefinitionNode varNode) + { + var symbolicName = varNode.Name; + var annotations = GetAnnotations(varNode).ToList(); + + var varsCount = _ctx.GetScope(_ctx.ScopeCount - 1).Variables.Count; + var fieldBuilder = BslFieldBuilder.Create() + .Name(symbolicName) + .SetAnnotations(annotations) + .SetDispatchingIndex(varsCount) + .DeclaringType(_module.ClassType); + + if (varNode.IsExported) + { + fieldBuilder.IsExported(true); + var propertyView = BslPropertyBuilder.Create() + .Name(symbolicName) + .IsExported(true) + .DeclaringType(_module.ClassType) + .SetAnnotations(annotations) + .SetDispatchingIndex(varsCount); + + _module.Properties.Add(propertyView.Build()); + } + + var field = fieldBuilder.Build(); + _module.Fields.Add(field); + var binding = _ctx.DefineVariable(field.ToSymbol()); + var descriptor = _ctx.GetBinding(binding.ScopeNumber); + var imageBinding = new ModuleSymbolBinding + { + Target = descriptor.Target, + MemberNumber = binding.MemberNumber, + Kind = descriptor.Kind, + ScopeIndex = descriptor.ScopeIndex + }; + _module.VariableRefs.Add(imageBinding); + } + + protected override void VisitModuleBody(BslSyntaxNode child) + { + if (child.Children.Count == 0) + return; + + var entry = _module.Code.Count; + var localCtx = new SymbolScope(); + _ctx.PushScope(localCtx, ScopeBindingDescriptor.ThisScope()); + + try + { + VisitCodeBlock(child.Children[0]); + } + catch + { + _ctx.PopScope(); + throw; + } + + _ctx.PopScope(); + + var topIdx = _ctx.ScopeCount - 1; + + if (entry != _module.Code.Count) + { + var methodInfo = NewMethod() + .Name(IExecutableModule.BODY_METHOD_NAME) + .DeclaringType(_module.ClassType) + .SetDispatchingIndex(_module.Methods.Count) + .Build(); + + methodInfo.SetRuntimeParameters(entry, GetVariableNames(localCtx)); + + var entryRefNumber = _module.MethodRefs.Count; + var descriptor = _ctx.GetBinding(topIdx); + var bodyBinding = new ModuleSymbolBinding + { + Target = descriptor.Target, + MemberNumber = _module.Methods.Count, + Kind = descriptor.Kind, + ScopeIndex = descriptor.ScopeIndex + }; + + _module.Methods.Add(methodInfo); + _module.MethodRefs.Add(bodyBinding); + _module.EntryMethodIndex = entryRefNumber; + } + } + + private static string[] GetVariableNames(SymbolScope localCtx) + { + return localCtx.Variables.Select(v => v.Name).ToArray(); + + } + + protected override void VisitGotoNode(NonTerminalNode node) + { + throw new NotSupportedException(); + } + + protected override void VisitLabelNode(LabelNode node) + { + throw new NotSupportedException(); + } + + protected override void VisitMethod(MethodNode methodNode) + { + if (methodNode.IsAsync) + { + AddError(LocalizedErrors.AsyncMethodsNotSupported(), methodNode.Location); + } + var signature = methodNode.Signature; + var methodBuilder = NewMethod(); + + methodBuilder.Name(signature.MethodName) + .ReturnType(signature.IsFunction ? typeof(BslValue) : typeof(void)) + .IsExported(signature.IsExported) + .SetDispatchingIndex(_ctx.GetScope(_ctx.ScopeCount - 1).Methods.Count) + .SetAnnotations(GetAnnotationAttributes(methodNode)); + + var methodCtx = new SymbolScope(); + + foreach (var paramNode in signature.GetParameters()) + { + var parameter = methodBuilder.NewParameter() + .Name(paramNode.Name) + .ByValue(paramNode.IsByValue) + .SetAnnotations(GetAnnotationAttributes(paramNode)); + + if (paramNode.HasDefaultValue) + { + var constDef = CreateConstDefinition(paramNode.DefaultValue); + var defValueIndex = GetConstNumber(constDef); + + parameter + .DefaultValue(_module.Constants[defValueIndex]) + .CompileTimeBslConstant(defValueIndex); + } + + methodCtx.DefineVariable(new LocalVariableSymbol(paramNode.Name)); + } + + _ctx.PushScope(methodCtx, ScopeBindingDescriptor.ThisScope()); + var entryPoint = _module.Code.Count; + try + { + VisitMethodBody(methodNode); + } + finally + { + _ctx.PopScope(); + } + + var methodInfo = methodBuilder.Build(); + methodInfo.SetRuntimeParameters(entryPoint, GetVariableNames(methodCtx)); + + SymbolBinding binding; + try + { + binding = _ctx.DefineMethod(methodInfo.ToSymbol()); + } + catch (CompilerException) + { + AddError(LocalizedErrors.DuplicateMethodDefinition(signature.MethodName), signature.Location); + binding = default; + } + var descriptor = _ctx.GetBinding(binding.ScopeNumber); + var imageBinding = new ModuleSymbolBinding + { + Target = descriptor.Target, + MemberNumber = binding.MemberNumber, + Kind = descriptor.Kind, + ScopeIndex = descriptor.ScopeIndex + }; + _module.MethodRefs.Add(imageBinding); + _module.Methods.Add(methodInfo); + } + + protected override void VisitMethodBody(MethodNode methodNode) + { + 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; + + VisitBlockEnd(methodNode.EndLocation); // debug last line num + + AddCommand(OperationCode.Return); + + // заменим Return на Jmp <сюда> + for (var i = codeStart; i < codeEnd; i++) + { + if (_module.Code[i].Code == OperationCode.Return) + { + _module.Code[i] = new Command() { Code = OperationCode.Jmp, Argument = codeEnd }; + } + } + } + + protected override void VisitMethodVariable(MethodNode method, VariableDefinitionNode variableDefinition) + { + _ctx.DefineVariable(new LocalVariableSymbol(variableDefinition.Name)); + } + + protected override void VisitStatement(BslSyntaxNode statement) + { + if (statement.Kind != NodeKind.TryExcept + && statement.Kind != NodeKind.WhileLoop) + AddLineNumber(statement.Location.LineNumber); + + base.VisitStatement(statement); + } + + protected override void VisitWhileNode(WhileLoopNode node) + { + var conditionIndex = AddLineNumber(node.Location.LineNumber); + var loopRecord = NestedLoopInfo.New(); + loopRecord.startPoint = conditionIndex; + _nestedLoops.Push(loopRecord); + 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); + CorrectBreakStatements(_nestedLoops.Pop(), endLoop); + } + + protected override void VisitForEachLoopNode(ForEachLoopNode node) + { + VisitIteratorExpression(node.CollectionExpression); + AddCommand(OperationCode.PushIterator); + + var loopBegin = AddLineNumber(node.Location.LineNumber); + AddCommand(OperationCode.IteratorNext); + var condition = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + + VisitIteratorLoopVariable(node.IteratorVariable); + + var loopRecord = NestedLoopInfo.New(); + loopRecord.startPoint = loopBegin; + _nestedLoops.Push(loopRecord); + + VisitIteratorLoopBody(node.LoopBody); + VisitBlockEnd(node.EndLocation); + + AddCommand(OperationCode.Jmp, loopBegin); + + var indexLoopEnd = AddCommand(OperationCode.StopIterator); + CorrectCommandArgument(condition, indexLoopEnd); + CorrectBreakStatements(_nestedLoops.Pop(), indexLoopEnd); + } + + protected override void VisitForLoopNode(ForLoopNode node) + { + var initializer = node.InitializationClause; + var counter = (TerminalNode) initializer.Children[0]; + VisitExpression(initializer.Children[1]); + VisitVariableWrite(counter); + + VisitExpression(node.UpperLimitExpression); + + AddCommand(OperationCode.MakeRawValue); + AddCommand(OperationCode.PushTmp); + + var jmpIndex = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); + var indexLoopBegin = AddLineNumber(node.Location.LineNumber); + + // increment + VisitVariableRead(counter); + AddCommand(OperationCode.Inc); + VisitVariableWrite(counter); + + var counterIndex = PushVariable(counter); + CorrectCommandArgument(jmpIndex, counterIndex); + var conditionIndex = AddCommand(OperationCode.JmpCounter, DUMMY_ADDRESS); + + var loopRecord = NestedLoopInfo.New(); + loopRecord.startPoint = indexLoopBegin; + _nestedLoops.Push(loopRecord); + + VisitCodeBlock(node.LoopBody); + VisitBlockEnd(node.EndLocation); + + // jmp to start + AddCommand(OperationCode.Jmp, indexLoopBegin); + + var indexLoopEnd = AddCommand(OperationCode.PopTmp, 1); + CorrectCommandArgument(conditionIndex, indexLoopEnd); + CorrectBreakStatements(_nestedLoops.Pop(), indexLoopEnd); + } + + protected override void VisitBreakNode(LineMarkerNode node) + { + ExitTryBlocks(); + var loopInfo = _nestedLoops.Peek(); + var idx = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); + loopInfo.breakStatements.Add(idx); + } + + protected override void VisitContinueNode(LineMarkerNode node) + { + ExitTryBlocks(); + var loopInfo = _nestedLoops.Peek(); + AddCommand(OperationCode.Jmp, loopInfo.startPoint); + } + + protected override void VisitReturnNode(BslSyntaxNode node) + { + if (node.Children.Count != 0) + { + VisitExpression(node.Children[0]); + AddCommand(OperationCode.MakeRawValue); + } + + AddCommand(OperationCode.Return); + } + + protected override void VisitRaiseNode(BslSyntaxNode node) + { + int arg = -1; + if (node.Children.Count != 0) + { + VisitExpression(node.Children[0]); + arg = 0; + } + + AddCommand(OperationCode.RaiseException, arg); + } + + protected override void VisitIfNode(ConditionNode node) + { + var exitIndices = new List(); + VisitIfExpression(node.Expression); + + var jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + + VisitIfTruePart(node.TruePart); + exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); + + bool hasAlternativeBranches = false; + + foreach (var alternative in node.GetAlternatives()) + { + CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); + if (alternative is ConditionNode elif) + { + AddLineNumber(alternative.Location.LineNumber); + VisitIfExpression(elif.Expression); + jumpFalseIndex = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + VisitIfTruePart(elif.TruePart); + exitIndices.Add(AddCommand(OperationCode.Jmp, DUMMY_ADDRESS)); + } + else + { + hasAlternativeBranches = true; + CorrectCommandArgument(jumpFalseIndex, _module.Code.Count); + AddLineNumber(alternative.Location.LineNumber, CodeGenerationFlags.CodeStatistics); + VisitCodeBlock(alternative); + } + } + + int exitIndex = AddLineNumber(node.EndLocation.LineNumber); + + if (!hasAlternativeBranches) + { + CorrectCommandArgument(jumpFalseIndex, exitIndex); + } + + foreach (var indexToWrite in exitIndices) + { + CorrectCommandArgument(indexToWrite, exitIndex); + } + } + + protected override void VisitBlockEnd(in CodeRange endLocation) + { + AddLineNumber( + endLocation.LineNumber, + CodeGenerationFlags.CodeStatistics | CodeGenerationFlags.DebugCode); + } + + private void CorrectBreakStatements(NestedLoopInfo nestedLoopInfo, int endLoopIndex) + { + foreach (var breakCmdIndex in nestedLoopInfo.breakStatements) + { + CorrectCommandArgument(breakCmdIndex, endLoopIndex); + } + } + + protected override void VisitAssignment(BslSyntaxNode assignment) + { + var left = assignment.Children[0]; + var right = assignment.Children[1]; + if (left is TerminalNode term) + { + VisitExpression(right); + VisitVariableWrite(term); + } + else + { + VisitReferenceRead(left); + VisitExpression(right); + AddCommand(OperationCode.AssignRef); + } + } + + protected override void VisitResolveProperty(TerminalNode operand) + { + ResolveProperty(operand.GetIdentifier()); + } + + protected override void VisitVariableRead(TerminalNode node) + { + PushVariable(node); + } + + protected override void VisitVariableWrite(TerminalNode node) + { + var identifier = node.GetIdentifier(); + var hasVar = _ctx.FindVariable(identifier, out var varBinding); + if (hasVar) + { + if (varBinding.ScopeNumber == _ctx.ScopeCount - 1) + { + AddCommand(OperationCode.LoadLoc, varBinding.MemberNumber); + } + else + { + var num = GetVariableRefNumber(varBinding); + AddCommand(OperationCode.LoadVar, num); + } + } + else + { + // can create variable + var binding = _ctx.DefineVariable(new LocalVariableSymbol(identifier)); + AddCommand(OperationCode.LoadLoc, binding.MemberNumber); + } + } + + protected override void VisitObjectFunctionCall(BslSyntaxNode node) + { + ResolveObjectMethod(node, true); + } + + protected override void VisitObjectProcedureCall(BslSyntaxNode node) + { + ResolveObjectMethod(node, false); + } + + protected override void VisitIndexExpression(BslSyntaxNode operand) + { + base.VisitIndexExpression(operand); + AddCommand(OperationCode.PushIndexed); + } + + protected override void VisitGlobalFunctionCall(CallNode node) + { + if (LanguageDef.IsBuiltInFunction(node.Identifier.Lexem.Token)) + { + BuiltInFunctionCall(node); + } + else + { + GlobalCall(node, true); + } + } + + private void BuiltInFunctionCall(CallNode node) + { + var funcId = BuiltInFunctionCode(node.Identifier.Lexem.Token); + + var argsPassed = node.ArgumentList.Children.Count; + PushArgumentsList(node.ArgumentList); + if (funcId == OperationCode.Min || funcId == OperationCode.Max) + { + if (argsPassed == 0) + AddError(CompilerErrors.TooFewArgumentsPassed(), node.ArgumentList.Location); + } + else + { + var parameters = BuiltinFunctions.ParametersInfo(funcId); + FullCheckFactArguments(parameters, node.ArgumentList); + } + + AddCommand(funcId, argsPassed); + } + + private void CheckFactArguments(ParameterInfo[] parameters, BslSyntaxNode argList) + { + var argsPassed = argList.Children.Count; + if (argsPassed > parameters.Length) + { + AddError(CompilerErrors.TooManyArgumentsPassed(), argList.Location); + return; + } + + if (parameters.Skip(argsPassed).Any(param => !param.HasDefaultValue)) + { + AddError(CompilerErrors.TooFewArgumentsPassed(), argList.Location); + } + } + + private void CheckFactArguments(BslMethodInfo method, BslSyntaxNode argList) + { + var argsToCheck = method is ContextMethodInfo { InjectsProcess: true } ? + method.GetParameters().Skip(1).ToArray() : + method.GetParameters(); + + CheckFactArguments(argsToCheck, argList); + } + + private void FullCheckFactArguments(ParameterInfo[] parameters, BslSyntaxNode argList) + { + var argsPassed = argList.Children.Count; + if (argsPassed > parameters.Length) + { + AddError(CompilerErrors.TooManyArgumentsPassed(), argList.Location); + return; + } + + int i = 0; + for (; i < argsPassed; i++) + { + if (!parameters[i].HasDefaultValue && argList.Children[i].Children.Count == 0) + { + AddError(CompilerErrors.MissedArgument(), argList.Location); + } + } + for (; i < parameters.Length; i++) + { + if (!parameters[i].HasDefaultValue) + { + AddError(CompilerErrors.TooFewArgumentsPassed(), argList.Location); + return; + } + } + } + + protected override void VisitGlobalProcedureCall(CallNode node) + { + if (LanguageDef.IsBuiltInFunction(node.Identifier.Lexem.Token)) + { + AddError(LocalizedErrors.UseBuiltInFunctionAsProcedure(), node.Location); + return; + } + GlobalCall(node, false); + } + + private void ResolveObjectMethod(BslSyntaxNode callNode, bool asFunction) + { + var name = callNode.Children[0]; + var args = callNode.Children[1]; + + Debug.Assert(name != null); + Debug.Assert(args != null); + + PushCallArguments(args); + + int lastIdentifierIndex = GetIdentNumber(name.GetIdentifier()); + + if (asFunction) + AddCommand(OperationCode.ResolveMethodFunc, lastIdentifierIndex); + else + AddCommand(OperationCode.ResolveMethodProc, lastIdentifierIndex); + } + + private void ResolveProperty(string identifier) + { + AddCommand(OperationCode.ResolveProp, GetIdentNumber(identifier)); + } + + private int PushVariable(TerminalNode node) + { + var identifier = node.GetIdentifier(); + if (!_ctx.FindVariable(identifier, out var varNum)) + { + AddError(LocalizedErrors.SymbolNotFound(identifier), node.Location); + return -1; + } + + var symbol = _ctx.GetVariable(varNum); + + if (symbol is IPropertySymbol propSymbol) + { + if (_importsOption != ExplicitImportsBehavior.Disabled && symbol is IPackageSymbol pkgSymbol) + { + CheckExplicitImport(node, pkgSymbol, symbol); + } + + if (propSymbol.Property is ISupportsDeprecation { IsDeprecated: true } && + !_reportedOldProperties.Contains(propSymbol.Property)) + { + var message = BilingualString.Localize( + $"Использование устаревшего свойства \"{identifier}\" в файле \"{_sourceCode.Location}\" ({node.Location})", + $"Usage of deprecated property \"{identifier}\" in \"{_sourceCode.Location}\" ({node.Location})" + ); + _reportedOldProperties.Add(propSymbol.Property); + SystemLogger.Write(message); + } + + return PushPropertyReference(varNum); + } + else + { + return PushSimpleVariable(varNum); + } + } + + private void CheckExplicitImport(TerminalNode node, IPackageSymbol pkgSymbol, IVariableSymbol symbol) + { + var packageInfo = pkgSymbol.GetPackageInfo(); + var id = packageInfo.Id; + + // Если модуль принадлежит той же библиотеке - не требуем явный импорт + if (_sourceCode.OwnerPackageId != null && id == _sourceCode.OwnerPackageId) + return; + + if (!_localImports.Contains(id)) + { + var error = CompilerErrors.MissedImport(symbol.Name, packageInfo.ShortName); + error.Position = MakeCodePosition(node.Location); + switch (_importsOption) + { + case ExplicitImportsBehavior.Enabled: + AddError(error); + break; + case ExplicitImportsBehavior.Warn: + SystemLogger.Write(error.ToString()); + break; + } + } + } + + private int PushSimpleVariable(SymbolBinding binding) + { + if (binding.ScopeNumber == _ctx.ScopeCount - 1) + { + return AddCommand(OperationCode.PushLoc, binding.MemberNumber); + } + else + { + var idx = GetVariableRefNumber(binding); + return AddCommand(OperationCode.PushVar, idx); + } + } + + private int PushPropertyReference(SymbolBinding binding) + { + var idx = GetVariableRefNumber(binding); + + return AddCommand(OperationCode.PushRef, idx); + } + + private void GlobalCall(CallNode call, bool asFunction) + { + var identifierNode = call.Identifier; + var argList = call.ArgumentList; + + Debug.Assert(identifierNode != null); + Debug.Assert(argList != null); + + var identifier = identifierNode.Lexem.Content; + + var hasMethod = _ctx.TryFindMethodBinding(identifier, out var methBinding); + if (hasMethod) + { + var scope = _ctx.GetScope(methBinding.ScopeNumber); + + var methInfo = scope.Methods[methBinding.MemberNumber].Method; + if (asFunction && !methInfo.IsFunction()) + { + AddError(CompilerErrors.UseProcAsFunction(), identifierNode.Location); + return; + } + + PushCallArguments(argList); + CheckFactArguments(methInfo, argList); + + if (asFunction) + AddCommand(OperationCode.CallFunc, GetMethodRefNumber(methBinding)); + else + AddCommand(OperationCode.CallProc, GetMethodRefNumber(methBinding)); + } + else + { + // can be defined later + var forwarded = new ForwardedMethodDecl + { + identifier = identifier, + asFunction = asFunction, + location = identifierNode.Location, + factArguments = argList + }; + + PushCallArguments(call.ArgumentList); + + var opCode = asFunction ? OperationCode.CallFunc : OperationCode.CallProc; + forwarded.commandIndex = AddCommand(opCode, DUMMY_ADDRESS); + _forwardedMethods.Add(forwarded); + } + } + + private void PushCallArguments(BslSyntaxNode argList) + { + PushArgumentsList(argList); + AddCommand(OperationCode.ArgNum, argList.Children.Count); + } + + private void PushArgumentsList(BslSyntaxNode argList) + { + var arguments = argList.Children; + for (int i = 0; i < arguments.Count; i++) + { + VisitCallArgument(arguments[i]); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void VisitCallArgument(BslSyntaxNode passedArg) + { + if (passedArg.Children.Count != 0) + { + VisitExpression(passedArg.Children[0]); + } + else + { + AddCommand(OperationCode.PushDefaultArg); + } + } + + protected override void VisitTernaryOperation(BslSyntaxNode expression) + { + VisitExpression(expression.Children[0]); + AddCommand(OperationCode.MakeBool); + var addrOfCondition = AddCommand(OperationCode.JmpFalse, DUMMY_ADDRESS); + + VisitExpression(expression.Children[1]); // построили true-part + + var endOfTruePart = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); // уход в конец оператора + + CorrectCommandArgument(addrOfCondition, _module.Code.Count); // отметили, куда переходить по false + VisitExpression(expression.Children[2]); // построили false-part + + CorrectCommandArgument(endOfTruePart, _module.Code.Count); + } + + protected override void VisitUnaryOperation(UnaryOperationNode unaryOperationNode) + { + var child = unaryOperationNode.Children[0]; + VisitExpression(child); + var opCode = TokenToOperationCode(unaryOperationNode.Operation); + AddCommand(opCode); + } + + protected override void VisitBinaryOperation(BinaryOperationNode binaryOperationNode) + { + if (LanguageDef.IsLogicalBinaryOperator(binaryOperationNode.Operation)) + { + VisitExpression(binaryOperationNode.Children[0]); + var logicalCmdIndex = AddCommand(TokenToOperationCode(binaryOperationNode.Operation)); + VisitExpression(binaryOperationNode.Children[1]); + AddCommand(OperationCode.MakeBool); + CorrectCommandArgument(logicalCmdIndex, _module.Code.Count - 1); + } + else + { + VisitExpression(binaryOperationNode.Children[0]); + VisitExpression(binaryOperationNode.Children[1]); + AddCommand(TokenToOperationCode(binaryOperationNode.Operation)); + } + } + + protected override void VisitTryExceptNode(TryExceptNode node) + { + var beginTryIndex = AddCommand(OperationCode.BeginTry, DUMMY_ADDRESS); + VisitTryBlock(node.TryBlock); + var jmpIndex = AddCommand(OperationCode.Jmp, DUMMY_ADDRESS); + + var beginHandler = AddLineNumber( + node.ExceptBlock.Location.LineNumber, + CodeGenerationFlags.CodeStatistics); + + CorrectCommandArgument(beginTryIndex, beginHandler); + + VisitExceptBlock(node.ExceptBlock); + + var endIndex = AddLineNumber(node.EndLocation.LineNumber, + CodeGenerationFlags.CodeStatistics | CodeGenerationFlags.DebugCode); + + AddCommand(OperationCode.EndTry, beginHandler); + CorrectCommandArgument(jmpIndex, endIndex); + } + + protected override void VisitTryBlock(CodeBatchNode node) + { + PushTryNesting(); + base.VisitTryBlock(node); + PopTryNesting(); + } + + protected override void VisitExecuteStatement(BslSyntaxNode node) + { + base.VisitExecuteStatement(node); + AddCommand(OperationCode.Execute); + } + + protected override void VisitHandlerOperation(BslSyntaxNode node) + { + var eventNameNode = node.Children[0]; + + // выражение источника события + VisitExpression(eventNameNode.Children[0]); + + if (eventNameNode.Kind == NodeKind.DereferenceOperation) + { + var eventName = eventNameNode.Children[1].AsTerminal().Lexem; + eventName.Type = LexemType.StringLiteral; + VisitConstant(eventName); + } + else + { + Debug.Assert(eventNameNode.Kind == NodeKind.IndexAccess); + VisitExpression(eventNameNode.Children[1]); + } + + var handlerNode = node.Children[1]; + int commandArg; + if (handlerNode.Kind == NodeKind.Identifier) + { + var terminal = handlerNode.AsTerminal(); + var identifier = terminal.GetIdentifier(); + if (_ctx.TryFindMethodBinding(identifier, out _)) + { + var lex = terminal.Lexem; + lex.Type = LexemType.StringLiteral; + VisitConstant(lex); + } + else + { + AddError(LocalizedErrors.SymbolNotFound(identifier)); + } + commandArg = 1; + } + else if (handlerNode.Kind == NodeKind.DereferenceOperation) + { + var eventName = handlerNode.Children[1].AsTerminal().Lexem; + eventName.Type = LexemType.StringLiteral; + VisitExpression(handlerNode.Children[0]); + VisitConstant(eventName); + commandArg = 0; + } + else + { + Debug.Assert(handlerNode.Kind == NodeKind.IndexAccess); + VisitExpression(handlerNode.Children[0]); + VisitExpression(handlerNode.Children[1]); + commandArg = 0; + } + + AddCommand(node.Kind == NodeKind.AddHandler ? OperationCode.AddHandler : OperationCode.RemoveHandler, commandArg); + } + + protected override void VisitNewObjectCreation(NewObjectNode node) + { + if (node.IsDynamic) + { + MakeNewObjectDynamic(node); + } + else + { + MakeNewObjectStatic(node); + } + } + + private void MakeNewObjectDynamic(NewObjectNode node) + { + VisitExpression(node.TypeNameNode); + + var argsPassed = node.ConstructorArguments.Children.Count; + if (argsPassed == 1) + { + VisitCallArgument(node.ConstructorArguments.Children[0]); ; + } + else if (argsPassed > 1) + { + AddError(CompilerErrors.TooManyArgumentsPassed(), node.ConstructorArguments.Location); + } + + AddCommand(OperationCode.NewFunc, argsPassed); + } + + private void MakeNewObjectStatic(NewObjectNode node) + { + if (node.ConstructorArguments != default) + { + PushCallArguments(node.ConstructorArguments); + } + else + { + AddCommand(OperationCode.ArgNum, 0); + } + + var idNum = GetIdentNumber(node.TypeNameNode.GetIdentifier()); + AddCommand(OperationCode.NewInstance, idNum); + } + + private void ExitTryBlocks() + { + var tryBlocks = _nestedLoops.Peek().tryNesting; + if (tryBlocks > 0) + AddCommand(OperationCode.ExitTry, tryBlocks); + } + + private void PushTryNesting() + { + if (_nestedLoops.Count != 0) + { + _nestedLoops.Peek().tryNesting++; + } + } + + private void PopTryNesting() + { + if (_nestedLoops.Count != 0) + { + _nestedLoops.Peek().tryNesting--; + } + } + + private void CorrectCommandArgument(int index, int newArgument) + { + var cmd = _module.Code[index]; + cmd.Argument = newArgument; + _module.Code[index] = cmd; + } + + private static OperationCode TokenToOperationCode(Token stackOp) + { + OperationCode opCode; + switch (stackOp) + { + case Token.Equal: + opCode = OperationCode.Equals; + break; + case Token.NotEqual: + opCode = OperationCode.NotEqual; + break; + case Token.Plus: + opCode = OperationCode.Add; + break; + case Token.Minus: + opCode = OperationCode.Sub; + break; + case Token.Multiply: + opCode = OperationCode.Mul; + break; + case Token.Division: + opCode = OperationCode.Div; + break; + case Token.Modulo: + opCode = OperationCode.Mod; + break; + case Token.UnaryPlus: + opCode = OperationCode.Number; + break; + case Token.UnaryMinus: + opCode = OperationCode.Neg; + break; + case Token.And: + opCode = OperationCode.And; + break; + case Token.Or: + opCode = OperationCode.Or; + break; + case Token.Not: + opCode = OperationCode.Not; + break; + case Token.LessThan: + opCode = OperationCode.Less; + break; + case Token.LessOrEqual: + opCode = OperationCode.LessOrEqual; + break; + case Token.MoreThan: + opCode = OperationCode.Greater; + break; + case Token.MoreOrEqual: + opCode = OperationCode.GreaterOrEqual; + break; + case Token.AddHandler: + opCode = OperationCode.AddHandler; + break; + case Token.RemoveHandler: + opCode = OperationCode.RemoveHandler; + break; + default: + throw new NotSupportedException(); + } + return opCode; + } + + protected override void VisitConstant(TerminalNode node) + { + VisitConstant(node.Lexem); + } + + private void VisitConstant(in Lexem constant) + { + if (constant.Type == LexemType.BooleanLiteral) + { + AddCommand(OperationCode.PushBool, (bool)(BslValue)BslBooleanValue.Parse(constant.Content) ? 1 : 0); + } + else if (constant.Type == LexemType.NumberLiteral && ParseIntegerString(constant.Content, out var integer)) + { + AddCommand(OperationCode.PushInt, integer); + } + else if (constant.Type == LexemType.UndefinedLiteral) + { + AddCommand(OperationCode.PushUndef); + } + else if (constant.Type == LexemType.NullLiteral) + { + AddCommand(OperationCode.PushNull); + } + else + { + var cDef = CreateConstDefinition(constant); + var num = GetConstNumber(cDef); + AddCommand(OperationCode.PushConst, num); + } + } + + private bool ParseIntegerString(string constantContent, out int integer) + { + return int.TryParse(constantContent, NumberStyles.None, NumberFormatInfo.InvariantInfo, out integer); + } + + private IEnumerable GetAnnotationAttributes(AnnotatableNode node) + { + var mappedAnnotations = new List(); + foreach (var annotation in node.Annotations) + { + var anno = new BslAnnotationAttribute(annotation.Name); + anno.SetParameters(GetAnnotationParameters(annotation)); + mappedAnnotations.Add(anno); + } + + return mappedAnnotations; + } + + private IEnumerable GetAnnotationParameters(AnnotationNode node) + { + return node.Children.Cast() + .Select(MakeAnnotationParameter) + .ToList(); + } + + private BslAnnotationParameter MakeAnnotationParameter(AnnotationParameterNode param) + { + var runtimeValue = MakeAnnotationParameterValueConstant(param); + return new BslAnnotationParameter(param.Name, runtimeValue); + } + + private BslPrimitiveValue MakeAnnotationParameterValueConstant(AnnotationParameterNode param) + { + if (param.AnnotationNode != null) + { + var runtimeValue = new BslAnnotationValue(param.AnnotationNode.Name); + foreach (var child in param.AnnotationNode.Children) + { + var parameter = (AnnotationParameterNode)child; + var parameterValue = MakeAnnotationParameterValueConstant(parameter); + runtimeValue.Parameters.Add(new BslAnnotationParameter(parameter.Name, parameterValue)); + } + return runtimeValue; + } + else + if (param.Value.Type != LexemType.NotALexem) + { + var constDef = CreateConstDefinition(param.Value); + var constNumber = GetConstNumber(constDef); + var runtimeValue = _module.Constants[constNumber]; + return runtimeValue; + } + else + { + return null; + } + } + + private IEnumerable GetAnnotations(AnnotatableNode parent) + { + return parent.Annotations.Select(a => + new BslAnnotationAttribute( + a.Name, + GetAnnotationParameters(a))); + } + + private static BslMethodBuilder NewMethod() => + new BslMethodInfoFactory(() => new MachineMethodInfo()) + .NewMethod(); + + private static ConstDefinition CreateConstDefinition(in Lexem lex) + { + DataType constType; + switch (lex.Type) + { + case LexemType.BooleanLiteral: + constType = DataType.Boolean; + break; + case LexemType.DateLiteral: + constType = DataType.Date; + break; + case LexemType.NumberLiteral: + constType = DataType.Number; + break; + case LexemType.StringLiteral: + constType = DataType.String; + break; + case LexemType.NullLiteral: + constType = DataType.Null; + break; + case LexemType.UndefinedLiteral: + constType = DataType.Undefined; + break; + default: + throw new ArgumentException($"Can't create constant for literal from {lex.ToString()}"); + } + + ConstDefinition cDef = new ConstDefinition() + { + Type = constType, + Presentation = lex.Content + }; + return cDef; + } + + private int GetConstNumber(in ConstDefinition cDef) + { + var idx = _constMap.IndexOf(cDef); + if (idx < 0) + { + idx = _constMap.Count; + _constMap.Add(cDef); + _module.Constants.Add((BslPrimitiveValue)ValueFactory.Parse(cDef.Presentation, cDef.Type)); + } + return idx; + } + + private int GetIdentNumber(string ident) + { + var idx = _module.Identifiers.IndexOf(ident); + if (idx < 0) + { + idx = _module.Identifiers.Count; + _module.Identifiers.Add(ident); + } + return idx; + } + + + private int GetMethodRefNumber(in SymbolBinding methodBinding) + { + var descriptor = _ctx.GetBinding(methodBinding.ScopeNumber); + var imageBinding = new ModuleSymbolBinding + { + Target = descriptor.Target, + MemberNumber = methodBinding.MemberNumber, + Kind = descriptor.Kind, + ScopeIndex = descriptor.ScopeIndex + }; + + var idx = _module.MethodRefs.IndexOf(imageBinding); + if (idx < 0) + { + idx = _module.MethodRefs.Count; + _module.MethodRefs.Add(imageBinding); + } + return idx; + } + + private int GetVariableRefNumber(in SymbolBinding binding) + { + var descriptor = _ctx.GetBinding(binding.ScopeNumber); + var imageBinding = new ModuleSymbolBinding + { + Target = descriptor.Target, + MemberNumber = binding.MemberNumber, + Kind = descriptor.Kind, + ScopeIndex = descriptor.ScopeIndex + }; + + var idx = _module.VariableRefs.IndexOf(imageBinding); + if (idx < 0) + { + idx = _module.VariableRefs.Count; + _module.VariableRefs.Add(imageBinding); + } + + return idx; + } + + private void AddError(CodeError error, in CodeRange location) + { + error.Position = MakeCodePosition(location); + _errorSink.AddError(error); + } + + private void AddError(CodeError error) + { + _errorSink.AddError(error); + } + + private ErrorPositionInfo MakeCodePosition(CodeRange range) + { + return new ErrorPositionInfo + { + Code = _sourceCode.GetCodeLine(range.LineNumber), + LineNumber = range.LineNumber, + ColumnNumber = range.ColumnNumber, + ModuleName = _sourceCode.Name + }; + } + + private int AddCommand(OperationCode code, int arg = 0) + { + var addr = _module.Code.Count; + _module.Code.Add(new Command() { Code = code, Argument = arg }); + return addr; + } + + private int AddLineNumber(int linenum, CodeGenerationFlags emitConditions = CodeGenerationFlags.Always) + { + var addr = _module.Code.Count; + bool emit = emitConditions == CodeGenerationFlags.Always || ExtraCodeConditionsMet(emitConditions); + if (emit) + { + _module.Code.Add(new Command() { Code = OperationCode.LineNum, Argument = linenum }); + } + return addr; + } + + private bool ExtraCodeConditionsMet(CodeGenerationFlags emitConditions) + { + return (((int)ProduceExtraCode) & (int)emitConditions) != 0; + } + + #region Static cache + + private static readonly Dictionary _tokenToOpCode; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static OperationCode BuiltInFunctionCode(Token token) + { + return _tokenToOpCode[token]; + } + + static StackMachineCodeGenerator() + { + _tokenToOpCode = new Dictionary(); + + var tokens = LanguageDef.BuiltInFunctions(); + var opCodes = BuiltinFunctions.GetOperationCodes(); + + Debug.Assert(tokens.Length == opCodes.Length); + for (int i = 0; i < tokens.Length; i++) + { + _tokenToOpCode.Add(tokens[i], opCodes[i]); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Compiler/SymbolScope.cs b/src/ScriptEngine/Compiler/SymbolScope.cs deleted file mode 100644 index 1fa81403c..000000000 --- a/src/ScriptEngine/Compiler/SymbolScope.cs +++ /dev/null @@ -1,169 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Machine; - -namespace ScriptEngine.Compiler -{ - class SymbolScope - { - readonly Dictionary _variableNumbers = new Dictionary(StringComparer.OrdinalIgnoreCase); - - readonly List _variables = new List(); - - readonly Dictionary _methodsNumbers = new Dictionary(StringComparer.OrdinalIgnoreCase); - - readonly List _methods = new List(); - - public MethodInfo GetMethod(string name) - { - var num = GetMethodNumber(name); - return _methods[num]; - } - - public MethodInfo GetMethod(int number) - { - return _methods[number]; - } - - public int GetVariableNumber(string name) - { - int varNumber; - if(_variableNumbers.TryGetValue(name, out varNumber)) - { - return varNumber; - } - else - { - throw new SymbolNotFoundException(name); - } - } - - public VariableInfo GetVariable(int number) - { - return _variables[number]; - } - - public int GetMethodNumber(string name) - { - try - { - return _methodsNumbers[name]; - } - catch (KeyNotFoundException) - { - throw new SymbolNotFoundException(name); - } - } - - public bool IsVarDefined(string name) - { - return _variableNumbers.ContainsKey(name); - } - - public bool IsMethodDefined(string name) - { - return _methodsNumbers.ContainsKey(name); - } - - public int DefineVariable(string name, string alias = null) - { - return DefineVariable(name, alias, SymbolType.Variable); - } - - public int DefineVariable(string name, SymbolType symbolType) - { - return DefineVariable(name, null, symbolType); - } - - public int DefineVariable(string name, string alias, SymbolType symbolType) - { - if (IsVarDefined(name)) - { - throw new InvalidOperationException($"Symbol already defined in the scope ({name})"); - } - if (!string.IsNullOrEmpty(alias) && IsVarDefined(alias)) - { - throw new InvalidOperationException($"Symbol already defined in the scope ({alias})"); - } - - var newIdx = _variables.Count; - _variableNumbers[name] = newIdx; - if (!string.IsNullOrEmpty(alias)) - { - _variableNumbers[alias] = newIdx; - } - - _variables.Add(new VariableInfo() - { - Index = newIdx, - Identifier = name, - Alias = alias, - Type = symbolType - }); - - return newIdx; - } - - public int DefineMethod(MethodInfo method) - { - if (!IsMethodDefined(method.Name)) - { - int newIdx = _methods.Count; - _methods.Add(method); - _methodsNumbers[method.Name] = newIdx; - - if (method.Alias != null) - _methodsNumbers[method.Alias] = newIdx; - - return newIdx; - } - else - { - throw new InvalidOperationException("Symbol already defined in the scope"); - } - } - - public string GetVariableName(int number) - { - return _variableNumbers.First(x => x.Value == number).Key; - } - - public int VariableCount - { - get - { - return _variables.Count; - } - } - - public int MethodCount - { - get - { - return _methods.Count; - } - } - - public bool IsDynamicScope - { - get; - set; - } - } - - class SymbolNotFoundException : CompilerException - { - public SymbolNotFoundException(string symbol) : base(string.Format("Неизвестный символ: {0}", symbol)) - { - - } - } - -} diff --git a/src/ScriptEngine/CompilerService.cs b/src/ScriptEngine/CompilerService.cs deleted file mode 100644 index d5d8d0b54..000000000 --- a/src/ScriptEngine/CompilerService.cs +++ /dev/null @@ -1,157 +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.Collections.Generic; -using OneScript.Language; -using OneScript.Language.LexicalAnalysis; -using ScriptEngine.Compiler; -using ScriptEngine.Environment; -using ScriptEngine.Machine; - -namespace ScriptEngine -{ - public class CompilerService - { - SymbolScope _scope; - - private readonly ModuleCompilerContext _currentContext; - private readonly List _predefinedVariables = new List(); - private readonly List _preprocessorVariables = new List(); - - - internal CompilerService(CompilerContext outerContext) - { - _currentContext = new ModuleCompilerContext(outerContext); - } - - public CodeGenerationFlags ProduceExtraCode { get; set; } - - public int DefineVariable(string name, string alias, SymbolType type) - { - RegisterScopeIfNeeded(); - - try - { - int varIdx; - if (type == SymbolType.Variable) - varIdx = _currentContext.DefineVariable(name, alias).CodeIndex; - else - varIdx = _currentContext.DefineProperty(name, alias).CodeIndex; - - _predefinedVariables.Add(varIdx); - return varIdx; - } - catch - { - _currentContext.PopScope(); - _scope = null; - throw; - } - } - - public int DefineMethod(MethodInfo methodInfo) - { - RegisterScopeIfNeeded(); - - return _currentContext.DefineMethod(methodInfo).CodeIndex; - } - - public void DefinePreprocessorValue(string name) - { - _preprocessorVariables.Add(name); - } - - private void RegisterScopeIfNeeded() - { - if (_scope == null) - { - _scope = new SymbolScope(); - _currentContext.PushScope(_scope); - } - } - - public ModuleImage Compile(ICodeSource source) - { - try - { - return CompileInternal(source); - } - finally - { - _currentContext.PopScope(); - _scope = null; - } - } - - private ModuleImage CompileInternal(ICodeSource source) - { - RegisterScopeIfNeeded(); - - var parser = new PreprocessingLexer(); - foreach (var variable in _preprocessorVariables) - { - parser.Define(variable); - } - parser.UnknownDirective += (sender, args) => - { - // все неизвестные директивы возвращать назад и обрабатывать старым кодом - args.IsHandled = true; - }; - parser.Code = source.Code; - - var compiler = new Compiler.Compiler(); - compiler.ProduceExtraCode = ProduceExtraCode; - compiler.DirectiveHandler = ResolveDirective; - - if (DirectiveResolver != null) - { - DirectiveResolver.Source = source; - } - - ModuleImage compiledImage; - try - { - compiledImage = compiler.Compile(parser, _currentContext); - } - catch (ScriptException e) - { - if(e.ModuleName == null) - e.ModuleName = source.SourceDescription; - - throw; - } - finally - { - if (DirectiveResolver != null) - { - DirectiveResolver.Source = null; - } - } - - var mi = new ModuleInformation(); - mi.CodeIndexer = parser.Iterator; - // пока у модулей нет собственных имен, будет совпадать с источником модуля - mi.ModuleName = source.SourceDescription; - mi.Origin = source.SourceDescription; - compiledImage.ModuleInfo = mi; - - return compiledImage; - } - - private bool ResolveDirective(string directive, string value, bool codeEntered) - { - if (DirectiveResolver != null) - { - return DirectiveResolver.Resolve(directive, value, codeEntered); - } - else - return false; - } - - public IDirectiveResolver DirectiveResolver { get; set; } - } -} diff --git a/src/ScriptEngine/Environment/CodeSources.cs b/src/ScriptEngine/Environment/CodeSources.cs deleted file mode 100644 index 16bc2ac40..000000000 --- a/src/ScriptEngine/Environment/CodeSources.cs +++ /dev/null @@ -1,108 +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.Text; -using System.IO; - -namespace ScriptEngine.Environment -{ - class StringBasedSource : ICodeSource - { - readonly string _src; - - public StringBasedSource(string src) - { - _src = src; - } - - #region ICodeSource Members - - string ICodeSource.Code - { - get - { - return _src; - } - } - - string ICodeSource.SourceDescription => $"" ; - - #endregion - - } - - class FileBasedSource : ICodeSource - { - readonly string _path; - string _code; - - readonly Encoding _noBomEncoding; - - public FileBasedSource(string path, Encoding defaultEncoding) - { - _path = System.IO.Path.GetFullPath(path); - _noBomEncoding = defaultEncoding; - } - - private string GetCodeString() - { - if (_code == null) - { - using (var fStream = new FileStream(_path, FileMode.Open, FileAccess.Read)) - { - var buf = new byte[2]; - fStream.Read(buf, 0, 2); - Encoding enc = null; - bool skipShebang = false; - if (IsLinuxScript(buf)) - { - enc = Encoding.UTF8; // скрипты с shebang считать в формате UTF-8 - skipShebang = true; - } - else - { - fStream.Position = 0; - enc = FileOpener.AssumeEncoding(fStream, _noBomEncoding); - } - - using (var reader = new StreamReader(fStream, enc)) - { - if (skipShebang) - reader.ReadLine(); - - _code = reader.ReadToEnd(); - } - - } - } - - return _code; - } - - private static bool IsLinuxScript(byte[] buf) - { - return buf[0] == '#' && buf[1] == '!'; - } - - #region ICodeSource Members - - string ICodeSource.Code - { - get - { - return GetCodeString(); - } - } - - string ICodeSource.SourceDescription - { - get { return _path[0].ToString().ToUpperInvariant() + _path.Substring(1); } - } - - #endregion - } -} diff --git a/src/ScriptEngine/Environment/ICodeSource.cs b/src/ScriptEngine/Environment/ICodeSource.cs deleted file mode 100644 index 9736b83e7..000000000 --- a/src/ScriptEngine/Environment/ICodeSource.cs +++ /dev/null @@ -1,16 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Environment -{ - public interface ICodeSource - { - string Code { get; } - string SourceDescription { get; } - } - -} diff --git a/src/ScriptEngine/Environment/ICodeSourceFactory.cs b/src/ScriptEngine/Environment/ICodeSourceFactory.cs deleted file mode 100644 index 1f4d0a5a2..000000000 --- a/src/ScriptEngine/Environment/ICodeSourceFactory.cs +++ /dev/null @@ -1,19 +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.Text; - -namespace ScriptEngine.Environment -{ - public interface ICodeSourceFactory - { - ICodeSource FromFile(string path); - ICodeSource FromString(string code); - - Encoding ReaderEncoding { get; set; } - } -} diff --git a/src/ScriptEngine/Environment/ScriptSourceFactory.cs b/src/ScriptEngine/Environment/ScriptSourceFactory.cs deleted file mode 100644 index 4f583ce53..000000000 --- a/src/ScriptEngine/Environment/ScriptSourceFactory.cs +++ /dev/null @@ -1,31 +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.Text; - -namespace ScriptEngine.Environment -{ - class ScriptSourceFactory : ICodeSourceFactory - { - public ScriptSourceFactory() - { - ReaderEncoding = Encoding.UTF8; - } - - public ICodeSource FromString(string code) - { - return new StringBasedSource(code); - } - - public ICodeSource FromFile(string path) - { - return new FileBasedSource(path, ReaderEncoding); - } - - public Encoding ReaderEncoding { get; set; } - } -} diff --git a/src/ScriptEngine/ExplicitImportsBehavior.cs b/src/ScriptEngine/ExplicitImportsBehavior.cs new file mode 100644 index 000000000..e2041643b --- /dev/null +++ b/src/ScriptEngine/ExplicitImportsBehavior.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine +{ + /// + /// Определяет поведение компилятора при использовании библиотек без явного импорта через директиву #Использовать. + /// + public enum ExplicitImportsBehavior + { + /// + /// Обращение к символу из внешнего пакета без явного импорта вызывает ошибку компиляции. + /// + Enabled, + + /// + /// Обращение к символу из внешнего пакета без явного импорта записывает предупреждение в лог, но компиляция продолжается. + /// + Warn, + + /// + /// В режиме разработки (при наличии включенного отладчика) обращение к символу без явного импорта записывает предупреждение в лог. + /// Без отладчика проверка отключена. + /// + Development, + + /// + /// Проверка явных импортов отключена. Все библиотеки доступны без использования директивы #Использовать (режим совместимости). + /// + Disabled + } +} \ No newline at end of file diff --git a/src/ScriptEngine/ExternalLibraryDef.cs b/src/ScriptEngine/ExternalLibraryDef.cs deleted file mode 100644 index f414c9cae..000000000 --- a/src/ScriptEngine/ExternalLibraryDef.cs +++ /dev/null @@ -1,84 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; - -namespace ScriptEngine -{ - public class ExternalLibraryDef - { - public ExternalLibraryDef(string name) - { - LibraryName = name; - } - - public string LibraryName { get; } - - public IList Classes { get; } = new List(); - public IList Modules { get; } = new List(); - - public UserAddedScript AddClass(string identifier, string filePath, ModuleImage image = null) - { - var item = new UserAddedScript - { - Type = UserAddedScriptType.Class, - Image = image, - Symbol = identifier, - FilePath = filePath, - LibraryName = LibraryName - }; - - Classes.Add(item); - - return item; - } - - public UserAddedScript AddModule(string identifier, string filePath, ModuleImage image = null) - { - var item = new UserAddedScript - { - Type = UserAddedScriptType.Module, - Image = image, - Symbol = identifier, - FilePath = filePath, - LibraryName = LibraryName - }; - - Modules.Add(item); - - return item; - } - } - - [Serializable] - public class UserAddedScript - { - public UserAddedScriptType Type; - public ModuleImage Image; - public string Symbol; - public int InjectOrder; - - [NonSerialized] - public string FilePath; - - [NonSerialized] - public string LibraryName; - - public string ModuleName() - { - return $"{LibraryName}.{Type}.{Symbol}"; - } - } - - [Serializable] - public enum UserAddedScriptType - { - Module, - Class - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Hosting/ConfigurationProviders.cs b/src/ScriptEngine/Hosting/ConfigurationProviders.cs new file mode 100644 index 000000000..9dae5ec81 --- /dev/null +++ b/src/ScriptEngine/Hosting/ConfigurationProviders.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ScriptEngine.Hosting +{ + public class ConfigurationProviders + { + private readonly List _providers = new List(); + + public void Add(IConfigProvider source) + { + _providers.Add(source); + } + + public KeyValueConfig CreateConfig() + { + var cfg = new KeyValueConfig(); + foreach (var provider in _providers) + { + var values = provider.Load(); + if (values != null && values.Count > 0) + { + cfg.Merge((IDictionary)values, provider); + } + } + + return cfg; + } + } +} diff --git a/src/ScriptEngine/Hosting/ConfigurationValue.cs b/src/ScriptEngine/Hosting/ConfigurationValue.cs new file mode 100644 index 000000000..e8765f9a9 --- /dev/null +++ b/src/ScriptEngine/Hosting/ConfigurationValue.cs @@ -0,0 +1,50 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ScriptEngine.Hosting +{ + public class ConfigurationValue + { + public string RawValue { get; } + public IConfigProvider Source { get; } + + public ConfigurationValue(string rawValue, IConfigProvider source) + { + RawValue = rawValue; + Source = source; + } + + /// + /// Разрешает значение как путь относительно источника конфигурации + /// + public string ResolvePath() + { + return string.IsNullOrEmpty(RawValue) ? RawValue : Source.ResolveRelativePath(RawValue.Trim()); + } + + /// + /// Разрешает значение как список путей + /// + public IEnumerable ResolvePathList(char separator = ';') + { + if (string.IsNullOrEmpty(RawValue)) + return Enumerable.Empty(); + + return RawValue.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries) + .Select(path => + { + var trimmed = path.Trim(); + return Source.ResolveRelativePath(trimmed); + }); + } + } +} diff --git a/src/ScriptEngine/Hosting/DefaultEngineBuilder.cs b/src/ScriptEngine/Hosting/DefaultEngineBuilder.cs new file mode 100644 index 000000000..948b83b44 --- /dev/null +++ b/src/ScriptEngine/Hosting/DefaultEngineBuilder.cs @@ -0,0 +1,52 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.DependencyInjection; +using ScriptEngine.Machine; + +namespace ScriptEngine.Hosting +{ + public class DefaultEngineBuilder : IEngineBuilder + { + protected DefaultEngineBuilder() + { + EnvironmentProviders.Add(environment => environment.AddAssembly(GetType().Assembly)); + } + + public static IEngineBuilder Create() + { + var builder = new DefaultEngineBuilder(); + return builder; + } + + public ConfigurationProviders ConfigurationProviders { get; } = new ConfigurationProviders(); + + public EnvironmentProviders EnvironmentProviders { get; } = new EnvironmentProviders(); + + public IServiceDefinitions Services { get; set; } = new TinyIocImplementation(); + + public virtual ScriptingEngine Build() + { + var container = GetContainer(); + + var engine = container.Resolve(); + var env = container.Resolve(); + + EnvironmentProviders.Invoke(env); + + var dependencyResolver = container.TryResolve(); + dependencyResolver?.Initialize(engine); + + return engine; + } + + protected virtual IServiceContainer GetContainer() + { + return Services.CreateContainer(); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs new file mode 100644 index 000000000..37225644d --- /dev/null +++ b/src/ScriptEngine/Hosting/EngineBuilderExtensions.cs @@ -0,0 +1,106 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics.CodeAnalysis; +using OneScript.Compilation; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Debugger; +using ScriptEngine.Machine.Interfaces; + +namespace ScriptEngine.Hosting +{ + public static class EngineBuilderExtensions + { + /// + /// Используется для замены DI системы, например в ASP.NET + /// + /// + /// + /// + public static IEngineBuilder WithServices(this IEngineBuilder b, IServiceDefinitions ioc) + { + b.Services = ioc; + return b; + } + + public static IEngineBuilder SetupEnvironment(this IEngineBuilder b, Action action) + { + b.EnvironmentProviders.Add(action); + return b; + } + + [SuppressMessage("ReSharper", "RedundantTypeArgumentsOfMethod")] + public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder) + { + var services = builder.Services; + + services.Register(sp => sp); + services.RegisterSingleton(); + services.RegisterSingleton(); + services.RegisterSingleton(); + services.RegisterSingleton(sp => sp.Resolve()); + services.RegisterSingleton(); + services.RegisterSingleton(svc => new ThrowingErrorSink(CompilerException.FromCodeError)); + services.RegisterSingleton(); + services.RegisterSingleton(); + services.RegisterSingleton(); + + services.RegisterScoped(); + + services.Register(); + + services.RegisterEnumerable(); + services.RegisterEnumerable(); + services.RegisterEnumerable(); + + services.Register(); + services.EnablePredefinedIterables(); + services.Register(sp => + { + var providers = sp.ResolveEnumerable(); + return new PreprocessorHandlers(providers); + }); + + services.RegisterSingleton(); + services.Register(sp => + { + var holder = sp.Resolve(); + return holder.GetConfig(); + }); + + services.Register(); + + return builder; + } + + public static IEngineBuilder UseImports(this IEngineBuilder b) + { + b.Services.UseImports(); + return b; + } + + public static IEngineBuilder WithDebugger(this IEngineBuilder b, IDebugger debugger) + { + b.Services.RegisterSingleton(debugger); + return b; + } + + public static IEngineBuilder SetupServices(this IEngineBuilder b, Action setup) + { + setup(b.Services); + return b; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Hosting/EngineConfiguration.cs b/src/ScriptEngine/Hosting/EngineConfiguration.cs new file mode 100644 index 000000000..46b80daf9 --- /dev/null +++ b/src/ScriptEngine/Hosting/EngineConfiguration.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Hosting +{ + /// + /// Предназначен для хранения стабильного конфига + /// который не изменяется и не вычитывает провайдеры при каждом обращении к опциям. + /// + public class EngineConfiguration + { + private KeyValueConfig _config; + private readonly ConfigurationProviders _providers; + + private readonly object _refreshLock = new object(); + + public EngineConfiguration(ConfigurationProviders providers) + { + _providers = providers; + _config = _providers.CreateConfig(); + } + + public KeyValueConfig GetConfig() + { + lock (_refreshLock) + { + return _config; + } + } + + public void Reload() + { + lock (_refreshLock) + { + _config = _providers.CreateConfig(); + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Hosting/EnvironmentProviders.cs b/src/ScriptEngine/Hosting/EnvironmentProviders.cs new file mode 100644 index 000000000..c5b2686c4 --- /dev/null +++ b/src/ScriptEngine/Hosting/EnvironmentProviders.cs @@ -0,0 +1,24 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using ScriptEngine.Machine; + +namespace ScriptEngine.Hosting +{ + public class EnvironmentProviders : List> + { + public void Invoke(ExecutionContext env) + { + foreach (var provider in this) + { + provider(env); + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Hosting/IConfigProvider.cs b/src/ScriptEngine/Hosting/IConfigProvider.cs new file mode 100644 index 000000000..1b1271d48 --- /dev/null +++ b/src/ScriptEngine/Hosting/IConfigProvider.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace ScriptEngine.Hosting +{ + public interface IConfigProvider + { + string SourceId { get; } + + IReadOnlyDictionary Load(); + + string ResolveRelativePath(string path); + } +} diff --git a/src/ScriptEngine/Hosting/IEngineBuilder.cs b/src/ScriptEngine/Hosting/IEngineBuilder.cs new file mode 100644 index 000000000..487ad0f61 --- /dev/null +++ b/src/ScriptEngine/Hosting/IEngineBuilder.cs @@ -0,0 +1,22 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.DependencyInjection; + +namespace ScriptEngine.Hosting +{ + public interface IEngineBuilder + { + ConfigurationProviders ConfigurationProviders { get; } + + EnvironmentProviders EnvironmentProviders { get; } + + IServiceDefinitions Services { get; set; } + + ScriptingEngine Build(); + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Hosting/KeyValueConfig.cs b/src/ScriptEngine/Hosting/KeyValueConfig.cs new file mode 100644 index 000000000..909d35fcb --- /dev/null +++ b/src/ScriptEngine/Hosting/KeyValueConfig.cs @@ -0,0 +1,52 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace ScriptEngine.Hosting +{ + public class KeyValueConfig + { + private readonly Dictionary _values = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public void Merge(IDictionary source, IConfigProvider sourceProvider) + { + foreach (var keyValue in source) + { + if (string.IsNullOrWhiteSpace(keyValue.Key)) + throw BadKeyException(keyValue.Key); + + _values[keyValue.Key] = new ConfigurationValue(keyValue.Value, sourceProvider); + } + } + + public ConfigurationValue GetEntry(string key) + { + if (string.IsNullOrWhiteSpace(key)) + throw BadKeyException(key); + + _values.TryGetValue(key, out var value); + return value; + } + + public string this[string key] + { + get + { + var entry = GetEntry(key); + return entry?.RawValue; + } + } + + private static ArgumentException BadKeyException(string key) + { + return new ArgumentException($"wrong config key format: {key}"); + } + + } +} diff --git a/src/ScriptEngine/Hosting/ServiceRegistrationExtensions.cs b/src/ScriptEngine/Hosting/ServiceRegistrationExtensions.cs new file mode 100644 index 000000000..64cb28ba7 --- /dev/null +++ b/src/ScriptEngine/Hosting/ServiceRegistrationExtensions.cs @@ -0,0 +1,70 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Reflection; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Language.SyntaxAnalysis; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Hosting +{ + public static class ServiceRegistrationExtensions + { + public static IEngineBuilder SetupConfiguration(this IEngineBuilder b, Action setup) + { + setup(b.ConfigurationProviders); + b.Services.RegisterSingleton(b.ConfigurationProviders); + return b; + } + + public static ExecutionContext AddAssembly(this ExecutionContext env, Assembly asm, Predicate filter = null) + { + var discoverer = env.Services.Resolve(); + discoverer.DiscoverClasses(asm, filter); + discoverer.DiscoverGlobalContexts(env.GlobalNamespace, asm, filter); + return env; + } + + public static ExecutionContext AddGlobalContext(this ExecutionContext env, IAttachableContext context) + { + env.GlobalNamespace.InjectObject(context); + env.GlobalInstances.RegisterInstance(context); + return env; + } + + public static IServiceDefinitions UseImports(this IServiceDefinitions services) + { + services.RegisterEnumerable(); + services.RegisterSingleton(); + return services; + } + + public static IServiceDefinitions UseImports(this IServiceDefinitions services) + where T : class, IDependencyResolver + { + services.RegisterEnumerable(); + services.RegisterSingleton(); + return services; + } + + public static IServiceDefinitions UseImports(this IServiceDefinitions services, Func factory) + { + services.RegisterEnumerable(); + services.RegisterSingleton(factory); + return services; + } + + public static IServiceDefinitions AddDirectiveHandler(this IServiceDefinitions services) where T : class, IDirectiveHandler + { + services.RegisterEnumerable(); + return services; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Hosting/TinyIoC.cs b/src/ScriptEngine/Hosting/TinyIoC.cs new file mode 100644 index 000000000..b68cfaa3a --- /dev/null +++ b/src/ScriptEngine/Hosting/TinyIoC.cs @@ -0,0 +1,4398 @@ +//OSCRIPT_SKIP_TESTING_MPL2_PREFIX +// +//=============================================================================== +// TinyIoC +// +// An easy to use, hassle free, Inversion of Control Container for small projects +// and beginners alike. +// +// https://github.com/grumpydev/TinyIoC +//=============================================================================== +// Copyright © Steven Robbins. All rights reserved. +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT +// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE. +//=============================================================================== + +#region Preprocessor Directives +// Uncomment this line if you want the container to automatically +// register the TinyMessenger messenger/event aggregator +//#define TINYMESSENGER + +// Uncomment this line if you want to internalize this library +//#define TINYIOC_INTERNAL + +// Uncomment this line if you want to target PCL. +//#define PORTABLE + +// Preprocessor directives for enabling/disabling functionality +// depending on platform features. If the platform has an appropriate +// #DEFINE then these should be set automatically below. +#define EXPRESSIONS + +// Platform supports System.Linq.Expressions +#define COMPILED_EXPRESSIONS // Platform supports compiling expressions +#define APPDOMAIN_GETASSEMBLIES // Platform supports getting all assemblies from the AppDomain object +#define UNBOUND_GENERICS_GETCONSTRUCTORS // Platform supports GetConstructors on unbound generic types +#define GETPARAMETERS_OPEN_GENERICS // Platform supports GetParameters on open generics +#define RESOLVE_OPEN_GENERICS // Platform supports resolving open generics +#define READER_WRITER_LOCK_SLIM // Platform supports ReaderWriterLockSlim +#define SERIALIZABLE // Platform supports SerializableAttribute/SerializationInfo/StreamingContext + +#if PORTABLE +#undef APPDOMAIN_GETASSEMBLIES +#undef COMPILED_EXPRESSIONS +#undef READER_WRITER_LOCK_SLIM +#undef SERIALIZABLE +#endif + +#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 +#undef COMPILED_EXPRESSIONS +#undef READER_WRITER_LOCK_SLIM +#endif + +#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 +#undef APPDOMAIN_GETASSEMBLIES +#endif + +#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 +#undef SERIALIZABLE +#endif + +// CompactFramework / Windows Phone 7 +// By default does not support System.Linq.Expressions. +// AppDomain object does not support enumerating all assemblies in the app domain. +#if PocketPC || WINDOWS_PHONE +#undef EXPRESSIONS +#undef COMPILED_EXPRESSIONS +#undef APPDOMAIN_GETASSEMBLIES +#undef UNBOUND_GENERICS_GETCONSTRUCTORS +#endif + +// PocketPC has a bizarre limitation on enumerating parameters on unbound generic methods. +// We need to use a slower workaround in that case. +#if PocketPC +#undef GETPARAMETERS_OPEN_GENERICS +#undef RESOLVE_OPEN_GENERICS +#undef READER_WRITER_LOCK_SLIM +#endif + +#if SILVERLIGHT +#undef APPDOMAIN_GETASSEMBLIES +#endif + +#if NETFX_CORE +#undef APPDOMAIN_GETASSEMBLIES +#undef RESOLVE_OPEN_GENERICS +#endif + +#if COMPILED_EXPRESSIONS +#define USE_OBJECT_CONSTRUCTOR +#endif + +#endregion +#if SERIALIZABLE +using System.Runtime.Serialization; +#endif + +namespace TinyIoC +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + +#if EXPRESSIONS + using System.Linq.Expressions; + using System.Threading; + +#endif + +#if NETFX_CORE + using System.Threading.Tasks; + using Windows.Storage.Search; + using Windows.Storage; + using Windows.UI.Xaml.Shapes; +#endif + + #region SafeDictionary +#if READER_WRITER_LOCK_SLIM +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class SafeDictionary : IDisposable + { + private readonly ReaderWriterLockSlim _padlock = new ReaderWriterLockSlim(); + private readonly Dictionary _Dictionary = new Dictionary(); + + public TValue this[TKey key] + { + set + { + _padlock.EnterWriteLock(); + + try + { + TValue current; + if (_Dictionary.TryGetValue(key, out current)) + { + var disposable = current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + + _Dictionary[key] = value; + } + finally + { + _padlock.ExitWriteLock(); + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + _padlock.EnterReadLock(); + try + { + return _Dictionary.TryGetValue(key, out value); + } + finally + { + _padlock.ExitReadLock(); + } + } + + public bool Remove(TKey key) + { + _padlock.EnterWriteLock(); + try + { + return _Dictionary.Remove(key); + } + finally + { + _padlock.ExitWriteLock(); + } + } + + public void Clear() + { + _padlock.EnterWriteLock(); + try + { + _Dictionary.Clear(); + } + finally + { + _padlock.ExitWriteLock(); + } + } + + public IEnumerable Keys + { + get + { + _padlock.EnterReadLock(); + try + { + return new List(_Dictionary.Keys); + } + finally + { + _padlock.ExitReadLock(); + } + } + } + + #region IDisposable Members + + public void Dispose() + { + _padlock.EnterWriteLock(); + + try + { + var disposableItems = from item in _Dictionary.Values + where item is IDisposable + select item as IDisposable; + + foreach (var item in disposableItems) + { + item.Dispose(); + } + } + finally + { + _padlock.ExitWriteLock(); + } + + GC.SuppressFinalize(this); + } + + #endregion + } +#else +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class SafeDictionary : IDisposable + { + private readonly object _Padlock = new object(); + private readonly Dictionary _Dictionary = new Dictionary(); + + public TValue this[TKey key] + { + set + { + lock (_Padlock) + { + TValue current; + if (_Dictionary.TryGetValue(key, out current)) + { + var disposable = current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + + _Dictionary[key] = value; + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + lock (_Padlock) + { + return _Dictionary.TryGetValue(key, out value); + } + } + + public bool Remove(TKey key) + { + lock (_Padlock) + { + return _Dictionary.Remove(key); + } + } + + public void Clear() + { + lock (_Padlock) + { + _Dictionary.Clear(); + } + } + + public IEnumerable Keys + { + get + { + return _Dictionary.Keys; + } + } + #region IDisposable Members + + public void Dispose() + { + lock (_Padlock) + { + var disposableItems = from item in _Dictionary.Values + where item is IDisposable + select item as IDisposable; + + foreach (var item in disposableItems) + { + item.Dispose(); + } + } + + GC.SuppressFinalize(this); + } + + #endregion + } +#endif + #endregion + + #region Extensions +#if TINYIOC_INTERNAL + internal +#else + public +#endif + static class AssemblyExtensions + { + public static Type[] SafeGetTypes(this Assembly assembly) + { + Type[] assemblies; + + try + { +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 + assemblies = assembly.ExportedTypes.ToArray(); +#else + assemblies = assembly.GetTypes(); +#endif + } + catch (System.IO.FileNotFoundException) + { + assemblies = new Type[] { }; + } + catch (NotSupportedException) + { + assemblies = new Type[] { }; + } +#if !NETFX_CORE + catch (ReflectionTypeLoadException e) + { + assemblies = e.Types.Where(t => t != null).ToArray(); + } +#endif + return assemblies; + } + } + +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 + [Flags] + public enum BindingFlags { + Default = 0, + IgnoreCase = 1, + DeclaredOnly = 2, + Instance = 4, + Static = 8, + Public = 16, + NonPublic = 32, + FlattenHierarchy = 64, + InvokeMethod = 256, + CreateInstance = 512, + GetField = 1024, + SetField = 2048, + GetProperty = 4096, + SetProperty = 8192, + PutDispProperty = 16384, + ExactBinding = 65536, + PutRefDispProperty = 32768, + SuppressChangeType = 131072, + OptionalParamBinding = 262144, + IgnoreReturn = 16777216 + } +#endif + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + static class TypeExtensions + { + private static SafeDictionary _genericMethodCache; + + static TypeExtensions() + { + _genericMethodCache = new SafeDictionary(); + } + +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 + private static BindingFlags DefaultFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; + + public static ConstructorInfo[] GetConstructors(this Type type) + { + return type.GetConstructors(DefaultFlags); + } + + public static ConstructorInfo[] GetConstructors(this Type type, BindingFlags bindingFlags) + { + return type.GetConstructors(bindingFlags, null); + } + + private static ConstructorInfo[] GetConstructors(this Type type, BindingFlags bindingFlags, IList parameterTypes) + { + return type.GetTypeInfo().DeclaredConstructors.Where( + c => + { + if (!TestAccessibility(c, bindingFlags)) + { + return false; + } + + if (parameterTypes != null && !c.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes)) + { + return false; + } + + return true; + }).ToArray(); + } + + public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo) { + return propertyInfo.GetGetMethod(false); + } + + public static MethodInfo GetGetMethod(this PropertyInfo propertyInfo, bool nonPublic) { + MethodInfo getMethod = propertyInfo.GetMethod; + if (getMethod != null && (getMethod.IsPublic || nonPublic)) { + return getMethod; + } + + return null; + } + + public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo) { + return propertyInfo.GetSetMethod(false); + } + + public static MethodInfo GetSetMethod(this PropertyInfo propertyInfo, bool nonPublic) { + MethodInfo setMethod = propertyInfo.SetMethod; + if (setMethod != null && (setMethod.IsPublic || nonPublic)) { + return setMethod; + } + + return null; + } + + public static Type[] GetGenericArguments(this Type type) + { + return type.GetTypeInfo().GenericTypeArguments; + } + + public static IEnumerable GetProperties(this Type type) + { + TypeInfo t = type.GetTypeInfo(); + IList properties = new List(); + while (t != null) + { + foreach (PropertyInfo member in t.DeclaredProperties) + { + if (!properties.Any(p => p.Name == member.Name)) + { + properties.Add(member); + } + } + t = (t.BaseType != null) ? t.BaseType.GetTypeInfo() : null; + } + + return properties; + } + + public static IEnumerable GetInterfaces(this Type type) + { + return type.GetTypeInfo().ImplementedInterfaces; + } + + public static MethodInfo GetMethod(this Type type, string name, IList parameterTypes) + { + return type.GetMethod(name, DefaultFlags, null, parameterTypes, null); + } + + public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingFlags, object placeHolder1, IList parameterTypes, object placeHolder2) + { + return type.GetTypeInfo().DeclaredMethods.Where( + m => + { + if (name != null && m.Name != name) + { + return false; + } + + if (!TestAccessibility(m, bindingFlags)) + { + return false; + } + + return m.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes); + }).SingleOrDefault(); + } + + public static IEnumerable GetMethods(this Type type, BindingFlags bindingFlags) + { + return type.GetTypeInfo().DeclaredMethods; + } + + public static bool IsAssignableFrom(this Type type, Type c) + { + return type.GetTypeInfo().IsAssignableFrom(c.GetTypeInfo()); + } + + private static bool TestAccessibility(MethodBase member, BindingFlags bindingFlags) + { + bool visibility = (member.IsPublic && bindingFlags.HasFlag(BindingFlags.Public)) || + (!member.IsPublic && bindingFlags.HasFlag(BindingFlags.NonPublic)); + + bool instance = (member.IsStatic && bindingFlags.HasFlag(BindingFlags.Static)) || + (!member.IsStatic && bindingFlags.HasFlag(BindingFlags.Instance)); + + return visibility && instance; + } +#endif + + /// + /// Gets a generic method from a type given the method name, binding flags, generic types and parameter types + /// + /// Source type + /// Binding flags + /// Name of the method + /// Generic types to use to make the method generic + /// Method parameters + /// MethodInfo or null if no matches found + /// + /// + public static MethodInfo GetGenericMethod(this Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) + { + MethodInfo method; + var cacheKey = new GenericMethodCacheKey(sourceType, methodName, genericTypes, parameterTypes); + + // Shouldn't need any additional locking + // we don't care if we do the method info generation + // more than once before it gets cached. + if (!_genericMethodCache.TryGetValue(cacheKey, out method)) + { + method = GetMethod(sourceType, bindingFlags, methodName, genericTypes, parameterTypes); + _genericMethodCache[cacheKey] = method; + } + + return method; + } + //#endif + +#if NETFX_CORE + private static MethodInfo GetMethod(Type sourceType, BindingFlags flags, string methodName, Type[] genericTypes, Type[] parameterTypes) + { + var methods = + sourceType.GetMethods(flags).Where( + mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( + mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). + Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( + mi => mi.MakeGenericMethod(genericTypes)).Where( + mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); + + if (methods.Count > 1) + { + throw new AmbiguousMatchException(); + } + + return methods.FirstOrDefault(); + } +#else + private static MethodInfo GetMethod(Type sourceType, BindingFlags bindingFlags, string methodName, Type[] genericTypes, Type[] parameterTypes) + { +#if GETPARAMETERS_OPEN_GENERICS + var methods = + sourceType.GetMethods(bindingFlags).Where( + mi => string.Equals(methodName, mi.Name, StringComparison.Ordinal)).Where( + mi => mi.ContainsGenericParameters).Where(mi => mi.GetGenericArguments().Length == genericTypes.Length). + Where(mi => mi.GetParameters().Length == parameterTypes.Length).Select( + mi => mi.MakeGenericMethod(genericTypes)).Where( + mi => mi.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes)).ToList(); +#else + var validMethods = from method in sourceType.GetMethods(bindingFlags) + where method.Name == methodName + where method.IsGenericMethod + where method.GetGenericArguments().Length == genericTypes.Length + let genericMethod = method.MakeGenericMethod(genericTypes) + where genericMethod.GetParameters().Count() == parameterTypes.Length + where genericMethod.GetParameters().Select(pi => pi.ParameterType).SequenceEqual(parameterTypes) + select genericMethod; + + var methods = validMethods.ToList(); +#endif + if (methods.Count > 1) + { + throw new AmbiguousMatchException(); + } + + return methods.FirstOrDefault(); + } +#endif + + private sealed class GenericMethodCacheKey + { + private readonly Type _sourceType; + + private readonly string _methodName; + + private readonly Type[] _genericTypes; + + private readonly Type[] _parameterTypes; + + private readonly int _hashCode; + + public GenericMethodCacheKey(Type sourceType, string methodName, Type[] genericTypes, Type[] parameterTypes) + { + _sourceType = sourceType; + _methodName = methodName; + _genericTypes = genericTypes; + _parameterTypes = parameterTypes; + _hashCode = GenerateHashCode(); + } + + public override bool Equals(object obj) + { + var cacheKey = obj as GenericMethodCacheKey; + if (cacheKey == null) + return false; + + if (_sourceType != cacheKey._sourceType) + return false; + + if (!String.Equals(_methodName, cacheKey._methodName, StringComparison.Ordinal)) + return false; + + if (_genericTypes.Length != cacheKey._genericTypes.Length) + return false; + + if (_parameterTypes.Length != cacheKey._parameterTypes.Length) + return false; + + for (int i = 0; i < _genericTypes.Length; ++i) + { + if (_genericTypes[i] != cacheKey._genericTypes[i]) + return false; + } + + for (int i = 0; i < _parameterTypes.Length; ++i) + { + if (_parameterTypes[i] != cacheKey._parameterTypes[i]) + return false; + } + + return true; + } + + public override int GetHashCode() + { + return _hashCode; + } + + private int GenerateHashCode() + { + unchecked + { + var result = _sourceType.GetHashCode(); + + result = (result * 397) ^ _methodName.GetHashCode(); + + for (int i = 0; i < _genericTypes.Length; ++i) + { + result = (result * 397) ^ _genericTypes[i].GetHashCode(); + } + + for (int i = 0; i < _parameterTypes.Length; ++i) + { + result = (result * 397) ^ _parameterTypes[i].GetHashCode(); + } + + return result; + } + } + } + + } + + // @mbrit - 2012-05-22 - shim for ForEach call on List... +#if NETFX_CORE + internal static class ListExtender + { + internal static void ForEach(this List list, Action callback) + { + foreach (T obj in list) + callback(obj); + } + } +#endif + +#endregion + +#region TinyIoC Exception Types +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCResolutionException : Exception + { + private const string ERROR_TEXT = "Unable to resolve type: {0}"; + + public TinyIoCResolutionException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCResolutionException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCResolutionException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCRegistrationTypeException : Exception + { + private const string REGISTER_ERROR_TEXT = "Cannot register type {0} - abstract classes or interfaces are not valid implementation types for {1}."; + + public TinyIoCRegistrationTypeException(Type type, string factory) + : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory)) + { + } + + public TinyIoCRegistrationTypeException(Type type, string factory, Exception innerException) + : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCRegistrationTypeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCRegistrationException : Exception + { + private const string CONVERT_ERROR_TEXT = "Cannot convert current registration of {0} to {1}"; + private const string GENERIC_CONSTRAINT_ERROR_TEXT = "Type {1} is not valid for a registration of type {0}"; + + public TinyIoCRegistrationException(Type type, string method) + : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method)) + { + } + + public TinyIoCRegistrationException(Type type, string method, Exception innerException) + : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method), innerException) + { + } + + public TinyIoCRegistrationException(Type registerType, Type implementationType) + : base(String.Format(GENERIC_CONSTRAINT_ERROR_TEXT, registerType.FullName, implementationType.FullName)) + { + } + + public TinyIoCRegistrationException(Type registerType, Type implementationType, Exception innerException) + : base(String.Format(GENERIC_CONSTRAINT_ERROR_TEXT, registerType.FullName, implementationType.FullName), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCRegistrationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCWeakReferenceException : Exception + { + private const string ERROR_TEXT = "Unable to instantiate {0} - referenced object has been reclaimed"; + + public TinyIoCWeakReferenceException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCWeakReferenceException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCWeakReferenceException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCConstructorResolutionException : Exception + { + private const string ERROR_TEXT = "Unable to resolve constructor for {0} using provided Expression."; + + public TinyIoCConstructorResolutionException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCConstructorResolutionException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } + + public TinyIoCConstructorResolutionException(string message, Exception innerException) + : base(message, innerException) + { + } + + public TinyIoCConstructorResolutionException(string message) + : base(message) + { + } +#if SERIALIZABLE + protected TinyIoCConstructorResolutionException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + } +#if SERIALIZABLE + [Serializable] +#endif +#if TINYIOC_INTERNAL + internal +#else + public +#endif + class TinyIoCAutoRegistrationException : Exception + { + private const string ERROR_TEXT = "Duplicate implementation of type {0} found ({1})."; + + public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types) + : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types))) + { + } + + public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types, Exception innerException) + : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types)), innerException) + { + } +#if SERIALIZABLE + protected TinyIoCAutoRegistrationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } +#endif + + private static string GetTypesString(IEnumerable types) + { + var typeNames = from type in types + select type.FullName; + + return string.Join(",", typeNames.ToArray()); + } + } +#endregion + +#region Public Setup / Settings Classes + /// + /// Name/Value pairs for specifying "user" parameters when resolving + /// +#if TINYIOC_INTERNAL + internal +#else + public +#endif + sealed class NamedParameterOverloads : Dictionary + { + public static NamedParameterOverloads FromIDictionary(IDictionary data) + { + return data as NamedParameterOverloads ?? new NamedParameterOverloads(data); + } + + public NamedParameterOverloads() + { + } + + public NamedParameterOverloads(IDictionary data) + : base(data) + { + } + + private static readonly NamedParameterOverloads _Default = new NamedParameterOverloads(); + + public static NamedParameterOverloads Default + { + get + { + return _Default; + } + } + } + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + enum UnregisteredResolutionActions + { + /// + /// Attempt to resolve type, even if the type isn't registered. + /// + /// Registered types/options will always take precedence. + /// + AttemptResolve, + + /// + /// Fail resolution if type not explicitly registered + /// + Fail, + + /// + /// Attempt to resolve unregistered type if requested type is generic + /// and no registration exists for the specific generic parameters used. + /// + /// Registered types/options will always take precedence. + /// + GenericsOnly + } + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + enum NamedResolutionFailureActions + { + AttemptUnnamedResolution, + Fail + } + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + enum DuplicateImplementationActions + { + RegisterSingle, + RegisterMultiple, + Fail + } + + /// + /// Resolution settings + /// +#if TINYIOC_INTERNAL + internal +#else + public +#endif + sealed class ResolveOptions + { + private static readonly ResolveOptions _Default = new ResolveOptions(); + private static readonly ResolveOptions _FailUnregisteredAndNameNotFound = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.Fail, UnregisteredResolutionAction = UnregisteredResolutionActions.Fail }; + private static readonly ResolveOptions _FailUnregisteredOnly = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution, UnregisteredResolutionAction = UnregisteredResolutionActions.Fail }; + private static readonly ResolveOptions _FailNameNotFoundOnly = new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.Fail, UnregisteredResolutionAction = UnregisteredResolutionActions.AttemptResolve }; + + private UnregisteredResolutionActions _UnregisteredResolutionAction = UnregisteredResolutionActions.AttemptResolve; + public UnregisteredResolutionActions UnregisteredResolutionAction + { + get { return _UnregisteredResolutionAction; } + set { _UnregisteredResolutionAction = value; } + } + + private NamedResolutionFailureActions _NamedResolutionFailureAction = NamedResolutionFailureActions.Fail; + public NamedResolutionFailureActions NamedResolutionFailureAction + { + get { return _NamedResolutionFailureAction; } + set { _NamedResolutionFailureAction = value; } + } + + /// + /// Gets the default options (attempt resolution of unregistered types, fail on named resolution if name not found) + /// + public static ResolveOptions Default + { + get + { + return _Default; + } + } + + /// + /// Preconfigured option for attempting resolution of unregistered types and failing on named resolution if name not found + /// + public static ResolveOptions FailNameNotFoundOnly + { + get + { + return _FailNameNotFoundOnly; + } + } + + /// + /// Preconfigured option for failing on resolving unregistered types and on named resolution if name not found + /// + public static ResolveOptions FailUnregisteredAndNameNotFound + { + get + { + return _FailUnregisteredAndNameNotFound; + } + } + + /// + /// Preconfigured option for failing on resolving unregistered types, but attempting unnamed resolution if name not found + /// + public static ResolveOptions FailUnregisteredOnly + { + get + { + return _FailUnregisteredOnly; + } + } + } +#endregion + +#if TINYIOC_INTERNAL + internal +#else + public +#endif + sealed partial class TinyIoCContainer : IDisposable + { +#region Fake NETFX_CORE Classes +#if NETFX_CORE + private sealed class MethodAccessException : Exception + { + } + + private sealed class AppDomain + { + public static AppDomain CurrentDomain { get; private set; } + + static AppDomain() + { + CurrentDomain = new AppDomain(); + } + + // @mbrit - 2012-05-30 - in WinRT, this should be done async... + public async Task> GetAssembliesAsync() + { + var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; + + List assemblies = new List(); + + var files = await folder.GetFilesAsync(); + + foreach (StorageFile file in files) + { + if (file.FileType == ".dll" || file.FileType == ".exe") + { + AssemblyName name = new AssemblyName() { Name = System.IO.Path.GetFileNameWithoutExtension(file.Name) }; + try + { + var asm = Assembly.Load(name); + assemblies.Add(asm); + } + catch + { + // ignore exceptions here... + } + } + } + + return assemblies; + } + } +#endif +#endregion + +#region "Fluent" API + /// + /// Registration options for "fluent" API + /// + public sealed class RegisterOptions + { + private TinyIoCContainer _Container; + private TypeRegistration _Registration; + + public RegisterOptions(TinyIoCContainer container, TypeRegistration registration) + { + _Container = container; + _Registration = registration; + } + + /// + /// Make registration a singleton (single instance) if possible + /// + /// RegisterOptions + /// + public RegisterOptions AsSingleton() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "singleton"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.SingletonVariant); + } + + /// + /// Make registration multi-instance if possible + /// + /// RegisterOptions + /// + public RegisterOptions AsMultiInstance() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "multi-instance"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.MultiInstanceVariant); + } + + /// + /// Make registration hold a weak reference if possible + /// + /// RegisterOptions + /// + public RegisterOptions WithWeakReference() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "weak reference"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.WeakReferenceVariant); + } + + /// + /// Make registration hold a strong reference if possible + /// + /// RegisterOptions + /// + public RegisterOptions WithStrongReference() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "strong reference"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.StrongReferenceVariant); + } + +#if EXPRESSIONS + public RegisterOptions UsingConstructor(Expression> constructor) + { + if(!IsValidAssignment(_Registration.Type, typeof(RegisterType))) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var lambda = constructor as LambdaExpression; + if (lambda == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var newExpression = lambda.Body as NewExpression; + if (newExpression == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var constructorInfo = newExpression.Constructor; + if (constructorInfo == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var currentFactory = _Container.GetCurrentFactory(_Registration); + if (currentFactory == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + currentFactory.SetConstructor(constructorInfo); + + return this; + } +#endif + /// + /// Switches to a custom lifetime manager factory if possible. + /// + /// Usually used for RegisterOptions "To*" extension methods such as the ASP.Net per-request one. + /// + /// RegisterOptions instance + /// Custom lifetime manager + /// Error string to display if switch fails + /// RegisterOptions + public static RegisterOptions ToCustomLifetimeManager(RegisterOptions instance, ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + if (instance == null) + throw new ArgumentNullException("instance", "instance is null."); + + if (lifetimeProvider == null) + throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); + + if (string.IsNullOrEmpty(errorString)) + throw new ArgumentException("errorString is null or empty.", "errorString"); + + var currentFactory = instance._Container.GetCurrentFactory(instance._Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(instance._Registration.Type, errorString); + + return instance._Container.AddUpdateRegistration(instance._Registration, currentFactory.GetCustomObjectLifetimeVariant(lifetimeProvider, errorString)); + } + } + + /// + /// Registration options for "fluent" API when registering multiple implementations + /// + public sealed class MultiRegisterOptions + { + private IEnumerable _RegisterOptions; + + /// + /// Initializes a new instance of the MultiRegisterOptions class. + /// + /// Registration options + public MultiRegisterOptions(IEnumerable registerOptions) + { + _RegisterOptions = registerOptions; + } + + /// + /// Make registration a singleton (single instance) if possible + /// + /// RegisterOptions + /// + public MultiRegisterOptions AsSingleton() + { + _RegisterOptions = ExecuteOnAllRegisterOptions(ro => ro.AsSingleton()); + return this; + } + + /// + /// Make registration multi-instance if possible + /// + /// MultiRegisterOptions + /// + public MultiRegisterOptions AsMultiInstance() + { + _RegisterOptions = ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance()); + return this; + } + + /// + /// Switches to a custom lifetime manager factory if possible. + /// + /// Usually used for RegisterOptions "To*" extension methods such as the ASP.Net per-request one. + /// + /// MultiRegisterOptions instance + /// Custom lifetime manager + /// Error string to display if switch fails + /// MultiRegisterOptions + public static MultiRegisterOptions ToCustomLifetimeManager( + MultiRegisterOptions instance, + ITinyIoCObjectLifetimeProvider lifetimeProvider, + string errorString) + { + if (instance == null) + throw new ArgumentNullException("instance", "instance is null."); + + if (lifetimeProvider == null) + throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); + + if (string.IsNullOrEmpty(errorString)) + throw new ArgumentException("errorString is null or empty.", "errorString"); + + instance._RegisterOptions = instance.ExecuteOnAllRegisterOptions(ro => RegisterOptions.ToCustomLifetimeManager(ro, lifetimeProvider, errorString)); + + return instance; + } + + private IEnumerable ExecuteOnAllRegisterOptions(Func action) + { + var newRegisterOptions = new List(); + + foreach (var registerOption in _RegisterOptions) + { + newRegisterOptions.Add(action(registerOption)); + } + + return newRegisterOptions; + } + } +#endregion + +#region Public API +#region Child Containers + public TinyIoCContainer GetChildContainer() + { + return new TinyIoCContainer(this); + } +#endregion + +#region Registration + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + public void AutoRegister() + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, null); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, DuplicateImplementationActions.RegisterSingle, null); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Predicate to determine if a particular type should be registered + public void AutoRegister(Func registrationPredicate) + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), DuplicateImplementationActions.RegisterSingle, registrationPredicate); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, DuplicateImplementationActions.RegisterSingle, registrationPredicate); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// + /// What action to take when encountering duplicate implementations of an interface/base class. + /// + public void AutoRegister(DuplicateImplementationActions duplicateAction) + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, null); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, duplicateAction, null); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// What action to take when encountering duplicate implementations of an interface/base class. + /// Predicate to determine if a particular type should be registered + /// + public void AutoRegister(DuplicateImplementationActions duplicateAction, Func registrationPredicate) + { +#if APPDOMAIN_GETASSEMBLIES + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, registrationPredicate); +#else + AutoRegisterInternal(new Assembly[] { this.GetType().Assembly() }, duplicateAction, registrationPredicate); +#endif + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Assemblies to process + public void AutoRegister(IEnumerable assemblies) + { + AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, null); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Assemblies to process + /// Predicate to determine if a particular type should be registered + public void AutoRegister(IEnumerable assemblies, Func registrationPredicate) + { + AutoRegisterInternal(assemblies, DuplicateImplementationActions.RegisterSingle, registrationPredicate); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// + /// Assemblies to process + /// What action to take when encountering duplicate implementations of an interface/base class. + /// + public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction) + { + AutoRegisterInternal(assemblies, duplicateAction, null); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// Types will only be registered if they pass the supplied registration predicate. + /// + /// Assemblies to process + /// What action to take when encountering duplicate implementations of an interface/base class. + /// Predicate to determine if a particular type should be registered + /// + public void AutoRegister(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) + { + AutoRegisterInternal(assemblies, duplicateAction, registrationPredicate); + } + + /// + /// Creates/replaces a container class registration with default options. + /// + /// Type to register + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType) + { + return RegisterInternal(registerType, string.Empty, GetDefaultObjectFactory(registerType, registerType)); + } + + /// + /// Creates/replaces a named container class registration with default options. + /// + /// Type to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, string name) + { + return RegisterInternal(registerType, name, GetDefaultObjectFactory(registerType, registerType)); + + } + + /// + /// Creates/replaces a container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation) + { + return this.RegisterInternal(registerType, string.Empty, GetDefaultObjectFactory(registerType, registerImplementation)); + } + + /// + /// Creates/replaces a named container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation, string name) + { + return this.RegisterInternal(registerType, name, GetDefaultObjectFactory(registerType, registerImplementation)); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, object instance) + { + return RegisterInternal(registerType, string.Empty, new InstanceFactory(registerType, registerType, instance)); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, object instance, string name) + { + return RegisterInternal(registerType, name, new InstanceFactory(registerType, registerType, instance)); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation, object instance) + { + return RegisterInternal(registerType, string.Empty, new InstanceFactory(registerType, registerImplementation, instance)); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Type registerImplementation, object instance, string name) + { + return RegisterInternal(registerType, name, new InstanceFactory(registerType, registerImplementation, instance)); + } + + /// + /// Creates/replaces a container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Func factory) + { + return RegisterInternal(registerType, string.Empty, new DelegateFactory(registerType, factory)); + } + + /// + /// Creates/replaces a container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Type registerType, Func factory, string name) + { + return RegisterInternal(registerType, name, new DelegateFactory(registerType, factory)); + } + + /// + /// Creates/replaces a container class registration with default options. + /// + /// Type to register + /// RegisterOptions for fluent API + public RegisterOptions Register() + where RegisterType : class + { + return this.Register(typeof(RegisterType)); + } + + /// + /// Creates/replaces a named container class registration with default options. + /// + /// Type to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(string name) + where RegisterType : class + { + return this.Register(typeof(RegisterType), name); + } + + /// + /// Creates/replaces a container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register() + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation)); + } + + /// + /// Creates/replaces a named container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(string name) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation), name); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterType instance) + where RegisterType : class + { + return this.Register(typeof(RegisterType), instance); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterType instance, string name) + where RegisterType : class + { + return this.Register(typeof(RegisterType), instance, name); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterImplementation instance) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation), instance); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterImplementation instance, string name) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return this.Register(typeof(RegisterType), typeof(RegisterImplementation), instance, name); + } + + /// + /// Creates/replaces a container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register(Func factory) + where RegisterType : class + { + if (factory == null) + { + throw new ArgumentNullException("factory"); + } + + return this.Register(typeof(RegisterType), (c, o) => factory(c, o)); + } + + /// + /// Creates/replaces a named container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(Func factory, string name) + where RegisterType : class + { + if (factory == null) + { + throw new ArgumentNullException("factory"); + } + + return this.Register(typeof(RegisterType), (c, o) => factory(c, o), name); + } + + /// + /// Register multiple implementations of a type. + /// + /// Internally this registers each implementation using the full name of the class as its registration name. + /// + /// Type that each implementation implements + /// Types that implement RegisterType + /// MultiRegisterOptions for the fluent API + public MultiRegisterOptions RegisterMultiple(IEnumerable implementationTypes) + { + return RegisterMultiple(typeof(RegisterType), implementationTypes); + } + + /// + /// Register multiple implementations of a type. + /// + /// Internally this registers each implementation using the full name of the class as its registration name. + /// + /// Type that each implementation implements + /// Types that implement RegisterType + /// MultiRegisterOptions for the fluent API + public MultiRegisterOptions RegisterMultiple(Type registrationType, IEnumerable implementationTypes) + { + if (implementationTypes == null) + throw new ArgumentNullException("types", "types is null."); + + foreach (var type in implementationTypes) + //#if NETFX_CORE + // if (!registrationType.GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + //#else + if (!registrationType.IsAssignableFrom(type)) + //#endif + throw new ArgumentException(String.Format("types: The type {0} is not assignable from {1}", registrationType.FullName, type.FullName)); + + if (implementationTypes.Count() != implementationTypes.Distinct().Count()) + { + var queryForDuplicatedTypes = from i in implementationTypes + group i by i + into j + where j.Count() > 1 + select j.Key.FullName; + + var fullNamesOfDuplicatedTypes = string.Join(",\n", queryForDuplicatedTypes.ToArray()); + var multipleRegMessage = string.Format("types: The same implementation type cannot be specified multiple times for {0}\n\n{1}", registrationType.FullName, fullNamesOfDuplicatedTypes); + throw new ArgumentException(multipleRegMessage); + } + + var registerOptions = new List(); + + foreach (var type in implementationTypes) + { + registerOptions.Add(Register(registrationType, type, type.FullName)); + } + + return new MultiRegisterOptions(registerOptions); + } +#endregion + +#region Unregistration + + /// + /// Remove a container class registration. + /// + /// Type to unregister + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister() + { + return Unregister(typeof(RegisterType), string.Empty); + } + + /// + /// Remove a named container class registration. + /// + /// Type to unregister + /// Name of registration + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister(string name) + { + return Unregister(typeof(RegisterType), name); + } + + /// + /// Remove a container class registration. + /// + /// Type to unregister + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister(Type registerType) + { + return Unregister(registerType, string.Empty); + } + + /// + /// Remove a named container class registration. + /// + /// Type to unregister + /// Name of registration + /// true if the registration is successfully found and removed; otherwise, false. + public bool Unregister(Type registerType, string name) + { + var typeRegistration = new TypeRegistration(registerType, name); + + return RemoveRegistration(typeRegistration); + } + +#endregion + +#region Resolution + /// + /// Attempts to resolve a type using default options. + /// + /// Type to resolve + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType) + { + return ResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a type using specified options. + /// + /// Type to resolve + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name) + { + return ResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a type using supplied options and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, NamedParameterOverloads parameters) + { + return ResolveInternal(new TypeRegistration(resolveType), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType), parameters, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name, NamedParameterOverloads parameters) + { + return ResolveInternal(new TypeRegistration(resolveType, name), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to resolve a named type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public object Resolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options) + { + return ResolveInternal(new TypeRegistration(resolveType, name), parameters, options); + } + + /// + /// Attempts to resolve a type using default options. + /// + /// Type to resolve + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve() + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType)); + } + + /// + /// Attempts to resolve a type using specified options. + /// + /// Type to resolve + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), options); + } + + /// + /// Attempts to resolve a type using default options and the supplied name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name); + } + + /// + /// Attempts to resolve a type using supplied options and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(NamedParameterOverloads parameters) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), parameters); + } + + /// + /// Attempts to resolve a type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), parameters, options); + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, NamedParameterOverloads parameters) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name, parameters); + } + + /// + /// Attempts to resolve a named type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return (ResolveType)Resolve(typeof(ResolveType), name, parameters, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType) + { + return CanResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given named type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Bool indicating whether the type can be resolved + private bool CanResolve(Type resolveType, string name) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, string name, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, NamedParameterOverloads parameters) + { + return CanResolveInternal(new TypeRegistration(resolveType), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, string name, NamedParameterOverloads parameters) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType), parameters, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options) + { + return CanResolveInternal(new TypeRegistration(resolveType, name), parameters, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Bool indicating whether the type can be resolved + public bool CanResolve() + where ResolveType : class + { + return CanResolve(typeof(ResolveType)); + } + + /// + /// Attempts to predict whether a given named type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name); + } + + /// + /// Attempts to predict whether a given type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(NamedParameterOverloads parameters) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), parameters); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, NamedParameterOverloads parameters) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name, parameters); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), parameters, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return CanResolve(typeof(ResolveType), name, parameters, options); + } + + /// + /// Attempts to resolve a type using the default options + /// + /// Type to resolve + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the given options + /// + /// Type to resolve + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the default options and given name + /// + /// Type to resolve + /// Name of registration + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, string name, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the given options and name + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, string name, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the default options and supplied constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, NamedParameterOverloads parameters, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the default options and supplied name and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, string name, NamedParameterOverloads parameters, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name, parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the supplied options and constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, NamedParameterOverloads parameters, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the supplied name, options and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(Type resolveType, string name, NamedParameterOverloads parameters, ResolveOptions options, out object resolvedType) + { + try + { + resolvedType = Resolve(resolveType, name, parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = null; + return false; + } + } + + /// + /// Attempts to resolve a type using the default options + /// + /// Type to resolve + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attempts to resolve a type using the given options + /// + /// Type to resolve + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attempts to resolve a type using the default options and given name + /// + /// Type to resolve + /// Name of registration + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(string name, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attempts to resolve a type using the given options and name + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(string name, ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attempts to resolve a type using the default options and supplied constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(NamedParameterOverloads parameters, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attempts to resolve a type using the default options and supplied name and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(string name, NamedParameterOverloads parameters, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name, parameters); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attempts to resolve a type using the supplied options and constructor parameters + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(NamedParameterOverloads parameters, ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Attempts to resolve a type using the supplied name, options and constructor parameters + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Resolved type or default if resolve fails + /// True if resolved successfully, false otherwise + public bool TryResolve(string name, NamedParameterOverloads parameters, ResolveOptions options, out ResolveType resolvedType) + where ResolveType : class + { + try + { + resolvedType = Resolve(name, parameters, options); + return true; + } + catch (TinyIoCResolutionException) + { + resolvedType = default(ResolveType); + return false; + } + } + + /// + /// Returns all registrations of a type + /// + /// Type to resolveAll + /// Whether to include un-named (default) registrations + /// IEnumerable + public IEnumerable ResolveAll(Type resolveType, bool includeUnnamed) + { + return ResolveAllInternal(resolveType, includeUnnamed); + } + + /// + /// Returns all registrations of a type, both named and unnamed + /// + /// Type to resolveAll + /// IEnumerable + public IEnumerable ResolveAll(Type resolveType) + { + return ResolveAll(resolveType, true); + } + + /// + /// Returns all registrations of a type + /// + /// Type to resolveAll + /// Whether to include un-named (default) registrations + /// IEnumerable + public IEnumerable ResolveAll(bool includeUnnamed) + where ResolveType : class + { + return this.ResolveAll(typeof(ResolveType), includeUnnamed).Cast(); + } + + /// + /// Returns all registrations of a type, both named and unnamed + /// + /// Type to resolveAll + /// IEnumerable + public IEnumerable ResolveAll() + where ResolveType : class + { + return ResolveAll(true); + } + + /// + /// Attempts to resolve all public property dependencies on the given object. + /// + /// Object to "build up" + public void BuildUp(object input) + { + BuildUpInternal(input, ResolveOptions.Default); + } + + /// + /// Attempts to resolve all public property dependencies on the given object using the given resolve options. + /// + /// Object to "build up" + /// Resolve options to use + public void BuildUp(object input, ResolveOptions resolveOptions) + { + BuildUpInternal(input, resolveOptions); + } +#endregion +#endregion + +#region Object Factories + /// + /// Provides custom lifetime management for ASP.Net per-request lifetimes etc. + /// + public interface ITinyIoCObjectLifetimeProvider + { + /// + /// Gets the stored object if it exists, or null if not + /// + /// Object instance or null + object GetObject(); + + /// + /// Store the object + /// + /// Object to store + void SetObject(object value); + + /// + /// Release the object + /// + void ReleaseObject(); + } + + private abstract class ObjectFactoryBase + { + /// + /// Whether to assume this factory successfully constructs its objects + /// + /// Generally set to true for delegate style factories as CanResolve cannot delve + /// into the delegates they contain. + /// + public virtual bool AssumeConstruction { get { return false; } } + + /// + /// The type the factory instantiates + /// + public abstract Type CreatesType { get; } + + /// + /// Constructor to use, if specified + /// + public ConstructorInfo Constructor { get; protected set; } + + /// + /// Create the type + /// + /// Type user requested to be resolved + /// Container that requested the creation + /// Any user parameters passed + /// + /// + public abstract object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options); + + public virtual ObjectFactoryBase SingletonVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "singleton"); + } + } + + public virtual ObjectFactoryBase MultiInstanceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "multi-instance"); + } + } + + public virtual ObjectFactoryBase StrongReferenceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "strong reference"); + } + } + + public virtual ObjectFactoryBase WeakReferenceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "weak reference"); + } + } + + public virtual ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + throw new TinyIoCRegistrationException(this.GetType(), errorString); + } + + public virtual void SetConstructor(ConstructorInfo constructor) + { + Constructor = constructor; + } + + public virtual ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) + { + return this; + } + } + + /// + /// IObjectFactory that creates new instances of types for each resolution + /// + private class MultiInstanceFactory : ObjectFactoryBase + { + private readonly Type registerType; + private readonly Type registerImplementation; + public override Type CreatesType { get { return this.registerImplementation; } } + + public MultiInstanceFactory(Type registerType, Type registerImplementation) + { + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + // throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); + //#endif + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "MultiInstanceFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + try + { + return container.ConstructType(requestedType, this.registerImplementation, Constructor, parameters, options); + } + catch (TinyIoCResolutionException ex) + { + throw new TinyIoCResolutionException(this.registerType, ex); + } + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + return new SingletonFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return this; + } + } + } + + /// + /// IObjectFactory that invokes a specified delegate to construct the object + /// + private class DelegateFactory : ObjectFactoryBase + { + private readonly Type registerType; + + private Func _factory; + + public override bool AssumeConstruction { get { return true; } } + + public override Type CreatesType { get { return this.registerType; } } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { +#if RESOLVE_OPEN_GENERICS + // Make the requested type available to the factory function + parameters = new NamedParameterOverloads(parameters); + parameters["__requestedType"] = requestedType; +#endif + + try + { + return _factory.Invoke(container, parameters); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(this.registerType, ex); + } + } + + public DelegateFactory(Type registerType, Func factory) + { + if (factory == null) + throw new ArgumentNullException("factory"); + + _factory = factory; + + this.registerType = registerType; + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + return new DelegateSingletonFactory(registerType, _factory); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return new WeakDelegateFactory(this.registerType, _factory); + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); + } + } + + /// + /// IObjectFactory that invokes a specified delegate to construct the object + /// Holds the delegate using a weak reference + /// + private class WeakDelegateFactory : ObjectFactoryBase + { + private readonly Type registerType; + + private WeakReference _factory; + + public override bool AssumeConstruction { get { return true; } } + + public override Type CreatesType { get { return this.registerType; } } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + var factory = _factory.Target as Func; + + if (factory == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + try + { + return factory.Invoke(container, parameters); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(this.registerType, ex); + } + } + + public WeakDelegateFactory(Type registerType, Func factory) + { + if (factory == null) + throw new ArgumentNullException("factory"); + + _factory = new WeakReference(factory); + + this.registerType = registerType; + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + var factory = _factory.Target as Func; + + if (factory == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + return new DelegateFactory(this.registerType, factory); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); + } + } + + /// + /// Stores an particular instance to return for a type + /// + private class InstanceFactory : ObjectFactoryBase, IDisposable + { + private readonly Type registerType; + private readonly Type registerImplementation; + private object _instance; + + public override bool AssumeConstruction { get { return true; } } + + public InstanceFactory(Type registerType, Type registerImplementation, object instance) + { + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "InstanceFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + _instance = instance; + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + return _instance; + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get { return new MultiInstanceFactory(this.registerType, this.registerImplementation); } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return new WeakInstanceFactory(this.registerType, this.registerImplementation, this._instance); + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); + } + + public void Dispose() + { + var disposable = _instance as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// Stores an particular instance to return for a type + /// + /// Stores the instance with a weak reference + /// + private class WeakInstanceFactory : ObjectFactoryBase, IDisposable + { + private readonly Type registerType; + private readonly Type registerImplementation; + private readonly WeakReference _instance; + + public WeakInstanceFactory(Type registerType, Type registerImplementation, object instance) + { + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "WeakInstanceFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + _instance = new WeakReference(instance); + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + var instance = _instance.Target; + + if (instance == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + return instance; + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return new MultiInstanceFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return this; + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + var instance = _instance.Target; + + if (instance == null) + throw new TinyIoCWeakReferenceException(this.registerType); + + return new InstanceFactory(this.registerType, this.registerImplementation, instance); + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); + } + + public void Dispose() + { + var disposable = _instance.Target as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// A factory that lazy instantiates a type and always returns the same instance + /// + private class SingletonFactory : ObjectFactoryBase, IDisposable + { + private readonly Type registerType; + private readonly Type registerImplementation; + private readonly object SingletonLock = new object(); + private object _Current; + + public SingletonFactory(Type registerType, Type registerImplementation) + { + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + //#endif + throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); + + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters.Count != 0) + throw new ArgumentException("Cannot specify parameters for singleton types"); + + lock (SingletonLock) + if (_Current == null) + _Current = container.ConstructType(requestedType, this.registerImplementation, Constructor, options); + + return _Current; + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + return this; + } + } + + public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return new MultiInstanceFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) + { + // We make sure that the singleton is constructed before the child container takes the factory. + // Otherwise the results would vary depending on whether or not the parent container had resolved + // the type before the child container does. + GetObject(type, parent, NamedParameterOverloads.Default, ResolveOptions.Default); + return this; + } + + public void Dispose() + { + if (this._Current == null) + return; + + var disposable = this._Current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// A factory that offloads lifetime to an external lifetime provider + /// + private class CustomObjectLifetimeFactory : ObjectFactoryBase, IDisposable + { + private readonly object SingletonLock = new object(); + private readonly Type registerType; + private readonly Type registerImplementation; + private readonly ITinyIoCObjectLifetimeProvider _LifetimeProvider; + + public CustomObjectLifetimeFactory(Type registerType, Type registerImplementation, ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorMessage) + { + if (lifetimeProvider == null) + throw new ArgumentNullException("lifetimeProvider", "lifetimeProvider is null."); + + if (!IsValidAssignment(registerType, registerImplementation)) + throw new TinyIoCRegistrationTypeException(registerImplementation, "SingletonFactory"); + + //#if NETFX_CORE + // if (registerImplementation.GetTypeInfo().IsAbstract() || registerImplementation.GetTypeInfo().IsInterface()) + //#else + if (registerImplementation.IsAbstract() || registerImplementation.IsInterface()) + //#endif + throw new TinyIoCRegistrationTypeException(registerImplementation, errorMessage); + + this.registerType = registerType; + this.registerImplementation = registerImplementation; + _LifetimeProvider = lifetimeProvider; + } + + public override Type CreatesType + { + get { return this.registerImplementation; } + } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + object current; + + lock (SingletonLock) + { + current = _LifetimeProvider.GetObject(); + if (current == null) + { + current = container.ConstructType(requestedType, this.registerImplementation, Constructor, options); + _LifetimeProvider.SetObject(current); + } + } + + return current; + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + _LifetimeProvider.ReleaseObject(); + return new SingletonFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + _LifetimeProvider.ReleaseObject(); + return new MultiInstanceFactory(this.registerType, this.registerImplementation); + } + } + + public override ObjectFactoryBase GetCustomObjectLifetimeVariant(ITinyIoCObjectLifetimeProvider lifetimeProvider, string errorString) + { + _LifetimeProvider.ReleaseObject(); + return new CustomObjectLifetimeFactory(this.registerType, this.registerImplementation, lifetimeProvider, errorString); + } + + public override ObjectFactoryBase GetFactoryForChildContainer(Type type, TinyIoCContainer parent, TinyIoCContainer child) + { + // We make sure that the singleton is constructed before the child container takes the factory. + // Otherwise the results would vary depending on whether or not the parent container had resolved + // the type before the child container does. + GetObject(type, parent, NamedParameterOverloads.Default, ResolveOptions.Default); + return this; + } + + public void Dispose() + { + _LifetimeProvider.ReleaseObject(); + } + } + + private class DelegateSingletonFactory : ObjectFactoryBase, IDisposable + { + private readonly Func _factory; + private readonly object _singletonLock = new object(); + private object _instance; + + public DelegateSingletonFactory(Type creatingType, Func factory) + { + _factory = factory; + CreatesType = creatingType; + } + + public override Type CreatesType { get; } + + public override object GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, + ResolveOptions options) + { + if (_instance == null) + { + lock (_singletonLock) + { + if(_instance == null) + _instance = _factory(container, parameters); + } + } + + return _instance; + } + + public void Dispose() + { + if (_instance is IDisposable disp) + { + disp.Dispose(); + _instance = null; + } + } + } +#endregion + +#region Singleton Container + private static readonly TinyIoCContainer _Current = new TinyIoCContainer(); + + static TinyIoCContainer() + { + } + + /// + /// Lazy created Singleton instance of the container for simple scenarios + /// + public static TinyIoCContainer Current + { + get + { + return _Current; + } + } +#endregion + +#region Type Registrations + public sealed class TypeRegistration + { + private int _hashCode; + + public Type Type { get; private set; } + public string Name { get; private set; } + + public TypeRegistration(Type type) + : this(type, string.Empty) + { + } + + public TypeRegistration(Type type, string name) + { + Type = type; + Name = name; + + _hashCode = String.Concat(Type.FullName, "|", Name).GetHashCode(); + } + + public override bool Equals(object obj) + { + var typeRegistration = obj as TypeRegistration; + + if (typeRegistration == null) + return false; + + if (Type != typeRegistration.Type) + return false; + + if (String.Compare(Name, typeRegistration.Name, StringComparison.Ordinal) != 0) + return false; + + return true; + } + + public override int GetHashCode() + { + return _hashCode; + } + } + private readonly SafeDictionary _RegisteredTypes; +#if USE_OBJECT_CONSTRUCTOR + private delegate object ObjectConstructor(params object[] parameters); + private static readonly SafeDictionary _ObjectConstructorCache = new SafeDictionary(); +#endif +#endregion + +#region Constructors + public TinyIoCContainer() + { + _RegisteredTypes = new SafeDictionary(); + + RegisterDefaultTypes(); + } + + TinyIoCContainer _Parent; + private TinyIoCContainer(TinyIoCContainer parent) + : this() + { + _Parent = parent; + } +#endregion + +#region Internal Methods + private readonly object _AutoRegisterLock = new object(); + private void AutoRegisterInternal(IEnumerable assemblies, DuplicateImplementationActions duplicateAction, Func registrationPredicate) + { + lock (_AutoRegisterLock) + { + var types = assemblies.SelectMany(a => a.SafeGetTypes()).Where(t => !IsIgnoredType(t, registrationPredicate)).ToList(); + + var concreteTypes = types + .Where(type => type.IsClass() && (type.IsAbstract() == false) && (type != this.GetType() && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition()))) + .ToList(); + + foreach (var type in concreteTypes) + { + try + { + RegisterInternal(type, string.Empty, GetDefaultObjectFactory(type, type)); + } +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 + catch (MemberAccessException) +#else + catch (MethodAccessException) +#endif + { + // Ignore methods we can't access - added for Silverlight + } + } + + var abstractInterfaceTypes = from type in types + where ((type.IsInterface() || type.IsAbstract()) && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition())) + select type; + + foreach (var type in abstractInterfaceTypes) + { + var localType = type; + var implementations = from implementationType in concreteTypes + where localType.IsAssignableFrom(implementationType) + select implementationType; + + if (implementations.Skip(1).Any()) + { + if (duplicateAction == DuplicateImplementationActions.Fail) + throw new TinyIoCAutoRegistrationException(type, implementations); + + if (duplicateAction == DuplicateImplementationActions.RegisterMultiple) + { + RegisterMultiple(type, implementations); + } + } + + var firstImplementation = implementations.FirstOrDefault(); + if (firstImplementation != null) + { + try + { + RegisterInternal(type, string.Empty, GetDefaultObjectFactory(type, firstImplementation)); + } +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 + catch (MemberAccessException) +#else + catch (MethodAccessException) +#endif + { + // Ignore methods we can't access - added for Silverlight + } + } + } + } + } + + private bool IsIgnoredAssembly(Assembly assembly) + { + // TODO - find a better way to remove "system" assemblies from the auto registration + var ignoreChecks = new List>() + { + asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal), + asm => asm.FullName.StartsWith("xunit.", StringComparison.Ordinal), + }; + + foreach (var check in ignoreChecks) + { + if (check(assembly)) + return true; + } + + return false; + } + + private bool IsIgnoredType(Type type, Func registrationPredicate) + { + // TODO - find a better way to remove "system" types from the auto registration + var ignoreChecks = new List>() + { + t => t.FullName.StartsWith("System.", StringComparison.Ordinal), + t => t.FullName.StartsWith("Microsoft.", StringComparison.Ordinal), + t => t.IsPrimitive(), +#if !UNBOUND_GENERICS_GETCONSTRUCTORS + t => t.IsGenericTypeDefinition(), +#endif + t => (t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0) && !(t.IsInterface() || t.IsAbstract()), + }; + + if (registrationPredicate != null) + { + ignoreChecks.Add(t => !registrationPredicate(t)); + } + + foreach (var check in ignoreChecks) + { + if (check(type)) + return true; + } + + return false; + } + + private void RegisterDefaultTypes() + { + Register(this); + +#if TINYMESSENGER + // Only register the TinyMessenger singleton if we are the root container + if (_Parent == null) + Register(); +#endif + } + + private ObjectFactoryBase GetCurrentFactory(TypeRegistration registration) + { + ObjectFactoryBase current = null; + + _RegisteredTypes.TryGetValue(registration, out current); + + return current; + } + + private RegisterOptions RegisterInternal(Type registerType, string name, ObjectFactoryBase factory) + { + var typeRegistration = new TypeRegistration(registerType, name); + + return AddUpdateRegistration(typeRegistration, factory); + } + + private RegisterOptions AddUpdateRegistration(TypeRegistration typeRegistration, ObjectFactoryBase factory) + { + _RegisteredTypes[typeRegistration] = factory; + + return new RegisterOptions(this, typeRegistration); + } + + private bool RemoveRegistration(TypeRegistration typeRegistration) + { + return _RegisteredTypes.Remove(typeRegistration); + } + + private ObjectFactoryBase GetDefaultObjectFactory(Type registerType, Type registerImplementation) + { + //#if NETFX_CORE + // if (registerType.GetTypeInfo().IsInterface() || registerType.GetTypeInfo().IsAbstract()) + //#else + if (registerType.IsInterface() || registerType.IsAbstract()) + //#endif + return new SingletonFactory(registerType, registerImplementation); + + return new MultiInstanceFactory(registerType, registerImplementation); + } + + private bool CanResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + Type checkType = registration.Type; + string name = registration.Name; + + ObjectFactoryBase factory; + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType, name), out factory)) + { + if (factory.AssumeConstruction) + return true; + + if (factory.Constructor == null) + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + else + return CanConstruct(factory.Constructor, parameters, options); + } + +#if RESOLVE_OPEN_GENERICS + if (checkType.IsInterface() && checkType.IsGenericType()) + { + // if the type is registered as an open generic, then see if the open generic is registered + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType.GetGenericTypeDefinition(), name), out factory)) + { + if (factory.AssumeConstruction) + return true; + + if (factory.Constructor == null) + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + else + return CanConstruct(factory.Constructor, parameters, options); + } + } +#endif + + // Fail if requesting named resolution and settings set to fail if unresolved + // Or bubble up if we have a parent + if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) + return (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; + + // Attempted unnamed fallback container resolution if relevant and requested + if (!string.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) + { + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType), out factory)) + { + if (factory.AssumeConstruction) + return true; + + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + } + } + + // Check if type is an automatic lazy factory request + if (IsAutomaticLazyFactoryRequest(checkType)) + return true; + + // Check if type is an IEnumerable + if (IsIEnumerableRequest(registration.Type)) + return true; + + // Attempt unregistered construction if possible and requested + // If we cant', bubble if we have a parent + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (checkType.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + return (GetBestConstructor(checkType, parameters, options) != null) ? true : (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; + + // Bubble resolution up the container tree if we have a parent + if (_Parent != null) + return _Parent.CanResolveInternal(registration, parameters, options); + + return false; + } + + private bool IsIEnumerableRequest(Type type) + { + if (!type.IsGenericType()) + return false; + + Type genericType = type.GetGenericTypeDefinition(); + + if (genericType == typeof(IEnumerable<>)) + return true; + + return false; + } + + private bool IsAutomaticLazyFactoryRequest(Type type) + { + if (!type.IsGenericType()) + return false; + + Type genericType = type.GetGenericTypeDefinition(); + + // Just a func + if (genericType == typeof(Func<>)) + return true; + + // 2 parameter func with string as first parameter (name) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string))) + //#else + if ((genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(string))) + //#endif + return true; + + // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,,>) && type.GetTypeInfo().GenericTypeArguments[0] == typeof(string) && type.GetTypeInfo().GenericTypeArguments[1] == typeof(IDictionary))) + //#else + if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + //#endif + return true; + + return false; + } + + private ObjectFactoryBase GetParentObjectFactory(TypeRegistration registration) + { + if (_Parent == null) + return null; + + ObjectFactoryBase factory; + + if (_Parent._RegisteredTypes.TryGetValue(registration, out factory)) + { + return factory.GetFactoryForChildContainer(registration.Type, _Parent, this); + } + +#if RESOLVE_OPEN_GENERICS + // Attempt container resolution of open generic + if (registration.Type.IsGenericType()) + { + var openTypeRegistration = new TypeRegistration(registration.Type.GetGenericTypeDefinition(), + registration.Name); + + if (_Parent._RegisteredTypes.TryGetValue(openTypeRegistration, out factory)) + { + return factory.GetFactoryForChildContainer(registration.Type, _Parent, this); + } + + return _Parent.GetParentObjectFactory(registration); + } +#endif + + return _Parent.GetParentObjectFactory(registration); + } + + private object ResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) + { + ObjectFactoryBase factory; + + // Attempt container resolution + if (_RegisteredTypes.TryGetValue(registration, out factory)) + { + try + { + return factory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + +#if RESOLVE_OPEN_GENERICS + // Attempt container resolution of open generic + if (registration.Type.IsGenericType()) + { + var openTypeRegistration = new TypeRegistration(registration.Type.GetGenericTypeDefinition(), + registration.Name); + + if (_RegisteredTypes.TryGetValue(openTypeRegistration, out factory)) + { + try + { + return factory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + } +#endif + + // Attempt to get a factory from parent if we can + var bubbledObjectFactory = GetParentObjectFactory(registration); + if (bubbledObjectFactory != null) + { + try + { + return bubbledObjectFactory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + + // Fail if requesting named resolution and settings set to fail if unresolved + if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) + throw new TinyIoCResolutionException(registration.Type); + + // Attempted unnamed fallback container resolution if relevant and requested + if (!string.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) + { + if (_RegisteredTypes.TryGetValue(new TypeRegistration(registration.Type, string.Empty), out factory)) + { + try + { + return factory.GetObject(registration.Type, this, parameters, options); + } + catch (TinyIoCResolutionException) + { + throw; + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + } + +#if EXPRESSIONS + // Attempt to construct an automatic lazy factory if possible + if (IsAutomaticLazyFactoryRequest(registration.Type)) + return GetLazyAutomaticFactoryRequest(registration.Type); +#endif + if (IsIEnumerableRequest(registration.Type)) + return GetIEnumerableRequest(registration.Type); + + // Attempt unregistered construction if possible and requested + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (registration.Type.IsGenericType() && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + { + if (!registration.Type.IsAbstract() && !registration.Type.IsInterface()) + return ConstructType(null, registration.Type, parameters, options); + } + + // Unable to resolve - throw + throw new TinyIoCResolutionException(registration.Type); + } + +#if EXPRESSIONS + private object GetLazyAutomaticFactoryRequest(Type type) + { + if (!type.IsGenericType()) + return null; + + Type genericType = type.GetGenericTypeDefinition(); + //#if NETFX_CORE + // Type[] genericArguments = type.GetTypeInfo().GenericTypeArguments.ToArray(); + //#else + Type[] genericArguments = type.GetGenericArguments(); + //#endif + + // Just a func + if (genericType == typeof(Func<>)) + { + Type returnType = genericArguments[0]; + + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => !mi.GetParameters().Any()); + //#else + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { }); + //#endif + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod); + + var resolveLambda = Expression.Lambda(resolveCall).Compile(); + + return resolveLambda; + } + + // 2 parameter func with string as first parameter (name) + if ((genericType == typeof(Func<,>)) && (genericArguments[0] == typeof(string))) + { + Type returnType = genericArguments[1]; + + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 1 && mi.GetParameters()[0].GetType() == typeof(String)); + //#else + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String) }); + //#endif + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + ParameterExpression[] resolveParameters = new ParameterExpression[] { Expression.Parameter(typeof(String), "name") }; + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, resolveParameters); + + var resolveLambda = Expression.Lambda(resolveCall, resolveParameters).Compile(); + + return resolveLambda; + } + + // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + //#if NETFX_CORE + // if ((genericType == typeof(Func<,,>) && type.GenericTypeArguments[0] == typeof(string) && type.GenericTypeArguments[1] == typeof(IDictionary))) + //#else + if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + //#endif + { + Type returnType = genericArguments[2]; + + var name = Expression.Parameter(typeof(string), "name"); + var parameters = Expression.Parameter(typeof(IDictionary), "parameters"); + + //#if NETFX_CORE + // MethodInfo resolveMethod = typeof(TinyIoCContainer).GetTypeInfo().GetDeclaredMethods("Resolve").First(mi => mi.GetParameters().Length == 2 && mi.GetParameters()[0].GetType() == typeof(String) && mi.GetParameters()[1].GetType() == typeof(NamedParameterOverloads)); + //#else + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String), typeof(NamedParameterOverloads) }); + //#endif + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, name, Expression.Call(typeof(NamedParameterOverloads), "FromIDictionary", null, parameters)); + + var resolveLambda = Expression.Lambda(resolveCall, name, parameters).Compile(); + + return resolveLambda; + } + + throw new TinyIoCResolutionException(type); + } +#endif + private object GetIEnumerableRequest(Type type) + { + //#if NETFX_CORE + // var genericResolveAllMethod = this.GetType().GetGenericMethod("ResolveAll", type.GenericTypeArguments, new[] { typeof(bool) }); + //#else + var genericResolveAllMethod = this.GetType().GetGenericMethod(BindingFlags.Public | BindingFlags.Instance, "ResolveAll", type.GetGenericArguments(), new[] { typeof(bool) }); + //#endif + + return genericResolveAllMethod.Invoke(this, new object[] { false }); + } + + private bool CanConstruct(ConstructorInfo ctor, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + foreach (var parameter in ctor.GetParameters()) + { + if (string.IsNullOrEmpty(parameter.Name)) + return false; + + var isParameterOverload = parameters.ContainsKey(parameter.Name); + + //#if NETFX_CORE + // if (parameter.ParameterType.GetTypeInfo().IsPrimitive && !isParameterOverload) + //#else + if (parameter.ParameterType.IsPrimitive() && !isParameterOverload) + //#endif + return false; + + if (!isParameterOverload && !CanResolveInternal(new TypeRegistration(parameter.ParameterType), NamedParameterOverloads.Default, options)) + return false; + } + + return true; + } + + private ConstructorInfo GetBestConstructor(Type type, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + //#if NETFX_CORE + // if (type.GetTypeInfo().IsValueType) + //#else + if (type.IsValueType()) + //#endif + return null; + + // Get constructors in reverse order based on the number of parameters + // i.e. be as "greedy" as possible so we satify the most amount of dependencies possible + var ctors = this.GetTypeConstructors(type); + + foreach (var ctor in ctors) + { + if (this.CanConstruct(ctor, parameters, options)) + return ctor; + } + + return null; + } + + private IEnumerable GetTypeConstructors(Type type) + { + //#if NETFX_CORE + // return type.GetTypeInfo().DeclaredConstructors.OrderByDescending(ctor => ctor.GetParameters().Count()); + //#else + var candidateCtors = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(x => !x.IsPrivate) // Includes internal constructors but not private constructors + .Where(x => !x.IsFamily) // Excludes protected constructors + .ToList(); + + var attributeCtors = candidateCtors.Where(x => x.GetCustomAttributes(typeof(TinyIoCConstructorAttribute), false).Any()) + .ToList(); + + if (attributeCtors.Any()) + candidateCtors = attributeCtors; + + return candidateCtors.OrderByDescending(ctor => ctor.GetParameters().Count()); + //#endif + } + + private object ConstructType(Type requestedType, Type implementationType, ResolveOptions options) + { + return ConstructType(requestedType, implementationType, null, NamedParameterOverloads.Default, options); + } + + private object ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, ResolveOptions options) + { + return ConstructType(requestedType, implementationType, constructor, NamedParameterOverloads.Default, options); + } + + private object ConstructType(Type requestedType, Type implementationType, NamedParameterOverloads parameters, ResolveOptions options) + { + return ConstructType(requestedType, implementationType, null, parameters, options); + } + + private object ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) + { + var typeToConstruct = implementationType; + +#if RESOLVE_OPEN_GENERICS + if (implementationType.IsGenericTypeDefinition()) + { + if (requestedType == null || !requestedType.IsGenericType() || !requestedType.GetGenericArguments().Any()) + throw new TinyIoCResolutionException(typeToConstruct); + + typeToConstruct = typeToConstruct.MakeGenericType(requestedType.GetGenericArguments()); + } +#endif + if (constructor == null) + { + // Try and get the best constructor that we can construct + // if we can't construct any then get the constructor + // with the least number of parameters so we can throw a meaningful + // resolve exception + constructor = GetBestConstructor(typeToConstruct, parameters, options) ?? GetTypeConstructors(typeToConstruct).LastOrDefault(); + } + + if (constructor == null) + throw new TinyIoCResolutionException(typeToConstruct); + + var ctorParams = constructor.GetParameters(); + object[] args = new object[ctorParams.Count()]; + + for (int parameterIndex = 0; parameterIndex < ctorParams.Count(); parameterIndex++) + { + var currentParam = ctorParams[parameterIndex]; + + try + { + args[parameterIndex] = parameters.ContainsKey(currentParam.Name) ? + parameters[currentParam.Name] : + ResolveInternal( + new TypeRegistration(currentParam.ParameterType), + NamedParameterOverloads.Default, + options); + } + catch (TinyIoCResolutionException ex) + { + // If a constructor parameter can't be resolved + // it will throw, so wrap it and throw that this can't + // be resolved. + throw new TinyIoCResolutionException(typeToConstruct, ex); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(typeToConstruct, ex); + } + } + + try + { +#if USE_OBJECT_CONSTRUCTOR + var constructionDelegate = CreateObjectConstructionDelegateWithCache(constructor); + return constructionDelegate.Invoke(args); +#else + return constructor.Invoke(args); +#endif + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(typeToConstruct, ex); + } + } + +#if USE_OBJECT_CONSTRUCTOR + private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor) + { + ObjectConstructor objectConstructor; + if (_ObjectConstructorCache.TryGetValue(constructor, out objectConstructor)) + return objectConstructor; + + // We could lock the cache here, but there's no real side + // effect to two threads creating the same ObjectConstructor + // at the same time, compared to the cost of a lock for + // every creation. + var constructorParams = constructor.GetParameters(); + var lambdaParams = Expression.Parameter(typeof(object[]), "parameters"); + var newParams = new Expression[constructorParams.Length]; + + for (int i = 0; i < constructorParams.Length; i++) + { + var paramsParameter = Expression.ArrayIndex(lambdaParams, Expression.Constant(i)); + + newParams[i] = Expression.Convert(paramsParameter, constructorParams[i].ParameterType); + } + + var newExpression = Expression.New(constructor, newParams); + + var constructionLambda = Expression.Lambda(typeof(ObjectConstructor), newExpression, lambdaParams); + + objectConstructor = (ObjectConstructor)constructionLambda.Compile(); + + _ObjectConstructorCache[constructor] = objectConstructor; + return objectConstructor; + } +#endif + + private void BuildUpInternal(object input, ResolveOptions resolveOptions) + { + //#if NETFX_CORE + // var properties = from property in input.GetType().GetTypeInfo().DeclaredProperties + // where (property.GetMethod != null) && (property.SetMethod != null) && !property.PropertyType.GetTypeInfo().IsValueType + // select property; + //#else + var properties = from property in input.GetType().GetProperties() + where (property.GetGetMethod() != null) && (property.GetSetMethod() != null) && !property.PropertyType.IsValueType() + select property; + //#endif + + foreach (var property in properties) + { + if (property.GetValue(input, null) == null) + { + try + { + property.SetValue(input, ResolveInternal(new TypeRegistration(property.PropertyType), NamedParameterOverloads.Default, resolveOptions), null); + } + catch (TinyIoCResolutionException) + { + // Catch any resolution errors and ignore them + } + } + } + } + + private IEnumerable GetParentRegistrationsForType(Type resolveType) + { + if (_Parent == null) + return new TypeRegistration[] { }; + + var registrations = _Parent._RegisteredTypes.Keys.Where(tr => tr.Type == resolveType); + + return registrations.Concat(_Parent.GetParentRegistrationsForType(resolveType)); + } + + private IEnumerable ResolveAllInternal(Type resolveType, bool includeUnnamed) + { + var registrations = _RegisteredTypes.Keys.Where(tr => tr.Type == resolveType).Concat(GetParentRegistrationsForType(resolveType)).Distinct(); + + if (!includeUnnamed) + registrations = registrations.Where(tr => tr.Name != string.Empty); + + return registrations.Select(registration => this.ResolveInternal(registration, NamedParameterOverloads.Default, ResolveOptions.Default)); + } + + private static bool IsValidAssignment(Type registerType, Type registerImplementation) + { + if (!registerType.IsGenericTypeDefinition()) + { + if (!registerType.IsAssignableFrom(registerImplementation)) + return false; + } + else + { + if (registerType.IsInterface()) + { +#if (PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6) + if (!registerImplementation.GetInterfaces().Any(t => t.Name == registerType.Name)) + return false; +#else + if (!registerImplementation.FindInterfaces((t, o) => t.Name == registerType.Name, null).Any()) + return false; +#endif + } + else if (registerType.IsAbstract() && registerImplementation.BaseType() != registerType) + { + return false; + } + } + //#endif + return true; + } + +#endregion + +#region IDisposable Members + bool disposed = false; + public void Dispose() + { + if (!disposed) + { + disposed = true; + + _RegisteredTypes.Dispose(); + + GC.SuppressFinalize(this); + } + } + +#endregion + } + +#if PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6 + static class ReverseTypeExtender + { + public static bool IsClass(this Type type) + { + return type.GetTypeInfo().IsClass; + } + + public static bool IsAbstract(this Type type) + { + return type.GetTypeInfo().IsAbstract; + } + + public static bool IsInterface(this Type type) + { + return type.GetTypeInfo().IsInterface; + } + + public static bool IsPrimitive(this Type type) + { + return type.GetTypeInfo().IsPrimitive; + } + + public static bool IsValueType(this Type type) + { + return type.GetTypeInfo().IsValueType; + } + + public static bool IsGenericType(this Type type) + { + return type.GetTypeInfo().IsGenericType; + } + + public static bool IsGenericParameter(this Type type) + { + return type.IsGenericParameter; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.GetTypeInfo().IsGenericTypeDefinition; + } + + public static Type BaseType(this Type type) + { + return type.GetTypeInfo().BaseType; + } + + public static Assembly Assembly(this Type type) + { + return type.GetTypeInfo().Assembly; + } + } +#endif + // reverse shim for WinRT SR changes... +#if (!NETFX_CORE && !PORTABLE && !NETSTANDARD1_0 && !NETSTANDARD1_1 && !NETSTANDARD1_2 && !NETSTANDARD1_3 && !NETSTANDARD1_4 && !NETSTANDARD1_5 && !NETSTANDARD1_6) + static class ReverseTypeExtender + { + public static bool IsClass(this Type type) + { + return type.IsClass; + } + + public static bool IsAbstract(this Type type) + { + return type.IsAbstract; + } + + public static bool IsInterface(this Type type) + { + return type.IsInterface; + } + + public static bool IsPrimitive(this Type type) + { + return type.IsPrimitive; + } + + public static bool IsValueType(this Type type) + { + return type.IsValueType; + } + + public static bool IsGenericType(this Type type) + { + return type.IsGenericType; + } + + public static bool IsGenericParameter(this Type type) + { + return type.IsGenericParameter; + } + + public static bool IsGenericTypeDefinition(this Type type) + { + return type.IsGenericTypeDefinition; + } + + public static Type BaseType(this Type type) + { + return type.BaseType; + } + + public static Assembly Assembly(this Type type) + { + return type.Assembly; + } + } +#endif + + [AttributeUsage(AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)] +#if TINYIOC_INTERNAL + internal +#else + public +#endif + sealed class TinyIoCConstructorAttribute : Attribute + { + } +} diff --git a/src/ScriptEngine/Hosting/TinyIocImplementation.cs b/src/ScriptEngine/Hosting/TinyIocImplementation.cs new file mode 100644 index 000000000..a2983c405 --- /dev/null +++ b/src/ScriptEngine/Hosting/TinyIocImplementation.cs @@ -0,0 +1,168 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.DependencyInjection; +using TinyIoC; + +namespace ScriptEngine.Hosting +{ + public class TinyIocImplementation : IServiceDefinitions, IServiceContainer + { + private readonly TinyIoCContainer _container; + private readonly Dictionary> _multiRegistrations = new Dictionary>(); + + private readonly ISet _scopedRegistrations = new HashSet(); + + #region Registration API + + public TinyIocImplementation() + { + _container = new TinyIoCContainer(); + } + + private TinyIocImplementation(TinyIoCContainer container) + { + _container = container; + } + + public IServiceContainer CreateContainer() + { + foreach (var registration in _multiRegistrations) + { + _container.RegisterMultiple(registration.Key, registration.Value).AsMultiInstance(); + } + + return this; + } + + public void Register(Type knownType) + { + _container.Register(knownType).AsMultiInstance(); + } + + public void Register(Type interfaceType, Type implementation) + { + _container.Register(interfaceType, implementation).AsMultiInstance(); + } + + public void Register() where T : class + { + _container.Register().AsMultiInstance(); + } + + public void Register(T instance) where T : class + { + _container.Register(instance); + } + + public void Register() where T : class where TImpl : class, T + { + _container.Register().AsMultiInstance(); + } + + public void Register(Func factory) where T : class + { + _container.Register((t,p) => factory(this)); + } + + public void RegisterSingleton(Type knownType) + { + _container.Register(knownType).AsSingleton(); + } + + public void RegisterSingleton(Type interfaceType, Type implementation) + { + _container.Register(interfaceType, implementation).AsSingleton(); + } + + public void RegisterSingleton() where T : class + { + _container.Register().AsSingleton(); + } + + public void RegisterSingleton(T instance) where T : class + { + _container.Register(instance); + } + + public void RegisterSingleton() where T : class where TImpl : class, T + { + _container.Register().AsSingleton(); + } + + public void RegisterSingleton(Func factory) where T : class + { + _container.Register((t, p) => factory(this)).AsSingleton(); + } + + public void RegisterScoped() where T : class + { + _scopedRegistrations.Add(typeof(T)); + } + + public void RegisterEnumerable() where T : class where TImpl : class, T + { + if (!_multiRegistrations.TryGetValue(typeof(T), out var list)) + { + list = new List(); + _multiRegistrations[typeof(T)] = list; + } + + list.Add(typeof(TImpl)); + } + + #endregion + + #region Resolution API + + public object Resolve(Type type) + { + return _container.Resolve(type); + } + + public T Resolve() where T : class + { + return (T) Resolve(typeof(T)); + } + + public object TryResolve(Type type) + { + var resolved = _container.TryResolve(type, out var instance); + return resolved ? instance : default; + } + + public T TryResolve() where T : class + { + return (T) TryResolve(typeof(T)); + } + + public IEnumerable ResolveEnumerable() where T : class + { + return _container.ResolveAll(); + } + + public IServiceContainer CreateScope() + { + var child = new TinyIocImplementation(_container.GetChildContainer()); + foreach (var scopedRegistration in _scopedRegistrations) + { + child._container.Register(scopedRegistration).AsSingleton(); + } + + return child; + } + + #endregion + + public void Dispose() + { + _container.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/IDependencyResolver.cs b/src/ScriptEngine/IDependencyResolver.cs new file mode 100644 index 000000000..76369776a --- /dev/null +++ b/src/ScriptEngine/IDependencyResolver.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Compilation; + +namespace ScriptEngine +{ + /// + /// Разрешитель внешних зависимостей (библиотек) + /// + public interface IDependencyResolver : ICompileTimeDependencyResolver + { + /// + /// Инициализировать разрешитель. Вызывается при создании ScriptingEngine + /// + /// Движок, который разрешитель может сохранить у себя + void Initialize(ScriptingEngine engine); + } +} \ No newline at end of file diff --git a/src/ScriptEngine/IGlobalsManager.cs b/src/ScriptEngine/IGlobalsManager.cs new file mode 100644 index 000000000..7ca04ed44 --- /dev/null +++ b/src/ScriptEngine/IGlobalsManager.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace ScriptEngine +{ + public interface IGlobalsManager : IEnumerable>, IDisposable + { + void RegisterInstance(object instance); + void RegisterInstance(Type type, object instance); + object GetInstance(Type type); + T GetInstance(); + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Libraries/ExternalLibraryInfo.cs b/src/ScriptEngine/Libraries/ExternalLibraryInfo.cs new file mode 100644 index 000000000..199c229d2 --- /dev/null +++ b/src/ScriptEngine/Libraries/ExternalLibraryInfo.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using OneScript.Compilation; + +namespace ScriptEngine.Libraries +{ + /// + /// Информация о внешней библиотеке и её компонентах. + /// + public class ExternalLibraryInfo + { + public ExternalLibraryInfo(PackageInfo package) + { + Package = package; + } + + public PackageInfo Package { get; } + + public List Modules { get; } = new List(); + public List Classes { get; } = new List(); + + public void AddModule(string symbol, string filePath) + { + Modules.Add(new UserAddedScript { Symbol = symbol, FilePath = filePath }); + } + + public void AddClass(string symbol, string filePath) + { + Classes.Add(new UserAddedScript { Symbol = symbol, FilePath = filePath }); + } + } +} + diff --git a/src/ScriptEngine/Libraries/ILibraryManager.cs b/src/ScriptEngine/Libraries/ILibraryManager.cs new file mode 100644 index 000000000..50134257c --- /dev/null +++ b/src/ScriptEngine/Libraries/ILibraryManager.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Execution; + +namespace ScriptEngine.Libraries +{ + /// + /// Менеджер загрузки внешних библиотек + /// + public interface ILibraryManager + { + void InitExternalLibrary(ScriptingEngine runtime, ExternalLibraryInfo library, IBslProcess process); + } +} diff --git a/src/ScriptEngine/Libraries/LibraryManager.cs b/src/ScriptEngine/Libraries/LibraryManager.cs new file mode 100644 index 000000000..0a11d13fc --- /dev/null +++ b/src/ScriptEngine/Libraries/LibraryManager.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Commons; +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; +using OneScript.Execution; + +namespace ScriptEngine.Libraries +{ + /// + /// Менеджер загрузки внешних библиотек + /// + internal class LibraryManager : ILibraryManager + { + private readonly IRuntimeContextInstance _contextOfGlobalSymbols; + + public LibraryManager(IRuntimeContextInstance contextOfGlobalSymbols) + { + _contextOfGlobalSymbols = contextOfGlobalSymbols; + } + + public void InitExternalLibrary(ScriptingEngine runtime, ExternalLibraryInfo library, IBslProcess process) + { + var loadedObjects = new ScriptDrivenObject[library.Modules.Count]; + int i = 0; + foreach (var module in library.Modules) + { + var instance = runtime.CreateUninitializedSDO(module.Module); + + var propId = _contextOfGlobalSymbols.GetPropertyNumber(module.Symbol); + _contextOfGlobalSymbols.SetPropValue(propId, instance); + loadedObjects[i++] = instance; + } + + loadedObjects.ForEach(sdo => runtime.InitializeSDO(sdo, process)); + } + } +} diff --git a/src/ScriptEngine/Libraries/UserAddedScript.cs b/src/ScriptEngine/Libraries/UserAddedScript.cs new file mode 100644 index 000000000..60748566f --- /dev/null +++ b/src/ScriptEngine/Libraries/UserAddedScript.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Execution; + +namespace ScriptEngine.Libraries +{ + /// + /// Информация о добавленном пользовательском скрипте. + /// Используется для сериализации в ApplicationDump. + /// + public class UserAddedScript + { + public string Symbol { get; set; } + public string FilePath { get; set; } + public string FileName { get; set; } + public IExecutableModule Module { get; set; } + } +} diff --git a/src/ScriptEngine/Machine/AnnotationDefinition.cs b/src/ScriptEngine/Machine/AnnotationDefinition.cs new file mode 100644 index 000000000..4a8aa2634 --- /dev/null +++ b/src/ScriptEngine/Machine/AnnotationDefinition.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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 ScriptEngine.Machine +{ + public struct AnnotationDefinition + { + public string Name; + public AnnotationParameter[] Parameters; + public readonly int ParamCount => Parameters?.Length ?? 0; + } +} diff --git a/src/ScriptEngine/Machine/AnnotationParameter.cs b/src/ScriptEngine/Machine/AnnotationParameter.cs new file mode 100644 index 000000000..2193df9d4 --- /dev/null +++ b/src/ScriptEngine/Machine/AnnotationParameter.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Commons; + +namespace ScriptEngine.Machine +{ + public struct AnnotationParameter + { + public string Name; + + public IValue RuntimeValue; + + public override readonly string ToString() + { + return Utils.NameAndValuePresentation(Name, RuntimeValue); + } + + } +} diff --git a/src/ScriptEngine/Machine/BuiltinFunctions.cs b/src/ScriptEngine/Machine/BuiltinFunctions.cs index f31e69ada..6b6bf3d12 100644 --- a/src/ScriptEngine/Machine/BuiltinFunctions.cs +++ b/src/ScriptEngine/Machine/BuiltinFunctions.cs @@ -6,24 +6,36 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Values; namespace ScriptEngine.Machine { - static class BuiltinFunctions + public static class BuiltinFunctions { - static readonly Dictionary _paramInfoCache = new Dictionary(); + static readonly Dictionary _paramInfoCache = new Dictionary(); - private static readonly ParameterDefinition MANDATORY_BYVAL = new ParameterDefinition { IsByValue = true }; - private static readonly ParameterDefinition OPTIONAL_BYVAL = new ParameterDefinition { IsByValue = true, HasDefaultValue = true }; + private static readonly ParameterInfo MANDATORY_BYVAL; + private static readonly ParameterInfo OPTIONAL_BYVAL; private const int BUILTIN_OPCODES_INDEX = (int)OperationCode.Eval; static BuiltinFunctions() { + MANDATORY_BYVAL = new BslParameterBuilder() + .ByValue(true) + .Build(); + + OPTIONAL_BYVAL = new BslParameterBuilder() + .ByValue(true) + .DefaultValue(BslUndefinedValue.Instance) + .Build(); + InitParametersInfo(); } - public static ParameterDefinition[] ParametersInfo(OperationCode funcOpcode) + public static ParameterInfo[] ParametersInfo(OperationCode funcOpcode) { return _paramInfoCache[funcOpcode]; } @@ -78,6 +90,7 @@ private static void InitParametersInfo() AddFunc(OperationCode.Hour, MANDATORY_BYVAL); AddFunc(OperationCode.Minute, MANDATORY_BYVAL); AddFunc(OperationCode.Second, MANDATORY_BYVAL); + AddFunc(OperationCode.BegOfWeek, MANDATORY_BYVAL); AddFunc(OperationCode.BegOfYear, MANDATORY_BYVAL); AddFunc(OperationCode.BegOfMonth, MANDATORY_BYVAL); AddFunc(OperationCode.BegOfDay, MANDATORY_BYVAL); @@ -90,6 +103,7 @@ private static void InitParametersInfo() AddFunc(OperationCode.EndOfHour, MANDATORY_BYVAL); AddFunc(OperationCode.EndOfMinute, MANDATORY_BYVAL); AddFunc(OperationCode.EndOfQuarter, MANDATORY_BYVAL); + AddFunc(OperationCode.EndOfWeek, MANDATORY_BYVAL); AddFunc(OperationCode.WeekOfYear, MANDATORY_BYVAL); AddFunc(OperationCode.DayOfYear, MANDATORY_BYVAL); AddFunc(OperationCode.DayOfWeek, MANDATORY_BYVAL); @@ -118,7 +132,7 @@ private static void InitParametersInfo() AddFunc(OperationCode.ModuleInfo); } - private static void AddFunc(OperationCode opCode, params ParameterDefinition[] parameters) + private static void AddFunc(OperationCode opCode, params ParameterInfo[] parameters) { _paramInfoCache[opCode] = parameters; } diff --git a/src/ScriptEngine/Machine/CodeStat/CodeStatProcessor.cs b/src/ScriptEngine/Machine/CodeStat/CodeStatProcessor.cs index cee7b1f86..15be6508f 100644 --- a/src/ScriptEngine/Machine/CodeStat/CodeStatProcessor.cs +++ b/src/ScriptEngine/Machine/CodeStat/CodeStatProcessor.cs @@ -1,94 +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.Diagnostics; -using System.Collections.Generic; - -namespace ScriptEngine.Machine -{ - public class CodeStatProcessor : ICodeStatCollector - { - private Dictionary _codeStat = new Dictionary(); - private Dictionary _watchers = new Dictionary(); - private Stopwatch _activeStopwatch = null; - private HashSet _preparedScripts = new HashSet(); - - public CodeStatProcessor() - { - } - - public bool IsPrepared(string ScriptFileName) - { - return _preparedScripts.Contains(ScriptFileName); - } - - public void MarkEntryReached(CodeStatEntry entry, int count = 1) - { - int oldValue = 0; - _codeStat.TryGetValue(entry, out oldValue); - _codeStat[entry] = oldValue + count; - - if (count == 0) - { - if (!_watchers.ContainsKey(entry)) - { - _watchers.Add(entry, new Stopwatch()); - } - } - else - { - _activeStopwatch?.Stop(); - _activeStopwatch = _watchers[entry]; - _activeStopwatch.Start(); - } - } - - public void MarkPrepared(string scriptFileName) - { - _preparedScripts.Add(scriptFileName); - } - - public CodeStatDataCollection GetStatData() - { - CodeStatDataCollection data = new CodeStatDataCollection(); - foreach (var item in _codeStat) - { - if (!IsPrepared(item.Key.ScriptFileName)) - { - continue; - } - data.Add(new CodeStatData(item.Key, _watchers[item.Key].ElapsedMilliseconds, item.Value)); - } - - return data; - } - - public void EndCodeStat() - { - _activeStopwatch?.Stop(); - } - - public void StopWatch(CodeStatEntry entry) - { - if (_watchers.ContainsKey(entry)) - { - _watchers[entry].Stop(); - } - } - - public void ResumeWatch(CodeStatEntry entry) - { - _activeStopwatch?.Stop(); - - if (_watchers.ContainsKey(entry)) - { - _activeStopwatch = _watchers[entry]; - _activeStopwatch.Start(); - } - } - } -} +/*---------------------------------------------------------- +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.Diagnostics; +using System.Collections.Generic; + +namespace ScriptEngine.Machine +{ + public class CodeStatProcessor : ICodeStatCollector + { + private Dictionary _codeStat = new Dictionary(); + private Dictionary _watchers = new Dictionary(); + private Stopwatch _activeStopwatch = null; + private HashSet _preparedScripts = new HashSet(); + + public bool IsPrepared(string ScriptFileName) + { + return _preparedScripts.Contains(ScriptFileName); + } + + public void MarkEntryReached(CodeStatEntry entry, int count = 1) + { + int oldValue = 0; + _codeStat.TryGetValue(entry, out oldValue); + _codeStat[entry] = oldValue + count; + + if (count == 0) + { + if (!_watchers.ContainsKey(entry)) + { + _watchers.Add(entry, new Stopwatch()); + } + } + else + { + _activeStopwatch?.Stop(); + _activeStopwatch = _watchers[entry]; + _activeStopwatch.Start(); + } + } + + public void MarkPrepared(string scriptFileName) + { + _preparedScripts.Add(scriptFileName); + } + + public CodeStatDataCollection GetStatData() + { + CodeStatDataCollection data = new CodeStatDataCollection(); + foreach (var item in _codeStat) + { + if (!IsPrepared(item.Key.ScriptFileName)) + { + continue; + } + data.Add(new CodeStatData(item.Key, _watchers[item.Key].ElapsedMilliseconds, item.Value)); + } + + return data; + } + + public void EndCodeStat() + { + _activeStopwatch?.Stop(); + } + + public void StopWatch(CodeStatEntry entry) + { + if (_watchers.ContainsKey(entry)) + { + _watchers[entry].Stop(); + } + } + + public void ResumeWatch(CodeStatEntry entry) + { + _activeStopwatch?.Stop(); + + if (_watchers.ContainsKey(entry)) + { + _activeStopwatch = _watchers[entry]; + _activeStopwatch.Start(); + } + } + } +} diff --git a/src/ScriptEngine/Machine/ComReflectionNameToIdMapper.cs b/src/ScriptEngine/Machine/ComReflectionNameToIdMapper.cs index 1feacb1cf..b7865000f 100644 --- a/src/ScriptEngine/Machine/ComReflectionNameToIdMapper.cs +++ b/src/ScriptEngine/Machine/ComReflectionNameToIdMapper.cs @@ -8,6 +8,8 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Linq; using System.Reflection; +using OneScript.Commons; +using OneScript.Exceptions; namespace ScriptEngine.Machine { @@ -54,7 +56,7 @@ public int FindProperty(string name) GetAllProperties(); var hasProperty = _propertyNames.TryGetIdOfName(name, out var id); if (!hasProperty) - throw RuntimeException.PropNotFoundException(name); + throw PropertyAccessException.PropNotFoundException(name); return id; } diff --git a/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs b/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs index 0d25f84f3..82f7db917 100755 --- a/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs +++ b/src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs @@ -7,25 +7,35 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; using System.Text; -using ScriptEngine.Environment; +using OneScript.Sources; using System.Security.Cryptography; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using ScriptEngine.Machine.Interfaces; namespace ScriptEngine.Machine.Contexts { public class AttachedScriptsFactory { - private readonly Dictionary _loadedModules; + private readonly Dictionary _loadedModules; private readonly Dictionary _fileHashes; private readonly ScriptingEngine _engine; internal AttachedScriptsFactory(ScriptingEngine engine) { - _loadedModules = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + _loadedModules = new Dictionary(StringComparer.InvariantCultureIgnoreCase); _fileHashes = new Dictionary(StringComparer.InvariantCultureIgnoreCase); _engine = engine; } + private ITypeManager TypeManager => _engine.TypeManager; + static string GetMd5Hash(MD5 md5Hash, string input) { @@ -47,7 +57,7 @@ static string GetMd5Hash(MD5 md5Hash, string input) return sBuilder.ToString(); } - public void AttachByPath(CompilerService compiler, string path, string typeName) + public void AttachByPath(ICompilerFrontend compiler, string path, string typeName, IBslProcess process) { if (!Utils.IsValidIdentifier(typeName)) throw RuntimeException.InvalidArgumentValue(); @@ -56,43 +66,45 @@ public void AttachByPath(CompilerService compiler, string path, string typeName) ThrowIfTypeExist(typeName, code); - LoadAndRegister(typeof(AttachedScriptsFactory), compiler, typeName, code); + CompileAndRegister(typeof(AttachedScriptsFactory), compiler, typeName, code, process); } - public void AttachFromString(CompilerService compiler, string text, string typeName) + public void AttachFromString(ICompilerFrontend compiler, string text, string typeName, IBslProcess process) { var code = _engine.Loader.FromString(text); ThrowIfTypeExist(typeName, code); - LoadAndRegister(typeof(AttachedScriptsFactory), compiler, typeName, code); + CompileAndRegister(typeof(AttachedScriptsFactory), compiler, typeName, code, process); } - public IRuntimeContextInstance LoadFromPath(CompilerService compiler, string path) + public UserScriptContextInstance LoadFromPath(ICompilerFrontend compiler, string path, IBslProcess process) { - return LoadFromPath(compiler, path, null); + return LoadFromPath(compiler, path, null, process); } - public IRuntimeContextInstance LoadFromPath(CompilerService compiler, string path, ExternalContextData externalContext) + public UserScriptContextInstance LoadFromPath(ICompilerFrontend compiler, string path, + ExternalContextData externalContext, IBslProcess process) { var code = _engine.Loader.FromFile(path); - return LoadAndCreate(compiler, code, externalContext); + return LoadAndCreate(compiler, code, externalContext, process); } - public IRuntimeContextInstance LoadFromString(CompilerService compiler, string text, ExternalContextData externalContext = null) + public UserScriptContextInstance LoadFromString(ICompilerFrontend compiler, string text, IBslProcess process, + ExternalContextData externalContext = null) { var code = _engine.Loader.FromString(text); - return LoadAndCreate(compiler, code, externalContext); + return LoadAndCreate(compiler, code, externalContext, process); } - private void ThrowIfTypeExist(string typeName, ICodeSource code) + private void ThrowIfTypeExist(string typeName, SourceCode code) { if (TypeManager.IsKnownType(typeName) && _loadedModules.ContainsKey(typeName)) { using (MD5 md5Hash = MD5.Create()) { - string moduleCode = code.Code; + string moduleCode = code.GetSourceCode(); string hash = GetMd5Hash(md5Hash, moduleCode); string storedHash = _fileHashes[typeName]; @@ -105,72 +117,61 @@ private void ThrowIfTypeExist(string typeName, ICodeSource code) } - private void LoadAndRegister(Type type, CompilerService compiler, string typeName, Environment.ICodeSource code) + private void CompileAndRegister(Type type, ICompilerFrontend compiler, string typeName, SourceCode code, IBslProcess process) { if(_loadedModules.ContainsKey(typeName)) { return; } - var module = CompileModuleFromSource(compiler, code, null); - var loaded = new LoadedModule(module); - - _loadedModules.Add(typeName, loaded); + var module = CompileModuleFromSource(compiler, code, null, process); + _loadedModules.Add(typeName, module); using(var md5Hash = MD5.Create()) { - var hash = GetMd5Hash(md5Hash, code.Code); + var hash = GetMd5Hash(md5Hash, code.GetSourceCode()); _fileHashes.Add(typeName, hash); } - TypeManager.RegisterType(typeName, type); + TypeManager.RegisterType(typeName, default, type); } - public void LoadAndRegister(string typeName, ModuleImage moduleImage) + public void RegisterTypeModule(string typeName, IExecutableModule module) { if (_loadedModules.ContainsKey(typeName)) { - var alreadyLoadedSrc = _loadedModules[typeName].ModuleInfo.Origin; - var currentSrc = moduleImage.ModuleInfo.Origin; + var alreadyLoadedSrc = (_loadedModules[typeName]).Source.Location; + var currentSrc = module.Source.Location; if(alreadyLoadedSrc != currentSrc) throw new RuntimeException("Type «" + typeName + "» already registered"); return; } - - var loadedModule = new LoadedModule(moduleImage); - _loadedModules.Add(typeName, loadedModule); - TypeManager.RegisterType(typeName, typeof(AttachedScriptsFactory)); - + _loadedModules.Add(typeName, module); + _engine.TypeManager.RegisterType(typeName, default, typeof(AttachedScriptsFactory)); } - - private IRuntimeContextInstance LoadAndCreate(CompilerService compiler, Environment.ICodeSource code, ExternalContextData externalContext) + + private UserScriptContextInstance LoadAndCreate(ICompilerFrontend compiler, SourceCode code, + ExternalContextData externalContext, IBslProcess process) { - var module = CompileModuleFromSource(compiler, code, externalContext); - var loadedHandle = new LoadedModule(module); - return _engine.NewObject(loadedHandle, externalContext); + var module = CompileModuleFromSource(compiler, code, externalContext, process); + return _engine.NewObject(module, process, externalContext); } - public ModuleImage CompileModuleFromSource(CompilerService compiler, Environment.ICodeSource code, ExternalContextData externalContext) + public IExecutableModule CompileModuleFromSource(ICompilerFrontend compiler, SourceCode code, ExternalContextData externalContext, IBslProcess process) { - compiler.DefineVariable("ЭтотОбъект", "ThisObject", SymbolType.ContextProperty); - - foreach (var methodInfo in UserScriptContextInstance.GetOwnMethodsDefinition()) - { - compiler.DefineMethod(methodInfo); - } - + var scope = compiler.FillSymbols(typeof(UserScriptContextInstance)); if (externalContext != null) { foreach (var item in externalContext) { - compiler.DefineVariable(item.Key, null, SymbolType.ContextProperty); + scope.Variables.Add(new LocalVariableSymbol(item.Key, item.Value.GetType())); } } - return compiler.Compile(code); + return compiler.Compile(code, process); } private static AttachedScriptsFactory _instance; @@ -184,20 +185,29 @@ internal static void SetInstance(AttachedScriptsFactory factory) _instance = factory; } - public static LoadedModule GetModuleOfType(string typeName) + public static IExecutableModule GetModuleOfType(string typeName) { return _instance._loadedModules[typeName]; } - [ScriptConstructor(ParametrizeWithClassName = true)] - public static UserScriptContextInstance ScriptFactory(string typeName, IValue[] arguments) + [ScriptConstructor] + public static UserScriptContextInstance ScriptFactory(TypeActivationContext context, IValue[] arguments) { - var module = _instance._loadedModules[typeName]; + var module = _instance._loadedModules[context.TypeName]; + + var type = context.TypeManager.GetTypeByName(context.TypeName); + UserScriptContextInstance newObj; + if (module.GetInterface() != null) + { + newObj = new UserIterableContextInstance(module, type, arguments); + } + else + { + newObj = new UserScriptContextInstance(module, type, arguments); + } - var newObj = new UserScriptContextInstance(module, typeName, arguments); - newObj.AddProperty("ЭтотОбъект", "ThisObject", newObj); newObj.InitOwnData(); - newObj.Initialize(); + newObj.Initialize(context.CurrentProcess); return newObj; } diff --git a/src/ScriptEngine/Machine/Contexts/AutoCollectionContext.cs b/src/ScriptEngine/Machine/Contexts/AutoCollectionContext.cs new file mode 100644 index 000000000..c59364eaf --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/AutoCollectionContext.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Types; + +namespace ScriptEngine.Machine.Contexts +{ + public abstract class AutoCollectionContext : AutoContext, ICollectionContext + where T : AutoContext + where TItem : IValue + { + protected AutoCollectionContext() + { + } + + protected AutoCollectionContext(TypeDescriptor type) : base(type) + { + } + + public abstract int Count(); + + public int Count(IBslProcess process) => Count(); + + public abstract IEnumerator GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/AutoContext.cs b/src/ScriptEngine/Machine/Contexts/AutoContext.cs index 3aca442eb..36933577c 100644 --- a/src/ScriptEngine/Machine/Contexts/AutoContext.cs +++ b/src/ScriptEngine/Machine/Contexts/AutoContext.cs @@ -7,11 +7,29 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Diagnostics; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using ScriptEngine.Types; namespace ScriptEngine.Machine.Contexts { public abstract class AutoContext : PropertyNameIndexAccessor where TInstance : AutoContext { + private static readonly ContextPropertyMapper _properties = new ContextPropertyMapper(); + private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); + private static readonly HashSet _warnedDeprecatedMethods = new HashSet(); + private static readonly TypeDescriptor _objectType = typeof(TInstance).GetTypeFromClassMarkup(); + + protected AutoContext() : base(_objectType) + { + } + + protected AutoContext(TypeDescriptor assignedType) : base(assignedType) + { + } + public override bool IsPropReadable(int propNum) { return _properties.GetProperty(propNum).CanRead; @@ -22,7 +40,7 @@ public override bool IsPropWritable(int propNum) return _properties.GetProperty(propNum).CanWrite; } - public override int FindProperty(string name) + public override int GetPropertyNumber(string name) { return _properties.FindProperty(name); } @@ -48,6 +66,7 @@ public override void SetPropValue(int propNum, IValue newVal) } catch (System.Reflection.TargetInvocationException e) { + Debug.Assert(e.InnerException != null); throw e.InnerException; } } @@ -62,7 +81,7 @@ public override string GetPropName(int propNum) return _properties.GetProperty(propNum).Name; } - public override int FindMethod(string name) + public override int GetMethodNumber(string name) { return _methods.FindMethod(name); } @@ -72,19 +91,30 @@ public override int GetMethodsCount() return _methods.Count; } - public override MethodInfo GetMethodInfo(int methodNumber) + public override BslMethodInfo GetMethodInfo(int methodNumber) + { + return _methods.GetRuntimeMethod(methodNumber); + } + + public override BslPropertyInfo GetPropertyInfo(int propertyNumber) { - return _methods.GetMethodInfo(methodNumber); + return _properties.GetProperty(propertyNumber).PropertyInfo; } + protected ContextPropertyMapper PropertyMapper => _properties; + protected ContextMethodsMapper MethodMapper => _methods; + private void CheckIfCallIsPossible(int methodNumber, IValue[] arguments) { - var methodInfo = _methods.GetMethodInfo(methodNumber); + var methodInfo = _methods.GetRuntimeMethod(methodNumber) as ContextMethodInfo; + if(methodInfo == null) + return; + if (!methodInfo.IsDeprecated) { return; } - if (methodInfo.ThrowOnUseDeprecated) + if (methodInfo.IsForbiddenToUse) { throw RuntimeException.DeprecatedMethodCall(methodInfo.Name); } @@ -96,12 +126,12 @@ private void CheckIfCallIsPossible(int methodNumber, IValue[] arguments) _warnedDeprecatedMethods.Add(methodNumber); } - public override void CallAsProcedure(int methodNumber, IValue[] arguments) + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) { CheckIfCallIsPossible(methodNumber, arguments); try { - _methods.GetMethod(methodNumber)((TInstance)this, arguments); + _methods.GetCallableDelegate(methodNumber)((TInstance)this, arguments, process); } catch (System.Reflection.TargetInvocationException e) { @@ -110,12 +140,12 @@ public override void CallAsProcedure(int methodNumber, IValue[] arguments) } } - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) { CheckIfCallIsPossible(methodNumber, arguments); try { - retValue = _methods.GetMethod(methodNumber)((TInstance)this, arguments); + retValue = _methods.GetCallableDelegate(methodNumber)((TInstance)this, arguments, process); } catch (System.Reflection.TargetInvocationException e) { @@ -123,9 +153,5 @@ public override void CallAsFunction(int methodNumber, IValue[] arguments, out IV throw e.InnerException; } } - - private static readonly ContextPropertyMapper _properties = new ContextPropertyMapper(); - private static readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - private static readonly HashSet _warnedDeprecatedMethods = new HashSet(); } } diff --git a/src/ScriptEngine/Machine/Contexts/AutoScriptDrivenObject.cs b/src/ScriptEngine/Machine/Contexts/AutoScriptDrivenObject.cs index 2256aeaee..cbd910a61 100644 --- a/src/ScriptEngine/Machine/Contexts/AutoScriptDrivenObject.cs +++ b/src/ScriptEngine/Machine/Contexts/AutoScriptDrivenObject.cs @@ -4,48 +4,54 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; -using ScriptEngine.Environment; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Sources; namespace ScriptEngine.Machine.Contexts { - public abstract class AutoScriptDrivenObject : ScriptDrivenObject where T : AutoScriptDrivenObject + public abstract class AutoScriptDrivenObject : ThisAwareScriptedObjectBase where T : AutoScriptDrivenObject { - private const int THISOBJ_VARIABLE_INDEX = 0; - private const string THISOBJ_EN = "ThisObject"; - private const string THISOBJ_RU = "ЭтотОбъект"; - private const int PRIVATE_PROPS_OFFSET = 1; + private readonly int _privatePropsOffset; protected static readonly ContextPropertyMapper _ownProperties = new ContextPropertyMapper(); protected static readonly ContextMethodsMapper _ownMethods = new ContextMethodsMapper(); #region SDO Methods - protected AutoScriptDrivenObject(LoadedModule module, bool deffered) - : base(module, deffered) + protected AutoScriptDrivenObject(IExecutableModule module, bool deferred) + : base(module, deferred) { + _privatePropsOffset = base.GetOwnVariableCount(); + if (!deferred) + InitOwnData(); } - protected AutoScriptDrivenObject(LoadedModule module) - : base(module) + protected AutoScriptDrivenObject(IExecutableModule module) + : this(module, false) { } - protected AutoScriptDrivenObject() + protected AutoScriptDrivenObject() : this(EmptyModule.Instance) { } protected override string GetOwnPropName(int index) { - if (index == THISOBJ_VARIABLE_INDEX) - return THISOBJ_RU; + if (index < _privatePropsOffset) + return base.GetOwnPropName(index); - return _ownProperties.GetProperty(index - PRIVATE_PROPS_OFFSET).Name; + return _ownProperties.GetProperty(index - _privatePropsOffset).Name; } protected override int GetOwnVariableCount() { - return _ownProperties.Count + PRIVATE_PROPS_OFFSET; + return _ownProperties.Count + _privatePropsOffset; } protected override int GetOwnMethodCount() @@ -53,48 +59,44 @@ protected override int GetOwnMethodCount() return _ownMethods.Count; } - protected override void UpdateState() - { - } - protected override int FindOwnProperty(string name) { - if (string.Compare(name, THISOBJ_RU, StringComparison.OrdinalIgnoreCase) == 0 - || string.Compare(name, THISOBJ_EN, StringComparison.OrdinalIgnoreCase) == 0) + var baseIndex = base.FindOwnProperty(name); + if (baseIndex != -1) { - return THISOBJ_VARIABLE_INDEX; + return baseIndex; } - return _ownProperties.FindProperty(name) + PRIVATE_PROPS_OFFSET; + return _ownProperties.FindProperty(name) + _privatePropsOffset; } protected override bool IsOwnPropReadable(int index) { - if (index == THISOBJ_VARIABLE_INDEX) - return true; + if (index < _privatePropsOffset) + return base.IsOwnPropReadable(index); - return _ownProperties.GetProperty(index - PRIVATE_PROPS_OFFSET).CanRead; + return _ownProperties.GetProperty(index - _privatePropsOffset).CanRead; } protected override bool IsOwnPropWritable(int index) { - if (index == THISOBJ_VARIABLE_INDEX) + if (index < _privatePropsOffset) return false; - return _ownProperties.GetProperty(index - PRIVATE_PROPS_OFFSET).CanWrite; + return _ownProperties.GetProperty(index - _privatePropsOffset).CanWrite; } protected override IValue GetOwnPropValue(int index) { - if (index == THISOBJ_VARIABLE_INDEX) + if (index < _privatePropsOffset) return this; - return _ownProperties.GetProperty(index - PRIVATE_PROPS_OFFSET).Getter((T)this); + return _ownProperties.GetProperty(index - _privatePropsOffset).Getter((T)this); } protected override void SetOwnPropValue(int index, IValue val) { - _ownProperties.GetProperty(index - PRIVATE_PROPS_OFFSET).Setter((T)this, val); + _ownProperties.GetProperty(index - _privatePropsOffset).Setter((T)this, val); } protected override int FindOwnMethod(string name) @@ -110,38 +112,45 @@ protected override int FindOwnMethod(string name) } } - protected override MethodInfo GetOwnMethod(int index) + protected override BslMethodInfo GetOwnMethod(int index) { - return _ownMethods.GetMethodInfo(index); + return _ownMethods.GetRuntimeMethod(index); } - protected override IValue CallOwnFunction(int index, IValue[] arguments) + protected override IValue CallOwnFunction(int index, IValue[] arguments, IBslProcess process) { - return _ownMethods.GetMethod(index)((T)this, arguments); + return _ownMethods.GetCallableDelegate(index)((T)this, arguments, process); } - protected override void CallOwnProcedure(int index, IValue[] arguments) + protected override void CallOwnProcedure(int index, IValue[] arguments, IBslProcess process) { - _ownMethods.GetMethod(index)((T)this, arguments); + _ownMethods.GetCallableDelegate(index)((T)this, arguments, process); } #endregion - public static ModuleImage CompileModule(CompilerService compiler, ICodeSource src) + [SymbolsProvider] + private static void FillSymbols(TypeSymbolsProviderFactory providerFactory, SymbolScope moduleScope) { - compiler.DefineVariable(THISOBJ_RU, THISOBJ_EN, SymbolType.ContextProperty); + var baseProvider = providerFactory.Get(); + baseProvider.FillSymbols(moduleScope); + for (int i = 0; i < _ownProperties.Count; i++) { var currentProp = _ownProperties.GetProperty(i); - compiler.DefineVariable(currentProp.Name, currentProp.Alias, SymbolType.ContextProperty); + moduleScope.Variables.Add(currentProp.PropertyInfo.ToSymbol()); } for (int i = 0; i < _ownMethods.Count; i++) { - compiler.DefineMethod(_ownMethods.GetMethodInfo(i)); + moduleScope.Methods.Add(_ownMethods.GetRuntimeMethod(i).ToSymbol()); } - - return compiler.Compile(src); + } + + public static IExecutableModule CompileModule(ICompilerFrontend compiler, SourceCode src, Type type, IBslProcess process) + { + compiler.FillSymbols(typeof(AutoScriptDrivenObject)); + return compiler.Compile(src, process, type); } } diff --git a/src/ScriptEngine/Machine/Contexts/BslPredefinedAnnotation.cs b/src/ScriptEngine/Machine/Contexts/BslPredefinedAnnotation.cs new file mode 100644 index 000000000..cc14e4dbf --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/BslPredefinedAnnotation.cs @@ -0,0 +1,21 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Localization; + +namespace ScriptEngine.Machine.Contexts +{ + public static class BslPredefinedAnnotation + { + public static bool NameMatches(this BslAnnotationAttribute attribute, BilingualString names) + { + return names.HasName(attribute.Name, StringComparison.CurrentCultureIgnoreCase); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/CLREnumValueWrapper.cs b/src/ScriptEngine/Machine/Contexts/CLREnumValueWrapper.cs deleted file mode 100644 index a64a8e3c0..000000000 --- a/src/ScriptEngine/Machine/Contexts/CLREnumValueWrapper.cs +++ /dev/null @@ -1,60 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Contexts -{ - public class CLREnumValueWrapper : EnumerationValue, IObjectWrapper where T :struct - { - private readonly T _realValue; - private DataType _redefinedDataType; - - public CLREnumValueWrapper(EnumerationContext owner, T realValue):base(owner) - { - _realValue = realValue; - _redefinedDataType = DataType.GenericValue; - } - - public CLREnumValueWrapper (EnumerationContext owner, T realValue, DataType newDataType) : base (owner) - { - _realValue = realValue; - _redefinedDataType = newDataType; - } - - public object UnderlyingObject - { - get - { - return _realValue; - } - } - - public T UnderlyingValue - { - get - { - return _realValue; - } - } - - public override DataType DataType - { - get - { - return _redefinedDataType; - } - } - - public override bool Equals(IValue other) - { - var otherWrapper = other.GetRawValue() as CLREnumValueWrapper; - if (otherWrapper == null) - return false; - - return UnderlyingValue.Equals(otherWrapper.UnderlyingValue); - } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs b/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs index e72a06b2e..f609daf36 100644 --- a/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs +++ b/src/ScriptEngine/Machine/Contexts/COMWrapperContext.cs @@ -1,317 +1,339 @@ -/*---------------------------------------------------------- -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/. -----------------------------------------------------------*/ -//#if !__MonoCS__ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ScriptEngine.Machine.Contexts -{ - [ContextClass("COMОбъект", "COMObject")] - public abstract class COMWrapperContext : PropertyNameIndexAccessor, ICollectionContext, IDisposable, IObjectWrapper, IEnumerable - { - protected static readonly DateTime MIN_OLE_DATE = new DateTime(100,1,1); - - protected object Instance; - - protected COMWrapperContext(object instance) - : base(TypeManager.GetTypeByFrameworkType(typeof(COMWrapperContext))) - { - Instance = instance; - } - - private static Type FindTypeByName(string typeName) - { - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Reverse()) - { - var tt = assembly.GetType(typeName, throwOnError:false, ignoreCase:true); - if (tt != null) - { - return tt; - } - } - return Type.GetType(typeName, throwOnError:false, ignoreCase:true); - } - - public static COMWrapperContext Create(string progId, IValue[] arguments) - { - Type type = null; - if (Type.GetType("Mono.Runtime") == null) - { - type = Type.GetTypeFromProgID(progId, throwOnError: false); - } - if (type == null) - { - type = FindTypeByName(progId); - } - - if (type == null) - { - throw new TypeLoadException(String.Format("Тип {0} не найден!", progId)); - } - - if (type.IsGenericType) - { - // В первом приближении мы заполняем параметры шаблона классом Object - // TODO: Продумать параметры шаблонного класса - var genericTypes = new List(); - foreach (var ga in type.GetGenericArguments()) - { - genericTypes.Add(typeof(object)); - } - type = type.MakeGenericType(genericTypes.ToArray()); - } - - object instance = Activator.CreateInstance(type, MarshalArguments(arguments)); - - return InitByInstance(type, instance); - } - - public static COMWrapperContext Create(object instance) - { - return InitByInstance(instance.GetType(), instance); - } - - private static COMWrapperContext InitByInstance(Type type, object instance) - { - if (TypeIsRuntimeCallableWrapper(type)) - { - return new UnmanagedCOMWrapperContext(instance); - } - else if (IsObjectType(type) || IsAStruct(type)) - { - return new ManagedCOMWrapperContext(instance); - } - else - throw new ArgumentException(String.Format("Can't create COM wrapper for type {0}", type.ToString())); - } - - private static bool IsObjectType(Type type) - { - return !type.IsPrimitive && !type.IsValueType; - } - - private static bool IsAStruct(Type type) - { - return !type.IsPrimitive && type.IsValueType; - } - - private static bool TypeIsRuntimeCallableWrapper(Type type) - { - return type.FullName == "System.__ComObject" || type.BaseType.FullName == "System.__ComObject"; // string, cause it's hidden type - } - - public static object[] MarshalArguments(IValue[] arguments) - { - var args = arguments.Select(x => MarshalIValue(x)).ToArray(); - return args; - } - - public static object MarshalIValue(IValue val) - { - object retValue; - if (val != null && val.DataType == Machine.DataType.Date) - { - var date = val.AsDate(); - if (date <= MIN_OLE_DATE) - { - retValue = MIN_OLE_DATE; - } - else - { - retValue = date; - } - } - else - { - retValue = ContextValuesMarshaller.ConvertToCLRObject(val); - } - - return retValue; - } - - protected static object[] MarshalArgumentsStrict(IValue[] arguments, Type[] argumentsTypes) - { - if (argumentsTypes.Length < arguments.Length) - throw RuntimeException.TooManyArgumentsPassed(); - - object[] marshalledArgs = new object[arguments.Length]; - for (int i = 0; i < arguments.Length; i++) - { - marshalledArgs[i] = ContextValuesMarshaller.ConvertParam(arguments[i], argumentsTypes[i]); - } - - return marshalledArgs; - } - - public static object[] MarshalArgumentsStrict(System.Reflection.MethodInfo method, IValue[] arguments) - { - var parameters = method.GetParameters(); - - object[] marshalledArgs = new object[parameters.Length]; - for (int i = 0; i < parameters.Length; i++) - { - if(i < arguments.Length) - { - if (IsMissedArg(arguments[i]) && parameters[i].IsOptional) - marshalledArgs[i] = Type.Missing; - else - marshalledArgs[i] = ContextValuesMarshaller.ConvertParam(arguments[i], parameters[i].ParameterType); - } - else - { - marshalledArgs[i] = Type.Missing; - } - } - - return marshalledArgs; - } - - private static bool IsMissedArg(IValue arg) - { - return arg == null || arg.DataType == DataType.NotAValidValue; - } - - public static IValue CreateIValue(object objParam) - { - if (objParam == null) - return ValueFactory.Create(); - - var type = objParam.GetType(); - if (typeof(IValue).IsAssignableFrom(type)) - { - return (IValue)objParam; - } - else if (type == typeof(string)) - { - return ValueFactory.Create((string)objParam); - } - else if (type == typeof(int) || type == typeof(uint) || type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort)) - { - return ValueFactory.Create(System.Convert.ToInt32(objParam)); - } - else if(type == typeof(long) || type == typeof(ulong)) - { - return ValueFactory.Create(System.Convert.ToInt64(objParam)); - } - else if (type == typeof(double)) - { - return ValueFactory.Create((decimal)(double)objParam); - } - else if (type == typeof(Single)) - { - return ValueFactory.Create((decimal)System.Convert.ToDouble(objParam)); - } - else if (type == typeof(decimal)) - { - return ValueFactory.Create((decimal)objParam); - } - else if (type == typeof(DateTime)) - { - var unboxed = (DateTime)objParam; - if (unboxed == MIN_OLE_DATE) - unboxed = DateTime.MinValue; - - return ValueFactory.Create(unboxed); - } - else if (type == typeof(bool)) - { - return ValueFactory.Create((bool)objParam); - } - else if (type.IsArray) - { - return new SafeArrayWrapper(objParam); - } - else if (IsObjectType(type) || IsAStruct(type)) - { - COMWrapperContext ctx; - try - { - ctx = COMWrapperContext.Create(objParam); - } - catch (ArgumentException e) - { - throw new RuntimeException("Тип " + type + " невозможно преобразовать в один из поддерживаемых типов", e); - } - return ValueFactory.Create(ctx); - } - - else - { - throw new RuntimeException("Тип " + type + " невозможно преобразовать в один из поддерживаемых типов"); - } - } - - #region ICollectionContext Members - - public virtual int Count() => 0; - - public virtual void Clear() - { - throw new NotImplementedException(); - } - - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - - public abstract IEnumerator GetEnumerator(); - - public object UnderlyingObject => Instance; - - #region IEnumerable Members - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } - - #endregion - - #endregion - - #region IDisposable Members - - protected virtual void Dispose(bool manualDispose) - { - if (manualDispose) - { - GC.SuppressFinalize(this); - } - - } - - public void Dispose() - { - Dispose(true); - } - - ~COMWrapperContext() - { - Dispose(false); - } - - #endregion - - public override bool DynamicMethodSignatures - { - get - { - return true; - } - } - - [ScriptConstructor] - public static COMWrapperContext Constructor(IValue[] args) - { - return COMWrapperContext.Create(args[0].AsString(), args.Skip(1).ToArray()); - } - - } -} -//#endif +/*---------------------------------------------------------- +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/. +----------------------------------------------------------*/ +//#if !__MonoCS__ +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Types; +using System.Reflection; +using OneScript.Execution; + +namespace ScriptEngine.Machine.Contexts +{ + [ContextClass("COMОбъект", "COMObject")] + public abstract class COMWrapperContext : PropertyNameIndexAccessor, + ICollectionContext, + IEmptyValueCheck, + IDisposable, + IObjectWrapper + { + private static readonly DateTime MIN_OLE_DATE = new DateTime(100,1,1); + protected static readonly TypeDescriptor ComObjectType = typeof(COMWrapperContext).GetTypeFromClassMarkup(); + + protected object Instance; + + protected COMWrapperContext(object instance) + : base(ComObjectType) + { + Instance = instance; + } + + private static Type FindTypeByName(string typeName) + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Reverse()) + { + var tt = assembly.GetType(typeName, throwOnError:false, ignoreCase:true); + if (tt != null) + { + return tt; + } + } + return Type.GetType(typeName, throwOnError:false, ignoreCase:true); + } + + private static COMWrapperContext Create(string progId, IValue[] arguments) + { + Type type = null; +#if NETFRAMEWORK + if (!Utils.IsMonoRuntime) + { + type = Type.GetTypeFromProgID(progId, throwOnError: false); + } + if (type == null) + { + type = FindTypeByName(progId); + } +#else + type = FindTypeByName(progId); + if (type == null) + { + type = Type.GetTypeFromProgID(progId, false); + } +#endif + if (type == null) + { + throw new TypeLoadException(String.Format("Тип {0} не найден!", progId)); + } + + if (type.IsGenericType) + { + // В первом приближении мы заполняем параметры шаблона классом Object + // TODO: Продумать параметры шаблонного класса + var genericTypes = new List(); + foreach (var ga in type.GetGenericArguments()) + { + genericTypes.Add(typeof(object)); + } + type = type.MakeGenericType(genericTypes.ToArray()); + } + + object instance = Activator.CreateInstance(type, MarshalArguments(arguments).values); + + return InitByInstance(type, instance); + } + + public static COMWrapperContext Create(object instance) + { + return InitByInstance(instance.GetType(), instance); + } + + private static COMWrapperContext InitByInstance(Type type, object instance) + { + if (TypeIsRuntimeCallableWrapper(type)) + { + return new UnmanagedCOMWrapperContext(instance); + } + else if (IsObjectType(type) || IsAStruct(type)) + { + return new ManagedCOMWrapperContext(instance); + } + else + throw new ArgumentException(String.Format("Can't create COM wrapper for type {0}", type.ToString())); + } + + private static bool IsObjectType(Type type) + { + return !type.IsPrimitive && !type.IsValueType; + } + + private static bool IsAStruct(Type type) + { + return !type.IsPrimitive && type.IsValueType; + } + + private static bool TypeIsRuntimeCallableWrapper(Type type) + { + return type.FullName == "System.__ComObject" || type.BaseType.FullName == "System.__ComObject"; // string, cause it's hidden type + } + + protected static (object[] values, ParameterModifier[] flags) MarshalArguments(IValue[] arguments) + { + var values = new object[arguments.Length]; + ParameterModifier[] flagsArray = new ParameterModifier[1]; + if (arguments.Length > 0) + { + var flags = new ParameterModifier(arguments.Length); + for (int i = 0; i < arguments.Length; i++) + { + values[i] = MarshalIValue(arguments[i]); + flags[i] = arguments[i] is IVariable; + } + + flagsArray[0] = flags; + } + else + { + flagsArray[0] = new ParameterModifier(); + } + + return (values, flagsArray); + } + + public static object MarshalIValue(IValue val) + { + object retValue; + if (val is BslDateValue dateVal) + { + var date = (DateTime)dateVal; + retValue = date < MIN_OLE_DATE ? MIN_OLE_DATE : date; + } + else + { + retValue = ContextValuesMarshaller.ConvertToClrObject(val) ?? Missing.Value;; + } + + return retValue; + } + + protected static object[] MarshalArgumentsStrict(IValue[] arguments, Type[] argumentsTypes) + { + if (argumentsTypes.Length < arguments.Length) + throw RuntimeException.TooManyArgumentsPassed(); + + object[] marshalledArgs = new object[arguments.Length]; + for (int i = 0; i < arguments.Length; i++) + { + marshalledArgs[i] = ContextValuesMarshaller.ConvertParam(arguments[i], argumentsTypes[i]); + } + + return marshalledArgs; + } + + public static object[] MarshalArgumentsStrict(System.Reflection.MethodInfo method, IValue[] arguments) + { + var parameters = method.GetParameters(); + + object[] marshalledArgs = new object[parameters.Length]; + for (int i = 0; i < parameters.Length; i++) + { + if(i < arguments.Length) + { + if (IsMissedArg(arguments[i]) && parameters[i].IsOptional) + marshalledArgs[i] = Type.Missing; + else + marshalledArgs[i] = ContextValuesMarshaller.ConvertParam(arguments[i], parameters[i].ParameterType); + } + else + { + marshalledArgs[i] = Type.Missing; + } + } + + return marshalledArgs; + } + + private static bool IsMissedArg(IValue arg) + { + return arg == null || arg.IsSkippedArgument(); + } + + public static IValue CreateIValue(object objParam) + { + if (objParam == null) + return ValueFactory.Create(); + + var type = objParam.GetType(); + if (typeof(IValue).IsAssignableFrom(type)) + { + return (IValue)objParam; + } + else if (type == typeof(string)) + { + return ValueFactory.Create((string)objParam); + } + else if (type == typeof(int) || type == typeof(uint) || type == typeof(byte) || type == typeof(sbyte) || type == typeof(short) || type == typeof(ushort)) + { + return ValueFactory.Create(System.Convert.ToInt32(objParam)); + } + else if(type == typeof(long) || type == typeof(ulong)) + { + return ValueFactory.Create(System.Convert.ToInt64(objParam)); + } + else if (type == typeof(double)) + { + return ValueFactory.Create((decimal)(double)objParam); + } + else if (type == typeof(Single)) + { + return ValueFactory.Create((decimal)System.Convert.ToDouble(objParam)); + } + else if (type == typeof(decimal)) + { + return ValueFactory.Create((decimal)objParam); + } + else if (type == typeof(DateTime)) + { + var unboxed = (DateTime)objParam; + if (unboxed == MIN_OLE_DATE) + unboxed = DateTime.MinValue; + + return ValueFactory.Create(unboxed); + } + else if (type == typeof(bool)) + { + return ValueFactory.Create((bool)objParam); + } + else if (type.IsArray) + { + return new SafeArrayWrapper(objParam); + } + else if (IsObjectType(type) || IsAStruct(type)) + { + COMWrapperContext ctx; + try + { + ctx = COMWrapperContext.Create(objParam); + } + catch (ArgumentException e) + { + throw new RuntimeException("Тип " + type + " невозможно преобразовать в один из поддерживаемых типов", e); + } + return ctx; + } + + else + { + throw new RuntimeException("Тип " + type + " невозможно преобразовать в один из поддерживаемых типов"); + } + } + + #region ICollectionContext Members + + public virtual int Count() => 0; + + public int Count(IBslProcess process) => Count(); + + bool IEmptyValueCheck.IsEmpty => false; + + public virtual void Clear() + { + throw new NotImplementedException(); + } + + public abstract IEnumerator GetEnumerator(); + + public object UnderlyingObject => Instance; + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + #endregion + + #endregion + + #region IDisposable Members + + protected virtual void Dispose(bool manualDispose) + { + if (manualDispose) + { + GC.SuppressFinalize(this); + } + } + + public void Dispose() + { + Dispose(true); + } + + ~COMWrapperContext() + { + Dispose(false); + } + + #endregion + + public override bool DynamicMethodSignatures => true; + + [ScriptConstructor] + public static COMWrapperContext Constructor(TypeActivationContext context, IValue[] args) + { + return COMWrapperContext.Create(args[0].AsString(context.CurrentProcess), args.Skip(1).ToArray()); + } + + } +} +//#endif diff --git a/src/ScriptEngine/Machine/Contexts/ClrEnumWrapper.cs b/src/ScriptEngine/Machine/Contexts/ClrEnumWrapper.cs new file mode 100644 index 000000000..19143699a --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/ClrEnumWrapper.cs @@ -0,0 +1,155 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Contexts.Enums; +using OneScript.Types; +using OneScript.Values; + +namespace ScriptEngine.Machine.Contexts +{ + /// + /// Обертка для штатных перечислений Clr, используемых в языке + /// + /// Оборачиваемое перечисление + public class ClrEnumWrapper : EnumerationContext where T : struct + { + public static ClrEnumWrapper Instance { get; private set; } + + /// + /// Constructor for inherited enum wrappers + /// + /// + /// + protected ClrEnumWrapper(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + } + + public virtual ClrEnumValueWrapper FromNativeValue(T native) + { + var result = TryGetFromNativeValue(native); + if (result == null) + throw new InvalidOperationException($"Item '{native}' not found"); + + return result; + } + + public ClrEnumValueWrapper TryGetFromNativeValue(T native) + { + ClrEnumValueWrapper wrapper = null; + foreach (var value in ValuesInternal) + { + wrapper = (ClrEnumValueWrapper) value; + if (wrapper.UnderlyingValue.Equals(native)) + return wrapper; + } + + return wrapper; + } + + private void Autoregister(TypeDescriptor valuesType) + { + var attrib = typeof(T).GetCustomAttributes(typeof(EnumerationTypeAttribute), false); + if(attrib.Length == 0) + throw new InvalidOperationException($"Enum cannot be autoregistered, no attribute {nameof(EnumerationTypeAttribute)} found"); + + var enumType = typeof(T); + + foreach (var field in enumType.GetFields()) + { + foreach (var contextFieldAttribute in field.GetCustomAttributes (typeof (EnumValueAttribute), false)) + { + var contextField = (EnumValueAttribute)contextFieldAttribute; + + string alias = contextField.Alias; + if ( alias == null) + { + if(StringComparer + .InvariantCultureIgnoreCase + .Compare(field.Name, contextField.Name) != 0) + alias = field.Name; + } + + var enumValue = (T)field.GetValue(null)!; + var osValue = new ClrEnumValueWrapper(valuesType, enumValue, + contextField.Name, alias); + + AddValue(osValue); + + // Deprecations + foreach (var deprecation in field.GetCustomAttributes()) + { + if (deprecation.Name.Equals(contextField.Name, StringComparison.InvariantCultureIgnoreCase) + || deprecation.Name.Equals(contextField.Alias, StringComparison.InvariantCultureIgnoreCase)) + { + throw new InvalidOperationException($"Enum value '{contextField.Name}' has same name in deprecations"); + } + + var deprecatedValue = new ClrEnumValueWrapper(valuesType, enumValue, deprecation.Name, null); + var propertyDef = new SystemPropertyInfo.Builder(deprecation.Name) + .SetPropertyType(deprecatedValue.UnderlyingValue.GetType()) + .SetDeprecated(true) + .SetDeclaringType(enumType) + .Build(); + + AddValue(deprecatedValue, propertyDef); + } + } + } + } + + public static ClrEnumWrapper CreateInstance(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + { + var instance = new ClrEnumWrapper(typeRepresentation, valuesType); + instance.Autoregister(valuesType); + Instance = instance; + + return instance; + } + + protected static void OnInstanceCreation(ClrEnumWrapper instance) + { + Instance = instance; + } + + protected static TE CreateInstance(ITypeManager typeManager,EnumCreationDelegate creator) + where TE: ClrEnumWrapper + { + var instance = EnumContextHelper.CreateClrEnumInstance(typeManager, creator); + + OnInstanceCreation(instance); + return instance; + } + } + + public abstract class ClrEnumWrapperCached : ClrEnumWrapper where T : struct + { + private static readonly Dictionary> _valuesCache + = new Dictionary>(); + + protected ClrEnumWrapperCached(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) + : base(typeRepresentation, valuesType) + { + _valuesCache.Clear(); + } + + protected void MakeValue(string name, string alias, T enumValue) + { + _valuesCache[enumValue] = this.WrapClrValue(name, alias, enumValue); + } + + public static new ClrEnumValueWrapper FromNativeValue(T native) + { + _valuesCache.TryGetValue(native, out ClrEnumValueWrapper value); + return value; // TODO: исключение или null? + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/CollectionEnumerator.cs b/src/ScriptEngine/Machine/Contexts/CollectionEnumerator.cs index a42407a8e..82a1c0bb8 100644 --- a/src/ScriptEngine/Machine/Contexts/CollectionEnumerator.cs +++ b/src/ScriptEngine/Machine/Contexts/CollectionEnumerator.cs @@ -6,6 +6,8 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Types; namespace ScriptEngine.Machine.Contexts { diff --git a/src/ScriptEngine/Machine/Contexts/ContextClassAttribute.cs b/src/ScriptEngine/Machine/Contexts/ContextClassAttribute.cs deleted file mode 100644 index a67619694..000000000 --- a/src/ScriptEngine/Machine/Contexts/ContextClassAttribute.cs +++ /dev/null @@ -1,40 +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 ScriptEngine.Machine.Contexts -{ - [AttributeUsage(AttributeTargets.Class)] - public class ContextClassAttribute : Attribute - { - private readonly string _name; - private readonly string _alias; - - public ContextClassAttribute(string typeName, string typeAlias = "") - { - if (!Utils.IsValidIdentifier(typeName)) - throw new ArgumentException("Name must be a valid identifier"); - - if (!string.IsNullOrEmpty(typeAlias) && !Utils.IsValidIdentifier(typeAlias)) - throw new ArgumentException("Alias must be a valid identifier"); - - _name = typeName; - _alias = typeAlias; - } - - public string GetName() - { - return _name; - } - - public string GetAlias() - { - return _alias; - } - - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/ContextDiscoverer.cs b/src/ScriptEngine/Machine/Contexts/ContextDiscoverer.cs index e308dd8d5..446b7738e 100644 --- a/src/ScriptEngine/Machine/Contexts/ContextDiscoverer.cs +++ b/src/ScriptEngine/Machine/Contexts/ContextDiscoverer.cs @@ -1,161 +1,212 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace ScriptEngine.Machine.Contexts -{ - static class ContextDiscoverer - { - private const string INSTANCE_RETRIEVER_NAME = "CreateInstance"; - - public static void DiscoverClasses(System.Reflection.Assembly assembly) - { - IEnumerable types; - try - { - types = assembly.GetTypes().AsParallel(); - } - catch (ReflectionTypeLoadException exc) - { - var sb = new StringBuilder(); - int i = 0; - foreach (var loaderException in exc.LoaderExceptions) - { - sb.AppendFormat("Inner exception [{0}]", i++); - sb.AppendLine(loaderException.ToString()); - } - throw new Exception("Error loading assemblies:\n" + sb.ToString()); - } - - var collection = GetMarkedTypes(types, typeof(ContextClassAttribute)); - - foreach (var type in collection) - { - RegisterSystemType(type); - } - } - - public static void DiscoverGlobalContexts(RuntimeEnvironment environment, System.Reflection.Assembly assembly) - { - var allTypes = assembly.GetTypes(); - var enums = GetMarkedTypes(allTypes.AsParallel(), typeof(SystemEnumAttribute)); - foreach (var item in enums) - { - RegisterSystemEnum(item, environment); - } - - var simpleEnums = GetMarkedTypes(allTypes.AsParallel(), typeof(EnumerationTypeAttribute)); - foreach (var item in simpleEnums) - { - RegisterSimpleEnum(item, environment); - } - - var contexts = GetMarkedTypes(allTypes.AsParallel(), typeof(GlobalContextAttribute)); - foreach (var item in contexts) - { - RegisterGlobalContext(item, environment); - } - } - - private static IEnumerable GetMarkedTypes(IEnumerable allTypes, Type attribute) - { - return allTypes.Where(t => t.IsDefined(attribute, false)); - } - - private static void RegisterSystemType(Type stdClass) - { - var attribData = stdClass.GetCustomAttributes(typeof(ContextClassAttribute), false); - System.Diagnostics.Trace.Assert(attribData.Length > 0, "Class is not marked as context"); - - var attr = (ContextClassAttribute)attribData[0]; - var newType = TypeManager.RegisterType(attr.GetName(), stdClass); - string alias = attr.GetAlias(); - if(!String.IsNullOrEmpty(alias)) - TypeManager.RegisterAliasFor(newType, alias); - } - - private static void RegisterSystemEnum(Type enumType, RuntimeEnvironment environment) - { - var method = enumType.GetMethod(INSTANCE_RETRIEVER_NAME, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); - - System.Diagnostics.Trace.Assert(method != null, "System enum must have a static method " + INSTANCE_RETRIEVER_NAME); - - var instance = (IValue)method.Invoke(null, null); - GlobalsManager.RegisterInstance(instance); - var enumMetadata = (SystemEnumAttribute)enumType.GetCustomAttributes(typeof(SystemEnumAttribute), false)[0]; - environment.InjectGlobalProperty(instance, enumMetadata.GetName(), true); - if(enumMetadata.GetAlias() != String.Empty) - environment.InjectGlobalProperty(instance, enumMetadata.GetAlias(), true); - } - - private static void RegisterSimpleEnum(Type enumType, RuntimeEnvironment environment) - { - var enumTypeAttribute = (EnumerationTypeAttribute)enumType.GetCustomAttributes (typeof (EnumerationTypeAttribute), false)[0]; - - var type = TypeManager.RegisterType ("Перечисление" + enumTypeAttribute.Name, typeof (EnumerationContext)); - if (enumTypeAttribute.Alias != null) - TypeManager.RegisterAliasFor (type, "Enum" + enumTypeAttribute.Alias); - - var enumValueType = TypeManager.RegisterType (enumTypeAttribute.Name, enumType); - - var instance = new EnumerationContext (type, enumValueType); - - var wrapperTypeUndefined = typeof (CLREnumValueWrapper<>); - var wrapperType = wrapperTypeUndefined.MakeGenericType (new Type [] { enumType } ); - var constructor = wrapperType.GetConstructor (new Type [] { typeof(EnumerationContext), enumType, typeof(DataType) }); - - foreach (var field in enumType.GetFields()) - { - foreach (var contextFieldAttribute in field.GetCustomAttributes (typeof (EnumItemAttribute), false)) - { - var contextField = (EnumItemAttribute)contextFieldAttribute; - var osValue = (EnumerationValue)constructor.Invoke (new object [] { instance, field.GetValue (null), DataType.Enumeration } ); - - if (contextField.Alias == null) - { - if(StringComparer - .InvariantCultureIgnoreCase - .Compare(field.Name, contextField.Name) != 0) - instance.AddValue(contextField.Name, field.Name, osValue); - else - instance.AddValue(contextField.Name, osValue); - } - else - instance.AddValue(contextField.Name, contextField.Alias, osValue); - } - } - - if (enumTypeAttribute.CreateGlobalProperty) - { - GlobalsManager.RegisterInstance(enumType, instance); - environment.InjectGlobalProperty(instance, enumTypeAttribute.Name, true); - if (enumTypeAttribute.Alias != null) - environment.InjectGlobalProperty(instance, enumTypeAttribute.Alias, true); - } - } - - - private static void RegisterGlobalContext(Type contextType, RuntimeEnvironment environment) - { - var attribData = (GlobalContextAttribute)contextType.GetCustomAttributes(typeof(GlobalContextAttribute), false)[0]; - if (attribData.ManualRegistration) - return; - - var method = contextType.GetMethod(INSTANCE_RETRIEVER_NAME, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); - System.Diagnostics.Trace.Assert(method != null, "Global context must have a static method " + INSTANCE_RETRIEVER_NAME); - var instance = (IAttachableContext)method.Invoke(null, null); - GlobalsManager.RegisterInstance(instance); - environment.InjectObject(instance, false); - - } - - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using OneScript.Contexts; +using OneScript.Contexts.Enums; +using OneScript.DependencyInjection; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Types; + +namespace ScriptEngine.Machine.Contexts +{ + public class ContextDiscoverer + { + private const string INSTANCE_RETRIEVER_NAME = "CreateInstance"; + + public ContextDiscoverer(ITypeManager types, IGlobalsManager globals, IServiceContainer services) + { + Types = types; + Globals = globals; + Services = services; + } + + private ITypeManager Types { get; } + private IGlobalsManager Globals { get; } + private IServiceContainer Services { get; } + + public void DiscoverClasses(Assembly assembly, Predicate filter = null) + { + IEnumerable types; + try + { + types = assembly.GetTypes().AsParallel(); + } + catch (ReflectionTypeLoadException exc) + { + var sb = new StringBuilder(); + int i = 0; + foreach (var loaderException in exc.LoaderExceptions) + { + sb.AppendFormat("Inner exception [{0}]", i++); + sb.AppendLine(loaderException.ToString()); + } + throw new Exception("Error loading assemblies:\n" + sb.ToString()); + } + + if (filter == null) + { + filter = t => true; + } + + var collection = GetMarkedTypes(types, typeof(ContextClassAttribute), filter); + + foreach (var type in collection) + { + RegisterSystemType(type); + } + } + + public void DiscoverGlobalContexts( + IRuntimeEnvironment environment, + Assembly assembly, + Predicate filter = null) + { + if (filter == null) + { + filter = t => true; + } + + var allTypes = assembly.GetTypes(); + var enums = GetMarkedTypes(allTypes.AsParallel(), typeof(SystemEnumAttribute), filter); + foreach (var item in enums) + { + RegisterSystemEnum(item, environment); + } + + var simpleEnums = GetMarkedTypes(allTypes.AsParallel(), typeof(EnumerationTypeAttribute), filter); + foreach (var item in simpleEnums) + { + RegisterSimpleEnum(item, environment); + } + + var contexts = GetMarkedTypes(allTypes.AsParallel(), typeof(GlobalContextAttribute), filter); + foreach (var item in contexts) + { + RegisterGlobalContext(item, environment); + } + } + + private static IEnumerable GetMarkedTypes(IEnumerable allTypes, Type attribute, Predicate filter) + { + return allTypes + .Where(t => t.IsDefined(attribute, false) && filter(t)); + } + + private void RegisterSystemType(Type stdClass) + { + Types.RegisterClass(stdClass); + } + + private void RegisterSystemEnum(Type enumType, IRuntimeEnvironment environment) + { + var method = enumType.GetMethod(INSTANCE_RETRIEVER_NAME, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); + + if(method == default) + throw new ArgumentException($"System enum must have a static method {INSTANCE_RETRIEVER_NAME}"); + + var parameters = method.GetParameters(); + if(parameters.Length != 1 || parameters[0].ParameterType != typeof(ITypeManager)) + throw new ArgumentException($"Method {enumType}::{INSTANCE_RETRIEVER_NAME} must accept ITypeManager as an argument"); + + var enumMetadata = (SystemEnumAttribute)enumType.GetCustomAttributes(typeof(SystemEnumAttribute), false)[0]; + var instance = (IValue)method.Invoke(null, new object[]{Types}); + + RegisterEnumGlobalProperty(instance, environment, enumType, enumMetadata); + } + + private void RegisterSimpleEnum(Type enumType, IRuntimeEnvironment environment) + { + var enumTypeAttribute = (EnumerationTypeAttribute)enumType.GetCustomAttributes (typeof (EnumerationTypeAttribute), false)[0]; + + var genericType = typeof(ClrEnumWrapper<>).MakeGenericType(enumType); + var genericValue = typeof(ClrEnumValueWrapper<>).MakeGenericType(enumType); + + var (enumTypeDescription, valueTypeDescription) = + EnumContextHelper.RegisterEnumType(genericType, genericValue, Types, enumTypeAttribute); + + var factory = genericType.GetMethod(INSTANCE_RETRIEVER_NAME, + BindingFlags.Public | BindingFlags.Static, + default, + new [] + { + typeof(TypeDescriptor), + typeof(TypeDescriptor) + }, + default); + + Debug.Assert(factory != null); + + var instance = (IValue)factory.Invoke(null, new object[]{enumTypeDescription, valueTypeDescription}); + + if (enumTypeAttribute.CreateGlobalProperty) + { + RegisterEnumGlobalProperty(instance, environment, enumType, enumTypeAttribute); + } + } + + private void RegisterEnumGlobalProperty( + IValue instance, + IRuntimeEnvironment environment, + Type enumType, + IEnumMetadataProvider enumTypeAttribute) + { + Globals.RegisterInstance(enumType, instance); + + var mainProperty = new SystemPropertyInfo.Builder(enumTypeAttribute.Name, enumTypeAttribute.Alias) + .SetPropertyType(enumType) + .Build(); + + environment.InjectGlobalProperty(instance, mainProperty); + + // Deprecations + foreach (var deprecation in enumType.GetCustomAttributes()) + { + if (deprecation.ThrowOnUse) + throw new NotSupportedException( + $"{nameof(deprecation.ThrowOnUse)} is not supported for enums yet. ({enumType})"); + + var deprecatedProperty = new SystemPropertyInfo.Builder(deprecation.Name) + .SetPropertyType(enumType) + .SetDeprecated(true) + .Build(); + + environment.InjectGlobalProperty(instance, deprecatedProperty); + } + } + + private void RegisterGlobalContext(Type contextType, IRuntimeEnvironment environment) + { + var attribData = (GlobalContextAttribute)contextType.GetCustomAttributes(typeof(GlobalContextAttribute), false)[0]; + if (attribData.ManualRegistration) + return; + + var method = contextType.GetMethod(INSTANCE_RETRIEVER_NAME, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); + System.Diagnostics.Trace.Assert(method != null, "Global context must have a static method " + INSTANCE_RETRIEVER_NAME); + var parameters = method.GetParameters(); + IAttachableContext instance; + if (parameters.Length == 0) + { + instance = (IAttachableContext)method.Invoke(null, null); + } + else + { + var resolvedArgs = parameters.Select(p => Services.Resolve(p.ParameterType)) + .ToArray(); + instance = (IAttachableContext)method.Invoke(null, resolvedArgs); + } + Globals.RegisterInstance(instance); + environment.InjectObject(instance); + + } + } +} diff --git a/src/ScriptEngine/Machine/Contexts/ContextIValueImpl.cs b/src/ScriptEngine/Machine/Contexts/ContextIValueImpl.cs index ad02d32bc..e8d8d07ac 100644 --- a/src/ScriptEngine/Machine/Contexts/ContextIValueImpl.cs +++ b/src/ScriptEngine/Machine/Contexts/ContextIValueImpl.cs @@ -1,323 +1,259 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Dynamic; -using System.Linq; - -namespace ScriptEngine.Machine.Contexts -{ - public abstract class ContextIValueImpl : DynamicObject, IRuntimeContextInstance, IValue - { - private TypeDescriptor _type; - - public ContextIValueImpl() - { - - } - - public ContextIValueImpl(TypeDescriptor type) - { - DefineType(type); - } - - protected void DefineType(TypeDescriptor type) - { - _type = type; - } - - public override string ToString() - { - return _type.Name ?? base.ToString(); - } - - #region IValue Members - - public DataType DataType - { - get { return Machine.DataType.Object; } - } - - public TypeDescriptor SystemType - { - get - { - if (_type.Name == null) - { - if (TypeManager.IsKnownType(this.GetType())) - { - _type = TypeManager.GetTypeByFrameworkType(this.GetType()); - } - else - { - throw new InvalidOperationException($"Type {GetType()} is not defined"); - } - } - - return _type; - } - } - - public decimal AsNumber() - { - throw RuntimeException.ConvertToNumberException(); - } - - public DateTime AsDate() - { - throw RuntimeException.ConvertToDateException(); - } - - public bool AsBoolean() - { - throw RuntimeException.ConvertToBooleanException(); - } - - public virtual string AsString() - { - return SystemType.Name; - } - - public IRuntimeContextInstance AsObject() - { - return this; - } - - public IValue GetRawValue() - { - return this; - } - - #endregion - - #region IComparable Members - - public int CompareTo(IValue other) - { - if (other.SystemType.Equals(this.SystemType)) - { - if (this.Equals(other)) - { - return 0; - } - else - { - throw RuntimeException.ComparisonNotSupportedException(); - } - } - else - { - return this.SystemType.ToString().CompareTo(other.SystemType.ToString()); - } - } - - #endregion - - #region IEquatable Members - - public virtual bool Equals(IValue other) - { - if (other == null) - return false; - - return other.SystemType.Equals(this.SystemType) && Object.ReferenceEquals(this.AsObject(), other.AsObject()); - } - - #endregion - - #region IRuntimeContextInstance Members - - public virtual bool IsIndexed - { - get { return false; } - } - - public virtual bool DynamicMethodSignatures - { - get { return false; } - } - - public virtual IValue GetIndexedValue(IValue index) - { - throw new NotImplementedException(); - } - - public virtual void SetIndexedValue(IValue index, IValue val) - { - throw new NotImplementedException(); - } - - public virtual int FindProperty(string name) - { - throw RuntimeException.PropNotFoundException(name); - } - public virtual bool IsPropReadable(int propNum) - { - throw new NotImplementedException(); - } - - public virtual bool IsPropWritable(int propNum) - { - throw new NotImplementedException(); - } - public virtual IValue GetPropValue(int propNum) - { - throw new NotImplementedException(); - } - public virtual void SetPropValue(int propNum, IValue newVal) - { - throw new NotImplementedException(); - } - - public virtual int GetPropCount() - { - throw new NotImplementedException(); - } - - public virtual string GetPropName(int propNum) - { - throw new NotImplementedException(); - } - - public virtual int GetMethodsCount() - { - throw new NotImplementedException(); - } - - public virtual int FindMethod(string name) - { - throw RuntimeException.MethodNotFoundException(name); - } - public virtual MethodInfo GetMethodInfo(int methodNumber) - { - throw new NotImplementedException(); - } - public virtual void CallAsProcedure(int methodNumber, IValue[] arguments) - { - throw new NotImplementedException(); - } - public virtual void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - throw new NotImplementedException(); - } - - #endregion - - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - try - { - var propIdx = FindProperty(binder.Name); - if (!IsPropReadable(propIdx)) - { - result = null; - return false; - } - - result = ContextValuesMarshaller.ConvertToCLRObject(GetPropValue(propIdx)); - return true; - } - catch (PropertyAccessException) - { - result = null; - return false; - } - catch (ValueMarshallingException) - { - result = null; - return false; - } - } - - public override bool TrySetMember(SetMemberBinder binder, object value) - { - try - { - var propIdx = FindProperty(binder.Name); - if (IsPropWritable(propIdx)) - { - return false; - } - - SetPropValue(propIdx, ContextValuesMarshaller.ConvertReturnValue(value, value.GetType())); - - return true; - } - catch (PropertyAccessException) - { - return false; - } - catch (NotSupportedException) - { - return false; - } - } - - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - if (!IsIndexed) - { - result = null; - return false; - } - - var index = ContextValuesMarshaller.ConvertReturnValue(indexes[0], indexes[0].GetType()); - result = ContextValuesMarshaller.ConvertToCLRObject(GetIndexedValue(index)); - return true; - } - - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - if (!IsIndexed) - { - return false; - } - - var index = ContextValuesMarshaller.ConvertReturnValue(indexes[0], indexes[0].GetType()); - SetIndexedValue(index, ContextValuesMarshaller.ConvertReturnValue(value, value.GetType())); - return true; - } - - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) - { - int methIdx; - try - { - methIdx = FindMethod(binder.Name); - } - catch (MethodAccessException) - { - result = null; - return false; - } - - var methInfo = GetMethodInfo(methIdx); - var valueArgs = new IValue[methInfo.Params.Length]; - var passedArgs = args.Select(x => ContextValuesMarshaller.ConvertReturnValue(x, x.GetType())).ToArray(); - for (int i = 0; i < valueArgs.Length; i++) - { - if (i < passedArgs.Length) - valueArgs[i] = passedArgs[i]; - else - valueArgs[i] = ValueFactory.CreateInvalidValueMarker(); - } - - IValue methResult; - CallAsFunction(methIdx, valueArgs, out methResult); - result = methResult == null? null : ContextValuesMarshaller.ConvertToCLRObject(methResult); - - return true; - - } - } - - [AttributeUsage(AttributeTargets.Method)] - public class ScriptConstructorAttribute : Attribute - { - public string Name { get; set; } - public bool ParametrizeWithClassName { get; set; } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Dynamic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; + +namespace ScriptEngine.Machine.Contexts +{ + public abstract class ContextIValueImpl : BslObjectValue, IRuntimeContextInstance, ISystemTypeAcceptor + { + private TypeDescriptor _type; + + protected ContextIValueImpl() : this(BasicTypes.UnknownType) + { + } + + protected ContextIValueImpl(TypeDescriptor type) + { + _type = type; + } + + void ISystemTypeAcceptor.AssignType(TypeDescriptor type) + { + _type = type; + } + + protected void DefineType(TypeDescriptor type) + { + _type = type; + } + + public override string ToString() + { + return SystemType.Name; + } + + #region IValue Members + + public override TypeDescriptor SystemType + { + get + { + if (_type != BasicTypes.UnknownType) + return _type; + + throw new InvalidOperationException($"Type {GetType()} is not defined"); + } + } + + #endregion + + #region IEquatable Members + + public override bool Equals(BslValue other) + { + if (!(other is BslObjectValue _)) + return false; + + return ReferenceEquals(this, other); + } + + #endregion + + #region IRuntimeContextInstance Members + + public virtual bool IsIndexed => false; + + public virtual bool DynamicMethodSignatures => false; + + public virtual IValue GetIndexedValue(IValue index) + { + throw new NotImplementedException(); + } + + public virtual void SetIndexedValue(IValue index, IValue val) + { + throw new NotImplementedException(); + } + + public virtual int GetPropertyNumber(string name) + { + throw PropertyAccessException.PropNotFoundException(name); + } + public virtual bool IsPropReadable(int propNum) + { + throw new NotImplementedException(); + } + + public virtual bool IsPropWritable(int propNum) + { + throw new NotImplementedException(); + } + public virtual IValue GetPropValue(int propNum) + { + throw new NotImplementedException(); + } + public virtual void SetPropValue(int propNum, IValue newVal) + { + throw new NotImplementedException(); + } + + public virtual int GetPropCount() => 0; + + public virtual string GetPropName(int propNum) + { + throw new NotImplementedException(); + } + + public virtual int GetMethodsCount() => 0; + + public virtual int GetMethodNumber(string name) + { + throw RuntimeException.MethodNotFoundException(name); + } + + public virtual BslMethodInfo GetMethodInfo(int methodNumber) + { + throw new NotImplementedException(); + } + + public virtual BslPropertyInfo GetPropertyInfo(int propertyNumber) + { + throw new NotImplementedException(); + } + + public virtual void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + throw new NotImplementedException(); + } + public virtual void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + throw new NotImplementedException(); + } + + #endregion + + #region DynamicObject members + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + try + { + var propIdx = GetPropertyNumber(binder.Name); + if (!IsPropReadable(propIdx)) + { + result = null; + return false; + } + + result = ContextValuesMarshaller.ConvertToClrObject(GetPropValue(propIdx)); + return true; + } + catch (PropertyAccessException) + { + result = null; + return false; + } + catch (ValueMarshallingException) + { + result = null; + return false; + } + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + try + { + var propIdx = GetPropertyNumber(binder.Name); + if (!IsPropWritable(propIdx)) + { + return false; + } + + SetPropValue(propIdx, ContextValuesMarshaller.ConvertDynamicValue(value)); + return true; + } + catch (PropertyAccessException) + { + return false; + } + catch (NotSupportedException) + { + return false; + } + } + + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + if (!IsIndexed) + { + result = null; + return false; + } + + var index = ContextValuesMarshaller.ConvertDynamicIndex(indexes[0]); + result = ContextValuesMarshaller.ConvertToClrObject(GetIndexedValue(index)); + return true; + } + + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + if (!IsIndexed) + { + return false; + } + + var index = ContextValuesMarshaller.ConvertDynamicIndex(indexes[0]); + SetIndexedValue(index, ContextValuesMarshaller.ConvertDynamicValue(value)); + return true; + } + + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + int methIdx; + try + { + methIdx = GetMethodNumber(binder.Name); + } + catch (MethodAccessException) + { + result = null; + return false; + } + + var parameters = GetMethodInfo(methIdx).GetParameters(); + if (args.Length > parameters.Length) + throw RuntimeException.TooManyArgumentsPassed(); + + var valueArgs = new IValue[parameters.Length]; + var passedArgs = args.Select(x => ContextValuesMarshaller.ConvertDynamicValue(x)).ToArray(); + + for (int i = 0; i < valueArgs.Length; i++) + { + if (i < passedArgs.Length) + valueArgs[i] = passedArgs[i]; + else + valueArgs[i] = ValueFactory.CreateInvalidValueMarker(); + } + + CallAsFunction(methIdx, valueArgs, out IValue methResult, ForbiddenBslProcess.Instance); + result = methResult == null ? null : ContextValuesMarshaller.ConvertToClrObject(methResult); + + return true; + } + + #endregion + } + +} diff --git a/src/ScriptEngine/Machine/Contexts/ContextMethodMapper.cs b/src/ScriptEngine/Machine/Contexts/ContextMethodMapper.cs index f11bbbfef..0cec69071 100644 --- a/src/ScriptEngine/Machine/Contexts/ContextMethodMapper.cs +++ b/src/ScriptEngine/Machine/Contexts/ContextMethodMapper.cs @@ -1,354 +1,316 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using OneScript.Language; - -namespace ScriptEngine.Machine.Contexts -{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class ContextMethodAttribute : Attribute - { - private readonly string _name; - private readonly string _alias; - - public ContextMethodAttribute(string name, string alias = null) - { - if (!Utils.IsValidIdentifier(name)) - throw new ArgumentException("Name must be a valid identifier"); - - if (!string.IsNullOrEmpty(alias) && !Utils.IsValidIdentifier(alias)) - throw new ArgumentException("Alias must be a valid identifier"); - - _name = name; - _alias = alias; - } - - public string GetName() - { - return _name; - } - - public string GetAlias() - { - return _alias; - } - - public string GetAlias(string nativeMethodName) - { - if (!string.IsNullOrEmpty(_alias)) - { - return _alias; - } - if (!IsDeprecated) - { - return nativeMethodName; - } - return null; - } - - public bool IsDeprecated { get; set; } - - public bool ThrowOnUse { get; set; } - - public bool IsFunction { get; set; } - } - - [AttributeUsage(AttributeTargets.Parameter)] - public class ByRefAttribute : Attribute - { - } - - public delegate IValue ContextCallableDelegate(TInstance instance, IValue[] args); - - public class ContextMethodsMapper - { - private List _methodPtrs; - private IdentifiersTrie _methodNumbers; - - private void Init() - { - if (_methodPtrs == null) - { - lock (this) - { - if (_methodPtrs == null) - { - _methodPtrs = new List(); - MapType(typeof(TInstance)); - } - } - } - } - - private void InitSearch() - { - if (_methodNumbers == null) - { - Init(); - _methodNumbers = new IdentifiersTrie(); - for (int idx = 0; idx < _methodPtrs.Count; ++idx) - { - var methinfo = _methodPtrs[idx].MethodInfo; - - _methodNumbers.Add(methinfo.Name, idx); - if (methinfo.Alias != null) - _methodNumbers.Add(methinfo.Alias, idx); - } - } - } - - public ContextCallableDelegate GetMethod(int number) - { - Init(); - return _methodPtrs[number].Method; - } - - public ScriptEngine.Machine.MethodInfo GetMethodInfo(int number) - { - Init(); - return _methodPtrs[number].MethodInfo; - } - - public IEnumerable GetMethods() - { - Init(); - return _methodPtrs.Select(x => x.MethodInfo); - } - - public int FindMethod(string name) - { - InitSearch(); - - if (!_methodNumbers.TryGetValue(name, out var idx)) - throw RuntimeException.MethodNotFoundException(name); - - return idx; - } - - public int Count - { - get - { - Init(); - return _methodPtrs.Count; - } - } - - private void MapType(Type type) - { - _methodPtrs = type.GetMethods() - .SelectMany(method => method.GetCustomAttributes(typeof(ContextMethodAttribute), false) - .Select(attr => new InternalMethInfo(method, (ContextMethodAttribute)attr)) ) - .ToList(); - } - - private class InternalMethInfo - { - private readonly Lazy> _method; - public MethodInfo MethodInfo { get; } - - public InternalMethInfo(System.Reflection.MethodInfo target, ContextMethodAttribute binding) - { - _method = new Lazy>(() => - { - var isFunc = target.ReturnType != typeof(void); - return isFunc ? CreateFunction(target) : CreateProcedure(target); - }); - - MethodInfo = CreateMetadata(target, binding); - } - - public ContextCallableDelegate Method => _method.Value; - - private static MethodInfo CreateMetadata(System.Reflection.MethodInfo target, ContextMethodAttribute binding) - { - var parameters = target.GetParameters(); - var isFunc = target.ReturnType != typeof(void); - var argNum = parameters.Length; - - var paramDefs = new ParameterDefinition[argNum]; - for (int i = 0; i < argNum; i++) - { - var pd = new ParameterDefinition(); - if (parameters[i].GetCustomAttributes(typeof(ByRefAttribute), false).Length != 0) - { - if (parameters[i].ParameterType != typeof(IVariable)) - { - throw new InvalidOperationException("Attribute ByRef can be applied only on IVariable parameters"); - } - pd.IsByValue = false; - } - else - { - pd.IsByValue = true; - } - - if (parameters[i].IsOptional) - { - pd.HasDefaultValue = true; - pd.DefaultValueIndex = ParameterDefinition.UNDEFINED_VALUE_INDEX; - } - - paramDefs[i] = pd; - - } - - var scriptMethInfo = new ScriptEngine.Machine.MethodInfo(); - scriptMethInfo.IsFunction = isFunc; - scriptMethInfo.IsExport = true; - scriptMethInfo.IsDeprecated = binding.IsDeprecated; - scriptMethInfo.ThrowOnUseDeprecated = binding.ThrowOnUse; - scriptMethInfo.Name = binding.GetName(); - scriptMethInfo.Alias = binding.GetAlias(target.Name); - - scriptMethInfo.Params = paramDefs; - - return scriptMethInfo; - } - - private static ContextCallableDelegate CreateFunction(System.Reflection.MethodInfo target) - { - var methodCall = MethodCallExpression(target, out var instParam, out var argsParam); - - var convertRetMethod = typeof(InternalMethInfo).GetMethod("ConvertReturnValue", BindingFlags.Static | BindingFlags.NonPublic)?.MakeGenericMethod(target.ReturnType); - System.Diagnostics.Debug.Assert(convertRetMethod != null); - var convertReturnCall = Expression.Call(convertRetMethod, methodCall); - var body = convertReturnCall; - - var l = Expression.Lambda>(body, instParam, argsParam); - - return l.Compile(); - - } - private static ContextCallableDelegate CreateProcedure(System.Reflection.MethodInfo target) - { - var methodCall = MethodCallExpression(target, out var instParam, out var argsParam); - var returnLabel = Expression.Label(typeof(IValue)); - var defaultValue = Expression.Constant(null, typeof(IValue)); - var returnExpr = Expression.Return( - returnLabel, - defaultValue, - typeof(IValue) - ); - - var body = Expression.Block( - methodCall, - returnExpr, - Expression.Label(returnLabel, defaultValue) - ); - - var l = Expression.Lambda>(body, instParam, argsParam); - return l.Compile(); - } - - private static InvocationExpression MethodCallExpression(System.Reflection.MethodInfo target, out ParameterExpression instParam, out ParameterExpression argsParam) - { - // For those who dare: - // Код ниже формирует следующую лямбду с 2-мя замыканиями realMethodDelegate и defaults: - // (inst, args) => - // { - // realMethodDelegate(inst, - // ConvertParam(args[i], defaults[i]), - // ... - // ConvertParam(args[i], defaults[i])); - // } - - var methodClojure = CreateDelegateExpr(target); - - instParam = Expression.Parameter(typeof(TInstance), "inst"); - argsParam = Expression.Parameter(typeof(IValue[]), "args"); - - var argsPass = new List(); - argsPass.Add(instParam); - - var parameters = target.GetParameters(); - object[] defaultValues = new object[parameters.Length]; - var defaultsClojure = Expression.Constant(defaultValues); - - for (int i = 0; i < parameters.Length; i++) - { - var convertMethod = typeof(InternalMethInfo).GetMethod("ConvertParam", - BindingFlags.Static | BindingFlags.NonPublic, - null, - new Type[] - { - typeof(IValue), - typeof(object) - }, - null)?.MakeGenericMethod(parameters[i].ParameterType); - System.Diagnostics.Debug.Assert(convertMethod != null); - - if (parameters[i].HasDefaultValue) - { - defaultValues[i] = parameters[i].DefaultValue; - } - - var indexedArg = Expression.ArrayIndex(argsParam, Expression.Constant(i)); - var defaultArg = Expression.ArrayIndex(defaultsClojure, Expression.Constant(i)); - var conversionCall = Expression.Call(convertMethod, indexedArg, defaultArg); - argsPass.Add(conversionCall); - - } - - var methodCall = Expression.Invoke(methodClojure, argsPass); - return methodCall; - } - - private static Expression CreateDelegateExpr(System.Reflection.MethodInfo target) - { - var types = new List(); - types.Add(target.DeclaringType); - types.AddRange(target.GetParameters().Select(x => x.ParameterType)); - Type delegateType; - if (target.ReturnType == typeof(void)) - { - delegateType = Expression.GetActionType(types.ToArray()); - } - else - { - types.Add(target.ReturnType); - delegateType = Expression.GetFuncType(types.ToArray()); - } - - var deleg = target.CreateDelegate(delegateType); - - var delegateExpr = Expression.Constant(deleg); - var conversion = Expression.Convert(delegateExpr, delegateType); - - var delegateCreator = Expression.Lambda(conversion).Compile(); - var methodClojure = Expression.Constant(delegateCreator.DynamicInvoke()); - - return methodClojure; - } - - // ReSharper disable once UnusedMember.Local - private static T ConvertParam(IValue value) - { - return ContextValuesMarshaller.ConvertParam(value); - } - - // ReSharper disable once UnusedMember.Local - private static T ConvertParam(IValue value, object def) - { - if (value == null || value.DataType == DataType.NotAValidValue) - return (T)def; - - return ContextValuesMarshaller.ConvertParam(value); - } - - private static IValue ConvertReturnValue(TRet param) - { - return ContextValuesMarshaller.ConvertReturnValue(param); - } - } - - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; + +namespace ScriptEngine.Machine.Contexts +{ + public delegate IValue ContextCallableDelegate(TInstance instance, IValue[] args, IBslProcess process); + + public class ContextMethodsMapper + { + private List _methodPtrs; + private IdentifiersTrie _methodNumbers; + + private readonly object _locker = new object(); + + private void Init() + { + if (_methodPtrs == null) + { + lock (_locker) + { + if (_methodPtrs == null) + { + var localPtrs = MapType(typeof(TInstance)); + _methodNumbers = new IdentifiersTrie(); + for (int idx = 0; idx < localPtrs.Count; ++idx) + { + var methinfo = localPtrs[idx].MethodSignature; + + _methodNumbers.Add(methinfo.Name, idx); + if (methinfo.Alias != null) + _methodNumbers.Add(methinfo.Alias, idx); + } + + _methodPtrs = localPtrs; + } + } + } + } + + public ContextCallableDelegate GetCallableDelegate(int number) + { + Init(); + return _methodPtrs[number].Method; + } + + public BslMethodInfo GetRuntimeMethod(int number) + { + Init(); + return _methodPtrs[number].ClrMethod; + } + + public IEnumerable GetMethods() + { + Init(); + return _methodPtrs.Select(x => x.ClrMethod); + } + + public int FindMethod(string name) + { + Init(); + + if (!_methodNumbers.TryGetValue(name, out var idx)) + throw RuntimeException.MethodNotFoundException(name); + + return idx; + } + + public int Count + { + get + { + Init(); + return _methodPtrs.Count; + } + } + + private static List MapType(Type type) + { + var mappedMethods = new List(); + foreach (var methodInfo in type.GetMethods() + .Where(method => Attribute.IsDefined(method, typeof(ContextMethodAttribute)))) + { + var mainMarkup = methodInfo.GetCustomAttribute(); + var mainMapping = new InternalMethInfo(methodInfo, mainMarkup); + mappedMethods.Add(mainMapping); + mappedMethods.AddRange(methodInfo.GetCustomAttributes() + .Select(deprecation => new InternalMethInfo(methodInfo, new ContextMethodAttribute(deprecation.Name, default) + { + IsDeprecated = true, + ThrowOnUse = deprecation.ThrowOnUse + })) + ); + } + + return mappedMethods; + } + + private class InternalMethInfo + { + private readonly Lazy> _method; + private readonly ContextMethodInfo _clrMethod; + + public MethodSignature MethodSignature { get; } + public BslMethodInfo ClrMethod => _clrMethod; + + public InternalMethInfo(MethodInfo target, ContextMethodAttribute binding) + { + _clrMethod = new ContextMethodInfo(target, binding); + MethodSignature = CreateMetadata(target, binding, _clrMethod.InjectsProcess); + + _method = new Lazy>(() => + { + var isFunc = target.ReturnType != typeof(void); + return isFunc ? CreateFunction(_clrMethod) : CreateProcedure(_clrMethod); + }); + } + + public ContextCallableDelegate Method => _method.Value; + + private static MethodSignature CreateMetadata(MethodInfo target, ContextMethodAttribute binding, + bool hasProcessParam) + { + return CreateMetadata(target, binding.Name, binding.Alias, binding.IsDeprecated, binding.ThrowOnUse, + hasProcessParam); + } + + private static MethodSignature CreateMetadata( + MethodInfo target, + string name, + string alias, + bool isDeprecated, + bool throwOnUse, + bool hasProcessParam) + { + var parameters = target.GetParameters(); + var isFunc = target.ReturnType != typeof(void); + + var (startIndex, argNum) = hasProcessParam ? (1, parameters.Length - 1) : (0, parameters.Length); + + var paramDefs = new ParameterDefinition[argNum]; + for (int i = 0, j = startIndex; i < argNum; i++, j++) + { + var pd = new ParameterDefinition(); + if (parameters[j].GetCustomAttributes(typeof(ByRefAttribute), false).Length != 0) + { + if (parameters[j].ParameterType != typeof(IVariable)) + { + throw new InvalidOperationException("Attribute ByRef can be applied only on IVariable parameters"); + } + pd.IsByValue = false; + } + else + { + pd.IsByValue = true; + } + + if (parameters[j].IsOptional) + { + pd.HasDefaultValue = true; + pd.DefaultValueIndex = ParameterDefinition.UNDEFINED_VALUE_INDEX; + } + + paramDefs[i] = pd; + + } + + return new MethodSignature + { + IsFunction = isFunc, + IsExport = true, + IsDeprecated = isDeprecated, + ThrowOnUseDeprecated = throwOnUse, + Name = name, + Alias = alias, + Params = paramDefs + }; + } + + private static ContextCallableDelegate CreateFunction(ContextMethodInfo target) + { + var methodCall = MethodCallExpression(target, out var instParam, out var argsParam, out var processParam); + + var convertRetMethod = ContextValuesMarshaller.BslReturnValueGenericConverter.MakeGenericMethod(target.ReturnType); + var convertReturnCall = Expression.Call(convertRetMethod, methodCall); + var body = convertReturnCall; + + var l = Expression.Lambda>(body, instParam, argsParam, processParam); + + return l.Compile(); + + } + private static ContextCallableDelegate CreateProcedure(ContextMethodInfo target) + { + var methodCall = MethodCallExpression(target, out var instParam, out var argsParam, out var processParam); + var returnLabel = Expression.Label(typeof(IValue)); + var defaultValue = Expression.Constant(null, typeof(IValue)); + var returnExpr = Expression.Return( + returnLabel, + defaultValue, + typeof(IValue) + ); + + var body = Expression.Block( + methodCall, + returnExpr, + Expression.Label(returnLabel, defaultValue) + ); + + var l = Expression.Lambda>(body, instParam, argsParam, processParam); + return l.Compile(); + } + + private static InvocationExpression MethodCallExpression( + ContextMethodInfo contextMethod, + out ParameterExpression instParam, + out ParameterExpression argsParam, + out ParameterExpression processParam) + { + // For those who dare: + // Код ниже формирует следующую лямбду с 2-мя замыканиями realMethodDelegate и defaults: + // (inst, args) => + // { + // realMethodDelegate(inst, + // ConvertParam(args[i], defaults[i]), + // ... + // ConvertParam(args[i], defaults[i])); + // } + + var target = contextMethod.GetWrappedMethod(); + var methodClojure = CreateDelegateExpr(target); + + instParam = Expression.Parameter(typeof(TInstance), "inst"); + argsParam = Expression.Parameter(typeof(IValue[]), "args"); + processParam = Expression.Parameter(typeof(IBslProcess), "process"); + + var parameters = target.GetParameters(); + + var (clrIndexStart, argsLen) = contextMethod.InjectsProcess ? (1, parameters.Length - 1) : (0, parameters.Length); + + var argsPass = new List(); + argsPass.Add(instParam); + + if (contextMethod.InjectsProcess) + argsPass.Add(processParam); + + for (int bslIndex = 0,clrIndex = clrIndexStart; bslIndex < argsLen; bslIndex++, clrIndex++) + { + var targetType = parameters[clrIndex].ParameterType; + var convertMethod = ContextValuesMarshaller.BslGenericParameterConverter.MakeGenericMethod(targetType); + + Expression defaultArg; + if (parameters[clrIndex].HasDefaultValue) + { + defaultArg = Expression.Constant(parameters[clrIndex].DefaultValue, targetType); + } + else + { + defaultArg = ContextValuesMarshaller.GetDefaultBslValueConstant(targetType); + } + + var indexedArg = Expression.ArrayIndex(argsParam, Expression.Constant(bslIndex)); + var conversionCall = Expression.Call(convertMethod, + indexedArg, + defaultArg, + processParam); + + argsPass.Add(Expression.Convert(conversionCall, targetType)); + } + + var methodCall = Expression.Invoke(methodClojure, argsPass); + return methodCall; + } + + private static Expression CreateDelegateExpr(MethodInfo target) + { + var types = new List(); + types.Add(target.DeclaringType); + types.AddRange(target.GetParameters().Select(x => x.ParameterType)); + Type delegateType; + if (target.ReturnType == typeof(void)) + { + delegateType = Expression.GetActionType(types.ToArray()); + } + else + { + types.Add(target.ReturnType); + delegateType = Expression.GetFuncType(types.ToArray()); + } + + var deleg = target.CreateDelegate(delegateType); + + var delegateExpr = Expression.Constant(deleg); + var conversion = Expression.Convert(delegateExpr, delegateType); + + var delegateCreator = Expression.Lambda(conversion).Compile(); + var methodClojure = Expression.Constant(delegateCreator.DynamicInvoke()); + + return methodClojure; + } + } + } +} diff --git a/src/ScriptEngine/Machine/Contexts/ContextPropertyMapper.cs b/src/ScriptEngine/Machine/Contexts/ContextPropertyMapper.cs index 3aecbefdd..96c975743 100644 --- a/src/ScriptEngine/Machine/Contexts/ContextPropertyMapper.cs +++ b/src/ScriptEngine/Machine/Contexts/ContextPropertyMapper.cs @@ -1,240 +1,124 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace ScriptEngine.Machine.Contexts -{ - [AttributeUsage(AttributeTargets.Property)] - public class ContextPropertyAttribute : Attribute - { - private readonly string _name; - private readonly string _alias; - - public ContextPropertyAttribute(string name, string alias = "") - { - if (!Utils.IsValidIdentifier(name)) - throw new ArgumentException("Name must be a valid identifier"); - - if (!string.IsNullOrEmpty(alias) && !Utils.IsValidIdentifier(alias)) - throw new ArgumentException("Alias must be a valid identifier"); - - _name = name; - _alias = alias; - CanRead = true; - CanWrite = true; - } - - public bool CanRead { get; set; } - public bool CanWrite { get; set; } - - public string GetName() - { - return _name; - } - - public string GetAlias() - { - return _alias; - } - - } - - public class PropertyTarget - { - private readonly Func _getter; - private readonly Action _setter; - private readonly string _name; - private readonly string _alias; - - public PropertyTarget(PropertyInfo propInfo) - { - var attrib = (ContextPropertyAttribute)propInfo.GetCustomAttributes(typeof(ContextPropertyAttribute), false)[0]; - _name = attrib.GetName(); - _alias = attrib.GetAlias(); - if (string.IsNullOrEmpty(_alias)) - _alias = propInfo.Name; - - IValue CantReadAction(TInstance inst) - { - throw RuntimeException.PropIsNotReadableException(_name); - } - - void CantWriteAction(TInstance inst, IValue val) - { - throw RuntimeException.PropIsNotWritableException(_name); - } - - this.CanRead = attrib.CanRead; - this.CanWrite = attrib.CanWrite; - - if (attrib.CanRead) - { - var getMethodInfo = propInfo.GetGetMethod(); - if (getMethodInfo == null) - { - _getter = CantReadAction; - } - else - { - var genericGetter = typeof(PropertyTarget).GetMembers(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) - .Where(x => x.MemberType == System.Reflection.MemberTypes.Method && x.Name == "CreateGetter") - .Select(x => (System.Reflection.MethodInfo)x) - .First(); - - var resolvedGetter = genericGetter.MakeGenericMethod(new Type[] { propInfo.PropertyType }); - - _getter = (Func)resolvedGetter.Invoke(this, new object[] { getMethodInfo }); - } - } - else - { - _getter = CantReadAction; - } - - if (attrib.CanWrite) - { - var setMethodInfo = propInfo.GetSetMethod(); - if (setMethodInfo == null) - { - _setter = CantWriteAction; - } - else - { - var genericSetter = typeof(PropertyTarget).GetMembers(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) - .Where(x => x.MemberType == System.Reflection.MemberTypes.Method && x.Name == "CreateSetter") - .Select(x => (System.Reflection.MethodInfo)x) - .First(); - - var resolvedSetter = genericSetter.MakeGenericMethod(new Type[] { propInfo.PropertyType }); - - _setter = (Action)resolvedSetter.Invoke(this, new object[] { setMethodInfo }); - } - } - else - { - _setter = CantWriteAction; - } - } - - public Func Getter => _getter; - - public Action Setter => _setter; - - public string Name => _name; - - public string Alias => _alias; - - public bool CanRead { get; private set; } - public bool CanWrite { get; private set; } - - private Func CreateGetter(System.Reflection.MethodInfo methInfo) - { - var method = (Func)Delegate.CreateDelegate(typeof(Func), methInfo); - return (inst) => ConvertReturnValue(method(inst)); - } - - private Action CreateSetter(System.Reflection.MethodInfo methInfo) - { - var method = (Action)Delegate.CreateDelegate(typeof(Action), methInfo); - return (inst, val) => method(inst, ConvertParam(val)); - } - - private T ConvertParam(IValue value) - { - return ContextValuesMarshaller.ConvertParam(value); - } - - private IValue ConvertReturnValue(TRet param) - { - return ContextValuesMarshaller.ConvertReturnValue(param); - } - - } - - public class ContextPropertyMapper - { - private List> _properties; - - public void Init() - { - if (_properties != null) return; - - lock (this) - { - if(_properties == null) - FindProperties(); - } - } - - private void FindProperties() - { - _properties = typeof(TInstance).GetProperties() - .Where(x => x.GetCustomAttributes(typeof(ContextPropertyAttribute), false).Any()) - .Select(x => new PropertyTarget(x)).ToList(); - } - - public bool ContainsProperty(string name) - { - return GetPropertyIndex(name) >= 0; - } - - public int FindProperty(string name) - { - var idx = GetPropertyIndex(name); - if (idx < 0) - throw RuntimeException.PropNotFoundException(name); - - return idx; - } - - public PropertyTarget GetProperty(int index) - { - Init(); - return _properties[index]; - } - - public int Count - { - get - { - Init(); - return _properties.Count; - } - } - - public VariableInfo GetPropertyInfo(int propNum) - { - var prop = _properties[propNum]; - return new VariableInfo() - { - Identifier = prop.Name, - Alias = prop.Alias, - Type = SymbolType.ContextProperty, - Index = propNum - }; - } - - public IEnumerable GetProperties() - { - Init(); - for (int i = 0; i < Count; i++) - { - yield return GetPropertyInfo(i); - } - } - - private int GetPropertyIndex(string name) - { - Init(); - return _properties.FindIndex(x => String.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) - || String.Equals(x.Alias, name, StringComparison.OrdinalIgnoreCase)); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Language; + +namespace ScriptEngine.Machine.Contexts +{ + public class ContextPropertyMapper + { + private List> _properties; + private IdentifiersTrie _propertyNumbers; + + private readonly object _locker = new object(); + + private void Init() + { + if (_properties != null) + return; + + lock (_locker) + { + if (_properties == null) + { + var localProps = MapProperties(); + _propertyNumbers = new IdentifiersTrie(); + for (int idx = 0; idx < localProps.Count; ++idx) + { + var propInfo = localProps[idx]; + + _propertyNumbers.Add(propInfo.Name, idx); + if (propInfo.Alias != null) + _propertyNumbers.Add(propInfo.Alias, idx); + } + + _properties = localProps; + } + } + } + + private static List> MapProperties() + { + var mappedProperties = new List>(); + foreach (var propertyInfo in typeof(TInstance).GetProperties() + .Where(x => Attribute.IsDefined(x, typeof(ContextPropertyAttribute)))) + { + var propertyMarkup = propertyInfo.GetCustomAttribute(); + var bslProp = new ContextPropertyInfo(propertyInfo, propertyMarkup); + var mainMapping = new PropertyTarget(bslProp); + mappedProperties.Add(mainMapping); + + foreach (var deprecation in propertyInfo.GetCustomAttributes()) + { + var deprecatedMarkup = new ContextPropertyAttribute(deprecation.Name) + { + CanRead = bslProp.CanRead, + CanWrite = bslProp.CanWrite, + IsDeprecated = true, + ThrowOnUse = deprecation.ThrowOnUse + }; + + bslProp = new ContextPropertyInfo(propertyInfo, deprecatedMarkup); + var deprecatedMapping = new PropertyTarget(bslProp); + mappedProperties.Add(deprecatedMapping); + } + } + + return mappedProperties; + } + + public bool ContainsProperty(string name) + { + return GetPropertyIndex(name) >= 0; + } + + public int FindProperty(string name) + { + var idx = GetPropertyIndex(name); + if (idx < 0) + throw PropertyAccessException.PropNotFoundException(name); + + return idx; + } + + public PropertyTarget GetProperty(int index) + { + Init(); + return _properties[index]; + } + + public IEnumerable GetProperties() + { + Init(); + return _properties.Select(p => p.PropertyInfo); + } + + public int Count + { + get + { + Init(); + return _properties.Count; + } + } + + private int GetPropertyIndex(string name) + { + Init(); + if (_propertyNumbers.TryGetValue(name, out var index)) + return index; + + return -1; + } + } +} diff --git a/src/ScriptEngine/Machine/Contexts/ContextValuesMarshaller.cs b/src/ScriptEngine/Machine/Contexts/ContextValuesMarshaller.cs index aa2577710..b6e5b9c7e 100644 --- a/src/ScriptEngine/Machine/Contexts/ContextValuesMarshaller.cs +++ b/src/ScriptEngine/Machine/Contexts/ContextValuesMarshaller.cs @@ -5,83 +5,194 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; +using System.Diagnostics; using System.Linq; -using ScriptEngine.Machine.Values; +using System.Linq.Expressions; +using System.Reflection; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; namespace ScriptEngine.Machine.Contexts { public static class ContextValuesMarshaller { - public static T ConvertParam(IValue value) + public static MethodInfo BslParameterConverter { get; private set; } + public static MethodInfo BslGenericParameterConverter { get; private set; } + public static MethodInfo BslReturnValueGenericConverter { get; private set; } + + static ContextValuesMarshaller() { - var type = typeof(T); - object valueObj = ConvertParam(value, type); - if (valueObj == null) - { - return default(T); - } + BslParameterConverter = typeof(ContextValuesMarshaller).GetMethods() + .First(x => x.Name == nameof(ConvertParam) && x.GetGenericArguments().Length == 0 && + x.GetParameters().Length == 4); + + BslGenericParameterConverter = typeof(ContextValuesMarshaller).GetMethods() + .First(x => x.Name == nameof(ConvertParam) && x.GetGenericArguments().Length == 1 && + x.GetParameters().Length == 3); + + BslReturnValueGenericConverter = typeof(ContextValuesMarshaller).GetMethods() + .First(x => x.Name == nameof(ConvertReturnValue) && x.GetGenericArguments().Length == 1); + } + + /// + /// Выполняет конвертацию значения из Bsl в значение параметра метода C# + /// + /// Универсальное значение из Bsl + /// Целевой тип в который надо сконвертировать значение. + /// Значение по умолчанию, которое будет возвращено, если не заполнен. + /// Текущий BSL-процесс, в рамках которого вызывается метод + /// Значение целевого типа + public static object ConvertParam(IValue value, Type type, object defaultValue, IBslProcess process) + { + Debug.Assert(defaultValue == null || type.IsInstanceOfType(defaultValue)); + var converted = ConvertParam(value, type, process); + return converted ?? defaultValue; + } + + /// + /// Выполняет конвертацию значения из Bsl в значение параметра метода C# + /// + /// Универсальное значение из Bsl + /// Целевой тип в который надо сконвертировать значение. + /// Текущий BSL-процесс, в рамках которого вызывается метод + /// Значение целевого типа + public static object ConvertParam(IValue value, Type type, IBslProcess process) + { try { - return (T)valueObj; + return ConvertValueType(value, type, process); } catch (InvalidCastException) { throw RuntimeException.InvalidArgumentType(); } - + catch (OverflowException) + { + throw RuntimeException.InvalidArgumentValue(); + } } - - public static T ConvertParam(IValue value, T defaultValue) + + /// + /// Выполняет конвертацию значения из Bsl в значение параметра метода C#. + /// В данный метод нельзя передавать значения, конвертация которых в целевой тип (напр. строку) + /// может приводить к вызову другого Bsl-кода. Метод выбросит исключение, если конвертация захочет выполнить bsl-код. + /// + /// Универсальное значение из Bsl + /// Значение по умолчанию, которое будет возвращено, если не заполнен. + /// Целевой тип в который надо сконвертировать значение + /// Значение целевого типа + public static T ConvertParam(IValue value, T defaultValue = default) { - var type = typeof(T); - object valueObj = ConvertParam(value, type); - if (valueObj == null) + return ConvertParam(value, defaultValue, ForbiddenBslProcess.Instance); + } + + /// + /// Выполняет конвертацию значения из Bsl в значение параметра метода C# + /// + /// Универсальное значение из Bsl + /// Текущий BSL-процесс, в рамках которого вызывается метод + /// Значение по умолчанию, которое будет возвращено, если не заполнен. + /// Целевой тип в который надо сконвертировать значение + /// Значение целевого типа + public static T ConvertParam(IValue value, T defaultValue, IBslProcess process) + { + object valueObj = ConvertParam(value, typeof(T), process); + return valueObj != null ? (T)valueObj : defaultValue; + } + + /// + /// Выполняет строгую конвертацию параметра в запрошенный тип. + /// Не выполняет приведение объектов к строке, в отличие от ConvertParam. + /// Это значит, что нельзя скормить объект в C# параметр с типом string через конверсию в AsString. + /// Выдает исключение о неверном типе параметра. + /// + public static T ConvertValueStrict(IValue value) + { + if (value == null || value.IsSkippedArgument()) { - return defaultValue; + return default; } + + if (value is T t) + return t; try { - return (T)valueObj; + var converted = ConvertToClrObject(value); + return converted switch + { + T casted => casted, + decimal _ => (T)Convert.ChangeType(converted, typeof(T)), + _ => throw RuntimeException.InvalidArgumentType() + }; } catch (InvalidCastException) { throw RuntimeException.InvalidArgumentType(); } - + catch (ValueMarshallingException) + { + throw RuntimeException.InvalidArgumentType(); + } + } + + public static Expression GetDefaultBslValueConstant(Type targetType) + { + if (targetType == typeof(string)) + { + return Expression.Constant(""); + } + + return Expression.Default(targetType); } public static object ConvertParam(IValue value, Type type) + { + return ConvertParam(value, type, ForbiddenBslProcess.Instance); + } + + private static object ConvertValueType(IValue value, Type type, IBslProcess process) { object valueObj; - if (value == null || value.DataType == DataType.NotAValidValue) + if (value == null || value.IsSkippedArgument()) { return null; } if (Nullable.GetUnderlyingType(type) != null) { - return ConvertParam(value, Nullable.GetUnderlyingType(type)); + return ConvertValueType(value, Nullable.GetUnderlyingType(type), process); } - if (type == typeof(IValue)) + if (type == typeof(IVariable)) { - valueObj = value; + return value; } - else if (type == typeof(IVariable)) + + if (value is IValueReference r) { - valueObj = value; + // Если целевой тип не требовал именно переменную, то разыменовываем ее + value = r.Value; } - else if (type == typeof(string)) + + if (type == typeof(IValue) || type == typeof(BslValue)) { - valueObj = value.AsString(); + valueObj = value; } - else if (value == UndefinedValue.Instance) + else if (value.SystemType == BasicTypes.Undefined) { // Если тип параметра не IValue и не IVariable && Неопределено -> null valueObj = null; } + else if (type == typeof(string)) + { + valueObj = value.AsString(process); + } else if (type == typeof(int)) { valueObj = (int)value.AsNumber(); @@ -131,164 +242,157 @@ public static object ConvertParam(IValue value, Type type) valueObj = value.AsBoolean(); } else if (typeof(IRuntimeContextInstance).IsAssignableFrom(type)) + { + if (value.GetType().IsAssignableTo(type)) + valueObj = value.AsObject(); + else + throw new InvalidCastException(); + } + else if (value is EnumerationValue && typeof(EnumerationValue).IsAssignableFrom(type)) { - valueObj = value.AsObject(); + valueObj = value; } else { - valueObj = CastToCLRObject(value); + if (value is IObjectWrapper wrapped) + { + if (!type.IsInstanceOfType(wrapped.UnderlyingObject)) + throw new InvalidCastException(); + } + else if (!type.IsInstanceOfType(value)) + { + throw new InvalidCastException(); + } + + valueObj = CastToClrObject(value); } return valueObj; } - public static IValue ConvertReturnValue(object objParam, Type type) + private static IValue ConvertReturnValue(object objParam, Type type) { if (objParam == null) return ValueFactory.Create(); - if (type == typeof(IValue)) - { - return (IValue)objParam; - } - else if (type == typeof(string)) - { - return ValueFactory.Create((string)objParam); - } - else if (type == typeof(int)) - { - return ValueFactory.Create((int)objParam); - } - else if (type == typeof(uint)) - { - return ValueFactory.Create((uint)objParam); - } - else if (type == typeof(long)) - { - return ValueFactory.Create((long)objParam); - } - else if (type == typeof(ulong)) - { - return ValueFactory.Create((ulong)objParam); - } - else if (type == typeof(decimal)) - { - return ValueFactory.Create((decimal)objParam); - } - else if (type == typeof(double)) - { - return ValueFactory.Create((decimal)(double)objParam); - } - else if (type == typeof(DateTime)) - { - return ValueFactory.Create((DateTime)objParam); - } - else if (type == typeof(bool)) + switch (objParam) { - return ValueFactory.Create((bool)objParam); + case IValue v: return v; + + case string s: return ValueFactory.Create(s); + case bool b: return ValueFactory.Create(b); + case DateTime d: return ValueFactory.Create(d); + + case int n: return ValueFactory.Create(n); + case uint n: return ValueFactory.Create(n); + case long n: return ValueFactory.Create(n); + case ulong n: return ValueFactory.Create(n); + case byte n: return ValueFactory.Create(n); + case sbyte n: return ValueFactory.Create(n); + case short n: return ValueFactory.Create(n); + case ushort n: return ValueFactory.Create(n); + case decimal n: return ValueFactory.Create(n); + case double n: return ValueFactory.Create((decimal)n); } - else if (type.IsEnum) + + if (type.IsEnum) { return ConvertEnum(objParam, type); } else if (typeof(IRuntimeContextInstance).IsAssignableFrom(type)) { - return ValueFactory.Create((IRuntimeContextInstance)objParam); + return (IValue)(IRuntimeContextInstance)objParam; } else if (typeof(IValue).IsAssignableFrom(type)) { return (IValue)objParam; } + else if (Nullable.GetUnderlyingType(type) != null) + { + return ConvertReturnValue(objParam, Nullable.GetUnderlyingType(type)); + } else { - throw new NotSupportedException($"Type {type} is not supported"); + throw ValueMarshallingException.TypeNotSupported(type); } } private static IValue ConvertEnum(object objParam, Type type) { - if (!type.IsAssignableFrom(objParam.GetType())) - throw new RuntimeException("Некорректный тип конвертируемого перечисления"); - - var memberInfo = type.GetMember(objParam.ToString()); - var valueInfo = memberInfo.FirstOrDefault(x => x.DeclaringType == type); - var attrs = valueInfo.GetCustomAttributes(typeof(EnumItemAttribute), false); + return SimpleEnumsMarshaller.ConvertEnum(objParam, type); + } - if (attrs.Length == 0) - throw new RuntimeException("Значение перечисления должно быть помечено атрибутом EnumItemAttribute"); + public static T ConvertWrappedEnum(IValue enumeration, T defValue) where T : struct + { + if (enumeration == null) + return defValue; - var itemName = ((EnumItemAttribute)attrs[0]).Name; - var enumImpl = GlobalsManager.GetSimpleEnum(type); + if (enumeration is ClrEnumValueWrapper wrapped) + { + return wrapped.UnderlyingValue; + } - return enumImpl.GetPropValue(itemName); + throw RuntimeException.InvalidArgumentValue(); } - public static IValue ConvertReturnValue(TRet param) + public static IValue ConvertDynamicValue(object param) { - var type = typeof(TRet); + if (param == null) + throw ValueMarshallingException.InvalidNullValue(); - return ConvertReturnValue(param, type); + return ConvertReturnValue(param, param.GetType()); } - public static object ConvertToCLRObject(IValue val) - { - object result; - if (val == null) - return val; - - switch (val.DataType) - { - case Machine.DataType.Boolean: - result = val.AsBoolean(); - break; - case Machine.DataType.Date: - result = val.AsDate(); - break; - case Machine.DataType.Number: - result = val.AsNumber(); - break; - case Machine.DataType.String: - result = val.AsString(); - break; - case Machine.DataType.Undefined: - result = null; - break; - default: - if (val.DataType == DataType.Object) - result = val.AsObject(); - - result = val.GetRawValue(); - if (result is IObjectWrapper) - result = ((IObjectWrapper)result).UnderlyingObject; - else - throw new ValueMarshallingException($"Тип {val.GetType()} не поддерживает преобразование в CLR-объект"); + public static IValue ConvertDynamicIndex(object param) + { + if (param == null) + throw ValueMarshallingException.InvalidNullIndex(); - break; - } - - return result; - } + return ConvertReturnValue(param, param.GetType()); + } - public static T CastToCLRObject(IValue val) + public static IValue ConvertReturnValue(TRet param) { - return (T)CastToCLRObject(val); + return ConvertReturnValue(param, typeof(TRet)); } - public static object CastToCLRObject(IValue val) + public static object ConvertToClrObject(IValue value) + { + if (value == null) + return null; + + // TODO: Вероятно, можно просто заменить на ассерт, что это не IValueReference + if (value is IValueReference r) + return ConvertToClrObject(r.Value); + + return value switch + { + BslNumericValue num => (decimal)num, + BslBooleanValue boolean => (bool)boolean, + BslStringValue str => (string)str, + BslDateValue date => (DateTime)date, + BslUndefinedValue _ => null, + BslNullValue _ => null, + BslTypeValue type => type.SystemType.ImplementingClass, + IObjectWrapper wrapper => wrapper.UnderlyingObject, + BslObjectValue obj => obj, + _ => throw ValueMarshallingException.NoConversionToCLR(value.GetType()) + }; + } + + private static object CastToClrObject(IValue val) { - var rawValue = val.GetRawValue(); object objectRef; - if (rawValue.DataType == DataType.GenericValue) + if (val is IObjectWrapper wrapper) { - objectRef = rawValue; + objectRef = wrapper.UnderlyingObject; } else { - objectRef = ConvertToCLRObject(rawValue); + objectRef = ConvertToClrObject(val); } return objectRef; - } } } diff --git a/src/ScriptEngine/Machine/Contexts/CriticalSectionContext.cs b/src/ScriptEngine/Machine/Contexts/CriticalSectionContext.cs new file mode 100644 index 000000000..c1b84fb04 --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/CriticalSectionContext.cs @@ -0,0 +1,64 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Threading; +using OneScript.Contexts; + +namespace ScriptEngine.Machine.Contexts +{ + /// + /// Класс предназначен для блокировки потока для монопольного доступа к ресурсу + /// + [ContextClass("БлокировкаРесурса", "ResourceLock")] + public class CriticalSectionContext : AutoContext, IDisposable + { + private object _lockObject; + + private CriticalSectionContext() + { + _lockObject = new object(); + } + + private CriticalSectionContext(object lockObject) + { + _lockObject = lockObject; + } + + [ContextMethod("Заблокировать", "Lock")] + public void Lock() + { + Monitor.Enter(_lockObject); + } + + [ContextMethod("Разблокировать", "Unlock")] + public void Unlock() + { + Monitor.Exit(_lockObject); + } + + public void Dispose() + { + if(Monitor.IsEntered(_lockObject)) + Monitor.Exit(_lockObject); + + _lockObject = null; + } + + [ScriptConstructor] + public static CriticalSectionContext Create() + { + return new CriticalSectionContext(); + } + + [ScriptConstructor(Name = "По объекту")] + public static CriticalSectionContext Create(IValue instance) + { + return new CriticalSectionContext(instance.AsObject()); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/DynamicPropertiesAccessor.cs b/src/ScriptEngine/Machine/Contexts/DynamicPropertiesAccessor.cs index 11544f3e5..c8a7f0d20 100644 --- a/src/ScriptEngine/Machine/Contexts/DynamicPropertiesAccessor.cs +++ b/src/ScriptEngine/Machine/Contexts/DynamicPropertiesAccessor.cs @@ -6,6 +6,9 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Values; namespace ScriptEngine.Machine.Contexts { @@ -15,22 +18,38 @@ public abstract class DynamicPropertiesAccessor : PropertyNameIndexAccessor public DynamicPropertiesAccessor() { - _propHolder = new DynamicPropertiesHolder(); + _propHolder = new DynamicPropertiesHolder(InfoFactory); } - - protected int RegisterProperty(string name) + + private BslPropertyInfo InfoFactory(int index, string identifier, bool canRead, bool canWrite) + { + return OnPropertyRegistration(index, identifier, canRead, canWrite); + } + + protected virtual BslPropertyInfo OnPropertyRegistration(int index, string propertyName, bool canRead, bool canWrite) { - return _propHolder.RegisterProperty(name); + return BslPropertyBuilder.Create() + .Name(propertyName) + .CanRead(canRead) + .CanWrite(canWrite) + .SetDispatchingIndex(index) + .ReturnType(typeof(BslValue)) + .Build(); } - protected void RemoveProperty(string name) + protected int RegisterProperty(string name, bool canRead = true, bool canWrite = true) { - _propHolder.RemoveProperty(name); + return _propHolder.RegisterProperty(name, canRead, canWrite); + } + + protected int RegisterProperty(BslPropertyInfo propInfo) + { + return _propHolder.RegisterProperty(propInfo); } - protected void ReorderPropertyNumbers() + protected void RemoveProperty(string name) { - _propHolder.ReorderPropertyNumbers(); + _propHolder.RemoveProperty(name); } protected void ClearProperties() @@ -43,11 +62,16 @@ protected string GetPropertyName(int idx) return _propHolder.GetPropertyName(idx); } - protected virtual IEnumerable> GetProperties() + protected virtual IEnumerable> GetDynamicProperties() { return _propHolder.GetProperties(); } + public override BslPropertyInfo GetPropertyInfo(int index) + { + return _propHolder[index]; + } + #region IRuntimeContextInstance Members public override bool IsIndexed @@ -65,7 +89,7 @@ public override string GetPropName(int propNum) return GetPropertyName(propNum); } - public override int FindProperty(string name) + public override int GetPropertyNumber(string name) { try { @@ -73,7 +97,7 @@ public override int FindProperty(string name) } catch (KeyNotFoundException) { - throw RuntimeException.PropNotFoundException(name); + throw PropertyAccessException.PropNotFoundException(name); } } diff --git a/src/ScriptEngine/Machine/Contexts/DynamicPropertiesHolder.cs b/src/ScriptEngine/Machine/Contexts/DynamicPropertiesHolder.cs index 4373fd210..5d444bbd8 100644 --- a/src/ScriptEngine/Machine/Contexts/DynamicPropertiesHolder.cs +++ b/src/ScriptEngine/Machine/Contexts/DynamicPropertiesHolder.cs @@ -6,19 +6,36 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; -using System.Linq; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; namespace ScriptEngine.Machine.Contexts { public class DynamicPropertiesHolder { - private readonly Dictionary _propNumbers = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IndexedNameValueCollection _propDefs = + new IndexedNameValueCollection(); + + public delegate BslPropertyInfo PropertyInfoFactory(int index, string name, bool canRead, bool canWrite); + + private readonly PropertyInfoFactory _infoFactory = MakeProperty; + + public DynamicPropertiesHolder() + { + } + + public DynamicPropertiesHolder(PropertyInfoFactory infoFactory) + { + _infoFactory = infoFactory; + } - public int RegisterProperty(string name) + public int RegisterProperty(string name, bool canRead = true, bool canWrite = true) { - if (_propNumbers.ContainsKey(name)) + var index = _propDefs.IndexOf(name); + if (index != -1) { - return _propNumbers[name]; + return index; } if (!IsValidIdentifier(name)) @@ -26,63 +43,66 @@ public int RegisterProperty(string name) throw RuntimeException.InvalidArgumentValue(); } - var idx = _propNumbers.Count; - _propNumbers.Add(name, idx); - return idx; + index = _propDefs.Count; + return _propDefs.Add(_infoFactory(index, name, canRead, canWrite), name); + } + + public int RegisterProperty(BslPropertyInfo prop) + { + var index = _propDefs.IndexOf(prop.Name); + if (index != -1) + { + throw new ArgumentException($"Property {prop.Name} already exists"); + } + + return _propDefs.Add(prop, prop.Name); } - public void RemoveProperty(string name) + private static BslPropertyInfo MakeProperty(int index, string name, bool canRead, bool canWrite) { - _propNumbers.Remove(name); + return BslPropertyBuilder.Create() + .Name(name) + .SetDispatchingIndex(index) + .CanRead(canRead) + .CanWrite(canWrite) + .Build(); } - public void ReorderPropertyNumbers() + public void RemoveProperty(string name) { - var sorted = _propNumbers.OrderBy(x => x.Value).Select(x => x.Key).ToArray(); - _propNumbers.Clear(); - for (int i = 0; i < sorted.Length; i++) - { - _propNumbers.Add(sorted[i], i); - } + _propDefs.RemoveValue(name); } public void ClearProperties() { - _propNumbers.Clear(); + _propDefs.Clear(); } public int GetPropertyNumber(string name) { - if (_propNumbers.TryGetValue(name, out var index)) + var index = _propDefs.IndexOf(name); + if (index != -1) return index; - throw RuntimeException.PropNotFoundException(name); + throw PropertyAccessException.PropNotFoundException(name); } public string GetPropertyName(int idx) { - return _propNumbers.First(x => x.Value == idx).Key; + return _propDefs[idx].Name; } public IEnumerable> GetProperties() { - return _propNumbers.AsEnumerable(); + return _propDefs.GetIndex(); } - public VariableInfo GetPropertyInfo(int idx) - { - return new VariableInfo() - { - Identifier = GetPropertyName(idx), - CanGet = true, - CanSet = true, - Index = idx, - Type = SymbolType.ContextProperty - }; - } + public int Count => _propDefs.Count; - public int Count => _propNumbers.Count; + public BslPropertyInfo this[int index] => _propDefs[index]; + public BslPropertyInfo this[string name] => _propDefs[name]; + private bool IsValidIdentifier(string name) { return Utils.IsValidIdentifier(name); diff --git a/src/ScriptEngine/Machine/Contexts/EnumContextHelper.cs b/src/ScriptEngine/Machine/Contexts/EnumContextHelper.cs index f2dc42d37..60b1c0b00 100644 --- a/src/ScriptEngine/Machine/Contexts/EnumContextHelper.cs +++ b/src/ScriptEngine/Machine/Contexts/EnumContextHelper.cs @@ -5,55 +5,87 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using System.Linq; +using OneScript.Contexts.Enums; +using OneScript.Types; +using OneScript.Values; namespace ScriptEngine.Machine.Contexts { public static class EnumContextHelper { - public static void RegisterValues(T instance) where T : EnumerationContext + public static (TypeDescriptor, TypeDescriptor) RegisterEnumType(ITypeManager typeManager) + where TEnum : EnumerationContext + where TValue : EnumerationValue { - var enumType = typeof(T); - var values = enumType.GetProperties() - .Where(x => x.GetCustomAttributes(typeof(EnumValueAttribute), false).Any()) - .Select(x => (EnumValueAttribute)x.GetCustomAttributes(typeof(EnumValueAttribute), false)[0]); - - foreach (var enumProperty in values) - { - instance.AddValue(enumProperty.GetName(), enumProperty.GetAlias(), new SelfAwareEnumValue(instance)); - } + return RegisterEnumType(typeof(TEnum), typeof(TValue), typeManager); } - - public static void RegisterEnumType(out TypeDescriptor enumType, out TypeDescriptor enumValueType) where T : EnumerationContext + + public static (TypeDescriptor, TypeDescriptor) RegisterEnumType(Type enumClass, Type enumValueClass, ITypeManager typeManager) { - var enumClassType = typeof(T); - var attribs = enumClassType.GetCustomAttributes(typeof(SystemEnumAttribute), false); + var attribs = enumClass.GetCustomAttributes(typeof(SystemEnumAttribute), false); if (attribs.Length == 0) - throw new InvalidOperationException("Enum is not marked as SystemEnum"); + throw new InvalidOperationException($"Enum {enumClass} is not marked as SystemEnum"); var enumMetadata = (SystemEnumAttribute)attribs[0]; - enumType = TypeManager.RegisterType("Перечисление" + enumMetadata.GetName(), typeof(T)); - enumValueType = TypeManager.RegisterType(enumMetadata.GetName(), typeof(SelfAwareEnumValue)); + return RegisterEnumType(enumClass, enumValueClass, typeManager, enumMetadata); } - public static T CreateEnumInstance(EnumCreationDelegate creator) where T : EnumerationContext + public static (TypeDescriptor, TypeDescriptor) RegisterEnumType( + Type enumClass, + Type enumValueClass, + ITypeManager typeManager, + IEnumMetadataProvider enumMetadata) { - T instance; - - TypeDescriptor enumType; - TypeDescriptor enumValType; - - EnumContextHelper.RegisterEnumType(out enumType, out enumValType); + var enumType = CreateEnumType(enumClass, enumMetadata); + typeManager.RegisterType(enumType); - instance = creator(enumType, enumValType); + var enumValueType = CreateEnumValueType(enumValueClass, enumMetadata); + typeManager.RegisterType(enumValueType); + + return (enumType, enumValueType); + } - EnumContextHelper.RegisterValues(instance); + private static TypeDescriptor CreateEnumType(Type enumType, IEnumMetadataProvider metadata) + { + return new TypeDescriptor( + metadata.TypeUUID ?? Guid.NewGuid().ToString(), + "Перечисление" + metadata.Name, + metadata.Alias != default ? "Enum" + metadata.Alias : default, + enumType + ); + } - return instance; + private static TypeDescriptor CreateEnumValueType(Type enumValueClass, IEnumMetadataProvider metadata) + { + return new TypeDescriptor( + metadata.ValueTypeUUID ?? Guid.NewGuid().ToString(), + metadata.Name, + metadata.Alias, + enumValueClass + ); } + public static TOwner CreateClrEnumInstance(ITypeManager typeManager, EnumCreationDelegate creator) + where TOwner : EnumerationContext + where TEnum : struct + { + var (enumType, enumValType) = RegisterEnumType>(typeManager); + return creator(enumType, enumValType); + } + + public static ClrEnumValueWrapper WrapClrValue( + this EnumerationContext owner, + string name, + string alias, + T value) + where T : struct + { + var wrappedValue = new ClrEnumValueWrapper(owner.ValuesType, value, name, alias); + owner.AddValue(wrappedValue); + return wrappedValue; + } } public delegate T EnumCreationDelegate(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) where T : EnumerationContext; diff --git a/src/ScriptEngine/Machine/Contexts/EnumItemAttribute.cs b/src/ScriptEngine/Machine/Contexts/EnumItemAttribute.cs deleted file mode 100644 index 9c7760fec..000000000 --- a/src/ScriptEngine/Machine/Contexts/EnumItemAttribute.cs +++ /dev/null @@ -1,28 +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 ScriptEngine -{ - [AttributeUsage(AttributeTargets.Field)] - public class EnumItemAttribute : Attribute - { - public EnumItemAttribute (string name, string alias = null) - { - if (!Utils.IsValidIdentifier(name)) - throw new ArgumentException("Name must be a valid identifier"); - - if (!string.IsNullOrEmpty(alias) && !Utils.IsValidIdentifier(alias)) - throw new ArgumentException("Alias must be a valid identifier"); - - Name = name; - Alias = alias; - } - - public string Name { get; } - public string Alias { get; } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/EnumValueAttribute.cs b/src/ScriptEngine/Machine/Contexts/EnumValueAttribute.cs deleted file mode 100644 index 1735e645a..000000000 --- a/src/ScriptEngine/Machine/Contexts/EnumValueAttribute.cs +++ /dev/null @@ -1,34 +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 ScriptEngine.Machine.Contexts -{ - [AttributeUsage(AttributeTargets.Property)] - public class EnumValueAttribute : Attribute - { - readonly string _name; - - readonly string _alias; - - public EnumValueAttribute(string name, string alias = null) - { - _name = name; - _alias = alias; - } - - public string GetName() - { - return _name; - } - - public string GetAlias() - { - return _alias; - } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/EnumerationContext.cs b/src/ScriptEngine/Machine/Contexts/EnumerationContext.cs index aed063580..b5708f5b8 100644 --- a/src/ScriptEngine/Machine/Contexts/EnumerationContext.cs +++ b/src/ScriptEngine/Machine/Contexts/EnumerationContext.cs @@ -4,92 +4,130 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; + +using System.Collections; using System.Collections.Generic; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; namespace ScriptEngine.Machine.Contexts { - public class EnumerationContext : PropertyNameIndexAccessor + public abstract class EnumerationContext : PropertyNameIndexAccessor, ICollectionContext { - private readonly List _values = new List(); - - readonly IndexedNamesCollection _nameIds = new IndexedNamesCollection(); + private readonly IndexedNameValueCollection _values; + private readonly List _definitions; private readonly TypeDescriptor _valuesType; + private readonly HashSet _checkedDeprecatedProps = new HashSet(); - public EnumerationContext(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) : base(typeRepresentation) + protected EnumerationContext(TypeDescriptor typeRepresentation, TypeDescriptor valuesType) : base(typeRepresentation) { _valuesType = valuesType; + _values = new IndexedNameValueCollection(); + _definitions = new List(); } - public void AddValue(string name, EnumerationValue val) + public void AddValue(EnumerationValue val) { - AddValue(name, null, val); + var index = _values.Add(val, val.Name, val.Alias); + + var propertyBuilder = BslPropertyBuilder.Create() + .SetNames(val.Name, val.Alias) + .CanRead(true) + .CanWrite(false) + .SetDispatchingIndex(index); + + if (_valuesType != null) + propertyBuilder.ReturnType(_valuesType.ImplementingClass); + + _definitions.Add(propertyBuilder.Build()); } - public void AddValue(string name, string alias, EnumerationValue val) + protected void AddValue(EnumerationValue val, BslPropertyInfo definition) { - System.Diagnostics.Debug.Assert(name != null); - System.Diagnostics.Debug.Assert(val != null); + _values.Add(val, definition.Name, definition.Alias); + _definitions.Add(definition); + } - if (!ScriptEngine.Utils.IsValidIdentifier(name)) - throw new ArgumentException("Name must be a valid identifier", "name"); + public TypeDescriptor ValuesType => _valuesType; - if(alias != null && !ScriptEngine.Utils.IsValidIdentifier(alias)) - throw new ArgumentException("Name must be a valid identifier", "alias"); + public EnumerationValue this[string name] => GetPropValueInternal(GetPropertyNumber(name)); - _nameIds.RegisterName(name, alias); - val.ValuePresentation = name; - _values.Add(val); + public override int GetPropCount() + { + return _values.Count; + } + public override int GetPropertyNumber(string name) + { + var id = _values.IndexOf(name); + + return id == -1 ? base.GetPropertyNumber(name) : id; } - public TypeDescriptor ValuesType + public override bool IsPropReadable(int propNum) { - get - { - return _valuesType; - } + return true; } - public EnumerationValue this[string name] + public override IValue GetPropValue(int propNum) { - get + return GetPropValueInternal(propNum); + } + + private EnumerationValue GetPropValueInternal(int propNum) + { + WarnDeprecation(propNum); + return _values[propNum]; + } + + private void WarnDeprecation(int propNum) + { + if (_checkedDeprecatedProps.Contains(propNum)) + return; + + if (GetPropertyInfo(propNum) is SystemPropertyInfo { IsDeprecated: true }) { - int id = FindProperty(name); - return _values[id]; + SystemLogger.Write($"Обращение к устаревшему свойству {GetPropertyInfo(propNum).Name}."); } + + _checkedDeprecatedProps.Add(propNum); } - public int IndexOf(EnumerationValue enumVal) + public override string GetPropName(int propNum) { - return _values.IndexOf(enumVal); + return _values.NameOf(propNum); } - - public override int FindProperty(string name) + + public override BslPropertyInfo GetPropertyInfo(int propNum) { - int id; - if (_nameIds.TryGetIdOfName(name, out id)) - return id; - else - return base.FindProperty(name); + return _definitions[propNum]; } - public override bool IsPropReadable(int propNum) - { - return true; - } + protected IEnumerable ValuesInternal => _values; - public override IValue GetPropValue(int propNum) + #region ICollectionContext Members + + public int Count(IBslProcess process) { - return _values[propNum]; + return _values.Count; } - protected IList ValuesInternal + public IEnumerator GetEnumerator() { - get + foreach (var item in _values) { - return _values; + yield return item; } } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion } } diff --git a/src/ScriptEngine/Machine/Contexts/EnumerationTypeAttribute.cs b/src/ScriptEngine/Machine/Contexts/EnumerationTypeAttribute.cs deleted file mode 100644 index 644aabf7e..000000000 --- a/src/ScriptEngine/Machine/Contexts/EnumerationTypeAttribute.cs +++ /dev/null @@ -1,24 +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 ScriptEngine -{ - [AttributeUsage(AttributeTargets.Enum)] - public class EnumerationTypeAttribute : Attribute - { - public EnumerationTypeAttribute (string name, string alias = null, bool createProperty = true) - { - Name = name; - Alias = alias; - CreateGlobalProperty = createProperty; - } - - public string Name { get; } - public string Alias { get; } - public bool CreateGlobalProperty { get; } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/EnumerationValue.cs b/src/ScriptEngine/Machine/Contexts/EnumerationValue.cs deleted file mode 100644 index 119ececa3..000000000 --- a/src/ScriptEngine/Machine/Contexts/EnumerationValue.cs +++ /dev/null @@ -1,99 +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 ScriptEngine.Machine.Contexts -{ - abstract public class EnumerationValue : IValue - { - readonly EnumerationContext _owner; - - public EnumerationValue(EnumerationContext owner) - { - _owner = owner; - } - - public EnumerationContext Owner - { - get - { - return _owner; - } - } - - public string ValuePresentation - { - get;set; - } - - public virtual DataType DataType - { - get { return Machine.DataType.GenericValue; } - } - - public virtual TypeDescriptor SystemType - { - get { return _owner.ValuesType; } - } - - public virtual decimal AsNumber() - { - throw RuntimeException.ConvertToNumberException(); - } - - public virtual DateTime AsDate() - { - throw RuntimeException.ConvertToDateException(); - } - - public virtual bool AsBoolean() - { - throw RuntimeException.ConvertToBooleanException(); - } - - public virtual string AsString() - { - return ValuePresentation == null ? SystemType.Name : ValuePresentation; - } - - public virtual IRuntimeContextInstance AsObject() - { - throw RuntimeException.ValueIsNotObjectException(); - } - - public IValue GetRawValue() - { - return this; - } - - public virtual int CompareTo(IValue other) - { - if (other != null) - { - if (other is EnumerationValue) - { - int thisIdx = _owner.IndexOf(this); - int otherIdx = _owner.IndexOf((EnumerationValue)other); - return thisIdx - otherIdx; - } - else - { - return SystemType.ID - other.SystemType.ID; - } - } - else - { - return 1; - } - } - - public virtual bool Equals(IValue other) - { - return other.GetRawValue() == this; - } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs b/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs index 02c708c45..5dbc8c407 100644 --- a/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs +++ b/src/ScriptEngine/Machine/Contexts/ExceptionInfoContext.cs @@ -1,201 +1,226 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using OneScript.Language; - -namespace ScriptEngine.Machine.Contexts -{ - /// - /// Класс позволяет узнать информацию о произошедшем исключении. - /// - [ContextClass("ИнформацияОбОшибке", "ErrorInfo")] - public class ExceptionInfoContext : AutoContext - { - readonly ScriptException _exc; - IValue _innerException; - - public ExceptionInfoContext(ScriptException source) - { - if (source == null) - throw new ArgumentNullException(); - - _exc = source; - } - - public ExceptionInfoContext(ParametrizedRuntimeException source):this((ScriptException)source) - { - Parameters = source.Parameter; - } - - /// - /// Значение, переданное при создании исключения в конструкторе объекта ИнформацияОбОшибке. - /// - [ContextProperty("Параметры", "Parameters", CanWrite = false)] - public IValue Parameters - { - get; - private set; - } - - /// - /// Содержит краткое описание ошибки. Эквивалент Exception.Message в C# - /// - [ContextProperty("Описание", "Description")] - public string Description - { - get { return _exc.ErrorDescription; } - } - - public string MessageWithoutCodeFragment - { - get { return _exc.MessageWithoutCodeFragment; } - } - - public string DetailedDescription - { - get { return _exc.Message; } - } - - /// - /// Имя модуля, вызвавшего исключение. - /// - [ContextProperty("ИмяМодуля", "ModuleName")] - public string ModuleName - { - get - { - return SafeMarshallingNullString(_exc.ModuleName); - } - } - - /// - /// Номер строки, вызвавшей исключение. - /// - [ContextProperty("НомерСтроки", "LineNumber")] - public int LineNumber - { - get - { - return _exc.LineNumber; - } - } - - /// - /// Строка исходного кода, вызвавшего исключение. - /// - [ContextProperty("ИсходнаяСтрока", "SourceLine")] - public string SourceLine - { - get - { - return SafeMarshallingNullString(_exc.Code); - } - } - - /// - /// Предоставляет доступ к стеку вызовов процедур. - /// Подробнее см. класс КоллекцияКадровСтекаВызовов - /// - /// - [ContextMethod("ПолучитьСтекВызовов", "GetStackTrace")] - public IValue GetStackTrace() - { - if (_exc is RuntimeException rte) - { - var frames = rte.CallStackFrames; - if (frames == null) - return ValueFactory.Create(); - - return new StackTraceCollectionContext(frames); - } - else - return ValueFactory.Create(); - } - - private string SafeMarshallingNullString(string src) - { - return src == null ? "" : src; - } - - /// - /// Содержит вложенное исключение, если таковое было. Эквивалент Exception.InnerException в C# - /// - [ContextProperty("Причина", "Cause")] - public IValue InnerException - { - get - { - if (_innerException == null) - _innerException = CreateInnerExceptionInfo(); - - return _innerException; - } - } - - private IValue CreateInnerExceptionInfo() - { - if (_exc.InnerException == null) - return ValueFactory.Create(); - - bool alreadyWrapped = _exc is ExternalSystemException; - if (!alreadyWrapped) - { - ScriptException inner; - inner = _exc.InnerException as ScriptException; - if (inner == null) - { - inner = new ExternalSystemException(_exc.InnerException); - } - if (inner.ModuleName == null) - inner.ModuleName = _exc.ModuleName; - if (inner.Code == null) - inner.Code = _exc.Code; - return new ExceptionInfoContext(inner); - } - else - { - if (_exc.InnerException.InnerException == null) - return ValueFactory.Create(); - - var inner = new ExternalSystemException(_exc.InnerException.InnerException); - if (inner.LineNumber == CodePositionInfo.OUT_OF_TEXT) - { - inner.ModuleName = this.ModuleName; - inner.Code = this.SourceLine; - inner.LineNumber = this.LineNumber; - } - - return new ExceptionInfoContext(inner); - } - } - - /// - /// Содержит подробное описание исключения, включая стек вызовов среды исполнения CLR. - /// т.е. не стек вызовов скрипта, а стек вызовов скриптового движка. - /// Эквивалентно функции Exception.ToString() в C#. - /// - /// Строка. - [ContextMethod("ПодробноеОписаниеОшибки", "DetailedDescription")] - public string GetDescription() - { - return _exc.ToString(); - } - - public override string ToString() - { - return Description; - } - - - [ScriptConstructor(Name = "С возможностью передачи параметров")] - public static ExceptionTemplate Create(IValue msg, IValue parameter) - { - return new ExceptionTemplate(msg.AsString(), parameter); - } - - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Text; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Language; +using OneScript.Localization; + +namespace ScriptEngine.Machine.Contexts +{ + /// + /// Класс позволяет узнать информацию о произошедшем исключении. + /// + [ContextClass("ИнформацияОбОшибке", "ErrorInfo")] + public class ExceptionInfoContext : AutoContext + { + private ScriptException _exc; + private IValue _innerException; + + public ExceptionInfoContext(ScriptException source) + { + SetActualException(source); + } + + private ExceptionInfoContext(string message, IValue parameters, ExceptionInfoContext cause) + { + Description = message; + Parameters = parameters; + _innerException = cause; + } + + public bool IsErrorTemplate => _exc == null; + + private void SetActualException(ScriptException exception) + { + _exc = exception ?? throw new ArgumentNullException(); + Description = _exc.ErrorDescription; + if (exception is ParametrizedRuntimeException pre) + { + Parameters = pre.Parameter; + _innerException = pre.Cause; + } + } + + public ScriptException ActualException() + { + if (IsErrorTemplate) + { + throw new RuntimeException(BilingualString.Localize( + "Эта ИнформацияОбОшибке еще не была выброшена оператором ВызватьИсключение", + "This ErrorInfo is not have been thrown by Raise operator yet")); + } + + return _exc; + } + + /// + /// Значение, переданное при создании исключения в конструкторе объекта ИнформацияОбОшибке. + /// + [ContextProperty("Параметры", "Parameters", CanWrite = false)] + public IValue Parameters + { + get; + private set; + } + + /// + /// Содержит краткое описание ошибки. Эквивалент Exception.Message в C# + /// + [ContextProperty("Описание", "Description")] + public string Description { get; private set; } + + public string MessageWithoutCodeFragment => ActualException().MessageWithoutCodeFragment; + + public string GetDetailedDescription() + { + var exc = ActualException(); + var sb = new StringBuilder(exc.Message); + var inner = exc.InnerException; + while (inner != default) + { + sb.AppendLine(); + sb.AppendLine(Locale.NStr("ru = 'по причине:';en = 'caused by:'")); + sb.AppendLine(inner.Message); + inner = inner.InnerException; + } + + return sb.ToString(); + } + + /// + /// Имя модуля, вызвавшего исключение. + /// + [ContextProperty("ИмяМодуля", "ModuleName")] + public string ModuleName => ActualException().ModuleName ?? string.Empty; + + /// + /// Номер строки, вызвавшей исключение. + /// + [ContextProperty("НомерСтроки", "LineNumber")] + public int LineNumber => ActualException().LineNumber; + + /// + /// Строка исходного кода, вызвавшего исключение. + /// + [ContextProperty("ИсходнаяСтрока", "SourceLine")] + public string SourceLine => ActualException().Code ?? string.Empty; + + /// + /// Предоставляет доступ к стеку вызовов процедур. + /// Подробнее см. класс КоллекцияКадровСтекаВызовов + /// + /// + [ContextMethod("ПолучитьСтекВызовов", "GetStackTrace")] + public IValue GetStackTrace() + { + if (ActualException().RuntimeSpecificInfo is IList frames) + { + return new StackTraceCollectionContext(frames); + } + return ValueFactory.Create(); + } + + /// + /// Содержит вложенное исключение, если таковое было. Эквивалент Exception.InnerException в C# + /// + [ContextProperty("Причина", "Cause")] + public IValue InnerException + { + get + { + if (_innerException == null) + _innerException = CreateInnerExceptionInfo(); + + return _innerException; + } + } + + private IValue CreateInnerExceptionInfo() + { + var exc = _exc; + if (exc?.InnerException == null) + return ValueFactory.Create(); + + var alreadyWrapped = ActualException() is ExternalSystemException; + if (!alreadyWrapped) + { + var inner = exc.InnerException as ScriptException ?? new ExternalSystemException(exc.InnerException); + inner.ModuleName ??= exc.ModuleName; + inner.Code ??= exc.Code; + return new ExceptionInfoContext(inner); + } + else + { + if (exc.InnerException.InnerException == null) + return ValueFactory.Create(); + + var inner = new ExternalSystemException(exc.InnerException.InnerException); + if (inner.LineNumber == ErrorPositionInfo.OUT_OF_TEXT) + { + inner.ModuleName = this.ModuleName; + inner.Code = this.SourceLine; + inner.LineNumber = this.LineNumber; + } + + return new ExceptionInfoContext(inner); + } + } + + /// + /// Содержит подробное описание исключения, включая стек вызовов среды исполнения CLR. + /// т.е. не стек вызовов скрипта, а стек вызовов скриптового движка. + /// Эквивалентно функции Exception.ToString() в C#. + /// + /// Строка. + [ContextMethod("ПодробноеОписаниеОшибки", "DetailedDescription")] + public string GetDescription() + { + return ActualException().ToString(); + } + + public override string ToString() + { + return Description; + } + + /// + /// + /// + /// Строка - Сообщение об ошибке + /// Произвольный - Дополнительная информация + /// ИнформацияОбОшибке - Причина, по которой произошло текущее исключение + /// + [ScriptConstructor(Name = "С возможностью передачи параметров")] + public static ExceptionInfoContext Create(string msg, IValue parameter, ExceptionInfoContext cause = null) + { + return new ExceptionInfoContext(msg, parameter, cause); + } + + public static ExceptionInfoContext EmptyExceptionInfo() + { + return new ExceptionInfoContext(EmptyScriptException.Instance); + } + + private class EmptyScriptException : ScriptException + { + public static readonly EmptyScriptException Instance = new EmptyScriptException(); + private EmptyScriptException() : base("") + { + LineNumber = 0; + ColumnNumber = 0; + } + + public override string Message => ""; + + public override string ToString() => ""; + } + } +} diff --git a/src/ScriptEngine/Machine/Contexts/ExceptionTemplate.cs b/src/ScriptEngine/Machine/Contexts/ExceptionTemplate.cs deleted file mode 100644 index 16c811a0d..000000000 --- a/src/ScriptEngine/Machine/Contexts/ExceptionTemplate.cs +++ /dev/null @@ -1,29 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Contexts -{ - /// - /// Служебный класс, создаваемый конструктором объекта "ИнформацияОбОшибке". - /// Превращается в полноценный объект ИнформацияОбОшибке в момент выброса исключения. - /// - /// Данный класс предназначен для создания параметризованных исключений. - /// - /// ВызватьИсключение Новый ИнформацияОбОшибке("Текст ошибки", ДополнительныеДанные); - [ContextClass("ИнформацияОбОшибкеШаблон", "ExceptionInfoTemplate")] - public class ExceptionTemplate : ContextIValueImpl - { - public string Message { get; private set; } - public IValue Parameter { get; private set; } - - public ExceptionTemplate(string msg, IValue parameter) - { - this.Message = msg; - this.Parameter = parameter; - } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/GlobalContextBase.cs b/src/ScriptEngine/Machine/Contexts/GlobalContextBase.cs index e40005e15..06ae83579 100644 --- a/src/ScriptEngine/Machine/Contexts/GlobalContextBase.cs +++ b/src/ScriptEngine/Machine/Contexts/GlobalContextBase.cs @@ -5,24 +5,17 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using System.Linq; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Values; namespace ScriptEngine.Machine.Contexts { - abstract public class GlobalContextBase : IRuntimeContextInstance, IAttachableContext where T : GlobalContextBase + public abstract class GlobalContextBase : BslObjectValue, IAttachableContext where T : GlobalContextBase { - private readonly ContextMethodsMapper _methods = new ContextMethodsMapper(); - private readonly ContextPropertyMapper _properties = new ContextPropertyMapper(); + protected ContextMethodsMapper Methods { get; } = new ContextMethodsMapper(); - protected ContextMethodsMapper Methods - { - get { return _methods; } - } - - protected ContextPropertyMapper Properties - { - get { return _properties; } - } + protected ContextPropertyMapper Properties { get; } = new ContextPropertyMapper(); #region IRuntimeContextInstance members @@ -31,10 +24,7 @@ public bool IsIndexed get { return false; } } - public bool DynamicMethodSignatures - { - get { return false; } - } + public bool DynamicMethodSignatures => false; public IValue GetIndexedValue(IValue index) { @@ -46,26 +36,26 @@ public void SetIndexedValue(IValue index, IValue val) throw new NotImplementedException(); } - public virtual int FindProperty(string name) + public virtual int GetPropertyNumber(string name) { - return _properties.FindProperty(name); + return Properties.FindProperty(name); } public virtual bool IsPropReadable(int propNum) { - return _properties.GetProperty(propNum).CanRead; + return Properties.GetProperty(propNum).CanRead; } public virtual bool IsPropWritable(int propNum) { - return _properties.GetProperty(propNum).CanWrite; + return Properties.GetProperty(propNum).CanWrite; } public virtual IValue GetPropValue(int propNum) { try { - return _properties.GetProperty(propNum).Getter((T)this); + return Properties.GetProperty(propNum).Getter((T)this); } catch (System.Reflection.TargetInvocationException e) { @@ -77,7 +67,7 @@ public virtual void SetPropValue(int propNum, IValue newVal) { try { - _properties.GetProperty(propNum).Setter((T)this, newVal); + Properties.GetProperty(propNum).Setter((T)this, newVal); } catch (System.Reflection.TargetInvocationException e) { @@ -96,39 +86,62 @@ public string GetPropName(int propNum) return prop.Name; } - public virtual int FindMethod(string name) + public virtual int GetMethodNumber(string name) + { + return Methods.FindMethod(name); + } + + public virtual BslMethodInfo GetMethodInfo(int methodNumber) { - return _methods.FindMethod(name); + return Methods.GetRuntimeMethod(methodNumber); } - public virtual MethodInfo GetMethodInfo(int methodNumber) + public virtual BslPropertyInfo GetPropertyInfo(int propertyNumber) { - return _methods.GetMethodInfo(methodNumber); + return Properties.GetProperty(propertyNumber).PropertyInfo; } - public virtual void CallAsProcedure(int methodNumber, IValue[] arguments) + public virtual void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) { - _methods.GetMethod(methodNumber)((T)this, arguments); + Methods.GetCallableDelegate(methodNumber)((T)this, arguments, process); } - public virtual void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) + public virtual void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) { - retValue = _methods.GetMethod(methodNumber)((T)this, arguments); + retValue = Methods.GetCallableDelegate(methodNumber)((T)this, arguments, process); + } + + public void CallAsProcedure(int methodNumber, IValue[] arguments) + { + CallAsProcedure(methodNumber, arguments, ForbiddenBslProcess.Instance); + } + + public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) + { + CallAsFunction(methodNumber, arguments, out retValue, ForbiddenBslProcess.Instance); } #endregion #region IAttachableContext members - public virtual void OnAttach(MachineInstance machine, out IVariable[] variables, out MethodInfo[] methods) + IVariable IAttachableContext.GetVariable(int index) { - variables = new IVariable[0]; - methods = this.GetMethods().ToArray(); + return Variable.CreateContextPropertyReference(this, index, GetPropName(index)); } + BslMethodInfo IAttachableContext.GetMethod(int index) + { + return GetMethodInfo(index); + } + + int IAttachableContext.VariablesCount => GetPropCount(); + + int IAttachableContext.MethodsCount => GetMethodsCount(); + public virtual int GetMethodsCount() { - return _methods.Count; + return Methods.Count; } #endregion diff --git a/src/ScriptEngine/Machine/Contexts/ICollectionContext.cs b/src/ScriptEngine/Machine/Contexts/ICollectionContext.cs deleted file mode 100644 index 25746cb50..000000000 --- a/src/ScriptEngine/Machine/Contexts/ICollectionContext.cs +++ /dev/null @@ -1,15 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Contexts -{ - public interface ICollectionContext - { - int Count(); - CollectionEnumerator GetManagedIterator(); - } -} diff --git a/src/ScriptEngine/Machine/Contexts/IObjectWrapper.cs b/src/ScriptEngine/Machine/Contexts/IObjectWrapper.cs deleted file mode 100644 index 9bd2b5757..000000000 --- a/src/ScriptEngine/Machine/Contexts/IObjectWrapper.cs +++ /dev/null @@ -1,14 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Contexts -{ - public interface IObjectWrapper - { - object UnderlyingObject { get; } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/ISystemTypeAcceptor.cs b/src/ScriptEngine/Machine/Contexts/ISystemTypeAcceptor.cs new file mode 100644 index 000000000..1f1dacfe9 --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/ISystemTypeAcceptor.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Types; + +namespace ScriptEngine.Machine.Contexts +{ + internal interface ISystemTypeAcceptor + { + void AssignType(TypeDescriptor systemTypeValue); + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/ManagedCOMWrapperContext.cs b/src/ScriptEngine/Machine/Contexts/ManagedCOMWrapperContext.cs index b2e39a397..d03c361f2 100644 --- a/src/ScriptEngine/Machine/Contexts/ManagedCOMWrapperContext.cs +++ b/src/ScriptEngine/Machine/Contexts/ManagedCOMWrapperContext.cs @@ -5,10 +5,16 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ //#if !__MonoCS__ + using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Values; namespace ScriptEngine.Machine.Contexts { @@ -43,9 +49,9 @@ public override bool IsIndexed if (_isIndexed == null) { _isIndexed = _instanceType.GetProperties().Any(x => x.GetIndexParameters().Length > 0); - } - - return (bool)_isIndexed; + } + + return (bool) _isIndexed; } } @@ -55,12 +61,11 @@ public override IEnumerator GetEnumerator() try { - - comEnumerator = (System.Collections.IEnumerator)_instanceType.InvokeMember("GetEnumerator", - BindingFlags.InvokeMethod, - null, - Instance, - new object[0]); + comEnumerator = (System.Collections.IEnumerator) _instanceType.InvokeMember("GetEnumerator", + BindingFlags.InvokeMethod, + null, + Instance, + new object[0]); } catch (MissingMethodException) { @@ -73,7 +78,7 @@ public override IEnumerator GetEnumerator() } } - public override int FindProperty(string name) + public override int GetPropertyNumber(string name) { return _nameMapper.FindProperty(name); } @@ -100,9 +105,9 @@ public override IValue GetPropValue(int propNum) public override void SetPropValue(int propNum, IValue newVal) { var pi = _nameMapper.GetProperty(propNum); - + var setMethod = pi.GetSetMethod(); - setMethod.Invoke(Instance, MarshalArgumentsStrict(new[] { newVal }, new[] { pi.PropertyType })); + setMethod.Invoke(Instance, MarshalArgumentsStrict(new[] {newVal}, new[] {pi.PropertyType})); } public override IValue GetIndexedValue(IValue index) @@ -110,91 +115,105 @@ public override IValue GetIndexedValue(IValue index) if (!IsIndexed) throw RuntimeException.IndexedAccessIsNotSupportedException(); - var member = _instanceType.GetMethod("get_Item", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance); + var member = _instanceType.GetMethod("get_Item", + BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance); if (member == null) // set only? throw RuntimeException.IndexedAccessIsNotSupportedException(); - object retValue = member.Invoke(Instance, MarshalArgumentsStrict(new[] { index }, GetMethodParametersTypes(member))); + object retValue = member.Invoke(Instance, + MarshalArgumentsStrict(new[] {index}, GetMethodParametersTypes(member))); return CreateIValue(retValue); - } - + public override void SetIndexedValue(IValue index, IValue value) { if (!IsIndexed) throw RuntimeException.IndexedAccessIsNotSupportedException(); - var member = _instanceType.GetMethod("set_Item", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance); + var member = _instanceType.GetMethod("set_Item", + BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance); if (member == null) // get only? throw RuntimeException.IndexedAccessIsNotSupportedException(); - object retValue = member.Invoke(Instance, MarshalArgumentsStrict(new[] { index, value }, GetMethodParametersTypes(member))); - + member.Invoke(Instance, MarshalArgumentsStrict(new[] {index, value}, GetMethodParametersTypes(member))); } - public override int FindMethod(string name) + public override int GetMethodNumber(string name) { return _nameMapper.FindMethod(Instance, name); } - public override MethodInfo GetMethodInfo(int methodNumber) + public override BslMethodInfo GetMethodInfo(int methodNumber) + { + var methodInfo = _nameMapper.GetMethod(methodNumber).Method; + return GetReflectableMethod(methodInfo); + } + + public override BslPropertyInfo GetPropertyInfo(int propertyNumber) { - var methodInfo = _nameMapper.GetMethod(methodNumber); - return GetReflectableMethod(methodInfo.Method); + var info = _nameMapper.GetProperty(propertyNumber); + return GetReflectableProperty(propertyNumber, info); } - public override void CallAsProcedure(int methodNumber, IValue[] arguments) + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) { var method = _nameMapper.GetMethod(methodNumber); method.Invoke(arguments); } - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) { var method = _nameMapper.GetMethod(methodNumber); var result = method.Invoke(arguments); retValue = CreateIValue(result); } - private MethodInfo GetReflectableMethod(System.Reflection.MethodInfo reflectionMethod) + private BslPropertyInfo GetReflectableProperty(int index, PropertyInfo p) { - MethodInfo methodInfo; + return BslPropertyBuilder.Create() + .Name(p.Name) + .SetDispatchingIndex(index) + .CanRead(p.CanRead) + .CanWrite(p.CanWrite) + .Build(); + } - methodInfo = new MethodInfo(); - methodInfo.Name = reflectionMethod.Name; + private BslMethodInfo GetReflectableMethod(MethodInfo reflectionMethod) + { + var builder = BslMethodBuilder.Create(); + builder.Name(reflectionMethod.Name); + builder.ReturnType(reflectionMethod.ReturnType); - var reflectedMethod = reflectionMethod as System.Reflection.MethodInfo; + var reflectionParams = reflectionMethod.GetParameters(); + FillMethodInfoParameters(builder, reflectionParams); - if (reflectedMethod != null) - { - methodInfo.IsFunction = reflectedMethod.ReturnType != typeof(void); - var reflectionParams = reflectedMethod.GetParameters(); - FillMethodInfoParameters(ref methodInfo, reflectionParams); - } - - return methodInfo; + return builder.Build(); } - - private static Type[] GetMethodParametersTypes(System.Reflection.MethodInfo method) + + private static Type[] GetMethodParametersTypes(MethodInfo method) { return method.GetParameters() .Select(x => x.ParameterType) .ToArray(); } - private static void FillMethodInfoParameters(ref MethodInfo methodInfo, System.Reflection.ParameterInfo[] reflectionParams) + private static void FillMethodInfoParameters(BslMethodBuilder methodSignature, + ParameterInfo[] reflectionParams) { - methodInfo.Params = new ParameterDefinition[reflectionParams.Length]; - for (int i = 0; i < reflectionParams.Length; i++) + foreach (var reflectedParam in reflectionParams) { - var reflectedParam = reflectionParams[i]; - var param = new ParameterDefinition(); - param.HasDefaultValue = reflectedParam.IsOptional; - param.IsByValue = !reflectedParam.IsOut; - methodInfo.Params[i] = param; + var paramBuilder = methodSignature.NewParameter() + .ByValue(!reflectedParam.IsOut); + + if (!reflectedParam.HasDefaultValue) + continue; + + var marshalled = ContextValuesMarshaller.ConvertReturnValue(reflectedParam.DefaultValue); + Debug.Assert(marshalled is BslPrimitiveValue); + paramBuilder.DefaultValue((BslPrimitiveValue) marshalled); } } } diff --git a/src/ScriptEngine/Machine/Contexts/PropertyNameIndexAccessor.cs b/src/ScriptEngine/Machine/Contexts/PropertyNameIndexAccessor.cs index ec69bc157..80fff467f 100644 --- a/src/ScriptEngine/Machine/Contexts/PropertyNameIndexAccessor.cs +++ b/src/ScriptEngine/Machine/Contexts/PropertyNameIndexAccessor.cs @@ -5,23 +5,22 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Exceptions; +using OneScript.Types; + namespace ScriptEngine.Machine.Contexts { public abstract class PropertyNameIndexAccessor : ContextIValueImpl { - public PropertyNameIndexAccessor() + protected PropertyNameIndexAccessor() { - } - - public PropertyNameIndexAccessor(TypeDescriptor type):base(type) + + protected PropertyNameIndexAccessor(TypeDescriptor type):base(type) { } - public override bool IsIndexed - { - get { return true; } - } + public override bool IsIndexed => true; public override bool IsPropReadable(int propNum) { @@ -35,30 +34,30 @@ public override bool IsPropWritable(int propNum) public override IValue GetIndexedValue(IValue index) { - if (index.DataType != DataType.String) + if (index.SystemType != BasicTypes.String) { throw RuntimeException.InvalidArgumentType(); } - var n = FindProperty(index.AsString()); + var n = GetPropertyNumber(index.ToString()); if (IsPropReadable(n)) return GetPropValue(n); else - throw RuntimeException.PropIsNotReadableException(index.AsString()); + throw PropertyAccessException.PropIsNotReadableException(index.ToString()); } public override void SetIndexedValue(IValue index, IValue val) { - if (index.DataType != DataType.String) + if (index.SystemType != BasicTypes.String) { throw RuntimeException.InvalidArgumentType(); } - var n = FindProperty(index.AsString()); + var n = GetPropertyNumber(index.ToString()); if (IsPropWritable(n)) SetPropValue(n, val); else - throw RuntimeException.PropIsNotWritableException(index.AsString()); + throw PropertyAccessException.PropIsNotWritableException(index.ToString()); } } } diff --git a/src/ScriptEngine/Machine/Contexts/PropertyTarget.cs b/src/ScriptEngine/Machine/Contexts/PropertyTarget.cs new file mode 100644 index 000000000..be25b109b --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/PropertyTarget.cs @@ -0,0 +1,169 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Exceptions; + +namespace ScriptEngine.Machine.Contexts +{ + public class PropertyTarget + { + private readonly ContextPropertyInfo _propertyInfo; + private volatile bool _deprecationIsWarned = false; + + internal PropertyTarget(ContextPropertyInfo propInfo) + { + _propertyInfo = propInfo; + Name = _propertyInfo.Name; + Alias = _propertyInfo.Alias; + + if (string.IsNullOrEmpty(Alias)) + Alias = propInfo.Name; + + IValue CantReadAction(TInstance inst) + { + throw PropertyAccessException.PropIsNotReadableException(Name); + } + + void CantWriteAction(TInstance inst, IValue val) + { + throw PropertyAccessException.PropIsNotWritableException(Name); + } + + if (_propertyInfo.CanRead) + { + var getMethodInfo = propInfo.GetGetMethod(); + if (getMethodInfo == null) + { + Getter = CantReadAction; + } + else + { + var genericGetter = typeof(PropertyTarget).GetMembers(BindingFlags.NonPublic | BindingFlags.Instance) + .Where(x => x.MemberType == MemberTypes.Method && x.Name == nameof(CreateGetter)) + .Select(x => (MethodInfo)x) + .First(); + + var resolvedGetter = genericGetter.MakeGenericMethod(propInfo.PropertyType); + + Getter = (Func)resolvedGetter.Invoke(this, new object[] { getMethodInfo }); + } + } + else + { + Getter = CantReadAction; + } + + if (_propertyInfo.CanWrite) + { + var setMethodInfo = propInfo.GetSetMethod(); + if (setMethodInfo == null) + { + Setter = CantWriteAction; + } + else + { + var genericSetter = typeof(PropertyTarget).GetMembers(BindingFlags.NonPublic | BindingFlags.Instance) + .Where(x => x.MemberType == MemberTypes.Method && x.Name == nameof(CreateSetter)) + .Select(x => (MethodInfo)x) + .First(); + + var resolvedSetter = genericSetter.MakeGenericMethod(propInfo.PropertyType); + + Setter = (Action)resolvedSetter.Invoke(this, new object[] { setMethodInfo }); + } + } + else + { + Setter = CantWriteAction; + } + } + + public Func Getter { get; } + + public Action Setter { get; } + + public string Name { get; } + + public string Alias { get; } + + public bool CanRead => _propertyInfo.CanRead; + public bool CanWrite => _propertyInfo.CanWrite; + + public BslPropertyInfo PropertyInfo => _propertyInfo; + + private Func CreateGetter(MethodInfo methInfo) + { + var method = (Func)Delegate.CreateDelegate(typeof(Func), methInfo); + + if (_propertyInfo.IsForbiddenToUse) + { + return inst => throw RuntimeException.DeprecatedPropertyAccess(Name); + } + + if (_propertyInfo.IsDeprecated) + { + return (inst) => + { + if (!_deprecationIsWarned) + { + SystemLogger.Write($"ВНИМАНИЕ! Обращение к устаревшему свойству {Name}"); + _deprecationIsWarned = true; + } + + return ConvertReturnValue(method(inst)); + }; + } + else + { + return inst => ConvertReturnValue(method(inst)); + } + } + + private Action CreateSetter(MethodInfo methInfo) + { + var method = (Action)Delegate.CreateDelegate(typeof(Action), methInfo); + + if (_propertyInfo.IsForbiddenToUse) + { + return (inst, val) => throw RuntimeException.DeprecatedPropertyAccess(Name); + } + + if (_propertyInfo.IsDeprecated) + { + return (inst, val) => + { + if (!_deprecationIsWarned) + { + SystemLogger.Write($"ВНИМАНИЕ! Обращение к устаревшему свойству {Name}"); + _deprecationIsWarned = true; + } + + method(inst, ConvertParam(val)); + }; + } + else + { + return (inst, val) => method(inst, ConvertParam(val)); + } + } + + private static T ConvertParam(IValue value) + { + return ContextValuesMarshaller.ConvertValueStrict(value); + } + + private static IValue ConvertReturnValue(TRet param) + { + return ContextValuesMarshaller.ConvertReturnValue(param); + } + + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/SafeArrayWrapper.cs b/src/ScriptEngine/Machine/Contexts/SafeArrayWrapper.cs index 94394c86c..e9b6d47ae 100644 --- a/src/ScriptEngine/Machine/Contexts/SafeArrayWrapper.cs +++ b/src/ScriptEngine/Machine/Contexts/SafeArrayWrapper.cs @@ -7,6 +7,9 @@ This Source Code Form is subject to the terms of the //#if !__MonoCS__ using System; using System.Collections.Generic; +using OneScript.Commons; +using OneScript.Contexts; +using OneScript.Execution; namespace ScriptEngine.Machine.Contexts { @@ -15,7 +18,7 @@ namespace ScriptEngine.Machine.Contexts /// На данный момент класс не является полноценной заменой для COMSafeArray и его нельзя создать вручную. /// [ContextClass("SafeArrayWrapper")] - public class SafeArrayWrapper : AutoContext, ICollectionContext, IObjectWrapper, IEnumerable + public class SafeArrayWrapper : AutoContext, ICollectionContext, IObjectWrapper { private readonly object[] _array; @@ -30,7 +33,7 @@ public SafeArrayWrapper(object[] safearray) } [ContextMethod("Количество", "Count")] - public int Count() + public int Count(IBslProcess process) { return _array.Length; } @@ -74,11 +77,6 @@ public override void SetIndexedValue(IValue index, IValue val) SetValue(intIndex, val); } - public CollectionEnumerator GetManagedIterator() - { - return new CollectionEnumerator(GetEnumerator()); - } - public IEnumerator GetEnumerator() { for (int i = 0; i < _array.Length; i++) diff --git a/src/ScriptEngine/Machine/Contexts/ScriptDrivenObject.cs b/src/ScriptEngine/Machine/Contexts/ScriptDrivenObject.cs index e3008f85d..49d473c3a 100644 --- a/src/ScriptEngine/Machine/Contexts/ScriptDrivenObject.cs +++ b/src/ScriptEngine/Machine/Contexts/ScriptDrivenObject.cs @@ -1,411 +1,412 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ScriptEngine.Machine.Contexts -{ - public abstract class ScriptDrivenObject : PropertyNameIndexAccessor, IRunnable - { - private LoadedModule _module; - private IVariable[] _state; - private int VARIABLE_COUNT; - private int METHOD_COUNT; - private MethodInfo[] _attachableMethods; - private readonly Dictionary _methodSearchCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary _propertySearchCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly Dictionary _allPropertiesSearchCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public LoadedModule Module => _module; - - protected ScriptDrivenObject(LoadedModule module, bool deffered) - : base(TypeManager.GetTypeByName("Object")) - { - _module = module; - if (!deffered) - { - InitOwnData(); - } - } - - protected ScriptDrivenObject(LoadedModule module) - { - _module = module; - InitOwnData(); - } - - protected ScriptDrivenObject() - { - } - - protected void SetModule(LoadedModule module) - { - _module = module; - } - - public void InitOwnData() - { - - VARIABLE_COUNT = GetOwnVariableCount(); - METHOD_COUNT = GetOwnMethodCount(); - - int stateSize = VARIABLE_COUNT + _module.Variables.Count; - _state = new IVariable[stateSize]; - for (int i = 0; i < stateSize; i++) - { - if (i < VARIABLE_COUNT) - _state[i] = Variable.CreateContextPropertyReference(this, i, GetOwnPropName(i)); - else - _state[i] = Variable.Create(ValueFactory.Create(), _module.Variables[i-VARIABLE_COUNT]); - } - - ReadExportedSymbols(_module.ExportedMethods, _methodSearchCache); - ReadExportedSymbols(_module.ExportedProperies, _propertySearchCache); - ReadVariables(_module.Variables, _allPropertiesSearchCache); - - } - - private void ReadVariables(VariablesFrame vars, Dictionary searchCache) - { - for (int i = 0; i < vars.Count; i++) - { - var variable = vars[i]; - searchCache[variable.Identifier] = variable.Index; - } - } - - private void ReadExportedSymbols(ExportedSymbol[] exportedSymbols, Dictionary searchCache) - { - for (int i = 0; i < exportedSymbols.Length; i++) - { - var es = exportedSymbols[i]; - searchCache[es.SymbolicName] = es.Index; - } - } - - protected abstract int GetOwnVariableCount(); - protected abstract int GetOwnMethodCount(); - protected abstract void UpdateState(); - - public bool MethodDefinedInScript(int index) - { - return index >= METHOD_COUNT; - } - - public bool PropDefinedInScript(int index) - { - return index >= VARIABLE_COUNT; - } - - internal int GetMethodDescriptorIndex(int indexInContext) - { - return indexInContext - METHOD_COUNT; - } - - protected virtual void OnInstanceCreation() - { - MachineInstance.Current.ExecuteModuleBody(this); - } - - public void Initialize() - { - OnInstanceCreation(); - } - - protected int GetScriptMethod(string methodName, string alias = null) - { - int index = -1; - - for (int i = 0; i < _module.Methods.Length; i++) - { - var item = _module.Methods[i]; - if (StringComparer.OrdinalIgnoreCase.Compare(item.Signature.Name, methodName) == 0 - || (alias != null && StringComparer.OrdinalIgnoreCase.Compare(item.Signature.Name, alias) == 0)) - { - index = i; - break; - } - } - - return index; - } - - protected IValue CallScriptMethod(int methodIndex, IValue[] parameters) - { - var returnValue = MachineInstance.Current.ExecuteMethod(this, methodIndex, parameters); - - return returnValue; - } - - public Action GetMethodExecutor(string methodName) - { - var id = GetScriptMethod(methodName); - if (id == -1) - throw RuntimeException.MethodNotFoundException(methodName, AsString()); - - return (args) => CallScriptMethod(id, args); - } - - #region Own Members Call - - protected virtual int FindOwnProperty(string name) - { - return -1; - } - - protected virtual int FindOwnMethod(string name) - { - return -1; - } - - protected virtual bool IsOwnPropReadable(int index) - { - return false; - } - - protected virtual bool IsOwnPropWritable(int index) - { - return false; - } - - protected virtual IValue GetOwnPropValue(int index) - { - throw new NotImplementedException(); - } - - protected virtual string GetOwnPropName(int index) - { - throw new NotImplementedException(); - } - - protected virtual void SetOwnPropValue(int index, IValue val) - { - throw new NotImplementedException(); - } - - protected virtual MethodInfo GetOwnMethod(int index) - { - throw new NotImplementedException(); - } - - protected virtual void CallOwnProcedure(int index, IValue[] arguments) - { - throw new NotImplementedException(); - } - - protected virtual IValue CallOwnFunction(int index, IValue[] arguments) - { - throw new NotImplementedException(); - } - - #endregion - - #region IAttachableContext Members - - public void OnAttach(MachineInstance machine, out IVariable[] variables, out MethodInfo[] methods) - { - UpdateState(); - - variables = _state; - methods = AttachMethods(); - } - - private MethodInfo[] AttachMethods() - { - if (_attachableMethods != null) - return _attachableMethods; - - int totalMethods = METHOD_COUNT + _module.Methods.Length; - _attachableMethods = new MethodInfo[totalMethods]; - - var moduleMethods = _module.Methods.Select(x => x.Signature).ToArray(); - - for (int i = 0; i < totalMethods; i++) - { - if (MethodDefinedInScript(i)) - { - _attachableMethods[i] = moduleMethods[i - METHOD_COUNT]; - } - else - { - _attachableMethods[i] = GetOwnMethod(i); - } - } - - return _attachableMethods; - } - - #endregion - - #region IRuntimeContextInstance Members - - public override int FindProperty(string name) - { - var idx = FindOwnProperty(name); - if (idx >= 0) - { - return idx; - } - else - { - int index; - if (_propertySearchCache.TryGetValue(name, out index)) - return index; - else - throw RuntimeException.PropNotFoundException(name); - } - } - - public override int FindMethod(string name) - { - var idx = FindOwnMethod(name); - if (idx >= 0) - { - return idx; - } - else - { - int index; - if (_methodSearchCache.TryGetValue(name, out index)) - return index; - else - throw RuntimeException.MethodNotFoundException(name, _module.ModuleInfo.ModuleName); - } - } - - public override bool IsPropReadable(int propNum) - { - if (PropDefinedInScript(propNum)) - { - return true; - } - else - { - return IsOwnPropReadable(propNum); - } - } - - public override bool IsPropWritable(int propNum) - { - if (PropDefinedInScript(propNum)) - { - return true; - } - else - { - return IsOwnPropWritable(propNum); - } - } - - public override IValue GetPropValue(int propNum) - { - if (PropDefinedInScript(propNum)) - { - return _state[propNum].Value; - } - else - { - return GetOwnPropValue(propNum); - } - - } - - public override void SetPropValue(int propNum, IValue newVal) - { - if (PropDefinedInScript(propNum)) - { - _state[propNum].Value = newVal; - } - else - { - SetOwnPropValue(propNum, newVal); - } - - } - - public override MethodInfo GetMethodInfo(int methodNumber) - { - if (MethodDefinedInScript(methodNumber)) - { - return _module.Methods[methodNumber-METHOD_COUNT].Signature; - } - else - { - return GetOwnMethod(methodNumber); - } - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) - { - if (MethodDefinedInScript(methodNumber)) - { - MachineInstance.Current.ExecuteMethod(this, methodNumber - METHOD_COUNT, arguments); - } - else - { - CallOwnProcedure(methodNumber, arguments); - } - } - - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) - { - if (MethodDefinedInScript(methodNumber)) - { - retValue = MachineInstance.Current.ExecuteMethod(this, methodNumber - METHOD_COUNT, arguments); - } - else - { - retValue = CallOwnFunction(methodNumber, arguments); - } - - } - - public override int GetPropCount() - { - return VARIABLE_COUNT + _module.ExportedProperies.Length; - } - - public override int GetMethodsCount() - { - return METHOD_COUNT + _module.ExportedMethods.Length; - } - - public override string GetPropName(int propNum) - { - if(PropDefinedInScript(propNum)) - { - return _module.ExportedProperies[propNum - VARIABLE_COUNT].SymbolicName; - } - else - { - return GetOwnPropName(propNum); - } - } - - #endregion - - public int FindAnyProperty(string name) - { - int index; - if (_allPropertiesSearchCache.TryGetValue(name, out index)) - return index; - else - throw RuntimeException.PropNotFoundException(name); - } - - public string[] GetExportedProperties() - { - return _module.ExportedProperies.Select(x => x.SymbolicName).ToArray(); - } - - public string[] GetExportedMethods() - { - return _module.ExportedMethods.Select(x => x.SymbolicName).ToArray(); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; + +namespace ScriptEngine.Machine.Contexts +{ + public abstract class ScriptDrivenObject : PropertyNameIndexAccessor, IRunnable + { + private IExecutableModule _module; + private IVariable[] _state; + private int VARIABLE_COUNT; + private int METHOD_COUNT; + private BslMethodInfo[] _attachableMethods; + private readonly Dictionary _methodSearchCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _propertySearchCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary _allPropertiesSearchCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public IExecutableModule Module => _module; + + protected ScriptDrivenObject(IExecutableModule module, bool deferred) + { + _module = module; + if (!deferred) + { + InitOwnData(); + } + } + + protected ScriptDrivenObject(StackRuntimeModule module) + { + _module = module; + InitOwnData(); + } + + protected ScriptDrivenObject() + { + } + + protected void SetModule(StackRuntimeModule module) + { + _module = module; + } + + public void InitOwnData() + { + VARIABLE_COUNT = GetOwnVariableCount(); + METHOD_COUNT = GetOwnMethodCount(); + + ClearSearchCaches(); + + int stateSize = VARIABLE_COUNT + _module.Fields.Count; + _state = new IVariable[stateSize]; + for (int i = 0; i < stateSize; i++) + { + if (i < VARIABLE_COUNT) + { + var name = GetOwnPropName(i); + _state[i] = Variable.CreateContextPropertyReference(this, i, name); + _allPropertiesSearchCache.Add(name, i); + } + else + { + var name = _module.Fields[i - VARIABLE_COUNT].Name; + _state[i] = Variable.Create(ValueFactory.Create(), name); + _allPropertiesSearchCache.Add(name, i); + } + } + + foreach (var prop in _module.Properties.Cast()) + { + _propertySearchCache.Add(prop.Name, prop.DispatchId); + } + + foreach (var method in _module.Methods.Cast()) + { + if(method.IsPublic) + _methodSearchCache.Add(method.Name, method.DispatchId); + } + + } + + private void ClearSearchCaches() + { + _propertySearchCache.Clear(); + _methodSearchCache.Clear(); + _allPropertiesSearchCache.Clear(); + } + + protected abstract int GetOwnVariableCount(); + protected abstract int GetOwnMethodCount(); + + public bool MethodDefinedInScript(int index) + { + return index >= METHOD_COUNT; + } + + public bool PropDefinedInScript(int index) + { + return index >= VARIABLE_COUNT; + } + + internal int GetMethodDescriptorIndex(int indexInContext) + { + return indexInContext - METHOD_COUNT; + } + + protected virtual void OnInstanceCreation(IBslProcess process) + { + if (_module.ModuleBody == null) + return; + + process.Run(this, _module, _module.ModuleBody, Array.Empty()); + } + + public void Initialize(IBslProcess process) + { + OnInstanceCreation(process); + } + + protected int GetScriptMethod(string methodName, string alias = null) + { + int index = -1; + + for (int i = 0; i < _module.Methods.Count; i++) + { + var item = _module.Methods[i]; + if (StringComparer.OrdinalIgnoreCase.Compare(item.Name, methodName) == 0 + || (alias != null && StringComparer.OrdinalIgnoreCase.Compare(item.Name, alias) == 0)) + { + index = i; + break; + } + } + + return index; + } + + protected IValue CallScriptMethod(int methodIndex, IValue[] parameters, IBslProcess process) + { + var returnValue = process.Run(this, _module, _module.Methods[methodIndex], parameters); + + return returnValue; + } + + public Action GetMethodExecutor(string methodName) + { + var id = GetScriptMethod(methodName); + if (id == -1) + throw RuntimeException.MethodNotFoundException(methodName, SystemType.Name); + + return (process, args) => CallScriptMethod(id, args, process); + } + + #region Own Members Call + + protected virtual int FindOwnProperty(string name) + { + return -1; + } + + protected virtual int FindOwnMethod(string name) + { + return -1; + } + + protected virtual bool IsOwnPropReadable(int index) + { + return false; + } + + protected virtual bool IsOwnPropWritable(int index) + { + return false; + } + + protected virtual IValue GetOwnPropValue(int index) + { + throw new NotImplementedException(); + } + + protected virtual string GetOwnPropName(int index) + { + throw new NotImplementedException(); + } + + protected virtual void SetOwnPropValue(int index, IValue val) + { + throw new NotImplementedException(); + } + + protected virtual BslMethodInfo GetOwnMethod(int index) + { + throw new NotImplementedException(); + } + + protected virtual BslPropertyInfo GetOwnPropertyInfo(int index) + { + throw new NotImplementedException(); + } + + protected virtual void CallOwnProcedure(int index, IValue[] arguments, IBslProcess process) + { + throw new NotImplementedException(); + } + + protected virtual IValue CallOwnFunction(int index, IValue[] arguments, IBslProcess process) + { + throw new NotImplementedException(); + } + + #endregion + + #region IAttachableContext Members + + IVariable IAttachableContext.GetVariable(int index) + { + return _state[index]; + } + + BslMethodInfo IAttachableContext.GetMethod(int index) + { + return GetMethodInfo(index); + } + + int IAttachableContext.VariablesCount => _state.Length; + + int IAttachableContext.MethodsCount => GetMethodsCount(); + + #endregion + + #region IRuntimeContextInstance Members + + public override int GetPropertyNumber(string name) + { + var idx = FindOwnProperty(name); + if (idx >= 0) + { + return idx; + } + else + { + int index; + if (_propertySearchCache.TryGetValue(name, out index)) + return index; + else + throw PropertyAccessException.PropNotFoundException(name); + } + } + + public override int GetMethodNumber(string name) + { + var idx = FindOwnMethod(name); + if (idx >= 0) + { + return idx; + } + else + { + int index; + if (_methodSearchCache.TryGetValue(name, out index)) + return index; + else + throw RuntimeException.MethodNotFoundException(name, _module.Source.Name); + } + } + + public override bool IsPropReadable(int propNum) + { + if (PropDefinedInScript(propNum)) + { + return true; + } + else + { + return IsOwnPropReadable(propNum); + } + } + + public override bool IsPropWritable(int propNum) + { + if (PropDefinedInScript(propNum)) + { + return true; + } + else + { + return IsOwnPropWritable(propNum); + } + } + + public override IValue GetPropValue(int propNum) + { + if (PropDefinedInScript(propNum)) + { + return _state[propNum].Value; + } + else + { + return GetOwnPropValue(propNum); + } + + } + + public override void SetPropValue(int propNum, IValue newVal) + { + if (PropDefinedInScript(propNum)) + { + _state[propNum].Value = newVal; + } + else + { + SetOwnPropValue(propNum, newVal); + } + + } + + public override BslPropertyInfo GetPropertyInfo(int propertyNumber) + { + if (PropDefinedInScript(propertyNumber)) + { + return _module.Properties[propertyNumber-VARIABLE_COUNT]; + } + else + { + return GetOwnPropertyInfo(propertyNumber); + } + } + + public override BslMethodInfo GetMethodInfo(int methodNumber) + { + if (MethodDefinedInScript(methodNumber)) + { + return _module.Methods[methodNumber-METHOD_COUNT]; + } + else + { + return GetOwnMethod(methodNumber); + } + } + + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) + { + if (MethodDefinedInScript(methodNumber)) + { + process.Run(this, _module, _module.Methods[methodNumber - METHOD_COUNT], arguments); + } + else + { + CallOwnProcedure(methodNumber, arguments, process); + } + } + + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) + { + if (MethodDefinedInScript(methodNumber)) + { + retValue = process.Run(this, _module, _module.Methods[methodNumber - METHOD_COUNT], arguments); + } + else + { + retValue = CallOwnFunction(methodNumber, arguments, process); + } + } + + public override int GetPropCount() + { + return VARIABLE_COUNT + _module.Properties.Count; + } + + public override int GetMethodsCount() + { + return METHOD_COUNT + _module.Methods.Count(x => x.IsPublic); + } + + public override string GetPropName(int propNum) + { + if(PropDefinedInScript(propNum)) + { + return _module.Properties[propNum - VARIABLE_COUNT].Name; + } + else + { + return GetOwnPropName(propNum); + } + } + + #endregion + + public int FindAnyProperty(string name) + { + int index; + if (_allPropertiesSearchCache.TryGetValue(name, out index)) + return index; + else + throw PropertyAccessException.PropNotFoundException(name); + } + + protected static Exception BslProcessRequired() + { + return new NotSupportedException("Implementation requires IBslProcess"); + } + } +} diff --git a/src/ScriptEngine/Machine/Contexts/ScriptInformationContext.cs b/src/ScriptEngine/Machine/Contexts/ScriptInformationContext.cs index b15b27037..9351ad5da 100644 --- a/src/ScriptEngine/Machine/Contexts/ScriptInformationContext.cs +++ b/src/ScriptEngine/Machine/Contexts/ScriptInformationContext.cs @@ -5,7 +5,8 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Environment; +using OneScript.Contexts; +using OneScript.Sources; namespace ScriptEngine.Machine.Contexts { @@ -17,14 +18,9 @@ public class ScriptInformationContext : AutoContext { private readonly string _origin; - internal ScriptInformationContext(ModuleInformation info) + public ScriptInformationContext(SourceCode codeSrc) { - _origin = info.Origin; - } - - public ScriptInformationContext(ICodeSource codeSrc) - { - _origin = codeSrc.SourceDescription; + _origin = codeSrc.Location; } /// diff --git a/src/ScriptEngine/Machine/Contexts/ScriptedEnumeratorWrapper.cs b/src/ScriptEngine/Machine/Contexts/ScriptedEnumeratorWrapper.cs new file mode 100644 index 000000000..4589b532f --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/ScriptedEnumeratorWrapper.cs @@ -0,0 +1,83 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Values; +using ScriptEngine.Machine.Interfaces; + +namespace ScriptEngine.Machine.Contexts +{ + public sealed class ScriptedEnumeratorWrapper : IEnumerator + { + private readonly UserScriptContextInstance _userObject; + private readonly IBslProcess _process; + + private BslScriptMethodInfo _moveNextMethod; + private BslScriptMethodInfo _getCurrentMethod; + private BslScriptMethodInfo _onDisposeMethod; + + internal ScriptedEnumeratorWrapper(UserScriptContextInstance userObject, IBslProcess process) + { + _userObject = userObject; + _process = process; + CheckAndSetMethods(); + } + + private void CheckAndSetMethods() + { + var bslInterface = _userObject.Module.GetInterface() + ?? IteratorBslInterfaceChecker.CheckModule(_userObject.Module); + + _moveNextMethod = bslInterface.MoveNextMethod; + _getCurrentMethod = bslInterface.GetCurrentMethod; + _onDisposeMethod = bslInterface.OnDisposeMethod; + } + + public bool MoveNext() + { + _userObject.CallAsFunction(_moveNextMethod.DispatchId, Array.Empty(), out var result, _process); + return result.AsBoolean(); + } + + public void Reset() + { + throw new System.NotSupportedException(); + } + + public BslValue Current + { + get + { + _userObject.CallAsFunction(_getCurrentMethod.DispatchId, Array.Empty(), out var result, _process); + return (BslValue)result; + } + } + + object IEnumerator.Current => Current; + + public void Dispose() + { + if (_onDisposeMethod != null) + _userObject.CallAsProcedure(_onDisposeMethod.DispatchId, Array.Empty(), _process); + } + + public static RuntimeException IncompatibleInterfaceError() + { + var error = new BilingualString( + "Итератор не соответствует интерфейсу итератора", + "Iterator doesn't match Iterator interface"); + + return new RuntimeException(error); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/SelfAwareEnumValue.cs b/src/ScriptEngine/Machine/Contexts/SelfAwareEnumValue.cs deleted file mode 100644 index 7ba5350b2..000000000 --- a/src/ScriptEngine/Machine/Contexts/SelfAwareEnumValue.cs +++ /dev/null @@ -1,16 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Contexts -{ - public class SelfAwareEnumValue : EnumerationValue where TOwner : EnumerationContext - { - public SelfAwareEnumValue(TOwner owner) : base(owner) - { - } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/SimpleEnumsMarshaller.cs b/src/ScriptEngine/Machine/Contexts/SimpleEnumsMarshaller.cs new file mode 100644 index 000000000..82312e4f9 --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/SimpleEnumsMarshaller.cs @@ -0,0 +1,63 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Diagnostics; +using System.Linq.Expressions; +using System.Reflection; +using OneScript.Values; + +namespace ScriptEngine.Machine.Contexts +{ + /// + /// Помогает конвертировать обычные clr-перечисления в значения ClrEnumValueWrapper + /// Предварительно перечисление должно быть обработано загрузчиком ContextDiscoverer. + /// + public static class SimpleEnumsMarshaller + { + // TODO Наверное можно и прямо отсюда регистрировать SimpleEnum-ы а из ContextDiscoverer этот класс вызывать + + private static LruCache> _gettersCache + = new LruCache>(32); + + /// + /// Получить IValue для значения clr-перечисления. + /// + /// Значение перечисления + /// тип перечисления + /// EnumerationValue оборачивающее значение перечисления + /// Что-то пошло не так + public static IValue ConvertEnum(object objParam, Type type) + { + if (!type.IsInstanceOfType(objParam)) + throw ValueMarshallingException.InvalidEnum(type); + + var getter = _gettersCache.GetOrAdd(type, CreateGetter); + + return getter(objParam); + } + + private static Func CreateGetter(Type type) + { + var enumWrapperType = typeof(ClrEnumWrapper<>).MakeGenericType(type); + var instanceProp = enumWrapperType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public); + var fromValueMethod = + enumWrapperType.GetMethod("FromNativeValue", BindingFlags.Instance | BindingFlags.Public); + + Debug.Assert(instanceProp != null && fromValueMethod != null); + + var incomingObjectParam = Expression.Parameter(typeof(object), "obj"); + var castedEnum = Expression.Convert(incomingObjectParam, type); + var propAccess = Expression.Property(null, instanceProp); + var call = Expression.Call(propAccess, fromValueMethod, castedEnum); + + var lambda = Expression.Lambda>(call, incomingObjectParam); + var getter = lambda.Compile(); + return getter; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/StackTraceCollectionContext.cs b/src/ScriptEngine/Machine/Contexts/StackTraceCollectionContext.cs index f28cbac59..ec110be52 100644 --- a/src/ScriptEngine/Machine/Contexts/StackTraceCollectionContext.cs +++ b/src/ScriptEngine/Machine/Contexts/StackTraceCollectionContext.cs @@ -7,6 +7,8 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; namespace ScriptEngine.Machine.Contexts { @@ -15,7 +17,7 @@ namespace ScriptEngine.Machine.Contexts /// Содержит объекты типа КадрСтекаВызовов /// [ContextClass("КоллекцияКадровСтекаВызовов", "CallStackFramesCollection")] - public class StackTraceCollectionContext : AutoContext, ICollectionContext + public class StackTraceCollectionContext : AutoCollectionContext { private List _frames; @@ -29,14 +31,31 @@ internal StackTraceCollectionContext(IEnumerable frames) }).ToList(); } - public int Count() + /// + /// Возвращает количество кадров в стеке вызовов + /// + /// Число - Количество кадров в стеке вызовов + [ContextMethod("Количество", "Count")] + public override int Count() { return _frames.Count; } - public CollectionEnumerator GetManagedIterator() + public override IEnumerator GetEnumerator() { - return new CollectionEnumerator(_frames.GetEnumerator()); + return _frames.GetEnumerator(); + } + + public override bool IsIndexed => true; + + public override IValue GetIndexedValue(IValue index) + { + var idx = (int)index.AsNumber(); + + if (idx < 0 || idx >= Count()) + throw RuntimeException.IndexOutOfRange(); + + return _frames[idx]; } } } diff --git a/src/ScriptEngine/Machine/Contexts/StackTraceItemContext.cs b/src/ScriptEngine/Machine/Contexts/StackTraceItemContext.cs index 1d35134c6..414b14a54 100644 --- a/src/ScriptEngine/Machine/Contexts/StackTraceItemContext.cs +++ b/src/ScriptEngine/Machine/Contexts/StackTraceItemContext.cs @@ -5,6 +5,8 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Contexts; + namespace ScriptEngine.Machine.Contexts { /// @@ -22,7 +24,7 @@ public class StackTraceItemContext : AutoContext [ContextProperty("ИмяМодуля", CanWrite = false)] public string ModuleName { get; set; } - public override string AsString() + public override string ToString() { return $"{Method}: {LineNumber} ({ModuleName})"; } diff --git a/src/ScriptEngine/Machine/Contexts/SystemEnumAttribute.cs b/src/ScriptEngine/Machine/Contexts/SystemEnumAttribute.cs deleted file mode 100644 index 213de99e0..000000000 --- a/src/ScriptEngine/Machine/Contexts/SystemEnumAttribute.cs +++ /dev/null @@ -1,33 +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 ScriptEngine.Machine.Contexts -{ - [AttributeUsage(AttributeTargets.Class)] - public class SystemEnumAttribute : Attribute - { - private readonly string _name; - private readonly string _alias; - - public SystemEnumAttribute(string name, string alias = "") - { - _name = name; - _alias = alias; - } - - public string GetName() - { - return _name; - } - - public string GetAlias() - { - return _alias; - } - } -} diff --git a/src/ScriptEngine/Machine/Contexts/SystemPropertyInfo.cs b/src/ScriptEngine/Machine/Contexts/SystemPropertyInfo.cs new file mode 100644 index 000000000..bb5433404 --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/SystemPropertyInfo.cs @@ -0,0 +1,125 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Globalization; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Values; + +namespace ScriptEngine.Machine.Contexts +{ + public class SystemPropertyInfo : BslPropertyInfo, ISupportsDeprecation + { + private string _name; + private string _alias; + private Type _declaringType; + private Type _propertyType; + + internal class Builder + { + private readonly SystemPropertyInfo _info; + + public Builder(string name, string alias = null) + { + _info = new SystemPropertyInfo(); + + _info._name = name; + _info._alias = alias; + _info._declaringType = typeof(PropertyBag); + _info._propertyType = typeof(BslValue); + } + + public Builder SetDeclaringType(Type declaringType) + { + _info._declaringType = declaringType; + return this; + } + + public Builder SetPropertyType(Type propertyType) + { + _info._propertyType = propertyType; + return this; + } + + public Builder SetDeprecated(bool isDeprecated) + { + _info.IsDeprecated = isDeprecated; + return this; + } + + public SystemPropertyInfo Build() + { + return _info; + } + } + + private SystemPropertyInfo() + { + + } + + + // ReSharper disable once ConvertToAutoProperty + public override Type DeclaringType => _declaringType; + // ReSharper disable once ConvertToAutoProperty + public override string Name => _name; + // ReSharper disable once ConvertToAutoProperty + public override string Alias => _alias; + + public bool IsDeprecated { get; private set; } + + public override bool Equals(BslPropertyInfo other) + { + if (! (other is SystemPropertyInfo gcProp)) + return false; + + return gcProp._name == _name && + gcProp._alias == _alias && + gcProp._declaringType == _declaringType && + gcProp._propertyType == _propertyType; + } + + public override Type ReflectedType => _declaringType; + + public override MethodInfo[] GetAccessors(bool nonPublic) + { + throw new NotImplementedException(); + } + + public override MethodInfo GetGetMethod(bool nonPublic) + { + throw new NotImplementedException(); + } + + public override ParameterInfo[] GetIndexParameters() + { + throw new NotImplementedException(); + } + + public override MethodInfo GetSetMethod(bool nonPublic) + { + throw new NotImplementedException(); + } + + public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, + CultureInfo culture) + { + throw new NotImplementedException(); + } + + public override PropertyAttributes Attributes { get; } + public override bool CanRead => true; + public override bool CanWrite => false; + public override Type PropertyType => _propertyType; + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/ThisAwareScriptedObjectBase.cs b/src/ScriptEngine/Machine/Contexts/ThisAwareScriptedObjectBase.cs new file mode 100644 index 000000000..dfa83d0cf --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/ThisAwareScriptedObjectBase.cs @@ -0,0 +1,107 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Execution; + +namespace ScriptEngine.Machine.Contexts +{ + public abstract class ThisAwareScriptedObjectBase : ScriptDrivenObject + { + protected const int THISOBJ_VARIABLE_INDEX = 0; + private const int INVALID_INDEX = -1; + + protected const string THISOBJ_EN = "ThisObject"; + protected const string THISOBJ_RU = "ЭтотОбъект"; + + protected ThisAwareScriptedObjectBase(IExecutableModule module, bool deferred) : base(module, deferred) + { + } + + protected ThisAwareScriptedObjectBase(StackRuntimeModule module) : base(module) + { + } + + protected ThisAwareScriptedObjectBase() + { + } + + protected override int GetOwnVariableCount() + { + return 1; + } + + protected override int FindOwnProperty(string name) + { + if (string.Compare(name, THISOBJ_RU, StringComparison.OrdinalIgnoreCase) == 0 + || string.Compare(name, THISOBJ_EN, StringComparison.OrdinalIgnoreCase) == 0) + { + return THISOBJ_VARIABLE_INDEX; + } + + return INVALID_INDEX; + } + + protected override bool IsOwnPropReadable(int index) + { + if (index == THISOBJ_VARIABLE_INDEX) + return true; + + throw new InvalidOperationException($"Property {index} should not be accessed through base class"); + } + + protected override bool IsOwnPropWritable(int index) + { + if (index == THISOBJ_VARIABLE_INDEX) + return false; + + throw new InvalidOperationException($"Property {index} should not be accessed through base class"); + } + + protected override IValue GetOwnPropValue(int index) + { + if (index == THISOBJ_VARIABLE_INDEX) + return this; + + throw new InvalidOperationException($"Property {index} should not be accessed through base class"); + } + + protected override string GetOwnPropName(int index) + { + return Locale.NStr($"ru='{THISOBJ_RU}';en='{THISOBJ_EN}'"); + } + + protected override BslPropertyInfo GetOwnPropertyInfo(int index) + { + if (index != THISOBJ_VARIABLE_INDEX) + throw new InvalidOperationException($"Property {index} should not be accessed through base class"); + + return BslPropertyBuilder.Create() + .SetNames(THISOBJ_RU, THISOBJ_EN) + .DeclaringType(GetType()) + .SetDispatchingIndex(index) + .Build(); + } + + [SymbolsProvider] + // ReSharper disable once UnusedMember.Local + private static void CompileTimeSymbols(TypeSymbolsProviderFactory providerFactory, SymbolScope moduleScope) + { + moduleScope.Variables.Add(BslFieldBuilder.Create() + .Name(THISOBJ_RU) + .Alias(THISOBJ_EN) + .ValueType(typeof(ThisAwareScriptedObjectBase)) + .SetDispatchingIndex(0) + .Build() + .ToSymbol()); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs b/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs index 0a69a5d63..13dc9fe30 100644 --- a/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs +++ b/src/ScriptEngine/Machine/Contexts/UnmanagedCOMWrapperContext.cs @@ -9,15 +9,17 @@ This Source Code Form is subject to the terms of the using System.Collections; using System.Collections.Generic; using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Rcw; +using OneScript.Values; using System.Reflection; -using ScriptEngine.Machine.Rcw; +using OneScript.Execution; namespace ScriptEngine.Machine.Contexts { public class UnmanagedCOMWrapperContext : COMWrapperContext, IDebugPresentationAcceptor { - private const uint E_DISP_MEMBERNOTFOUND = 0x80020003; - private readonly RcwMembersMetadataCollection _props; private readonly RcwMembersMetadataCollection _methods; private readonly bool _isCollection; @@ -79,10 +81,10 @@ public override IEnumerator GetEnumerator() public override string GetPropName(int propNum) => _props[propNum].Name; - public override int FindProperty(string name) + public override int GetPropertyNumber(string name) { if(!TryFindProperty(name, out var md)) - throw RuntimeException.PropNotFoundException(name); + throw PropertyAccessException.PropNotFoundException(name); return _props.IndexOf(md); } @@ -104,18 +106,18 @@ public override IValue GetPropValue(int propNum) var result = DispatchUtility.Invoke(Instance, dispId, null); return CreateIValue(result); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } } - catch (System.MissingMemberException) + catch (MissingMemberException) { - throw RuntimeException.PropNotFoundException(prop.Name); + throw PropertyAccessException.PropNotFoundException(prop.Name); } - catch (System.MemberAccessException) + catch (MemberAccessException) { - throw RuntimeException.PropIsNotReadableException(prop.Name); + throw PropertyAccessException.PropIsNotReadableException(prop.Name); } } @@ -130,9 +132,9 @@ public override void SetPropValue(int propNum, IValue newVal) try { object argToPass; - if(newVal.DataType == Machine.DataType.Date) + if(newVal is BslDateValue dateVal) { - var date = newVal.AsDate(); + var date = (DateTime)dateVal; if(date == DateTime.MinValue) { argToPass = new DateTime(100, 1, 1); // Min OLEAuth Date @@ -148,22 +150,22 @@ public override void SetPropValue(int propNum, IValue newVal) } DispatchUtility.InvokeSetProperty(Instance, dispId, argToPass); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } } - catch (System.MissingMemberException) + catch (MissingMemberException) { - throw RuntimeException.PropNotFoundException(prop.Name); + throw PropertyAccessException.PropNotFoundException(prop.Name); } - catch (System.MemberAccessException) + catch (MemberAccessException) { - throw RuntimeException.PropIsNotWritableException(prop.Name); + throw PropertyAccessException.PropIsNotWritableException(prop.Name); } } - public override int FindMethod(string name) + public override int GetMethodNumber(string name) { if (!TryFindMethod(name, out var md)) throw RuntimeException.MethodNotFoundException(name); @@ -171,18 +173,16 @@ public override int FindMethod(string name) return _methods.IndexOf(md); } - public override MethodInfo GetMethodInfo(int methodNumber) + public override BslMethodInfo GetMethodInfo(int methodNumber) { - return GetMethodDescription(methodNumber); + var md = _methods[methodNumber]; + return BslMethodBuilder.Create() + .Name(md.Name) + .ReturnType(md.IsFunction ?? true ? typeof(IValue) : typeof(void)) + .Build(); } - private MethodInfo GetMethodDescription(int methodNumber) - { - //TODO: Доработать RcwMethodMetadata - return new MethodInfo(); - } - - public override void CallAsProcedure(int methodNumber, IValue[] arguments) + public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) { var method = _methods[methodNumber]; @@ -192,20 +192,24 @@ public override void CallAsProcedure(int methodNumber, IValue[] arguments) { try { - DispatchUtility.Invoke(Instance, dispId, MarshalArguments(arguments)); + var argsData = MarshalArguments(arguments); + var initialValues = new object[argsData.values.Length]; + Array.Copy(argsData.values, initialValues, initialValues.Length); + DispatchUtility.Invoke(Instance, dispId, argsData.values, argsData.flags); + RemapOutputParams(arguments, argsData.values, argsData.flags[0], initialValues); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } } - catch (System.MissingMemberException) + catch (MissingMemberException) { throw RuntimeException.MethodNotFoundException(method.Name); } } - public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) + public override void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) { var method = _methods[methodNumber]; @@ -218,44 +222,64 @@ public override void CallAsFunction(int methodNumber, IValue[] arguments, out IV { try { - var result = DispatchUtility.Invoke(Instance, dispId, MarshalArguments(arguments)); + var argsData = MarshalArguments(arguments); + var initialValues = new object[argsData.values.Length]; + Array.Copy(argsData.values, initialValues, initialValues.Length); + var result = DispatchUtility.Invoke(Instance, dispId, argsData.values, argsData.flags); + RemapOutputParams(arguments, argsData.values, argsData.flags[0], initialValues); retValue = CreateIValue(result); } - catch (System.Reflection.TargetInvocationException e) + catch (TargetInvocationException e) { throw e.InnerException ?? e; } } - catch (System.MissingMemberException) + catch (MissingMemberException) { throw RuntimeException.MethodNotFoundException(method.Name); } } + + private static void RemapOutputParams(IValue[] arguments, object[] values, ParameterModifier flags, + object[] initialValues) + { + for (int i = 0; i < arguments.Length; i++) + { + var initialValue = initialValues[i]; + var valueAfterCall = values[i]; + + if (flags[i] && !Equals(initialValue, valueAfterCall)) + { + var variable = (IVariable)arguments[i]; + variable.Value = CreateIValue(values[i]); + } + } + } private bool TryFindMethod(string name, out RcwMethodMetadata md) { - if (_methods.Names.TryGetValue(name, out md)) + if (_methods.ByName.TryGetValue(name, out md)) return true; if (!DispatchUtility.TryGetDispId(Instance, name, out var dispatchId)) return false; _methods.Add(new RcwMethodMetadata(name, dispatchId, null)); - md = _methods.DispatchIds[dispatchId]; + md = _methods.ByDispatchId[dispatchId]; return true; } private bool TryFindProperty(string name, out RcwPropertyMetadata md) { - if (_props.Names.TryGetValue(name, out md)) + if (_props.ByName.TryGetValue(name, out md)) return true; if (!DispatchUtility.TryGetDispId(Instance, name, out var dispatchId)) return false; _props.Add(new RcwPropertyMetadata(name, dispatchId)); - md = _props.DispatchIds[dispatchId]; + md = _props.ByDispatchId[dispatchId]; return true; } diff --git a/src/ScriptEngine/Machine/Contexts/UserIterableContextInstance.cs b/src/ScriptEngine/Machine/Contexts/UserIterableContextInstance.cs new file mode 100644 index 000000000..bf0da1996 --- /dev/null +++ b/src/ScriptEngine/Machine/Contexts/UserIterableContextInstance.cs @@ -0,0 +1,70 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine.Interfaces; + +namespace ScriptEngine.Machine.Contexts +{ + public class UserIterableContextInstance : UserScriptContextInstance, ICollectionContext + { + private readonly BslScriptMethodInfo _getCountMethod; + private readonly BslScriptMethodInfo _getIteratorMethod; + public static readonly BilingualString GetIteratorTerms = new BilingualString("ПолучитьИтератор", "GetIterator"); + public static readonly BilingualString GetCountTerms = new BilingualString("Количество", "Count"); + + public UserIterableContextInstance(IExecutableModule module, TypeDescriptor asObjectOfType, IValue[] args = null) : base(module, asObjectOfType, args) + { + var methods = module.GetInterface(); + _getIteratorMethod = methods.GetIteratorMethod; + _getCountMethod = methods.GetCountMethod; + } + + public int Count(IBslProcess process) + { + if (_getCountMethod == null) + throw new RuntimeException( + new BilingualString( + "Класс не поддерживает получение количества элементов, т.к. в нем отсутствует метод "+GetCountTerms.Russian+"()", + "Class doesn't support items counting, because method "+GetCountTerms.English+"() is not defined") + ); + + CallAsFunction(_getCountMethod.DispatchId, Array.Empty(), out var ret, process); + + return (int)ret.AsNumber(); + } + + public IEnumerator GetEnumerator(IBslProcess process) + { + CallAsFunction(_getIteratorMethod.DispatchId, Array.Empty(), out var enumerator, process); + if (!(enumerator is UserScriptContextInstance userObject)) + { + throw ScriptedEnumeratorWrapper.IncompatibleInterfaceError(); + } + + return new ScriptedEnumeratorWrapper(userObject, process); + } + + public IEnumerator GetEnumerator() + { + throw BslProcessRequired(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs b/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs index 9583a0902..ed5171fd2 100644 --- a/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs +++ b/src/ScriptEngine/Machine/Contexts/UserScriptContextInstance.cs @@ -1,265 +1,349 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace ScriptEngine.Machine.Contexts -{ - public class UserScriptContextInstance : ScriptDrivenObject, IDebugPresentationAcceptor - { - readonly LoadedModule _module; - Dictionary _ownPropertyIndexes; - List _ownProperties; - - private Func _asStringOverride; - - private const string RAISEEVENT_RU = "ВызватьСобытие"; - private const string RAISEEVENT_EN = "RaiseEvent"; - private const int RAIZEEVENT_INDEX = 0; - - public IValue[] ConstructorParams { get; private set; } - - public UserScriptContextInstance(LoadedModule module) : base(module) - { - _module = module; - ConstructorParams = new IValue[0]; - } - - public UserScriptContextInstance(LoadedModule module, string asObjectOfType, IValue[] args = null) - : base(module, true) - { - DefineType(TypeManager.GetTypeByName(asObjectOfType)); - _module = module; - - ConstructorParams = args; - if (args == null) - { - ConstructorParams = new IValue[0]; - } - - } - - protected override void OnInstanceCreation() - { - ActivateAsStringOverride(); - - base.OnInstanceCreation(); - var methId = GetScriptMethod("ПриСозданииОбъекта", "OnObjectCreate"); - int constructorParamsCount = ConstructorParams.Count(); - - if (methId > -1) - { - var procInfo = GetMethodInfo(GetOwnMethodCount()+methId); - - int procParamsCount = procInfo.Params.Count(); - - int reqParamsCount = procInfo.Params.Count(x => !x.HasDefaultValue); - - if (constructorParamsCount < reqParamsCount || constructorParamsCount > procParamsCount) - throw new RuntimeException("Параметры конструктора: " - + "необходимых параметров: " + Math.Min(procParamsCount, reqParamsCount).ToString() - + ", передано параметров " + constructorParamsCount.ToString() - ); - else if (procInfo.Params.Skip(constructorParamsCount).Any(param => !param.HasDefaultValue)) - throw RuntimeException.TooFewArgumentsPassed(); - - CallScriptMethod(methId, ConstructorParams); - } - else - { - if (constructorParamsCount > 0) - { - throw new RuntimeException("Конструктор не определен, но переданы параметры конструктора."); - } - } - } - - private void ActivateAsStringOverride() - { - var methId = GetScriptMethod("ОбработкаПолученияПредставления", "PresentationGetProcessing"); - if (methId == -1) - _asStringOverride = base.AsString; - else - { - var signature = GetMethodInfo(methId); - if (signature.ArgCount != 2) - throw new RuntimeException("Обработчик получения представления должен иметь 2 параметра"); - - _asStringOverride = () => GetOverridenPresentation(methId); - } - } - - private string GetOverridenPresentation(int methId) - { - var standard = ValueFactory.Create(true); - var strValue = ValueFactory.Create(); - - var arguments = new IValue[2] - { - Variable.Create(strValue, "string"), - Variable.Create(standard, "standardProcessing") - }; - - CallScriptMethod(methId, arguments); - - if (arguments[1].AsBoolean() == true) - return base.AsString(); - - return arguments[0].AsString(); - } - - public void AddProperty(string name, string alias, IValue value) - { - if(_ownProperties == null) - { - _ownProperties = new List(); - _ownPropertyIndexes = new Dictionary(); - } - - var newIndex = _ownProperties.Count; - _ownPropertyIndexes.Add(name, newIndex); - if (!string.IsNullOrEmpty(alias)) - { - _ownPropertyIndexes.Add(alias, newIndex); - } - _ownProperties.Add(value); - - } - - public void AddProperty(string name, IValue value) - { - AddProperty(name, null, value); - } - - protected override int GetOwnMethodCount() - { - return 1; - } - - protected override int FindOwnMethod(string name) - { - if (string.Equals(RAISEEVENT_EN, name, StringComparison.OrdinalIgnoreCase) - || string.Equals(RAISEEVENT_RU, name, StringComparison.OrdinalIgnoreCase)) - { - return RAIZEEVENT_INDEX; - } - - return base.FindOwnMethod(name); - } - - protected override MethodInfo GetOwnMethod(int index) - { - Debug.Assert(index == RAIZEEVENT_INDEX); - - return GetOwnMethodsDefinition()[RAIZEEVENT_INDEX]; - } - - public static MethodInfo[] GetOwnMethodsDefinition() - { - return new []{ - new MethodInfo { - Name = RAISEEVENT_RU, - Alias = RAISEEVENT_EN, - IsFunction = false, - IsExport = false, - Annotations = new AnnotationDefinition[0], - Params = new[] - { - new ParameterDefinition - { - Name = "eventName", - HasDefaultValue = false - }, - new ParameterDefinition - { - Name = "eventArgs", - HasDefaultValue = true, - DefaultValueIndex = ParameterDefinition.UNDEFINED_VALUE_INDEX - } - } - } - }; - } - - protected override void CallOwnProcedure(int index, IValue[] arguments) - { - Debug.Assert(index == RAIZEEVENT_INDEX); - - var eventName = arguments[0].AsString(); - IValue[] eventArgs = null; - if (arguments.Length > 1) - { - if (arguments[1].AsObject() is IEnumerable argsArray) - { - eventArgs = argsArray.ToArray(); - } - } - - if (eventArgs == null) - eventArgs = new IValue[0]; - - MachineInstance.Current.EventProcessor?.HandleEvent(this, eventName, eventArgs); - } - - protected override int GetOwnVariableCount() - { - if (_ownProperties == null) - return 0; - else - return _ownProperties.Count; - } - - protected override void UpdateState() - { - } - - protected override bool IsOwnPropReadable(int index) - { - if (_ownProperties == null) - return false; - - if (index >= 0 && index < _ownProperties.Count) - return true; - else - return false; - } - - protected override IValue GetOwnPropValue(int index) - { - return _ownProperties[index]; - } - - protected override string GetOwnPropName(int index) - { - if (_ownProperties == null) - throw new ArgumentException("Unknown property index"); - - return _ownPropertyIndexes.Where(x => x.Value == index).First().Key; - } - - public override int GetMethodsCount() - { - return GetOwnMethodCount() + _module.Methods.Length; - } - - public override string AsString() - { - return _asStringOverride(); - } - - void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) - { - var propVariables = this.GetProperties() - .Where(x => x.Identifier != "ЭтотОбъект") - .Select(x => Variable.Create(GetPropValue(x.Index), x.Identifier)); - - visitor.ShowCustom(propVariables.ToList()); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Types; + +namespace ScriptEngine.Machine.Contexts +{ + [ContextClass("Сценарий", "Script")] + public class UserScriptContextInstance : ThisAwareScriptedObjectBase, IDebugPresentationAcceptor + { + public static readonly BilingualString OnInstanceCreationTerms = + new BilingualString("ПриСозданииОбъекта", "OnObjectCreate"); + + public static readonly BilingualString PresentationGetProcessingTerms = + new BilingualString("ОбработкаПолученияПредставления", "PresentationGetProcessing"); + + public static readonly BilingualString RaiseEventTerms = + new BilingualString("ВызватьСобытие", "RaiseEvent"); + + private const int RAIZEEVENT_INDEX = 0; + + Dictionary _ownPropertyIndexes; + List _ownProperties; + + private Func _asStringOverride; + + + public UserScriptContextInstance(IExecutableModule module, bool deferred = false) : base(module, deferred) + { + ConstructorParams = Array.Empty(); + DefineType(GetType().GetTypeFromClassMarkup()); + } + + public UserScriptContextInstance(IExecutableModule module, TypeDescriptor asObjectOfType, IValue[] args = null) + : base(module, true) + { + DefineType(asObjectOfType); + + ConstructorParams = args; + if (args == null) + { + ConstructorParams = Array.Empty(); + } + } + + private IValue[] ConstructorParams { get; } + + protected override void OnInstanceCreation(IBslProcess process) + { + ActivateAsStringOverride(); + + base.OnInstanceCreation(process); + var methId = GetScriptMethod(OnInstanceCreationTerms.Russian, OnInstanceCreationTerms.English); + int constructorParamsCount = ConstructorParams.Length; + + if (methId > -1) + { + var procInfo = GetMethodInfo(GetOwnMethodCount()+methId); + + var parameters = procInfo.GetParameters(); + int procParamsCount = parameters.Length; + int reqParamsCount = parameters.Count(x => !x.HasDefaultValue); + + if (constructorParamsCount < reqParamsCount || constructorParamsCount > procParamsCount) + throw new RuntimeException("Параметры конструктора: " + + "необходимых параметров: " + Math.Min(procParamsCount, reqParamsCount).ToString() + + ", передано параметров " + constructorParamsCount.ToString() + ); + else if (parameters.Skip(constructorParamsCount).Any(param => !param.HasDefaultValue)) + throw RuntimeException.TooFewArgumentsPassed(); + + if (constructorParamsCount < procParamsCount) + { + var ctorParameters = new IValue[procParamsCount]; + ConstructorParams.CopyTo(ctorParameters, 0); + for (int i = constructorParamsCount; i < procParamsCount; i++) + { + ctorParameters[i] = (IValue)parameters[i].DefaultValue; + } + CallScriptMethod(methId, ctorParameters, process); + } + else + CallScriptMethod(methId, ConstructorParams, process); + } + else + { + if (constructorParamsCount > 0) + { + throw new RuntimeException("Конструктор не определен, но переданы параметры конструктора."); + } + } + } + + public override string ToString(IBslProcess process) + { + return _asStringOverride(process); + } + + private void ActivateAsStringOverride() + { + var methId = GetScriptMethod(PresentationGetProcessingTerms.Russian, PresentationGetProcessingTerms.English); + if (methId == -1) + _asStringOverride = base.ToString; + else + { + var signature = GetMethodInfo(GetOwnMethodCount()+methId); + if (signature.GetParameters().Length != 2) + throw new RuntimeException("Обработчик получения представления должен иметь 2 параметра"); + + _asStringOverride = (p) => GetOverridenPresentation(methId, p); + } + } + + private string GetOverridenPresentation(int methId, IBslProcess process) + { + var standard = ValueFactory.Create(true); + var strValue = ValueFactory.Create(); + + var arguments = new IValue[2] + { + Variable.Create(strValue, "string"), + Variable.Create(standard, "standardProcessing") + }; + + CallScriptMethod(methId, arguments, process); + + if (arguments[1].AsBoolean() == true) + return base.ToString(process); + + if (arguments[0].SystemType != BasicTypes.String && arguments[0].SystemType != BasicTypes.Undefined) + { + throw new RuntimeException(new BilingualString( + $"Полученное представление имеет тип {arguments[0].SystemType}. Ожидается тип Строка", + $"Returned presentation has type {arguments[0].SystemType}. Expected type is String")); + } + + return arguments[0].ToString(); + } + + public void AddProperty(string name, string alias, IValue value) + { + if(_ownProperties == null) + { + _ownProperties = new List(); + _ownPropertyIndexes = new Dictionary(); + } + + var newIndex = _ownProperties.Count + base.GetOwnVariableCount(); + _ownPropertyIndexes.Add(name, newIndex); + if (!string.IsNullOrEmpty(alias)) + { + _ownPropertyIndexes.Add(alias, newIndex); + } + _ownProperties.Add(value); + + } + + public void AddProperty(string name, IValue value) + { + AddProperty(name, null, value); + } + + protected override int GetOwnMethodCount() + { + return 1; + } + + protected override int FindOwnMethod(string name) + { + return RaiseEventTerms.HasName(name) ? RAIZEEVENT_INDEX : base.FindOwnMethod(name); + } + + protected override int FindOwnProperty(string name) + { + if (_ownPropertyIndexes != default && _ownPropertyIndexes.TryGetValue(name, out var index)) + { + return index; + } + + return base.FindOwnProperty(name); + } + + protected override BslMethodInfo GetOwnMethod(int index) + { + Debug.Assert(index == RAIZEEVENT_INDEX); + + return GetOwnMethodsDefinition()[RAIZEEVENT_INDEX]; + } + + protected override BslPropertyInfo GetOwnPropertyInfo(int index) + { + if (index == THISOBJ_VARIABLE_INDEX) + return base.GetOwnPropertyInfo(index); + + var names = _ownPropertyIndexes.Where(x => x.Value == index) + .Select(x => x.Key) + .ToArray(); + + Debug.Assert(names.Length > 0 && names.Length <= 2); + + var builder = BslPropertyBuilder.Create() + .Name(names[0]); + if (names.Length == 2) + { + builder.Alias(names[1]); + } + + builder.SetDispatchingIndex(index); + + return builder.Build(); + } + + [SymbolsProvider] + private static void PrepareCompilation(TypeSymbolsProviderFactory providerFactory, SymbolScope scope) + { + var baseSymbols = providerFactory.Get(); + baseSymbols.FillSymbols(scope); + GetOwnMethodsDefinition().ForEach(x => scope.DefineMethod(x.ToSymbol())); + } + + private static BslMethodInfo[] GetOwnMethodsDefinition() + { + var methodBuilder = BslMethodBuilder.Create(); + methodBuilder.SetNames(RaiseEventTerms.Russian, RaiseEventTerms.English) + .DeclaringType(typeof(UserScriptContextInstance)); + + methodBuilder.NewParameter() + .Name("eventName") + .ParameterType(typeof(string)); + + methodBuilder.NewParameter() + .Name("eventArgs") + .ParameterType(typeof(BslValue[])) + .DefaultValue(BslSkippedParameterValue.Instance); + + return new BslMethodInfo[]{methodBuilder.Build()}; + } + + protected override void CallOwnProcedure(int index, IValue[] arguments, IBslProcess process) + { + Debug.Assert(index == RAIZEEVENT_INDEX); + var eventProcessor = process.Services.TryResolve(); + if (eventProcessor == default) + return; + + var eventName = arguments[0].ExplicitString(); + IValue[] eventArgs = null; + if (arguments.Length > 1) + { + if (arguments[1].AsObject() is IEnumerable argsArray) + { + eventArgs = argsArray.ToArray(); + } + } + + if (eventArgs == null) + eventArgs = new IValue[0]; + + eventProcessor.HandleEvent(this, eventName, eventArgs, process); + } + + protected override int GetOwnVariableCount() + { + return base.GetOwnVariableCount() + (_ownProperties?.Count ?? 0); + } + + protected override bool IsOwnPropReadable(int index) + { + if (_ownProperties == null) + return base.IsOwnPropReadable(index); + + var baseProps = base.GetOwnVariableCount(); + if (index >= baseProps) + return true; + else + return base.IsOwnPropReadable(index); + } + + protected override bool IsOwnPropWritable(int index) + { + if (_ownProperties == null) + return base.IsOwnPropWritable(index); + + return false; + } + + protected override IValue GetOwnPropValue(int index) + { + var baseProps = base.GetOwnVariableCount(); + if (index >= baseProps) + return _ownProperties[index-baseProps]; + else + return base.GetOwnPropValue(index); + } + + protected override string GetOwnPropName(int index) + { + if (_ownProperties == null || index < base.GetOwnVariableCount()) + return base.GetOwnPropName(index); + + return _ownPropertyIndexes.First(x => x.Value == index).Key; + } + + public override int GetMethodsCount() + { + return GetOwnMethodCount() + Module.Methods.Count; + } + + void IDebugPresentationAcceptor.Accept(IDebugValueVisitor visitor) + { + var instanceProps = this.GetProperties() + .OfType() + .Where(p => p.DispatchId != THISOBJ_VARIABLE_INDEX) + .OrderBy(x => x.DispatchId) + .ToDictionary(x => x.Name, x => x.DispatchId); + + var instanceFields = Module + .Fields + .OfType() + .OrderBy(x => x.DispatchId) + .Where(x => !instanceProps.ContainsKey(x.Name)) + .ToDictionary(x => $"${x.Name}", x => x.DispatchId); + + var props = instanceProps + .Concat(instanceFields) + .Select(x => + Variable.Create(GetPropValue(x.Value), x.Key)) + .ToList(); + + visitor.ShowCustom(props); + } + } +} diff --git a/src/ScriptEngine/Machine/Core.cs b/src/ScriptEngine/Machine/Core.cs index 03d3edcbe..a80ebf919 100644 --- a/src/ScriptEngine/Machine/Core.cs +++ b/src/ScriptEngine/Machine/Core.cs @@ -1,329 +1,184 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine -{ - [Serializable] - public enum OperationCode - { - Nop, - PushVar, - PushConst, - PushLoc, - PushRef, - LoadVar, - LoadLoc, - AssignRef, - Add, - Sub, - Mul, - Div, - Mod, - Neg, - Equals, - Less, - Greater, - LessOrEqual, - GreaterOrEqual, - NotEqual, - Not, - And, - Or, - CallFunc, - CallProc, - ArgNum, - PushDefaultArg, - ResolveProp, - ResolveMethodProc, - ResolveMethodFunc, - Jmp, - JmpFalse, - PushIndexed, - Return, - JmpCounter, - Inc, - NewInstance, - PushIterator, - IteratorNext, - StopIterator, - BeginTry, - EndTry, - RaiseException, - LineNum, - MakeRawValue, - MakeBool, - PushTmp, - PopTmp, - Execute, - AddHandler, - RemoveHandler, - ExitTry, - - // built-in functions - Eval, - Bool, - Number, - Str, - Date, - Type, - ValType, - StrLen, - TrimL, - TrimR, - TrimLR, - Left, - Right, - Mid, - StrPos, - UCase, - LCase, - TCase, - Chr, - ChrCode, - EmptyStr, - StrReplace, - StrGetLine, - StrLineCount, - StrEntryCount, - Year, - Month, - Day, - Hour, - Minute, - Second, - BegOfYear, - BegOfMonth, - BegOfDay, - BegOfHour, - BegOfMinute, - BegOfQuarter, - EndOfYear, - EndOfMonth, - EndOfDay, - EndOfHour, - EndOfMinute, - EndOfQuarter, - WeekOfYear, - DayOfYear, - DayOfWeek, - AddMonth, - CurrentDate, - Integer, - Round, - Log, - Log10, - Sin, - Cos, - Tan, - ASin, - ACos, - ATan, - Exp, - Pow, - Sqrt, - Min, - Max, - Format, - ExceptionInfo, - ExceptionDescr, - ModuleInfo - } - - [Serializable] - public struct Command - { - public OperationCode Code; - public int Argument; - - public override string ToString() - { - return Enum.GetName(typeof(OperationCode), Code) + ":" + Argument.ToString(); - } - } - - [Serializable] - public enum DataType - { - Undefined, - String, - Number, - Date, - Boolean, - Type, - Object, - NotAValidValue, // default argument value - Enumeration, - GenericValue - } - - [Serializable] - public struct ConstDefinition : IEquatable - { - public DataType Type; - public string Presentation; - - public override string ToString() - { - return Enum.GetName(typeof(DataType), Type) + ":" + Presentation; - } - - public bool Equals(ConstDefinition other) - { - return Type == other.Type && string.Equals(Presentation, other.Presentation, StringComparison.Ordinal); - } - - } - - [Serializable] - public struct MethodInfo - { - public string Name; - public string Alias; - public bool IsFunction; - public bool IsExport; - [NonSerialized] - public bool IsDeprecated; - [NonSerialized] - public bool ThrowOnUseDeprecated; - public ParameterDefinition[] Params; - public AnnotationDefinition[] Annotations; - - public int ArgCount - { - get - { - return Params != null ? Params.Length : 0; - } - } - - public int AnnotationsCount => Annotations?.Length ?? 0; - - } - - [Serializable] - public struct ParameterDefinition - { - public string Name; - public bool IsByValue; - public bool HasDefaultValue; - public int DefaultValueIndex; - public AnnotationDefinition[] Annotations; - - public int AnnotationsCount => Annotations?.Length ?? 0; - - public const int UNDEFINED_VALUE_INDEX = -1; - - public bool IsDefaultValueDefined() - { - return HasDefaultValue && DefaultValueIndex != UNDEFINED_VALUE_INDEX; - } - } - - [Serializable] - public struct AnnotationDefinition - { - public string Name; - public AnnotationParameter[] Parameters; - - public int ParamCount => Parameters?.Length ?? 0; - } - - [Serializable] - public struct AnnotationParameter - { - public string Name; - public int ValueIndex; - - [NonSerialized] - public IValue RuntimeValue; - - public const int UNDEFINED_VALUE_INDEX = -1; - - public override string ToString() - { - if (string.IsNullOrEmpty(Name)) - { - return string.Format("[{0}]", ValueIndex); - } - if (ValueIndex == UNDEFINED_VALUE_INDEX) - { - return Name; - } - return String.Format("{0}=[{1}]", Name, ValueIndex); - } - } - - public struct TypeDescriptor : IEquatable - { - public int ID; - public string Name; - - public override string ToString() - { - return Name; - } - - public static TypeDescriptor FromDataType(DataType srcType) - { - System.Diagnostics.Debug.Assert( - srcType == DataType.Boolean - || srcType == DataType.Date - || srcType == DataType.Number - || srcType == DataType.String - || srcType == DataType.Undefined - || srcType == DataType.Type); - - return TypeManager.GetTypeById((int)srcType); - } - - public bool Equals(TypeDescriptor other) - { - return other.ID == this.ID; - } - } - - [Serializable] - public struct SymbolBinding - { - public int CodeIndex; - public int ContextIndex; - } - - public enum SymbolType - { - Variable, - ContextProperty - } - - [Serializable] - public struct VariableInfo - { - public int Index; - public string Identifier; - public string Alias; - public SymbolType Type; - - public bool CanGet; - public bool CanSet; - - public AnnotationDefinition[] Annotations; - - public int AnnotationsCount => Annotations?.Length ?? 0; - - public override string ToString() - { - return $"{Index}:{Identifier}"; - } - } - - struct VariableBinding - { - public SymbolType type; - public SymbolBinding binding; - } -} +/*---------------------------------------------------------- +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 ScriptEngine.Machine +{ + [Serializable] + public enum OperationCode + { + Nop, + PushVar, + PushConst, + PushInt, + PushBool, + PushUndef, + PushNull, + PushLoc, + PushRef, + LoadVar, + LoadLoc, + AssignRef, + Add, + Sub, + Mul, + Div, + Mod, + Neg, + Equals, + Less, + Greater, + LessOrEqual, + GreaterOrEqual, + NotEqual, + Not, + And, + Or, + CallFunc, + CallProc, + ArgNum, + PushDefaultArg, + ResolveProp, + ResolveMethodProc, + ResolveMethodFunc, + Jmp, + JmpFalse, + PushIndexed, + Return, + JmpCounter, + Inc, + NewInstance, + NewFunc, + PushIterator, + IteratorNext, + StopIterator, + BeginTry, + EndTry, + RaiseException, + LineNum, + MakeRawValue, + MakeBool, + PushTmp, + PopTmp, + Execute, + AddHandler, + RemoveHandler, + ExitTry, + + // built-in functions + Eval, + Bool, + Number, + Str, + Date, + Type, + ValType, + StrLen, + TrimL, + TrimR, + TrimLR, + Left, + Right, + Mid, + StrPos, + UCase, + LCase, + TCase, + Chr, + ChrCode, + EmptyStr, + StrReplace, + StrGetLine, + StrLineCount, + StrEntryCount, + Year, + Month, + Day, + Hour, + Minute, + Second, + BegOfWeek, + BegOfYear, + BegOfMonth, + BegOfDay, + BegOfHour, + BegOfMinute, + BegOfQuarter, + EndOfWeek, + EndOfYear, + EndOfMonth, + EndOfDay, + EndOfHour, + EndOfMinute, + EndOfQuarter, + WeekOfYear, + DayOfYear, + DayOfWeek, + AddMonth, + CurrentDate, + Integer, + Round, + Log, + Log10, + Sin, + Cos, + Tan, + ASin, + ACos, + ATan, + Exp, + Pow, + Sqrt, + Min, + Max, + Format, + ExceptionInfo, + ExceptionDescr, + ModuleInfo + } + + [Serializable] + public struct Command + { + public OperationCode Code; + public int Argument; + + public override string ToString() + { + return Enum.GetName(typeof(OperationCode), Code) + ":" + Argument.ToString(); + } + } + + [Serializable] + public enum DataType + { + Undefined, + String, + Number, + Date, + Boolean, + Null + } + + [Serializable] + public struct ConstDefinition : IEquatable + { + public DataType Type; + public string Presentation; + + public override string ToString() + { + return Enum.GetName(typeof(DataType), Type) + ":" + Presentation; + } + + public bool Equals(ConstDefinition other) + { + return Type == other.Type && string.Equals(Presentation, other.Presentation, StringComparison.Ordinal); + } + + } +} diff --git a/src/ScriptEngine/Machine/DebugEventType.cs b/src/ScriptEngine/Machine/DebugEventType.cs deleted file mode 100644 index 7ca988403..000000000 --- a/src/ScriptEngine/Machine/DebugEventType.cs +++ /dev/null @@ -1,16 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine -{ - public enum DebugEventType - { - BeginExecution, - ProcessExited, - Continue - } -} diff --git a/src/ScriptEngine/Machine/Debugger/DisabledDebugSession.cs b/src/ScriptEngine/Machine/Debugger/DisabledDebugSession.cs new file mode 100644 index 000000000..89370e8dc --- /dev/null +++ b/src/ScriptEngine/Machine/Debugger/DisabledDebugSession.cs @@ -0,0 +1,61 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Machine.Debugger +{ + public sealed class DisabledDebugSession : IDebugSession + { + public IBreakpointManager BreakpointManager { get; } = new NoOpBreakpointManager(); + public IThreadEventsListener ThreadManager { get; } = new NoOpThreadEventsListener(); + public bool IsActive => false; + + public void WaitReadyToRun() + { + } + public void Dispose() + { + } + + private class NoOpBreakpointManager : IBreakpointManager + { + public void SetExceptionBreakpoints((string Id, string Condition)[] filters) + { + } + + public void SetBreakpoints(string module, (int Line, string Condition)[] breakpoints) + { + } + + public bool StopOnAnyException(string message) => false; + + public bool StopOnUncaughtException(string message) => false; + + public bool FindBreakpoint(string module, int line) => false; + + public string GetCondition(string module, int line) => ""; + + public void Clear() + { + } + } + + private class NoOpThreadEventsListener : IThreadEventsListener + { + public void ThreadStarted(int threadId, MachineInstance machine) + { + } + + public void ThreadStopped(int threadId, MachineStopReason reason, string errorMessage) + { + } + + public void ThreadExited(int threadId) + { + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Debugger/DisabledDebugger.cs b/src/ScriptEngine/Machine/Debugger/DisabledDebugger.cs new file mode 100644 index 000000000..bea5f62f0 --- /dev/null +++ b/src/ScriptEngine/Machine/Debugger/DisabledDebugger.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Machine.Debugger +{ + /// + /// Отладчик, который ничего не делает. Заглушка для выключенного отладчика. + /// + public class DisabledDebugger : IDebugger + { + public bool IsEnabled => false; + + public void Start() + { + } + + public IDebugSession GetSession() => new DisabledDebugSession(); + + public void NotifyProcessExit(int exitCode) + { + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Debugger/IBreakpointManager.cs b/src/ScriptEngine/Machine/Debugger/IBreakpointManager.cs new file mode 100644 index 000000000..0bdedc9ff --- /dev/null +++ b/src/ScriptEngine/Machine/Debugger/IBreakpointManager.cs @@ -0,0 +1,26 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Machine +{ + public interface IBreakpointManager + { + void SetExceptionBreakpoints((string Id, string Condition)[] filters); + + void SetBreakpoints(string module, (int Line, string Condition)[] breakpoints); + + bool StopOnAnyException(string message); + + bool StopOnUncaughtException(string message); + + bool FindBreakpoint(string module, int line); + + string GetCondition(string module, int line); + + void Clear(); + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Debugger/IDebugController.cs b/src/ScriptEngine/Machine/Debugger/IDebugController.cs new file mode 100644 index 000000000..471ab5c92 --- /dev/null +++ b/src/ScriptEngine/Machine/Debugger/IDebugController.cs @@ -0,0 +1,38 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using ScriptEngine.Machine.Debugger; + +namespace ScriptEngine.Machine +{ + /// + /// Управляет возможностью отладки на уровне всего хост-процесса. + /// + public interface IDebugController : IDisposable + { + /// + /// Начинает слушать входящие подключения со стороны IDE. + /// + void ListenConnections(); + + /// + /// Блокирует поток до момента подключения от IDE. + /// + void WaitForSession(); + + /// + /// Уведомляет IDE о завершении отлаживаемого процесса. + /// + /// код выхода приложения + void NotifyProcessExit(int exitCode); + + IBreakpointManager BreakpointManager { get; } + + IThreadEventsListener ThreadManager { get; } + } +} diff --git a/src/ScriptEngine/Machine/IDebugPresentationAcceptor.cs b/src/ScriptEngine/Machine/Debugger/IDebugPresentationAcceptor.cs similarity index 100% rename from src/ScriptEngine/Machine/IDebugPresentationAcceptor.cs rename to src/ScriptEngine/Machine/Debugger/IDebugPresentationAcceptor.cs diff --git a/src/ScriptEngine/Machine/Debugger/IDebugSession.cs b/src/ScriptEngine/Machine/Debugger/IDebugSession.cs new file mode 100644 index 000000000..a1ffd1197 --- /dev/null +++ b/src/ScriptEngine/Machine/Debugger/IDebugSession.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; + +namespace ScriptEngine.Machine.Debugger +{ + public interface IDebugSession : IDisposable + { + /// + /// Поставщик точек останова для машины. + /// + IBreakpointManager BreakpointManager { get; } + + /// + /// Уведомляет о создании или завершении потоков. + /// + IThreadEventsListener ThreadManager { get; } + + /// + /// Ожидает явной команды Execute от IDE. + /// В процессе запуска IDE устанавливает точки останова и присылает команду Execute, + /// запускающую поток исполнения. К моменту исполнения самой первой строчки кода + /// все точки останова уже заданы и мы можем ожидать их срабатывания. + /// + /// Метод не блокируется, если сессия отладки запущена в режиме attach. + /// Метод блокируется в т.ч. если сессия отладки не начата вообще. + /// + void WaitReadyToRun(); + + /// + /// Указывает, что сессия отладки активна (инициирована со стороны IDE). + /// + bool IsActive { get; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/IDebugValueVisitor.cs b/src/ScriptEngine/Machine/Debugger/IDebugValueVisitor.cs similarity index 96% rename from src/ScriptEngine/Machine/IDebugValueVisitor.cs rename to src/ScriptEngine/Machine/Debugger/IDebugValueVisitor.cs index 358b8d035..ff2daa366 100644 --- a/src/ScriptEngine/Machine/IDebugValueVisitor.cs +++ b/src/ScriptEngine/Machine/Debugger/IDebugValueVisitor.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Collections.Generic; +using OneScript.Contexts; namespace ScriptEngine.Machine { diff --git a/src/ScriptEngine/Machine/Debugger/IDebugger.cs b/src/ScriptEngine/Machine/Debugger/IDebugger.cs new file mode 100644 index 000000000..685c41597 --- /dev/null +++ b/src/ScriptEngine/Machine/Debugger/IDebugger.cs @@ -0,0 +1,37 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Machine.Debugger +{ + /// + /// Основной фасад функциональности отладки. + /// Управляет стартом и остановкой отладки. + /// + public interface IDebugger + { + /// + /// Возвращает true, если отладка в процессе возможна. + /// + bool IsEnabled { get; } + + /// + /// Запускает отладчик. С этого момента к отладчику можно подключиться. + /// + void Start(); + + /// + /// Получает текущую сессию отладки. + /// + IDebugSession GetSession(); + + /// + /// Уведомляет IDE о завершении отлаживаемого процесса. + /// + /// код выхода приложения + void NotifyProcessExit(int exitCode); + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Debugger/IThreadEventsListener.cs b/src/ScriptEngine/Machine/Debugger/IThreadEventsListener.cs new file mode 100644 index 000000000..fe34468a9 --- /dev/null +++ b/src/ScriptEngine/Machine/Debugger/IThreadEventsListener.cs @@ -0,0 +1,19 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Machine.Debugger +{ + /// + /// Менеджер состояния и количества потоков для отладчика + /// + public interface IThreadEventsListener + { + void ThreadStarted(int threadId, MachineInstance machine); + void ThreadStopped(int threadId, MachineStopReason reason, string errorMessage); + void ThreadExited(int threadId); + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/DefaultTypeManager.cs b/src/ScriptEngine/Machine/DefaultTypeManager.cs new file mode 100644 index 000000000..db89f2ba5 --- /dev/null +++ b/src/ScriptEngine/Machine/DefaultTypeManager.cs @@ -0,0 +1,172 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Types; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Types; + +namespace ScriptEngine.Machine +{ + public class DefaultTypeManager : ITypeManager + { + private readonly Dictionary _knownTypesIndexes = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly List _knownTypes = new List(); + private readonly TypeFactoryCache _factoryCache = new TypeFactoryCache(); + + private Type _dynamicFactory; + + public DefaultTypeManager() + { + RegisterTypeInternal(BasicTypes.Undefined); + RegisterTypeInternal(BasicTypes.Boolean); + RegisterTypeInternal(BasicTypes.String); + RegisterTypeInternal(BasicTypes.Date); + RegisterTypeInternal(BasicTypes.Number); + RegisterTypeInternal(BasicTypes.Null); + RegisterTypeInternal(BasicTypes.Type); + + // TODO тут был еще тип Object для конструирования + } + + #region ITypeManager Members + + public TypeDescriptor GetTypeByName(string name) + { + if (_knownTypesIndexes.TryGetValue(name, out var index)) + { + return _knownTypes[index]; + } + var clrType = Type.GetType(name, throwOnError: false, ignoreCase: true); + if (clrType != null) + { + var td = RegisterType(name, default, typeof(COMWrapperContext)); + return td; + } + + throw RuntimeException.TypeIsNotRegistered(name); + } + + public bool TryGetType(Type frameworkType, out TypeDescriptor type) + { + type = _knownTypes.FirstOrDefault(x => x.ImplementingClass == frameworkType); + return type != default; + } + + public bool TryGetType(string name, out TypeDescriptor type) + { + if (_knownTypesIndexes.TryGetValue(name, out var index)) + { + type = _knownTypes[index]; + return true; + } + + type = default; + return false; + } + + public TypeDescriptor RegisterType(string name, string alias, Type implementingClass) + { + if (_knownTypesIndexes.ContainsKey(name)) + { + var td = GetTypeByName(name); + if (td.ImplementingClass != implementingClass) + { + throw new InvalidOperationException($"Name `{name}` is already registered"); + } + + return td; + } + else + { + var typeDesc = new TypeDescriptor(implementingClass, name, alias); + RegisterTypeInternal(typeDesc); + return typeDesc; + } + + } + + public void RegisterType(TypeDescriptor typeDescriptor) + { + if (TryGetType(typeDescriptor.Name, out var knownType)) + { + if (knownType != typeDescriptor) + throw new InvalidOperationException($"Type {typeDescriptor} already registered"); + + return; + } + + RegisterTypeInternal(typeDescriptor); + } + + public ITypeFactory GetFactoryFor(TypeDescriptor type) + { + return _factoryCache.GetFactoryFor(type); + } + + private void RegisterTypeInternal(TypeDescriptor td) + { + var nextListId = _knownTypes.Count; + _knownTypesIndexes.Add(td.Name, nextListId); + if (!string.IsNullOrWhiteSpace(td.Alias) && td.Alias != td.Name) + _knownTypesIndexes[td.Alias] = nextListId; + + _knownTypes.Add(td); + } + + public TypeDescriptor GetTypeByFrameworkType(Type type) + { + return _knownTypes.First(x => x.ImplementingClass == type); + } + + public bool IsKnownType(Type type) + { + return _knownTypes.Any(x => x.ImplementingClass == type); + } + + public bool IsKnownType(string typeName) + { + var nameToUpper = typeName.ToUpperInvariant(); + return _knownTypes.Any(x => x.Name.ToUpperInvariant() == nameToUpper); + } + + public IReadOnlyList RegisteredTypes() + { + return _knownTypes; + } + + public Type NewInstanceHandler + { + get + { + return _dynamicFactory; + } + + set + { + if (value + .GetMethods(BindingFlags.Static | BindingFlags.Public) + .Any(x => x.GetCustomAttributes(false).Any(y => y is ScriptConstructorAttribute))) + { + _dynamicFactory = value; + } + else + { + throw new InvalidOperationException("Class " + value.ToString() + " can't be registered as New handler"); + } + } + } + + #endregion + + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/DefaultValueVisitor.cs b/src/ScriptEngine/Machine/DefaultValueVisitor.cs index 43070716f..b5305335c 100644 --- a/src/ScriptEngine/Machine/DefaultValueVisitor.cs +++ b/src/ScriptEngine/Machine/DefaultValueVisitor.cs @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; +using OneScript.Contexts; namespace ScriptEngine.Machine { diff --git a/src/ScriptEngine/Machine/EvalExecLocalContext.cs b/src/ScriptEngine/Machine/EvalExecLocalContext.cs new file mode 100644 index 000000000..68a35ee7f --- /dev/null +++ b/src/ScriptEngine/Machine/EvalExecLocalContext.cs @@ -0,0 +1,34 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Machine +{ + internal class EvalExecLocalContext : ContextIValueImpl, IAttachableContext + { + private readonly IVariable[] _locals; + + public EvalExecLocalContext(IVariable[] locals) + { + _locals = locals; + } + + public IVariable GetVariable(int index) => _locals[index]; + + public BslMethodInfo GetMethod(int index) + { + throw new ArgumentOutOfRangeException(); + } + + int IAttachableContext.VariablesCount => _locals.Length; + + int IAttachableContext.MethodsCount => 0; + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/ExceptionInfoFactory.cs b/src/ScriptEngine/Machine/ExceptionInfoFactory.cs new file mode 100644 index 000000000..bcbd04ed3 --- /dev/null +++ b/src/ScriptEngine/Machine/ExceptionInfoFactory.cs @@ -0,0 +1,65 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Values; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Machine +{ + /// + /// Уровень косвенности между нативным рантаймом и ExceptionInfoContext, который тот не видит. + /// + public class ExceptionInfoFactory : IExceptionInfoFactory + { + private readonly IBslProcessFactory _processFactory; + + public ExceptionInfoFactory(IBslProcessFactory processFactory) + { + _processFactory = processFactory; + } + + public BslObjectValue GetExceptionInfo(Exception exception) + { + if (exception == null) + { + return ExceptionInfoContext.EmptyExceptionInfo(); + } + + if (exception is ScriptException script) + return new ExceptionInfoContext(script); + + return new ExceptionInfoContext(new ExternalSystemException(exception)); + } + + public string GetExceptionDescription(IRuntimeContextInstance exceptionInfo) + { + if (exceptionInfo == null) + { + return ""; + } + + var info = (ExceptionInfoContext)exceptionInfo; + return info.MessageWithoutCodeFragment; + } + + public Exception Raise(object raiseValue) + { + return raiseValue switch + { + ExceptionInfoContext { IsErrorTemplate: true } excInfo => + new ParametrizedRuntimeException(excInfo.Description, excInfo.Parameters, excInfo.InnerException), + UserScriptContextInstance userContext => new RuntimeException(userContext.ToString(_processFactory.NewProcess())), + _ => new RuntimeException(raiseValue.ToString()) + }; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/ExceptionJumpInfo.cs b/src/ScriptEngine/Machine/ExceptionJumpInfo.cs new file mode 100644 index 000000000..e3f721a1e --- /dev/null +++ b/src/ScriptEngine/Machine/ExceptionJumpInfo.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Machine +{ + internal struct ExceptionJumpInfo + { + public int HandlerAddress; + public ExecutionFrame HandlerFrame; + public int StackSize; + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/ExecutionContext.cs b/src/ScriptEngine/Machine/ExecutionContext.cs new file mode 100644 index 000000000..ca155373f --- /dev/null +++ b/src/ScriptEngine/Machine/ExecutionContext.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Types; + +namespace ScriptEngine.Machine +{ + public class ExecutionContext + { + public ExecutionContext( + ITypeManager typeManager, + IRuntimeEnvironment globalNamespace, + IGlobalsManager globalInstances, + IServiceContainer services) + { + TypeManager = typeManager; + GlobalNamespace = globalNamespace; + GlobalInstances = globalInstances; + Services = services; + } + + public ITypeManager TypeManager { get; } + + public IRuntimeEnvironment GlobalNamespace { get; } + + public IGlobalsManager GlobalInstances { get; } + + public IServiceContainer Services { get; set; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/ExecutionFrame.cs b/src/ScriptEngine/Machine/ExecutionFrame.cs index 1f96046a3..7949493e7 100644 --- a/src/ScriptEngine/Machine/ExecutionFrame.cs +++ b/src/ScriptEngine/Machine/ExecutionFrame.cs @@ -6,29 +6,31 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Language; namespace ScriptEngine.Machine { - class ExecutionFrame + internal class ExecutionFrame { public IVariable[] Locals; public int InstructionPointer; public int LineNumber; public bool DiscardReturnValue; public string MethodName; - public RuntimeException LastException; - public LoadedModule Module; + public ScriptException LastException; + public StackRuntimeModule Module; public bool IsReentrantCall; - public Stack LocalFrameStack = new Stack(); + public readonly Stack LocalFrameStack = new Stack(); - - public Scope ModuleScope { get; set; } - public int ModuleLoadIndex { get; set; } + public IAttachableContext ThisScope { get; set; } + + public IReadOnlyList Scopes { get; set; } public override string ToString() { - return $"{MethodName}: {LineNumber} ({Module.ModuleInfo.ModuleName})"; + return $"{MethodName}: {LineNumber} ({Module.Source.Name})"; } } diff --git a/src/ScriptEngine/Machine/ExternalContextData.cs b/src/ScriptEngine/Machine/ExternalContextData.cs index ec0de12ef..56339102d 100644 --- a/src/ScriptEngine/Machine/ExternalContextData.cs +++ b/src/ScriptEngine/Machine/ExternalContextData.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; +using OneScript.Commons; namespace ScriptEngine.Machine { diff --git a/src/ScriptEngine/Machine/ExternalSystemException.cs b/src/ScriptEngine/Machine/ExternalSystemException.cs index 3c4e806bb..02a715d78 100644 --- a/src/ScriptEngine/Machine/ExternalSystemException.cs +++ b/src/ScriptEngine/Machine/ExternalSystemException.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; +using OneScript.Language; namespace ScriptEngine.Machine { - public class ExternalSystemException : RuntimeException + public class ExternalSystemException : ScriptException { public ExternalSystemException(Exception reason) - : base(string.Format("Внешнее исключение ({0}): {1}", reason.GetType().FullName, reason.Message), reason) + : base(new ErrorPositionInfo(), $"Внешнее исключение ({reason.GetType().FullName}): {reason.Message}", reason) { } } diff --git a/src/ScriptEngine/Machine/GenericIValueComparer.cs b/src/ScriptEngine/Machine/GenericIValueComparer.cs index 40c936fc6..e8c53da8a 100644 --- a/src/ScriptEngine/Machine/GenericIValueComparer.cs +++ b/src/ScriptEngine/Machine/GenericIValueComparer.cs @@ -5,13 +5,36 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Collections.Generic; +using OneScript.Values; using ScriptEngine.Machine.Contexts; +using OneScript.Execution; +using OneScript.Types; namespace ScriptEngine.Machine { public class GenericIValueComparer : IEqualityComparer, IComparer { + private readonly IBslProcess _process; + private readonly Func _comparer; + + static private readonly ListorderedTypes = new List + { BasicTypes.Undefined, BasicTypes.Null, BasicTypes.Boolean, + BasicTypes.Number, BasicTypes.String, BasicTypes.Date, BasicTypes.Type }; + + private const int INDEX_OF_TYPE = 6; + + public GenericIValueComparer() + { + _comparer = CompareAsStrings; + } + + public GenericIValueComparer(IBslProcess proc) + { + _process = proc; + _comparer = CompareByPresentations; + } public bool Equals(IValue x, IValue y) { @@ -21,27 +44,72 @@ public bool Equals(IValue x, IValue y) public int GetHashCode(IValue obj) { object CLR_obj; - if (obj.DataType == DataType.Undefined) + if (obj is BslUndefinedValue) + return obj.GetHashCode(); + + if (obj is BslNullValue) return obj.GetHashCode(); try { - CLR_obj = ContextValuesMarshaller.ConvertToCLRObject(obj); + CLR_obj = ContextValuesMarshaller.ConvertToClrObject(obj); } catch (ValueMarshallingException) { CLR_obj = obj; } + if (CLR_obj == null) + return 0; + return CLR_obj.GetHashCode(); } + private int CompareAsStrings(IValue x, IValue y) + { + return x.ToString().CompareTo(y.ToString()); + } + + private int CompareByPresentations(IValue x, IValue y) + { + return ((BslValue)x).ToString(_process).CompareTo(((BslValue)y).ToString(_process)); + } + + /// + /// Сравнение переменных разных типов. Правила сравнения соответствуют 1С v8.3.27: + /// Переменные типа "Тип" следуют за всеми прочими; + /// Другие примитивные типы предшествуют объектным типам; + /// Между собой примитивные типы упорядочиваются в последовательности, указанной в orderedTypes; + /// Объектные типы упорядочиваются по строковому представлению. + /// + private int CompareByTypes(IValue x, IValue y) + { + var ix = orderedTypes.IndexOf(x.SystemType); + var iy = orderedTypes.IndexOf(y.SystemType); + + if (ix >= 0) + if (iy >= 0) + return ix - iy; + else + return ix == INDEX_OF_TYPE ? 1 : -1; + else if (iy >= 0) + return iy == INDEX_OF_TYPE ? -1 : 1; + + return _comparer(x,y); + } + public int Compare(IValue x, IValue y) { - if (x.SystemType.ID == y.SystemType.ID) + if (ReferenceEquals(x, y)) + return 0; + + if (x.SystemType != y.SystemType ) + return CompareByTypes(x,y); + + if (x is IBslComparable) return x.CompareTo(y); else - return x.AsString().CompareTo(y.AsString()); + return _comparer(x,y); } } } diff --git a/src/ScriptEngine/Machine/GlobalInstancesManager.cs b/src/ScriptEngine/Machine/GlobalInstancesManager.cs index 3f92ae6ec..94a8d607e 100644 --- a/src/ScriptEngine/Machine/GlobalInstancesManager.cs +++ b/src/ScriptEngine/Machine/GlobalInstancesManager.cs @@ -4,18 +4,19 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine.Contexts; + using System; +using System.Collections; using System.Collections.Generic; using System.Linq; namespace ScriptEngine.Machine { - public static class GlobalsManager + public class GlobalInstancesManager : IGlobalsManager { - static readonly Dictionary _instances = new Dictionary(); + private readonly Dictionary _instances = new Dictionary(); - internal static void Reset() + public void Dispose() { foreach (var disposable in _instances .Select(x=>x.Value) @@ -27,34 +28,34 @@ internal static void Reset() _instances.Clear(); } - public static void RegisterInstance(object instance) + public void RegisterInstance(object instance) { _instances.Add(instance.GetType(), instance); } - public static void RegisterInstance(Type type, object instance) + public void RegisterInstance(Type type, object instance) { _instances.Add(type, instance); } - public static T GetGlobalContext() + public object GetInstance(Type type) { - return InternalGetInstance(); + return _instances[type]; } - public static T GetEnum() + public T GetInstance() { - return InternalGetInstance(); + return (T)_instances[typeof(T)]; } - public static EnumerationContext GetSimpleEnum(Type type) + public IEnumerator> GetEnumerator() { - return (EnumerationContext)_instances[type]; + return _instances.GetEnumerator(); } - private static T InternalGetInstance() + IEnumerator IEnumerable.GetEnumerator() { - return (T)_instances[typeof(T)]; + return GetEnumerator(); } } } diff --git a/src/ScriptEngine/Machine/IAttachableContext.cs b/src/ScriptEngine/Machine/IAttachableContext.cs deleted file mode 100644 index a6ac4f16f..000000000 --- a/src/ScriptEngine/Machine/IAttachableContext.cs +++ /dev/null @@ -1,21 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine -{ - public interface IAttachableContext : IRuntimeContextInstance - { - void OnAttach(MachineInstance machine, - out IVariable[] variables, - out MethodInfo[] methods); - } - - internal interface IRunnable : IAttachableContext - { - LoadedModule Module { get; } - } -} diff --git a/src/ScriptEngine/Machine/IDebugController.cs b/src/ScriptEngine/Machine/IDebugController.cs deleted file mode 100644 index fae8a2812..000000000 --- a/src/ScriptEngine/Machine/IDebugController.cs +++ /dev/null @@ -1,31 +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 ScriptEngine.Machine -{ - public interface IDebugController : IDisposable - { - void Init(); - void Wait(); - void NotifyProcessExit(int exitCode); - - void AttachToThread(); - - void DetachFromThread(); - - IBreakpointManager BreakpointManager { get; } - } - - public interface IBreakpointManager - { - void SetLineStops(string module, int[] lines); - - bool Find(string module, int line); - } -} diff --git a/src/ScriptEngine/Machine/IEventProcessor.cs b/src/ScriptEngine/Machine/IEventProcessor.cs index c0d6923d9..9c667fd79 100644 --- a/src/ScriptEngine/Machine/IEventProcessor.cs +++ b/src/ScriptEngine/Machine/IEventProcessor.cs @@ -5,7 +5,8 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using ScriptEngine.Machine.Contexts; +using OneScript.Contexts; +using OneScript.Execution; namespace ScriptEngine.Machine { @@ -23,6 +24,6 @@ void RemoveHandler( IRuntimeContextInstance handlerTarget, string handlerMethod); - void HandleEvent(IRuntimeContextInstance eventSource, string eventName, IValue[] eventArgs); + void HandleEvent(IRuntimeContextInstance eventSource, string eventName, IValue[] eventArgs, IBslProcess process); } } \ No newline at end of file diff --git a/src/ScriptEngine/Machine/IRuntimeContextInstance.cs b/src/ScriptEngine/Machine/IRuntimeContextInstance.cs deleted file mode 100644 index 31c5d476b..000000000 --- a/src/ScriptEngine/Machine/IRuntimeContextInstance.cs +++ /dev/null @@ -1,79 +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.Collections.Generic; - -namespace ScriptEngine.Machine -{ - public interface IRuntimeContextInstance - { - bool IsIndexed { get; } - bool DynamicMethodSignatures { get; } - - IValue GetIndexedValue(IValue index); - void SetIndexedValue(IValue index, IValue val); - - int FindProperty(string name); - bool IsPropReadable(int propNum); - bool IsPropWritable(int propNum); - IValue GetPropValue(int propNum); - void SetPropValue(int propNum, IValue newVal); - - int GetPropCount(); - string GetPropName(int propNum); - - int FindMethod(string name); - int GetMethodsCount(); - MethodInfo GetMethodInfo(int methodNumber); - void CallAsProcedure(int methodNumber, IValue[] arguments); - void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue); - - } - - public static class RCIHelperExtensions - { - public static IEnumerable GetMethods(this IRuntimeContextInstance context) - { - MethodInfo[] methods = new MethodInfo[context.GetMethodsCount()]; - for (int i = 0; i < methods.Length; i++) - { - methods[i] = context.GetMethodInfo(i); - } - - return methods; - } - - public static IEnumerable GetProperties(this IRuntimeContextInstance context) - { - VariableInfo[] infos = new VariableInfo[context.GetPropCount()]; - for (int i = 0; i < infos.Length; i++) - { - infos[i] = new VariableInfo() - { - Identifier = context.GetPropName(i), - Type = SymbolType.ContextProperty, - Index = i - }; - } - - return infos; - } - - public static IValue GetPropValue(this IRuntimeContextInstance context, string propName) - { - int propNum = context.FindProperty(propName); - - if (propNum == -1) - { - throw RuntimeException.InvalidArgumentValue(propName); - } - - return context.GetPropValue(propNum); - } - } - -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/IValue.cs b/src/ScriptEngine/Machine/IValue.cs deleted file mode 100644 index 25ff00119..000000000 --- a/src/ScriptEngine/Machine/IValue.cs +++ /dev/null @@ -1,25 +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 ScriptEngine.Machine -{ - public interface IValue : IComparable, IEquatable - { - DataType DataType { get; } - TypeDescriptor SystemType { get; } - - decimal AsNumber(); - DateTime AsDate(); - bool AsBoolean(); - string AsString(); - IRuntimeContextInstance AsObject(); - IValue GetRawValue(); - - } - -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/IndexedNameValueCollection.cs b/src/ScriptEngine/Machine/IndexedNameValueCollection.cs deleted file mode 100755 index 9e2428257..000000000 --- a/src/ScriptEngine/Machine/IndexedNameValueCollection.cs +++ /dev/null @@ -1,87 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace ScriptEngine.Machine -{ - /// - /// Универсальный класс для коллекций доступных по имени и номеру. - /// Уже создано много с похожим функционалом по всему проекту. Постепенно можно переводить сюда. - /// - /// - public class IndexedNameValueCollection : IEnumerable - { - private readonly Dictionary _nameIndex = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - private readonly List _values = new List(); - - public void Add(T item, string name) - { - if (_nameIndex.ContainsKey(name)) - throw new InvalidOperationException($"Name {name} already registered"); - - var idx = _values.Count; - _values.Add(item); - _nameIndex[name] = idx; - } - - public T this[int index] - { - get { return _values[index]; } - set { _values[index] = value; } - } - - public T this[string name] - { - get { return this[IndexOf(name)]; } - set { this[IndexOf(name)] = value; } - } - - public void AddName(int index, string name) - { - if (index < 0 || index >= _values.Count) - throw new ArgumentOutOfRangeException(); - - _nameIndex[name] = index; - } - - public int IndexOf(string name) - { - int idx; - if (!_nameIndex.TryGetValue(name, out idx)) - return -1; - - return _nameIndex[name]; - } - - public bool TryGetValue(string name, out T result) - { - int idx; - if(_nameIndex.TryGetValue(name, out idx)) - { - result = _values[idx]; - return true; - } - else - { - result = default(T); - return false; - } - } - - public IEnumerator GetEnumerator() - { - return _values.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/src/ScriptEngine/Machine/Interfaces/InterfaceRegistrationExtensions.cs b/src/ScriptEngine/Machine/Interfaces/InterfaceRegistrationExtensions.cs new file mode 100644 index 000000000..a5ae0336c --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/InterfaceRegistrationExtensions.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language.SyntaxAnalysis; + +namespace ScriptEngine.Machine.Interfaces +{ + public static class InterfaceRegistrationExtensions + { + public static IServiceDefinitions EnablePredefinedIterables(this IServiceDefinitions services) + { + services.RegisterEnumerable(); + services.RegisterEnumerable(); + services.RegisterEnumerable(); + + return services; + } + + public static void AddInterface(this IExecutableModule module, T interfaceData) where T : class + { + module.Interfaces[typeof(T)] = interfaceData; + } + + public static T GetInterface(this IExecutableModule module) where T : class + { + if (!module.Interfaces.TryGetValue(typeof(T), out var value)) + return null; + + return (T)value; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IterableBslInterface.cs b/src/ScriptEngine/Machine/Interfaces/IterableBslInterface.cs new file mode 100644 index 000000000..192d0d462 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IterableBslInterface.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + public class IterableBslInterface + { + public IterableBslInterface(BslScriptMethodInfo getIteratorMethod, BslScriptMethodInfo getCountMethod) + { + GetIteratorMethod = getIteratorMethod; + GetCountMethod = getCountMethod; + } + + public BslScriptMethodInfo GetIteratorMethod { get; } + public BslScriptMethodInfo GetCountMethod { get; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IterableBslInterfaceChecker.cs b/src/ScriptEngine/Machine/Interfaces/IterableBslInterfaceChecker.cs new file mode 100644 index 000000000..e649c3a68 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IterableBslInterfaceChecker.cs @@ -0,0 +1,73 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + internal class IterableBslInterfaceChecker : IPredefinedInterfaceChecker + { + public static readonly BilingualString IterableAnnotation = new BilingualString("Обходимое", "Iterable"); + + public IEnumerable GetRegistrations() + { + return new[] + { + PredefinedInterfaceRegistration.OnMethod(IterableAnnotation, UserScriptContextInstance.OnInstanceCreationTerms), + PredefinedInterfaceRegistration.OnModule(IterableAnnotation) + }; + } + + public void Validate(IExecutableModule module) + { + var error = new BilingualString( + "Модуль объекта Обходимое должен содержать функцию " + UserIterableContextInstance.GetIteratorTerms.Russian + "()", + "Module of Iterable must contain function " + UserIterableContextInstance.GetIteratorTerms.English + "()"); + + BslScriptMethodInfo getIteratorMethod; + + try + { + getIteratorMethod = (BslScriptMethodInfo)module.Methods.Single(method => + UserIterableContextInstance.GetIteratorTerms.HasName(method.Name)); + } + catch (InvalidOperationException e) + { + throw new InterfaceCheckException(error, e); + } + + if (!getIteratorMethod.IsFunction() || getIteratorMethod.GetBslParameters().Length != 0) + { + throw new InterfaceCheckException(error); + } + + BslScriptMethodInfo getCountMethod = (BslScriptMethodInfo)module.Methods.FirstOrDefault(method => + UserIterableContextInstance.GetCountTerms.HasName(method.Name)); + + if (getCountMethod != null) + { + var countError = new BilingualString( + "Метод " + UserIterableContextInstance.GetCountTerms.Russian + " должен быть функцией без параметров", + "Method " + UserIterableContextInstance.GetCountTerms.English + "() must be a function without parameters"); + + if (!getCountMethod.IsFunction() || getCountMethod.GetBslParameters().Length != 0) + { + throw new InterfaceCheckException(countError); + } + } + + module.AddInterface(new IterableBslInterface(getIteratorMethod, getCountMethod)); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IterablesModuleAnnotationsHandler.cs b/src/ScriptEngine/Machine/Interfaces/IterablesModuleAnnotationsHandler.cs new file mode 100644 index 000000000..620d9b226 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IterablesModuleAnnotationsHandler.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +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 OneScript.Language; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Localization; + +namespace ScriptEngine.Machine.Interfaces +{ + public class IterablesModuleAnnotationsHandler : SingleWordModuleAnnotationHandler + { + private static HashSet SupportedNames = new HashSet() + { + IterableBslInterfaceChecker.IterableAnnotation, + IteratorBslInterfaceChecker.IterableAnnotation + }; + + public IterablesModuleAnnotationsHandler(IErrorSink errorSink) : base(SupportedNames, errorSink) + { + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IteratorBslInterface.cs b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterface.cs new file mode 100644 index 000000000..e80481ef5 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterface.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 OneScript.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + public class IteratorBslInterface + { + public IteratorBslInterface(BslScriptMethodInfo moveNextMethod, BslScriptMethodInfo getCurrentMethod, BslScriptMethodInfo onDisposeMethod) + { + MoveNextMethod = moveNextMethod; + GetCurrentMethod = getCurrentMethod; + OnDisposeMethod = onDisposeMethod; + } + + public BslScriptMethodInfo MoveNextMethod { get; } + public BslScriptMethodInfo GetCurrentMethod { get; } + public BslScriptMethodInfo OnDisposeMethod { get; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Interfaces/IteratorBslInterfaceChecker.cs b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterfaceChecker.cs new file mode 100644 index 000000000..35c3315e1 --- /dev/null +++ b/src/ScriptEngine/Machine/Interfaces/IteratorBslInterfaceChecker.cs @@ -0,0 +1,102 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Localization; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Machine.Interfaces +{ + internal class IteratorBslInterfaceChecker : IPredefinedInterfaceChecker + { + public static readonly BilingualString IterableAnnotation = new BilingualString("Итератор", "Iterator"); + + private static readonly BilingualString + MoveNextMethodName = new BilingualString("Следующий", "Next"); + + private static readonly BilingualString + GetCurrentMethodName = new BilingualString("ТекущийЭлемент", "CurrentItem"); + + private static readonly BilingualString + DisposeMethodName = new BilingualString("ПриОсвобожденииОбъекта", "OnDispose"); + + public IEnumerable GetRegistrations() + { + return new[] + { + PredefinedInterfaceRegistration.OnMethod(IterableAnnotation, UserScriptContextInstance.OnInstanceCreationTerms), + PredefinedInterfaceRegistration.OnModule(IterableAnnotation) + }; + } + + public void Validate(IExecutableModule module) + { + CheckModule(module); + } + + internal static IteratorBslInterface CheckModule(IExecutableModule module) + { + var moveNextMethod = FindRequiredMethod(module, MoveNextMethodName); + var getCurrentMethod = FindRequiredMethod(module, GetCurrentMethodName); + var onDisposeMethod = FindOptionalMethod(module, DisposeMethodName); + + if (!moveNextMethod.IsFunction() || moveNextMethod.GetBslParameters().Length != 0) + { + throw MissingMethod(MoveNextMethodName); + } + + if (!getCurrentMethod.IsFunction() || getCurrentMethod.GetBslParameters().Length != 0) + { + throw MissingMethod(GetCurrentMethodName); + } + + if (onDisposeMethod != null && onDisposeMethod.GetBslParameters().Length != 0) + { + throw MissingMethod(DisposeMethodName); + } + + var bslInterface = new IteratorBslInterface(moveNextMethod, getCurrentMethod, onDisposeMethod); + module.AddInterface(bslInterface); + + return bslInterface; + } + + private static BslScriptMethodInfo FindRequiredMethod(IExecutableModule module, BilingualString names) + { + try + { + return (BslScriptMethodInfo)module.Methods.Single(m => + names.HasName(m.Name)); + } + catch (InvalidOperationException e) + { + throw MissingMethod(names, e); + } + } + + private static BslScriptMethodInfo FindOptionalMethod(IExecutableModule module,BilingualString names) + { + return (BslScriptMethodInfo)module.Methods.FirstOrDefault(m => + names.HasName(m.Name)); + + } + + private static InterfaceCheckException MissingMethod(BilingualString methodName, Exception parent = null) + { + var error = new BilingualString( + "Обязательный метод "+methodName.Russian+" отсутствует, или не соответствует интерфейсу итератора", + "Required method "+methodName.English+" is missing or doesn't match iterator interface"); + + return new InterfaceCheckException(error, parent); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/JoinedScopes.cs b/src/ScriptEngine/Machine/JoinedScopes.cs new file mode 100644 index 000000000..60fb56c5d --- /dev/null +++ b/src/ScriptEngine/Machine/JoinedScopes.cs @@ -0,0 +1,61 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using OneScript.Contexts; + +namespace ScriptEngine.Machine +{ + /// + /// Оборачивающий контейнер. Хранит внешние области видимости и одну локальную, как единый плоский список. + /// + public class JoinedScopes : IReadOnlyList + { + private readonly IReadOnlyList _outerScopes; + private readonly IAttachableContext _innerScope; + + public JoinedScopes(IReadOnlyList outerScopes, IAttachableContext innerScope) + { + _outerScopes = outerScopes ?? throw new ArgumentNullException(nameof(outerScopes)); + _innerScope = innerScope ?? throw new ArgumentNullException(nameof(innerScope)); + } + + public IEnumerator GetEnumerator() + { + foreach (var scope in _outerScopes) + { + yield return scope; + } + + yield return _innerScope; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public int Count => _outerScopes.Count + 1; + + public IAttachableContext this[int index] + { + get + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException(); + + if (index == _outerScopes.Count) + return _innerScope; + + return _outerScopes[index]; + } + } + } +} + diff --git a/src/ScriptEngine/Machine/LoadedModule.cs b/src/ScriptEngine/Machine/LoadedModule.cs deleted file mode 100644 index 25809c626..000000000 --- a/src/ScriptEngine/Machine/LoadedModule.cs +++ /dev/null @@ -1,84 +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 ScriptEngine.Environment; -using System.Linq; - -namespace ScriptEngine.Machine -{ - public class LoadedModule - { - public LoadedModule(ModuleImage image) - { - Code = image.Code.ToArray(); - EntryMethodIndex = image.EntryMethodIndex; - MethodRefs = image.MethodRefs.ToArray(); - VariableRefs = image.VariableRefs.ToArray(); - Methods = image.Methods.ToArray(); - Constants = new IValue[image.Constants.Count]; - Variables = new VariablesFrame(image.Variables); - ExportedProperies = image.ExportedProperties.ToArray(); - ExportedMethods = image.ExportedMethods.ToArray(); - ModuleInfo = image.ModuleInfo; - LoadAddress = image.LoadAddress; - for (int i = 0; i < image.Constants.Count; i++) - { - var def = image.Constants[i]; - Constants[i] = ValueFactory.Parse(def.Presentation, def.Type); - } - - ResolveAnnotationConstants(); - } - - private void ResolveAnnotationConstants() - { - for (int i = 0; i < Variables.Count; i++) - { - EvaluateAnnotationParametersValues(Variables[i].Annotations); - } - - for (int i = 0; i < Methods.Length; i++) - { - EvaluateAnnotationParametersValues(Methods[i].Signature.Annotations); - for (int j = 0; j < Methods[i].Signature.ArgCount; j++) - { - EvaluateAnnotationParametersValues(Methods[i].Signature.Params[j].Annotations); - } - } - } - - private void EvaluateAnnotationParametersValues(AnnotationDefinition[] annotations) - { - for (int i = 0; i < annotations?.Length; i++) - { - var parameters = annotations[i].Parameters; - for (int j = 0; j < parameters?.Length; j++) - { - var pa = parameters[j]; - if (pa.ValueIndex != AnnotationParameter.UNDEFINED_VALUE_INDEX) - { - annotations[i].Parameters[j].RuntimeValue = Constants[pa.ValueIndex]; - } - } - } - } - - public VariablesFrame Variables { get; } - public int EntryMethodIndex { get; } - public Command[] Code { get; } - public SymbolBinding[] VariableRefs { get; } - public SymbolBinding[] MethodRefs { get; } - public MethodDescriptor[] Methods { get; } - public IValue[] Constants { get; } - public ExportedSymbol[] ExportedProperies { get; } - public ExportedSymbol[] ExportedMethods { get; } - public ModuleInformation ModuleInfo { get; } - public int LoadAddress { get; } - } - - -} diff --git a/src/ScriptEngine/Machine/LruCache.cs b/src/ScriptEngine/Machine/LruCache.cs new file mode 100644 index 000000000..08241ed97 --- /dev/null +++ b/src/ScriptEngine/Machine/LruCache.cs @@ -0,0 +1,72 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; + +namespace ScriptEngine.Machine +{ + public class LruCache + { + private readonly int _capacity; + + private readonly Dictionary> _index = + new Dictionary>(); + + private readonly LinkedList _list = new LinkedList(); + + public LruCache(int capacity) + { + _capacity = capacity; + } + + public bool IsEmpty() => _index.Count == 0; + + public void Clear() + { + _index.Clear(); + _list.Clear(); + } + + public TValue GetOrAdd(TKey key, Func factory) + { + if (_index.TryGetValue(key, out var listNode)) + { + _list.Remove(listNode); + _list.AddFirst(listNode); + return listNode.Value.Value; + } + + if (_index.Count == _capacity) + { + var keyOfOld = _list.Last.Value.Key; + _index.Remove(keyOfOld); + _list.RemoveLast(); + } + + var newItem = _list.AddFirst(CacheItem.Create(key, factory(key))); + _index[key] = newItem; + return newItem.Value.Value; + } + + private class CacheItem + { + public static CacheItem Create(TKey key, TValue value) => + new CacheItem(key, value); + + private CacheItem(TKey key, TValue value) + { + Key = key; + Value = value; + } + + public TKey Key { get; } + + public TValue Value { get; } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs index 0e05de6d3..eb5d73bf1 100644 --- a/src/ScriptEngine/Machine/MachineInstance.cs +++ b/src/ScriptEngine/Machine/MachineInstance.cs @@ -1,2498 +1,2595 @@ -/*---------------------------------------------------------- -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 ScriptEngine.Machine.Contexts; -using System; -using System.Collections.Generic; -using System.Linq; -using OneScript.Language; -using OneScript.Language.LexicalAnalysis; -using ScriptEngine.Compiler; -using ScriptEngine.Environment; -using ScriptEngine.Machine.Values; - -namespace ScriptEngine.Machine -{ - public class MachineInstance - { - private List _scopes; - private Stack _operationStack; - private Stack _callStack; - private ExecutionFrame _currentFrame; - private Action[] _commands; - private Stack _exceptionsStack; - - private LoadedModule _module; - private ICodeStatCollector _codeStatCollector; - private MachineStopManager _stopManager; - - // для отладчика. - // актуален в момент останова машины - private IList _fullCallstackCache; - - internal MachineInstance() - { - InitCommands(); - Reset(); - } - - public event EventHandler MachineStopped; - - private struct ExceptionJumpInfo - { - public int handlerAddress; - public ExecutionFrame handlerFrame; - public int stackSize; - } - - public void AttachContext(IAttachableContext context) - { - IVariable[] vars; - MethodInfo[] methods; - context.OnAttach(this, out vars, out methods); - var scope = new Scope() - { - Variables = vars, - Methods = methods, - Instance = context - }; - - _scopes.Add(scope); - } - - private Scope CreateModuleScope(IAttachableContext context) - { - IVariable[] vars; - MethodInfo[] methods; - context.OnAttach(this, out vars, out methods); - var scope = new Scope() - { - Variables = vars, - Methods = methods, - Instance = context - }; - return scope; - } - - public void ContextsAttached() - { - // module scope - _scopes.Add(default(Scope)); - } - - internal void ExecuteModuleBody(IRunnable sdo) - { - var module = sdo.Module; - if (module.EntryMethodIndex >= 0) - { - var entryRef = module.MethodRefs[module.EntryMethodIndex]; - PrepareReentrantMethodExecution(sdo, entryRef.CodeIndex); - ExecuteCode(); - if (_callStack.Count > 1) - PopFrame(); - } - } - - public bool IsRunning => _callStack.Count != 0; - - internal IValue ExecuteMethod(IRunnable sdo, int methodIndex, IValue[] arguments) - { - PrepareReentrantMethodExecution(sdo, methodIndex); - var method = _module.Methods[methodIndex]; - var parameters = method.Signature.Params; - for (int i = 0; i < parameters.Length; i++) - { - if (i >= arguments.Length) - _currentFrame.Locals[i] = Variable.Create(GetDefaultArgValue(methodIndex, i), method.Variables[i]); - else if (arguments[i] is IVariable) - { - if (parameters[i].IsByValue) - { - var value = ((IVariable)arguments[i]).Value; - _currentFrame.Locals[i] = Variable.Create(value, method.Variables[i]); - } - else - { - // TODO: Alias ? - _currentFrame.Locals[i] = - Variable.CreateReference((IVariable)arguments[i], method.Variables[i].Identifier); - } - } - else if (arguments[i] == null || arguments[i].DataType == DataType.NotAValidValue) - _currentFrame.Locals[i] = Variable.Create(GetDefaultArgValue(methodIndex, i), method.Variables[i]); - else - _currentFrame.Locals[i] = Variable.Create(arguments[i], method.Variables[i]); - } - ExecuteCode(); - - IValue methodResult = null; - if (_module.Methods[methodIndex].Signature.IsFunction) - { - methodResult = _operationStack.Pop(); - } - - // Этот Pop связан с методом Return. - // Если идет возврат из вложенного вызова, то Pop делается здесь, а не в Return, - // т.к. мы должны выйти из MainCommandLoop и вернуться в предыдущий цикл машины - // - // P.S. it's fuckin spaghetti ( - if (_callStack.Count > 1) - PopFrame(); - - return methodResult; - } - - #region Debug protocol methods - - public void SetDebugMode(IBreakpointManager breakpointManager) - { - if(_stopManager == null) - _stopManager = new MachineStopManager(this, breakpointManager); - } - - public void StepOver() - { - if (_stopManager == null) - throw new InvalidOperationException("Machine is not in debug mode"); - - _stopManager.StepOver(_currentFrame); - } - - public void StepIn() - { - if (_stopManager == null) - throw new InvalidOperationException("Machine is not in debug mode"); - - _stopManager.StepIn(); - } - - public void StepOut() - { - if (_stopManager == null) - throw new InvalidOperationException("Machine is not in debug mode"); - - _stopManager.StepOut(_currentFrame); - } - - public void PrepareDebugContinuation() - { - if (_stopManager == null) - throw new InvalidOperationException("Machine is not in debug mode"); - - _stopManager.Continue(); - } - - public IValue Evaluate(string expression, bool separate = false) - { - var code = CompileExpressionModule(expression); - - MachineInstance runner; - MachineInstance currentMachine; - if (separate) - { - runner = new MachineInstance(); - runner._scopes = new List(_scopes); - currentMachine = Current; - SetCurrentMachineInstance(runner); - } - else - { - currentMachine = null; - runner = this; - } - - ExecutionFrame frame; - - try - { - frame = new ExecutionFrame(); - frame.MethodName = code.ModuleInfo.ModuleName; - frame.Locals = new IVariable[0]; - frame.InstructionPointer = 0; - frame.Module = code; - - var mlocals = new Scope(); - mlocals.Instance = new UserScriptContextInstance(code); - mlocals.Methods = TopScope.Methods; - mlocals.Variables = _currentFrame.Locals; - runner._scopes.Add(mlocals); - frame.ModuleScope = mlocals; - frame.ModuleLoadIndex = runner._scopes.Count - 1; - } - finally - { - if (separate) - { - SetCurrentMachineInstance(currentMachine); - } - } - - try - { - runner.PushFrame(frame); - runner.MainCommandLoop(); - } - finally - { - if (!separate) - { - PopFrame(); - _scopes.RemoveAt(_scopes.Count - 1); - } - else - { - SetCurrentMachineInstance(currentMachine); - } - } - - var result = runner._operationStack.Pop(); - - return result; - - } - - #endregion - - /// - /// Обработчик событий, генерируемых классами прикладной логики. - /// - public IEventProcessor EventProcessor { get; set; } - - private ScriptInformationContext CurrentScript - { - get - { - if (_module.ModuleInfo != null) - return new ScriptInformationContext(_module.ModuleInfo); - else - return null; - } - } - - private IValue GetDefaultArgValue(int methodIndex, int paramIndex) - { - var meth = _module.Methods[methodIndex].Signature; - var param = meth.Params[paramIndex]; - if (!param.IsDefaultValueDefined()) - { - return ValueFactory.Create(); - } - - return _module.Constants[param.DefaultValueIndex]; - } - - private void SetModule(LoadedModule module) - { - _module = module; - } - - internal void Cleanup() - { - Reset(); - } - - private void PushFrame(ExecutionFrame frame) - { - _callStack.Push(frame); - SetFrame(frame); - } - - private void PopFrame() - { - _callStack.Pop(); - SetFrame(_callStack.Peek()); - } - - private void SetFrame(ExecutionFrame frame) - { - SetModule(frame.Module); - _scopes[frame.ModuleLoadIndex] = frame.ModuleScope; - _currentFrame = frame; - } - - private Scope TopScope - { - get - { - if (_scopes.Count > 0) - { - return _scopes[_scopes.Count - 1]; - } - else - { - throw new InvalidOperationException("Nothing is attached"); - } - } - } - - private void Reset() - { - _scopes = new List(); - _operationStack = new Stack(); - _callStack = new Stack(); - _exceptionsStack = new Stack(); - _module = null; - _currentFrame = null; - } - - private void PrepareReentrantMethodExecution(IRunnable sdo, int methodIndex) - { - var module = sdo.Module; - var methDescr = module.Methods[methodIndex]; - var frame = CreateNewFrame(); - frame.MethodName = methDescr.Signature.Name; - frame.Locals = new IVariable[methDescr.Variables.Count]; - frame.Module = module; - frame.ModuleScope = CreateModuleScope(sdo); - frame.ModuleLoadIndex = module.LoadAddress; - frame.IsReentrantCall = true; - for (int i = 0; i < frame.Locals.Length; i++) - { - frame.Locals[i] = Variable.Create(ValueFactory.Create(), methDescr.Variables[i]); - } - - frame.InstructionPointer = methDescr.EntryPoint; - PushFrame(frame); - } - - private void PrepareCodeStatisticsData(LoadedModule _module) - { - if (_codeStatCollector == null - || _codeStatCollector.IsPrepared(_module.ModuleInfo.Origin)) - { - return; - } - - foreach (var method in _module.Methods) - { - var instructionPointer = method.EntryPoint; - while (instructionPointer < _module.Code.Length) - { - if (_module.Code[instructionPointer].Code == OperationCode.LineNum) - { - var entry = new CodeStatEntry( - _module.ModuleInfo.Origin, - method.Signature.Name, - _module.Code[instructionPointer].Argument - ); - _codeStatCollector.MarkEntryReached(entry, count: 0); - } - - if (_module.Code[instructionPointer].Code == OperationCode.Return) - { - break; - } - - instructionPointer++; - } - } - _codeStatCollector.MarkPrepared(_module.ModuleInfo.Origin); - } - - private void ExecuteCode() - { - PrepareCodeStatisticsData(_module); - - while (true) - { - try - { - MainCommandLoop(); - break; - } - catch (RuntimeException exc) - { - if(exc.LineNumber == CodePositionInfo.OUT_OF_TEXT) - SetScriptExceptionSource(exc); - - if (_exceptionsStack.Count == 0) - { - throw; - } - - if (exc.CallStackFrames == null) - { - CreateFullCallstack(); - exc.InitCallStackFrames(_fullCallstackCache); - } - - var handler = _exceptionsStack.Pop(); - - // Раскрутка стека вызовов - while (_currentFrame != handler.handlerFrame) - { - if (_currentFrame.IsReentrantCall) - { - _exceptionsStack.Push(handler); - PopFrame(); - throw; - } - - PopFrame(); - } - - _currentFrame.InstructionPointer = handler.handlerAddress; - _currentFrame.LastException = exc; - - // При возникновении исключения посредине выражения - // некому почистить стек операндов. - // Сделаем это - while (_operationStack.Count > handler.stackSize) - _operationStack.Pop(); - - - } - } - } - - public void SetCodeStatisticsCollector(ICodeStatCollector collector) - { - _codeStatCollector = collector; - } - - private CodeStatEntry CurrentCodeEntry() - { - return new CodeStatEntry(CurrentScript?.Source, _currentFrame.MethodName, _currentFrame.LineNumber); - } - - private void CodeStat_LineReached() - { - if (_codeStatCollector == null) - return; - - _codeStatCollector.MarkEntryReached(CurrentCodeEntry()); - } - - private void CodeStat_StopFrameStatistics() - { - _codeStatCollector?.StopWatch(CurrentCodeEntry()); - } - - private void CodeStat_ResumeFrameStatistics() - { - _codeStatCollector?.ResumeWatch(CurrentCodeEntry()); - } - - private void MainCommandLoop() - { - try - { - while (_currentFrame.InstructionPointer >= 0 - && _currentFrame.InstructionPointer < _module.Code.Length) - { - var command = _module.Code[_currentFrame.InstructionPointer]; - _commands[(int)command.Code](command.Argument); - } - } - catch (RuntimeException) - { - throw; - } - catch(ScriptInterruptionException) - { - throw; - } - catch (Exception exc) - { - var excWrapper = new ExternalSystemException(exc); - SetScriptExceptionSource(excWrapper); - throw excWrapper; - } - } - - private void SetScriptExceptionSource(RuntimeException exc) - { - exc.LineNumber = _currentFrame.LineNumber; - if (_module.ModuleInfo != null) - { - exc.ModuleName = _module.ModuleInfo.ModuleName; - exc.Code = _module.ModuleInfo.CodeIndexer?.GetCodeLine(exc.LineNumber) ?? "<исходный код недоступен>"; - } - else - { - exc.ModuleName = "<имя модуля недоступно>"; - exc.Code = "<исходный код недоступен>"; - } - } - - #region Commands - - private void InitCommands() - { - _commands = new Action[] - { - (i)=>{NextInstruction();}, - PushVar, - PushConst, - PushLoc, - PushRef, - LoadVar, - LoadLoc, - AssignRef, - Add, - Sub, - Mul, - Div, - Mod, - Neg, - Equals, - Less, - Greater, - LessOrEqual, - GreaterOrEqual, - NotEqual, - Not, - And, - Or, - CallFunc, - CallProc, - ArgNum, - PushDefaultArg, - ResolveProp, - ResolveMethodProc, - ResolveMethodFunc, - Jmp, - JmpFalse, - PushIndexed, - Return, - JmpCounter, - Inc, - NewInstance, - PushIterator, - IteratorNext, - StopIterator, - BeginTry, - EndTry, - RaiseException, - LineNum, - MakeRawValue, - MakeBool, - PushTmp, - PopTmp, - Execute, - AddHandler, - RemoveHandler, - ExitTry, - - //built-ins - Eval, - Bool, - Number, - Str, - Date, - Type, - ValType, - StrLen, - TrimL, - TrimR, - TrimLR, - Left, - Right, - Mid, - StrPos, - UCase, - LCase, - TCase, - Chr, - ChrCode, - EmptyStr, - StrReplace, - StrGetLine, - StrLineCount, - StrEntryCount, - Year, - Month, - Day, - Hour, - Minute, - Second, - BegOfYear, - BegOfMonth, - BegOfDay, - BegOfHour, - BegOfMinute, - BegOfQuarter, - EndOfYear, - EndOfMonth, - EndOfDay, - EndOfHour, - EndOfMinute, - EndOfQuarter, - WeekOfYear, - DayOfYear, - this.DayOfWeek, - AddMonth, - CurrentDate, - Integer, - Round, - Log, - Log10, - Sin, - Cos, - Tan, - ASin, - ACos, - ATan, - Exp, - Pow, - Sqrt, - Min, - Max, - Format, - ExceptionInfo, - ExceptionDescr, - ModuleInfo - }; - } - - #region Simple operations - private void PushVar(int arg) - { - var vm = _module.VariableRefs[arg]; - var scope = _scopes[vm.ContextIndex]; - _operationStack.Push(scope.Variables[vm.CodeIndex]); - NextInstruction(); - } - - private void PushConst(int arg) - { - _operationStack.Push(_module.Constants[arg]); - NextInstruction(); - } - - private void PushLoc(int arg) - { - _operationStack.Push(_currentFrame.Locals[arg]); - NextInstruction(); - } - - private void PushRef(int arg) - { - var vm = _module.VariableRefs[arg]; - var scope = _scopes[vm.ContextIndex]; - var reference = Variable.CreateContextPropertyReference(scope.Instance, vm.CodeIndex, "$stackvar"); - _operationStack.Push(reference); - NextInstruction(); - } - - private void LoadVar(int arg) - { - var vm = _module.VariableRefs[arg]; - var scope = _scopes[vm.ContextIndex]; - scope.Variables[vm.CodeIndex].Value = BreakVariableLink(_operationStack.Pop()); - NextInstruction(); - } - - private void LoadLoc(int arg) - { - _currentFrame.Locals[arg].Value = BreakVariableLink(_operationStack.Pop()); - NextInstruction(); - } - - private void AssignRef(int arg) - { - var value = BreakVariableLink(_operationStack.Pop()); - - IVariable reference; - try - { - reference = (IVariable)_operationStack.Pop(); - } - catch (InvalidCastException) - { - throw new WrongStackConditionException(); - } - reference.Value = value; - NextInstruction(); - } - - private void Add(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Add(op1, op2)); - NextInstruction(); - - } - - private void Sub(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Sub(op1, op2)); - NextInstruction(); - } - - private void Mul(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Mul(op1, op2)); - NextInstruction(); - } - - private void Div(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Div(op1, op2)); - NextInstruction(); - } - - private void Mod(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Mod(op1, op2)); - NextInstruction(); - } - - private void Neg(int arg) - { - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Neg(op1)); - NextInstruction(); - } - - private void Equals(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Create(op1.Equals(op2))); - NextInstruction(); - } - - private void Less(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) < 0)); - NextInstruction(); - } - - private void Greater(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) > 0)); - NextInstruction(); - } - - private void LessOrEqual(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) <= 0)); - NextInstruction(); - } - - private void GreaterOrEqual(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) >= 0)); - NextInstruction(); - } - - private void NotEqual(int arg) - { - var op2 = _operationStack.Pop(); - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Create(!op1.Equals(op2))); - NextInstruction(); - } - - private void Not(int arg) - { - var op1 = _operationStack.Pop(); - _operationStack.Push(ValueFactory.Create(!op1.AsBoolean())); - NextInstruction(); - } - - private void And(int arg) - { - var op = _operationStack.Peek().AsBoolean(); - if (op == false) - { - Jmp(arg); - } - else - { - _operationStack.Pop(); - NextInstruction(); - } - - } - - private void Or(int arg) - { - var op = _operationStack.Peek().AsBoolean(); - if (op == true) - { - Jmp(arg); - } - else - { - _operationStack.Pop(); - NextInstruction(); - } - } - - private void CallFunc(int arg) - { - bool needsDiscarding = MethodCallImpl(arg, true); - _currentFrame.DiscardReturnValue = needsDiscarding; - } - - private void CallProc(int arg) - { - bool needsDiscarding = MethodCallImpl(arg, false); - _currentFrame.DiscardReturnValue = needsDiscarding; - } - - private ExecutionFrame CreateNewFrame() - { - var frame = new ExecutionFrame(); - frame.ModuleLoadIndex = _scopes.Count - 1; - return frame; - } - - private bool MethodCallImpl(int arg, bool asFunc) - { - var methodRef = _module.MethodRefs[arg]; - var scope = _scopes[methodRef.ContextIndex]; - var methInfo = scope.Methods[methodRef.CodeIndex]; - - var isLocalCall = scope.Instance == this.TopScope.Instance; - - int argCount = (int)_operationStack.Pop().AsNumber(); - IValue[] argValues = new IValue[argCount]; - - // fact args - for (int i = argCount - 1; i >= 0; i--) - { - var argValue = _operationStack.Pop(); - if (argValue.DataType == DataType.NotAValidValue) - { - if (i < methInfo.Params.Length) - { - if (!methInfo.Params[i].IsDefaultValueDefined() || !isLocalCall) - argValue = null; - else - { - var constId = methInfo.Params[i].DefaultValueIndex; - argValue = _module.Constants[constId]; - } - } - else - { - argValue = null; - } - } - - argValues[i] = argValue; - - } - - bool needsDiscarding; - - if (isLocalCall) - { - var sdo = scope.Instance as ScriptDrivenObject; - System.Diagnostics.Debug.Assert(sdo != null); - - if (sdo.MethodDefinedInScript(methodRef.CodeIndex)) - { - // заранее переведем указатель на адрес возврата. В опкоде Return инкремента нет. - NextInstruction(); - - var methDescr = _module.Methods[sdo.GetMethodDescriptorIndex(methodRef.CodeIndex)]; - var frame = CreateNewFrame(); - frame.Module = _module; - frame.ModuleScope = TopScope; - frame.MethodName = methInfo.Name; - frame.Locals = new IVariable[methDescr.Variables.Count]; - for (int i = 0; i < frame.Locals.Length; i++) - { - if (i < argValues.Length) - { - var paramDef = methInfo.Params[i]; - if (argValues[i] is IVariable) - { - if (paramDef.IsByValue) - { - var value = ((IVariable)argValues[i]).Value; - frame.Locals[i] = Variable.Create(value, methDescr.Variables[i]); - } - else - { - // TODO: Alias ? - frame.Locals[i] = Variable.CreateReference((IVariable) argValues[i], - methDescr.Variables[i].Identifier); - } - } - else if (argValues[i] == null) - { - frame.Locals[i] = Variable.Create(ValueFactory.Create(), methDescr.Variables[i]); - } - else - { - frame.Locals[i] = Variable.Create(argValues[i], methDescr.Variables[i]); - } - - } - else if (i < methInfo.Params.Length) - { - if (!methInfo.Params[i].IsDefaultValueDefined()) - { - frame.Locals[i] = Variable.Create(ValueFactory.Create(), methDescr.Variables[i]); - } - else - { - frame.Locals[i] = Variable.Create(_module.Constants[methInfo.Params[i].DefaultValueIndex], methDescr.Variables[i]); - } - } - else - frame.Locals[i] = Variable.Create(ValueFactory.Create(), methDescr.Variables[i]); - - } - - frame.InstructionPointer = methDescr.EntryPoint; - PushFrame(frame); - if (_stopManager != null) - { - //_stopManager.OnFrameEntered(frame); - } - - needsDiscarding = methInfo.IsFunction && !asFunc; - } - else - { - needsDiscarding = _currentFrame.DiscardReturnValue; - CallContext(scope.Instance, methodRef.CodeIndex, ref methInfo, argValues, asFunc); - } - - } - else - { - // при вызове библиотечного метода (из другого scope) - // статус вызова текущего frames не должен изменяться. - // - needsDiscarding = _currentFrame.DiscardReturnValue; - CallContext(scope.Instance, methodRef.CodeIndex, ref methInfo, argValues, asFunc); - } - - return needsDiscarding; - } - - private void CallContext(IRuntimeContextInstance instance, int index, ref MethodInfo methInfo, IValue[] argValues, bool asFunc) - { - IValue[] realArgs; - if (!instance.DynamicMethodSignatures) - { - realArgs = new IValue[methInfo.ArgCount]; - var skippedArg = ValueFactory.CreateInvalidValueMarker(); - for (int i = 0; i < realArgs.Length; i++) - { - if (i < argValues.Length) - { - realArgs[i] = argValues[i]; - } - else - { - realArgs[i] = skippedArg; - } - } - } - else - { - realArgs = argValues; - } - - if (asFunc) - { - IValue retVal; - instance.CallAsFunction(index, realArgs, out retVal); - _operationStack.Push(retVal); - } - else - { - instance.CallAsProcedure(index, realArgs); - } - NextInstruction(); - } - - private void ArgNum(int arg) - { - _operationStack.Push(ValueFactory.Create(arg)); - NextInstruction(); - } - - private void PushDefaultArg(int arg) - { - _operationStack.Push(ValueFactory.CreateInvalidValueMarker()); - NextInstruction(); - } - - private void ResolveProp(int arg) - { - var objIValue = _operationStack.Pop(); - if (objIValue.DataType != DataType.Object) - { - throw RuntimeException.ValueIsNotObjectException(); - } - - var context = objIValue.AsObject(); - var propName = _module.Constants[arg].AsString(); - var propNum = context.FindProperty(propName); - - var propReference = Variable.CreateContextPropertyReference(context, propNum, "stackvar"); - _operationStack.Push(propReference); - NextInstruction(); - - } - - private void ResolveMethodProc(int arg) - { - IRuntimeContextInstance context; - int methodId; - IValue[] argValues; - PrepareContextCallArguments(arg, out context, out methodId, out argValues); - - context.CallAsProcedure(methodId, argValues); - NextInstruction(); - - } - - private void ResolveMethodFunc(int arg) - { - IRuntimeContextInstance context; - int methodId; - IValue[] argValues; - PrepareContextCallArguments(arg, out context, out methodId, out argValues); - - if (!context.DynamicMethodSignatures && !context.GetMethodInfo(methodId).IsFunction) - { - throw RuntimeException.UseProcAsAFunction(); - } - - IValue retVal; - context.CallAsFunction(methodId, argValues, out retVal); - _operationStack.Push(retVal); - NextInstruction(); - } - - private void PrepareContextCallArguments(int arg, out IRuntimeContextInstance context, out int methodId, out IValue[] argValues) - { - var argCount = (int)_operationStack.Pop().AsNumber(); - IValue[] factArgs = new IValue[argCount]; - for (int i = argCount - 1; i >= 0; i--) - { - factArgs[i] = _operationStack.Pop(); - } - - var objIValue = _operationStack.Pop(); - if (objIValue.DataType != DataType.Object) - { - throw RuntimeException.ValueIsNotObjectException(); - } - - context = objIValue.AsObject(); - var methodName = _module.Constants[arg].AsString(); - methodId = context.FindMethod(methodName); - var methodInfo = context.GetMethodInfo(methodId); - - if(context.DynamicMethodSignatures) - argValues = new IValue[argCount]; - else - argValues = new IValue[methodInfo.Params.Length]; - - bool[] signatureCheck = new bool[argCount]; - - // fact args - for (int i = 0; i < factArgs.Length; i++) - { - var argValue = factArgs[i]; - if (argValue.DataType == DataType.NotAValidValue) - { - signatureCheck[i] = false; - } - else - { - signatureCheck[i] = true; - if (context.DynamicMethodSignatures) - { - argValues[i] = BreakVariableLink(argValue); - } - else if (i < methodInfo.Params.Length) - { - if (methodInfo.Params[i].IsByValue) - argValues[i] = BreakVariableLink(argValue); - else - argValues[i] = argValue; - } - } - - } - factArgs = null; - if (!context.DynamicMethodSignatures) - { - CheckFactArguments(methodInfo, signatureCheck); - - //manage default vals - for (int i = argCount; i < argValues.Length; i++) - { - if (methodInfo.Params[i].HasDefaultValue) - { - argValues[i] = null; - } - } - } - } - - private void CheckFactArguments(MethodInfo methInfo, bool[] argsPassed) - { - if (argsPassed.Length > methInfo.Params.Length) - { - throw RuntimeException.TooManyArgumentsPassed(); - } - - if (methInfo.Params.Skip(argsPassed.Length).Any(param => !param.HasDefaultValue)) - { - throw RuntimeException.TooFewArgumentsPassed(); - } - } - - private void Jmp(int arg) - { - _currentFrame.InstructionPointer = arg; - } - - private void JmpFalse(int arg) - { - var op1 = _operationStack.Pop(); - - if (!op1.AsBoolean()) - { - _currentFrame.InstructionPointer = arg; - } - else - { - NextInstruction(); - } - } - - private void PushIndexed(int arg) - { - var index = BreakVariableLink(_operationStack.Pop()); - var context = _operationStack.Pop().AsObject(); - if (context == null || !context.IsIndexed) - { - throw RuntimeException.IndexedAccessIsNotSupportedException(); - } - - _operationStack.Push(Variable.CreateIndexedPropertyReference(context, index, "$stackvar")); - NextInstruction(); - - } - - private void Return(int arg) - { - if (_currentFrame.DiscardReturnValue) - _operationStack.Pop(); - - while(_exceptionsStack.Count > 0 && _exceptionsStack.Peek().handlerFrame == _currentFrame) - { - _exceptionsStack.Pop(); - } - - if (_currentFrame.IsReentrantCall) - _currentFrame.InstructionPointer = -1; - else - { - PopFrame(); - if(DebugStepInProgress()) - EmitStopEventIfNecessary(); - } - } - - private bool DebugStepInProgress() - { - if (_stopManager == null) - return false; - - return _stopManager.CurrentState == DebugState.SteppingOut || _stopManager.CurrentState == DebugState.SteppingOver; - } - - private void JmpCounter(int arg) - { - var counter = _operationStack.Pop(); - var limit = _currentFrame.LocalFrameStack.Peek(); - - if (counter.CompareTo(limit) <= 0) - { - NextInstruction(); - } - else - { - Jmp(arg); - } - } - - private void Inc(int arg) - { - var operand = _operationStack.Pop().AsNumber(); - operand = operand + 1; - _operationStack.Push(ValueFactory.Create(operand)); - NextInstruction(); - } - - private void NewInstance(int argCount) - { - IValue[] argValues = new IValue[argCount]; - // fact args - for (int i = argCount - 1; i >= 0; i--) - { - var argValue = _operationStack.Pop(); - if(argValue.DataType != DataType.NotAValidValue) - argValues[i] = BreakVariableLink(argValue); - } - - var typeName = _operationStack.Pop().AsString(); - var factory = TypeManager.GetFactoryFor(typeName); - - var constructor = factory.GetConstructor(typeName, argValues); - if(constructor == null) - { - throw new RuntimeException("Конструктор не найден (" + typeName + ")"); - } - - var instance = constructor(typeName, argValues); - _operationStack.Push(instance); - NextInstruction(); - - } - - private void PushIterator(int arg) - { - var collection = _operationStack.Pop(); - if (collection.DataType == DataType.Object) - { - var context = collection.AsObject() as ICollectionContext; - if (context == null) - { - throw RuntimeException.IteratorIsNotDefined(); - } - - var iterator = context.GetManagedIterator(); - _currentFrame.LocalFrameStack.Push(iterator); - NextInstruction(); - - } - else - { - throw RuntimeException.ValueIsNotObjectException(); - } - } - - private void IteratorNext(int arg) - { - var iterator = _currentFrame.LocalFrameStack.Peek() as CollectionEnumerator; - if (iterator == null) - { - throw new WrongStackConditionException(); - } - - var hasNext = iterator.MoveNext(); - if (hasNext) - { - _operationStack.Push(iterator.Current); - } - _operationStack.Push(ValueFactory.Create(hasNext)); - NextInstruction(); - } - - private void StopIterator(int arg) - { - var iterator = _currentFrame.LocalFrameStack.Pop() as CollectionEnumerator; - if (iterator == null) - { - throw new WrongStackConditionException(); - } - - iterator.Dispose(); - NextInstruction(); - } - - private void BeginTry(int exceptBlockAddress) - { - var info = new ExceptionJumpInfo(); - info.handlerAddress = exceptBlockAddress; - info.handlerFrame = _currentFrame; - info.stackSize = _operationStack.Count; - - _exceptionsStack.Push(info); - NextInstruction(); - } - - private void EndTry(int arg) - { - if (_exceptionsStack.Count > 0 && _exceptionsStack.Peek().handlerFrame == _currentFrame) - _exceptionsStack.Pop(); - _currentFrame.LastException = null; - NextInstruction(); - } - - private void RaiseException(int arg) - { - if (arg < 0) - { - if (_currentFrame.LastException == null) - // Если в блоке Исключение была еще одна Попытка, то она затерла lastException - // 1С в этом случае бросает новое пустое исключение - throw new RuntimeException(""); - - throw _currentFrame.LastException; - } - else - { - var exceptionValue = _operationStack.Pop().GetRawValue(); - if (exceptionValue is ExceptionTemplate) - { - var excInfo = exceptionValue as ExceptionTemplate; - throw new ParametrizedRuntimeException(excInfo.Message, excInfo.Parameter); - } - else - { - throw new RuntimeException(exceptionValue.AsString()); - } - } - } - - private void LineNum(int arg) - { - if (_currentFrame.LineNumber != arg) - { - _currentFrame.LineNumber = arg; - CodeStat_LineReached(); - } - - EmitStopEventIfNecessary(); - - NextInstruction(); - } - - private void EmitStopEventIfNecessary() - { - if (MachineStopped != null && _stopManager != null && _stopManager.ShouldStopAtThisLine(_module.ModuleInfo.Origin, _currentFrame)) - { - CreateFullCallstack(); - MachineStopped?.Invoke(this, new MachineStoppedEventArgs(_stopManager.LastStopReason)); - } - } - - private void CreateFullCallstack() - { - var result = _callStack.Select(x => FrameInfo(x.Module, x)).ToList(); - _fullCallstackCache = result; - } - - private void MakeRawValue(int arg) - { - var value = BreakVariableLink(_operationStack.Pop()); - _operationStack.Push(value); - NextInstruction(); - } - - private void MakeBool(int arg) - { - var value = _operationStack.Pop().AsBoolean(); - _operationStack.Push(ValueFactory.Create(value)); - NextInstruction(); - } - - private void PushTmp(int arg) - { - var value = _operationStack.Pop(); - _currentFrame.LocalFrameStack.Push(value); - NextInstruction(); - } - - private void PopTmp(int arg) - { - var tmpVal = _currentFrame.LocalFrameStack.Pop(); - - if (arg == 0) - _operationStack.Push(tmpVal); - - NextInstruction(); - } - - private void Execute(int arg) - { - var code = _operationStack.Pop().AsString(); - var module = CompileExecutionBatchModule(code); - PrepareCodeStatisticsData(module); - - var frame = new ExecutionFrame(); - var method = module.Methods[0]; - frame.MethodName = method.Signature.Name; - frame.Locals = new IVariable[method.Variables.Count]; - frame.InstructionPointer = 0; - frame.Module = module; - for (int i = 0; i < frame.Locals.Length; i++) - { - frame.Locals[i] = Variable.Create(ValueFactory.Create(), method.Variables[i]); - } - - var mlocals = new Scope(); - mlocals.Instance = new UserScriptContextInstance(module); - mlocals.Methods = TopScope.Methods; - mlocals.Variables = _currentFrame.Locals; - _scopes.Add(mlocals); - frame.ModuleScope = mlocals; - frame.ModuleLoadIndex = _scopes.Count - 1; - - try - { - PushFrame(frame); - MainCommandLoop(); - } - finally - { - PopFrame(); - _scopes.RemoveAt(_scopes.Count - 1); - } - - NextInstruction(); - - } - - - private void Eval(int arg) - { - IValue value = Evaluate(_operationStack.Pop().AsString()); - _operationStack.Push(value); - NextInstruction(); - } - - private void AddHandler(int arg) - { - var handlerMethod = _operationStack.Pop().AsString(); - var handlerTarget = _operationStack.Pop().AsObject(); - var eventName = _operationStack.Pop().AsString(); - var eventSource = _operationStack.Pop().AsObject(); - - EventProcessor?.AddHandler(eventSource, eventName, handlerTarget, handlerMethod); - - NextInstruction(); - } - - private void RemoveHandler(int arg) - { - var handlerMethod = _operationStack.Pop().AsString(); - var handlerTarget = _operationStack.Pop().AsObject(); - var eventName = _operationStack.Pop().AsString(); - var eventSource = _operationStack.Pop().AsObject(); - - EventProcessor?.RemoveHandler(eventSource, eventName, handlerTarget, handlerMethod); - - NextInstruction(); - } - - private void ExitTry(int arg) - { - while (arg-- > 0) - _exceptionsStack.Pop(); - - NextInstruction(); - } - - #endregion - - #region Built-in functions - - private void Bool(int arg) - { - bool value = _operationStack.Pop().AsBoolean(); - _operationStack.Push(ValueFactory.Create(value)); - NextInstruction(); - } - - private void Number(int arg) - { - decimal value = _operationStack.Pop().AsNumber(); - _operationStack.Push(ValueFactory.Create(value)); - NextInstruction(); - } - - private void Str(int arg) - { - string value = _operationStack.Pop().AsString(); - _operationStack.Push(ValueFactory.Create(value)); - NextInstruction(); - } - - private void Date(int arg) - { - if (arg == 1) - { - var strDate = _operationStack.Pop().AsString(); - _operationStack.Push(ValueFactory.Parse(strDate, DataType.Date)); - } - else if (arg >= 3 && arg <= 6) - { - int[] factArgs = new int[6]; - - for (int i = arg - 1; i >= 0; i--) - { - factArgs[i] = (int)_operationStack.Pop().AsNumber(); - } - - var date = new DateTime( - factArgs[0], - factArgs[1], - factArgs[2], - factArgs[3], - factArgs[4], - factArgs[5]); - - _operationStack.Push(ValueFactory.Create(date)); - - } - else - { - throw new RuntimeException("Неверное количество параметров"); - } - - NextInstruction(); - } - - private void Type(int arg) - { - var typeName = _operationStack.Pop().AsString(); - var value = new TypeTypeValue(typeName); - _operationStack.Push(value); - NextInstruction(); - } - - private void ValType(int arg) - { - var value = _operationStack.Pop(); - var valueType = new TypeTypeValue(value.SystemType); - _operationStack.Push(valueType); - NextInstruction(); - } - - private void StrLen(int arg) - { - var str = _operationStack.Pop().AsString(); - _operationStack.Push(ValueFactory.Create(str.Length)); - NextInstruction(); - } - - private void TrimL(int arg) - { - var str = _operationStack.Pop().AsString(); - - for (int i = 0; i < str.Length; i++) - { - if(!Char.IsWhiteSpace(str[i])) - { - var trimmed = str.Substring(i); - _operationStack.Push(ValueFactory.Create(trimmed)); - NextInstruction(); - return; - } - } - - _operationStack.Push(ValueFactory.Create("")); - NextInstruction(); - - } - - private void TrimR(int arg) - { - var str = _operationStack.Pop().AsString(); - - int lastIdx = str.Length-1; - for (int i = lastIdx; i >= 0; i--) - { - if (!Char.IsWhiteSpace(str[i])) - { - var trimmed = str.Substring(0, i+1); - _operationStack.Push(ValueFactory.Create(trimmed)); - NextInstruction(); - return; - } - } - - _operationStack.Push(ValueFactory.Create("")); - NextInstruction(); - - } - - private void TrimLR(int arg) - { - var str = _operationStack.Pop().AsString().Trim(); - _operationStack.Push(ValueFactory.Create(str)); - NextInstruction(); - } - - private void Left(int arg) - { - var len = (int)_operationStack.Pop().AsNumber(); - var str = _operationStack.Pop().AsString(); - - if (len > str.Length) - len = str.Length; - else if (len < 0) - { - _operationStack.Push(ValueFactory.Create("")); - NextInstruction(); - return; - } - - _operationStack.Push(ValueFactory.Create(str.Substring(0, len))); - NextInstruction(); - } - - private void Right(int arg) - { - var len = (int)_operationStack.Pop().AsNumber(); - var str = _operationStack.Pop().AsString(); - - if (len > str.Length) - len = str.Length; - else if (len < 0) - { - _operationStack.Push(ValueFactory.Create("")); - NextInstruction(); - return; - } - - int startIdx = str.Length - len; - _operationStack.Push(ValueFactory.Create(str.Substring(startIdx, len))); - - NextInstruction(); - } - - private void Mid(int arg) - { - string str; - int start; - int len; - if (arg == 2) - { - start = (int)_operationStack.Pop().AsNumber(); - str = _operationStack.Pop().AsString(); - len = str.Length-start+1; - } - else - { - len = (int)_operationStack.Pop().AsNumber(); - start = (int)_operationStack.Pop().AsNumber(); - str = _operationStack.Pop().AsString(); - } - - if (start < 1) - start = 1; - - if (start+len > str.Length || len < 0) - len = str.Length-start+1; - - string result; - - if (start > str.Length || len == 0) - { - result = ""; - } - else - { - result = str.Substring(start - 1, len); - } - - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void StrPos(int arg) - { - var needle = _operationStack.Pop().AsString(); - var haystack = _operationStack.Pop().AsString(); - - var result = haystack.IndexOf(needle, StringComparison.Ordinal) + 1; - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void UCase(int arg) - { - var result = _operationStack.Pop().AsString().ToUpper(); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void LCase(int arg) - { - var result = _operationStack.Pop().AsString().ToLower(); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void TCase(int arg) - { - var argValue = _operationStack.Pop().AsString(); - - char[] array = argValue.ToCharArray(); - // Handle the first letter in the string. - bool inWord = false; - if (array.Length >= 1) - { - if (char.IsLetter(array[0])) - inWord = true; - - if(char.IsLower(array[0])) - { - array[0] = char.ToUpper(array[0]); - } - } - // Scan through the letters, checking for spaces. - // ... Uppercase the lowercase letters following spaces. - for (int i = 1; i < array.Length; i++) - { - if (inWord && Char.IsLetter(array[i])) - array[i] = Char.ToLower(array[i]); - else if (Char.IsSeparator(array[i]) || Char.IsPunctuation(array[i])) - inWord = false; - else if(!inWord && Char.IsLetter(array[i])) - { - inWord = true; - if (char.IsLower(array[i])) - { - array[i] = char.ToUpper(array[i]); - } - } - } - - var result = new string(array); - - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void Chr(int arg) - { - var code = (int)_operationStack.Pop().AsNumber(); - - var result = new string(new char[1] { (char)code }); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void ChrCode(int arg) - { - string strChar; - int position; - - if(arg == 2) - { - position = (int)_operationStack.Pop().AsNumber()-1; - strChar = _operationStack.Pop().AsString(); - } - else if(arg == 1) - { - strChar = _operationStack.Pop().AsString(); - position = 0; - } - else - { - throw new WrongStackConditionException(); - } - - int result; - if (strChar.Length == 0) - result = 0; - else if (position >= 0 && position < strChar.Length) - result = (int)strChar[position]; - else - throw RuntimeException.InvalidArgumentValue(); - - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void EmptyStr(int arg) - { - var str = _operationStack.Pop().AsString(); - - _operationStack.Push(ValueFactory.Create(String.IsNullOrWhiteSpace(str))); - NextInstruction(); - } - - private void StrReplace(int arg) - { - var newVal = _operationStack.Pop().AsString(); - var searchVal = _operationStack.Pop().AsString(); - var sourceString = _operationStack.Pop().AsString(); - - var result = sourceString.Replace(searchVal, newVal); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void StrGetLine(int arg) - { - var lineNumber = (int)_operationStack.Pop().AsNumber(); - var strArg = _operationStack.Pop().AsString(); - string result = ""; - if (lineNumber >= 1) - { - string[] subStrVals = strArg.Split(new Char[] { '\n' }, lineNumber + 1); - result = subStrVals[lineNumber - 1]; - } - - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void StrLineCount(int arg) - { - var strArg = _operationStack.Pop().AsString(); - int pos = 0; - int lineCount = 1; - while (pos >= 0 && pos < strArg.Length) - { - pos = strArg.IndexOf('\n', pos); - if (pos >= 0) - { - lineCount++; - pos++; - } - } - - _operationStack.Push(ValueFactory.Create(lineCount)); - - NextInstruction(); - } - - private void StrEntryCount(int arg) - { - var what = _operationStack.Pop().AsString(); - var where = _operationStack.Pop().AsString(); - - var pos = where.IndexOf(what); - var entryCount = 0; - while(pos >= 0) - { - entryCount++; - var nextIndex = pos + what.Length; - if (nextIndex >= where.Length) - break; - - pos = where.IndexOf(what, nextIndex); - } - - _operationStack.Push(ValueFactory.Create(entryCount)); - - NextInstruction(); - } - - private void Year(int arg) - { - var date = _operationStack.Pop().AsDate().Year; - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void Month(int arg) - { - var date = _operationStack.Pop().AsDate().Month; - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void Day(int arg) - { - var date = _operationStack.Pop().AsDate().Day; - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void Hour(int arg) - { - var date = _operationStack.Pop().AsDate().Hour; - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void Minute(int arg) - { - var date = _operationStack.Pop().AsDate().Minute; - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void Second(int arg) - { - var date = _operationStack.Pop().AsDate().Second; - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void BegOfYear(int arg) - { - var year = _operationStack.Pop().AsDate().Year; - _operationStack.Push(ValueFactory.Create(new DateTime(year,1,1))); - NextInstruction(); - } - - private void BegOfMonth(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, 1); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void BegOfDay(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, date.Day); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void BegOfHour(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, 0, 0); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void BegOfMinute(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void BegOfQuarter(int arg) - { - //1,4,7,10 - var date = _operationStack.Pop().AsDate(); - var month = date.Month; - int quarterMonth; - if (date.Month >= 1 && date.Month <= 3) - { - quarterMonth = 1; - } - else if (date.Month >= 4 && date.Month <= 6) - { - quarterMonth = 4; - } - else if (date.Month >= 7 && date.Month <= 9) - { - quarterMonth = 7; - } - else - { - quarterMonth = 10; - } - var result = new DateTime(date.Year, quarterMonth, 1); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void EndOfYear(int arg) - { - var year = _operationStack.Pop().AsDate().Year; - _operationStack.Push(ValueFactory.Create(new DateTime(year, 12, DateTime.DaysInMonth(year,12), 23, 59, 59))); - NextInstruction(); - } - - private void EndOfMonth(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month), 23, 59, 59); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void EndOfDay(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, date.Day, 23, 59, 59); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void EndOfHour(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, 59, 59); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void EndOfMinute(int arg) - { - var date = _operationStack.Pop().AsDate(); - var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 59); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void EndOfQuarter(int arg) - { - //1,4,7,10 - var date = _operationStack.Pop().AsDate(); - var month = date.Month; - int quarterMonth; - if (date.Month >= 1 && date.Month <= 3) - { - quarterMonth = 3; - } - else if (date.Month >= 4 && date.Month <= 6) - { - quarterMonth = 6; - } - else if (date.Month >= 7 && date.Month <= 9) - { - quarterMonth = 9; - } - else - { - quarterMonth = 12; - } - var result = new DateTime(date.Year, quarterMonth, DateTime.DaysInMonth(date.Year, quarterMonth), 23, 59, 59); - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void WeekOfYear(int arg) - { - var date = _operationStack.Pop().AsDate(); - var cal = new System.Globalization.GregorianCalendar(); - - _operationStack.Push(ValueFactory.Create(cal.GetWeekOfYear(date, System.Globalization.CalendarWeekRule.FirstDay, System.DayOfWeek.Monday))); - NextInstruction(); - } - - private void DayOfYear(int arg) - { - var date = _operationStack.Pop().AsDate().DayOfYear; - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void DayOfWeek(int arg) - { - var day = (int)_operationStack.Pop().AsDate().DayOfWeek; - - if (day == 0) - { - day = 7; - } - - _operationStack.Push(ValueFactory.Create(day)); - NextInstruction(); - } - - private void AddMonth(int arg) +/*---------------------------------------------------------- +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 ScriptEngine.Machine.Contexts; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; +using OneScript.Commons; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Sources; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Compiler; +using ScriptEngine.Machine.Debugger; + +namespace ScriptEngine.Machine +{ + public class MachineInstance + { + private Stack _operationStack; + private Stack _callStack; + private ExecutionFrame _currentFrame; + private Action[] _commands; + private Stack _exceptionsStack; + private readonly LruCache _executeModuleCache = new LruCache(64); + + private StackRuntimeModule _module; + private ICodeStatCollector _codeStatCollector; + private MachineStopManager _stopManager; + + private volatile bool _debugEnabled; + + private IBslProcess _process; + private ITypeManager _typeManager; + private IRuntimeEnvironment _runtimeEnvironment; + + // для отладчика. + // актуален в момент останова машины + private IList _fullCallstackCache; + private ScriptInformationContext _debugInfo; + + // кешированный обработчик событий + private readonly Lazy _eventProcessor; + + internal MachineInstance() + { + InitCommands(); + Reset(); + + _eventProcessor = new Lazy(() => _process.Services.TryResolve(), + LazyThreadSafetyMode.None); + } + + public void Setup(IBslProcess process) + { + Cleanup(); + + _process = process; + _codeStatCollector = process.Services.TryResolve(); + _typeManager = process.Services.Resolve(); + _runtimeEnvironment = process.Services.Resolve(); + } + + internal IBslProcess Process => _process; + + public bool IsRunning => _callStack.Count != 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private IReadOnlyList CreateRootFrameScopes(IAttachableContext thisScope) + { + if (_runtimeEnvironment == null) + throw new InvalidOperationException("Runtime environment is not initialized"); + + var globals = _runtimeEnvironment.AttachedContexts; + return new JoinedScopes(globals, thisScope); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IReadOnlyList CreateFrameScopes(IReadOnlyList outerScopes, IAttachableContext thisScope) + { + if (outerScopes == null) + throw new ArgumentNullException(nameof(outerScopes)); + + return new JoinedScopes(outerScopes, thisScope); + } + + internal IValue ExecuteMethod(IRunnable sdo, MachineMethodInfo methodInfo, IValue[] arguments) + { + var module = sdo.Module as StackRuntimeModule; + Debug.Assert(module != null); + + var thisScope = sdo; + var scopes = CreateRootFrameScopes(thisScope); + + var frame = new ExecutionFrame + { + Module = module, + ThisScope = thisScope, + Scopes = scopes, + IsReentrantCall = true, + }; + SetExecutionFrame(frame, methodInfo, arguments); + + ExecuteCode(); + + IValue methodResult = null; + if (methodInfo.IsFunction()) + { + methodResult = _operationStack.Pop(); + } + + // Этот Pop связан с методом Return. + // Если идет возврат из вложенного вызова, то Pop делается здесь, а не в Return, + // т.к. мы должны выйти из MainCommandLoop и вернуться в предыдущий цикл машины + // + // P.S. it's fuckin spaghetti ( + if (_callStack.Count > 1) + PopFrame(); + + return methodResult; + } + + private void SetExecutionFrame(ExecutionFrame frame, MachineMethodInfo methodInfo, IValue[] argValues) + { + var methDescr = methodInfo.GetRuntimeMethod(); + frame.MethodName = methodInfo.Name; + frame.InstructionPointer = methDescr.EntryPoint; + + var parameters = methodInfo.GetBslParameters(); + var variables = methDescr.LocalVariables; + var locals = new IVariable[variables.Length]; + int i = 0; + for (; i < argValues.Length; i++) + { + var paramDef = parameters[i]; + var argValue = argValues[i]; + if (argValue is IVariable argVar) + { + if (paramDef.ExplicitByVal) + { + locals[i] = Variable.Create(argVar.Value, variables[i]); + } + else + { + // TODO: Alias ? + locals[i] = Variable.CreateReference(argVar, variables[i]); + } + } + else if (argValue == null || argValue.IsSkippedArgument()) + { + var value = paramDef.HasDefaultValue ? (IValue)paramDef.DefaultValue : ValueFactory.Create(); + locals[i] = Variable.Create(value, variables[i]); + } + else + { + locals[i] = Variable.Create(argValue, variables[i]); + } + } + for (; i < parameters.Length; i++) + { + var paramDef = parameters[i]; + var value = paramDef.HasDefaultValue ? (IValue)paramDef.DefaultValue : ValueFactory.Create(); + locals[i] = Variable.Create(value, variables[i]); + } + for (; i < locals.Length; i++) + { + locals[i] = Variable.Create(ValueFactory.Create(), variables[i]); + } + + frame.Locals = locals; + PushFrame(frame); + } + + #region Debug protocol methods + + [MethodImpl(MethodImplOptions.Synchronized)] + public void SetDebugMode(IThreadEventsListener threadManager, IBreakpointManager breakpointManager) + { + if (!_debugEnabled) + { + _stopManager = new MachineStopManager(this, threadManager, breakpointManager); + _debugEnabled = true; + } + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void UnsetDebugMode() + { + _debugEnabled = false; + } + + public void StepOver() + { + if (!_debugEnabled) + throw new InvalidOperationException("Machine is not in debug mode"); + + _stopManager.StepOver(); + } + + public void StepIn() + { + if (!_debugEnabled) + throw new InvalidOperationException("Machine is not in debug mode"); + + _stopManager.StepIn(); + } + + public void StepOut() + { + if (!_debugEnabled) + throw new InvalidOperationException("Machine is not in debug mode"); + + _stopManager.StepOut(); + } + + public IValue Evaluate(string expression) + { + var code = CompileCached(expression, CompileExpressionModule); + + IAttachableContext localScope = new EvalExecLocalContext(_currentFrame.Locals); + + var frame = new ExecutionFrame + { + MethodName = code.Source.Name, + Module = code, + ThisScope = localScope, + Scopes = CreateFrameScopes(_currentFrame.Scopes, localScope), + Locals = Array.Empty(), + InstructionPointer = 0, + }; + + try + { + PushFrame(frame); + MainCommandLoop(); + } + finally + { + PopFrame(); + } + + return _operationStack.Pop(); + } + + internal BslValue EvaluateInFrame(string expression, ExecutionFrame selectedFrame) + { + MachineInstance runner = new MachineInstance + { + _process = this._process, + _runtimeEnvironment = this._runtimeEnvironment, + _typeManager = this._typeManager, + _debugInfo = CurrentScript + }; + + runner.SetFrame(selectedFrame); + + ExecutionFrame frame; + + var code = runner.CompileExpressionModule(expression); + + IAttachableContext localScope = new EvalExecLocalContext(selectedFrame.Locals); + + frame = new ExecutionFrame + { + MethodName = code.Source.Name, + Module = code, + ThisScope = localScope, + Locals = Array.Empty(), + Scopes = CreateFrameScopes(selectedFrame.Scopes, localScope), + InstructionPointer = 0, + LineNumber = 1 + }; + + runner.PushFrame(frame); + runner.MainCommandLoop(); + + return (BslValue)runner.PopRawValue(); + } + + public BslValue EvaluateInFrame(string expression, int frameId) + { + System.Diagnostics.Debug.Assert(_fullCallstackCache != null); + if (frameId < 0 || frameId >= _fullCallstackCache.Count) + throw new ScriptException("Wrong stackframe"); + + ExecutionFrame selectedFrame = _fullCallstackCache[frameId].FrameObject; + + return EvaluateInFrame(expression, selectedFrame); + } + + private StackRuntimeModule CompileCached(string code, Func compile) + { + var cacheKey = HashCode.Combine(code, _module.Source.Location, _currentFrame.ToString()).ToString("X8"); + return _executeModuleCache.GetOrAdd(cacheKey, _ => compile(code)); + } + + #endregion + + private IEventProcessor EventProcessor => _eventProcessor.Value ?? throw new InvalidOperationException("Host does not support events"); + + private ScriptInformationContext CurrentScript + { + get + { + if (_module.Source != null) + return new ScriptInformationContext(_module.Source); + else + return null; + } + } + + internal void Cleanup() + { + Reset(); + } + + private void PushFrame(ExecutionFrame frame) + { + _callStack.Push(frame); + SetFrame(frame); + } + + private void PopFrame() + { + _callStack.Pop(); + SetFrame(_callStack.Peek()); + } + + private void SetFrame(ExecutionFrame frame) + { + _module = frame.Module; + _currentFrame = frame; + } + + private void Reset() + { + _operationStack = new Stack(); + _callStack = new Stack(); + _exceptionsStack = new Stack(); + _module = null; + _currentFrame = null; + _process = null; + _typeManager = null; + _runtimeEnvironment = null; + } + + private void PrepareCodeStatisticsData(StackRuntimeModule _module) + { + if (_codeStatCollector == null + || _codeStatCollector.IsPrepared(_module.Source.Location)) + { + return; + } + + foreach (var method in _module.Methods + .Cast()) + { + var instructionPointer = method.GetRuntimeMethod().EntryPoint; + while (instructionPointer < _module.Code.Count) + { + if (_module.Code[instructionPointer].Code == OperationCode.LineNum) + { + var entry = new CodeStatEntry( + _module.Source.Location, + method.Name, + _module.Code[instructionPointer].Argument + ); + _codeStatCollector.MarkEntryReached(entry, count: 0); + } + + if (_module.Code[instructionPointer].Code == OperationCode.Return) + { + break; + } + + instructionPointer++; + } + } + _codeStatCollector.MarkPrepared(_module.Source.Location); + } + + private void ExecuteCode() + { + PrepareCodeStatisticsData(_module); + + while (true) + { + try + { + MainCommandLoop(); + break; + } + catch (ScriptException exc) + { + SetScriptExceptionSource(exc); + + var shouldRethrow = ShouldRethrowException(exc); + + if (_debugEnabled) + { + if (_stopManager.Breakpoints.StopOnAnyException(exc.MessageWithoutCodeFragment) || + shouldRethrow && _stopManager.Breakpoints.StopOnUncaughtException(exc.MessageWithoutCodeFragment)) + EmitStopOnException(); + } + + if (shouldRethrow) + throw; + } + } + } + + private bool ShouldRethrowException(ScriptException exc) + { + if (_exceptionsStack.Count == 0) + { + return true; + } + + if (!(exc.RuntimeSpecificInfo is IList)) + { + CreateFullCallstack(); + IList callStackFrames = new List(_fullCallstackCache); + exc.RuntimeSpecificInfo = callStackFrames; + } + + var handler = _exceptionsStack.Pop(); + + // Раскрутка стека вызовов + while (_currentFrame != handler.HandlerFrame) + { + if (_currentFrame.IsReentrantCall) + { + _exceptionsStack.Push(handler); + PopFrame(); + return true; + } + + PopFrame(); + } + + _currentFrame.InstructionPointer = handler.HandlerAddress; + _currentFrame.LastException = exc; + + // При возникновении исключения посредине выражения + // некому почистить стек операндов. + // Сделаем это + while (_operationStack.Count > handler.StackSize) + _operationStack.Pop(); + + return false; + } + + private CodeStatEntry CurrentCodeEntry() + { + return new CodeStatEntry(CurrentScript?.Source, _currentFrame.MethodName, _currentFrame.LineNumber); + } + + private void CodeStat_LineReached() + { + if (_codeStatCollector == null) + return; + + _codeStatCollector.MarkEntryReached(CurrentCodeEntry()); + } + + private void MainCommandLoop() + { + try + { + while (_currentFrame.InstructionPointer >= 0 + && _currentFrame.InstructionPointer < _module.Code.Count) + { + var command = _module.Code[_currentFrame.InstructionPointer]; + _commands[(int) command.Code](command.Argument); + } + } + catch (ScriptInterruptionException) + { + throw; + } + catch (ScriptException exc) + { + exc.SetPositionIfEmpty(GetPositionInfo()); + + throw; + } + catch (Exception exc) + { + var excWrapper = new ExternalSystemException(exc); + SetScriptExceptionSource(excWrapper); + throw excWrapper; + } + } + + private ErrorPositionInfo GetPositionInfo() + { + var epi = new ErrorPositionInfo + { + LineNumber = _currentFrame.LineNumber + }; + + if (_module.Source != null && epi.LineNumber > 0) + { + epi.ModuleName = _module.Source.Name; + epi.Code = _module.Source.GetCodeLine(epi.LineNumber) ?? "<исходный код недоступен>"; + } + else + { + epi.ModuleName = "<имя модуля недоступно>"; + epi.Code = "<исходный код недоступен>"; + } + return epi; + } + + private void SetScriptExceptionSource(ScriptException exc) + { + exc.SetPositionIfEmpty(GetPositionInfo()); + } + + #region Commands + + private void InitCommands() + { + _commands = new Action[] + { + (i)=>{NextInstruction();}, + PushVar, + PushConst, + PushInt, + PushBool, + PushUndef, + PushNull, + PushLoc, + PushRef, + LoadVar, + LoadLoc, + AssignRef, + Add, + Sub, + Mul, + Div, + Mod, + Neg, + Equals, + Less, + Greater, + LessOrEqual, + GreaterOrEqual, + NotEqual, + Not, + And, + Or, + CallFunc, + CallProc, + ArgNum, + PushDefaultArg, + ResolveProp, + ResolveMethodProc, + ResolveMethodFunc, + Jmp, + JmpFalse, + PushIndexed, + Return, + JmpCounter, + Inc, + NewInstance, + NewFunc, + PushIterator, + IteratorNext, + StopIterator, + BeginTry, + EndTry, + RaiseException, + LineNum, + MakeRawValue, + MakeBool, + PushTmp, + PopTmp, + Execute, + AddHandler, + RemoveHandler, + ExitTry, + + //built-ins + Eval, + Bool, + Number, + Str, + Date, + Type, + ValType, + StrLen, + TrimL, + TrimR, + TrimLR, + Left, + Right, + Mid, + StrPos, + UCase, + LCase, + TCase, + Chr, + ChrCode, + EmptyStr, + StrReplace, + StrGetLine, + StrLineCount, + StrEntryCount, + Year, + Month, + Day, + Hour, + Minute, + Second, + BegOfWeek, + BegOfYear, + BegOfMonth, + BegOfDay, + BegOfHour, + BegOfMinute, + BegOfQuarter, + EndOfWeek, + EndOfYear, + EndOfMonth, + EndOfDay, + EndOfHour, + EndOfMinute, + EndOfQuarter, + WeekOfYear, + DayOfYear, + DayOfWeek, + AddMonth, + CurrentDate, + Integer, + Round, + Log, + Log10, + Sin, + Cos, + Tan, + ASin, + ACos, + ATan, + Exp, + Pow, + Sqrt, + Min, + Max, + Format, + ExceptionInfo, + ExceptionDescr, + ModuleInfo + }; + } + + #region Simple operations + private void PushVar(int arg) + { + var binding = _module.VariableRefs[arg]; + var target = ResolveBindingTarget(binding); + _operationStack.Push(target.GetVariable(binding.MemberNumber)); + + NextInstruction(); + } + + private void PushConst(int arg) + { + _operationStack.Push(_module.Constants[arg]); + NextInstruction(); + } + + private void PushBool(int arg) + { + _operationStack.Push(BslBooleanValue.Create(arg == 1)); + NextInstruction(); + } + + private void PushInt(int arg) + { + _operationStack.Push(BslNumericValue.Create(arg)); + NextInstruction(); + } + + private void PushUndef(int arg) + { + _operationStack.Push(BslUndefinedValue.Instance); + NextInstruction(); + } + + private void PushNull(int arg) + { + _operationStack.Push(BslNullValue.Instance); + NextInstruction(); + } + + private void PushLoc(int arg) + { + _operationStack.Push(_currentFrame.Locals[arg]); + NextInstruction(); + } + + private void PushRef(int arg) + { + var binding = _module.VariableRefs[arg]; + + var target = ResolveBindingTarget(binding); + var reference = Variable.CreateContextPropertyReference(target, binding.MemberNumber, "$stackvar"); + + _operationStack.Push(reference); + NextInstruction(); + } + + private void LoadVar(int arg) + { + var binding = _module.VariableRefs[arg]; + var target = ResolveBindingTarget(binding); + target.GetVariable(binding.MemberNumber).Value = PopRawValue(); + + NextInstruction(); + } + + private void LoadLoc(int arg) + { + _currentFrame.Locals[arg].Value = PopRawValue(); + NextInstruction(); + } + + private void AssignRef(int arg) + { + var value = PopRawValue(); + + IVariable reference; + try + { + reference = (IVariable)_operationStack.Pop(); + } + catch (InvalidCastException) + { + throw new WrongStackConditionException(); + } + reference.Value = value; + NextInstruction(); + } + + private void Add(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Add(op1, op2, _process)); + NextInstruction(); + } + + private void Sub(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Sub(op1, op2)); + NextInstruction(); + } + + private void Mul(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Mul(op1, op2)); + NextInstruction(); + } + + private void Div(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Div(op1, op2)); + NextInstruction(); + } + + private void Mod(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Mod(op1, op2)); + NextInstruction(); + } + + private void Neg(int arg) + { + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Neg(op1)); + NextInstruction(); + } + + private void Equals(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Create(op1.Equals(op2))); + NextInstruction(); + } + + private void Less(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) < 0)); + NextInstruction(); + } + + private void Greater(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) > 0)); + NextInstruction(); + } + + private void LessOrEqual(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) <= 0)); + NextInstruction(); + } + + private void GreaterOrEqual(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Create(op1.CompareTo(op2) >= 0)); + NextInstruction(); + } + + private void NotEqual(int arg) + { + var op2 = PopRawValue(); + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Create(!op1.Equals(op2))); + NextInstruction(); + } + + private void Not(int arg) + { + var op1 = PopRawValue(); + _operationStack.Push(ValueFactory.Create(!op1.AsBoolean())); + NextInstruction(); + } + + private void And(int arg) + { + var op = _operationStack.Peek().AsBoolean(); + if (op == false) + { + Jmp(arg); + } + else + { + _operationStack.Pop(); + NextInstruction(); + } + } + + private void Or(int arg) + { + var op = _operationStack.Peek().AsBoolean(); + if (op == true) + { + Jmp(arg); + } + else + { + _operationStack.Pop(); + NextInstruction(); + } + } + + private void CallFunc(int arg) + { + bool needsDiscarding = MethodCallImpl(arg, true); + _currentFrame.DiscardReturnValue = needsDiscarding; + } + + private void CallProc(int arg) + { + bool needsDiscarding = MethodCallImpl(arg, false); + _currentFrame.DiscardReturnValue = needsDiscarding; + } + + private IValue[] PopArguments() + { + int argCount = (int)_operationStack.Pop().AsNumber(); + IValue[] args = new IValue[argCount]; + + for (--argCount; argCount >= 0; --argCount) + { + args[argCount] = _operationStack.Pop(); + } + return args; + } + + private bool MethodCallImpl(int arg, bool asFunc) + { + var methodRef = _module.MethodRefs[arg]; + + var boundInstance = ResolveBindingTarget(methodRef); + var methodSignature = boundInstance.GetMethod(methodRef.MemberNumber); + + IValue[] argValues = PopArguments(); + + var definedParameters = methodSignature.GetBslParameters(); + bool needsDiscarding; + + if (ReferenceEquals(boundInstance, _currentFrame.ThisScope)) // local call + { + var sdo = boundInstance as ScriptDrivenObject; + System.Diagnostics.Debug.Assert(sdo != null); + + if (sdo.MethodDefinedInScript(methodRef.MemberNumber)) + { + // заранее переведем указатель на адрес возврата. В опкоде Return инкремента нет. + NextInstruction(); + + var methodInfo = (MachineMethodInfo)_module.Methods[sdo.GetMethodDescriptorIndex(methodRef.MemberNumber)]; + var frame = new ExecutionFrame + { + Module = _module, + ThisScope = _currentFrame.ThisScope, + Scopes = _currentFrame.Scopes + }; + SetExecutionFrame(frame, methodInfo, argValues); + + needsDiscarding = methodSignature.IsFunction() && !asFunc; + } + else + { + needsDiscarding = _currentFrame.DiscardReturnValue; + CallContext(boundInstance, methodRef.MemberNumber, definedParameters, argValues, asFunc); + } + } + else + { + // при вызове библиотечного метода (из другого scope) + // статус вызова текущего frames не должен изменяться. + // + needsDiscarding = _currentFrame.DiscardReturnValue; + CallContext(boundInstance, methodRef.MemberNumber, definedParameters, argValues, asFunc); + } + + return needsDiscarding; + } + + private void CallContext(IRuntimeContextInstance instance, int index, ParameterInfo[] definedParameters, IValue[] argValues, bool asFunc) + { + IValue[] realArgs; + if (instance.DynamicMethodSignatures) + { + realArgs = argValues; + } + else + { + realArgs = new IValue[definedParameters.Length]; + var skippedArg = BslSkippedParameterValue.Instance; + int i = 0; + for (; i < argValues.Length; i++) + { + realArgs[i] = argValues[i]; + } + for (; i < realArgs.Length; i++) + { + realArgs[i] = skippedArg; + } + } + + if (asFunc) + { + instance.CallAsFunction(index, realArgs, out IValue retVal, _process); + _operationStack.Push(retVal); + } + else + { + instance.CallAsProcedure(index, realArgs, _process); + } + NextInstruction(); + } + + private void ArgNum(int arg) + { + _operationStack.Push(ValueFactory.Create(arg)); + NextInstruction(); + } + + private void PushDefaultArg(int arg) + { + _operationStack.Push(ValueFactory.CreateInvalidValueMarker()); + NextInstruction(); + } + + private void ResolveProp(int arg) + { + var objIValue = _operationStack.Pop(); + + var context = objIValue.AsObject(); + var propName = _module.Identifiers[arg]; + var propNum = context.GetPropertyNumber(propName); + + var propReference = Variable.CreateContextPropertyReference(context, propNum, "stackvar"); + _operationStack.Push(propReference); + NextInstruction(); + } + + private void ResolveMethodProc(int arg) + { + PrepareContextCallArguments(arg, out IRuntimeContextInstance context, out int methodId, out IValue[] argValues); + + context.CallAsProcedure(methodId, argValues, _process); + NextInstruction(); + } + + private void ResolveMethodFunc(int arg) + { + PrepareContextCallArguments(arg, out IRuntimeContextInstance context, out int methodId, out IValue[] argValues); + + if (!context.DynamicMethodSignatures && context.GetMethodInfo(methodId).ReturnType == typeof(void)) + { + throw RuntimeException.UseProcAsAFunction(); + } + + context.CallAsFunction(methodId, argValues, out IValue retVal, _process); + _operationStack.Push(retVal); + NextInstruction(); + } + + private void PrepareContextCallArguments(int arg, out IRuntimeContextInstance context, out int methodId, out IValue[] argValues) + { + var factArgs = PopArguments(); + var argCount = factArgs.Length; + + var objIValue = _operationStack.Pop(); + context = objIValue.AsObject(); + var methodName = _module.Identifiers[arg]; + methodId = context.GetMethodNumber(methodName); + + if (context.DynamicMethodSignatures) + { + argValues = new IValue[argCount]; + for (int i = 0; i < argCount; i++) + { + var argValue = factArgs[i]; + if (!argValue.IsSkippedArgument()) + { + argValues[i] = argValue; + } + } + } + else + { + var methodInfo = context.GetMethodInfo(methodId); + var methodParams = methodInfo.GetBslParameters(); + + if (argCount > methodParams.Length) + throw RuntimeException.TooManyArgumentsPassed(); + + argValues = new IValue[methodParams.Length]; + int i = 0; + for (; i < argCount; i++) + { + var argValue = factArgs[i]; + if (!argValue.IsSkippedArgument()) + { + if (methodParams[i].IsByRef()) + { + argValues[i] = argValue is IVariable? argValue : Variable.Create(argValue, ""); + } + else + argValues[i] = RawValue(argValue); + } + else if(!methodParams[i].HasDefaultValue) + throw RuntimeException.MissedArgument(); + } + for (; i < methodParams.Length; i++) + { + if (!methodParams[i].HasDefaultValue) + throw RuntimeException.TooFewArgumentsPassed(); + } + } + } + + private void Jmp(int arg) + { + _currentFrame.InstructionPointer = arg; + } + + private void JmpFalse(int arg) + { + var op1 = _operationStack.Pop(); + + if (!op1.AsBoolean()) + { + _currentFrame.InstructionPointer = arg; + } + else + { + NextInstruction(); + } + } + + private void PushIndexed(int arg) + { + var index = PopRawValue(); + var context = _operationStack.Pop().AsObject(); + if (context == null || !context.IsIndexed) + { + throw RuntimeException.IndexedAccessIsNotSupportedException(); + } + + _operationStack.Push(Variable.CreateIndexedPropertyReference(context, index, "$stackvar")); + NextInstruction(); + } + + private void Return(int arg) + { + if (_currentFrame.DiscardReturnValue) + _operationStack.Pop(); + + while(_exceptionsStack.Count != 0 && _exceptionsStack.Peek().HandlerFrame == _currentFrame) + { + _exceptionsStack.Pop(); + } + + if (_currentFrame.IsReentrantCall) + _currentFrame.InstructionPointer = -1; + else + { + PopFrame(); + if(IsSteppingOutFromHere()) + EmitStopEventIfNecessary(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsSteppingOutFromHere() + { + if (!_debugEnabled) + return false; + + return _stopManager.CurrentState == DebugState.SteppingOut; + } + + private void JmpCounter(int arg) + { + var counter = _operationStack.Pop(); + var limit = _currentFrame.LocalFrameStack.Peek(); + + if (counter.CompareTo(limit) <= 0) + { + NextInstruction(); + } + else + { + Jmp(arg); + } + } + + private void Inc(int arg) + { + var operand = _operationStack.Pop().AsNumber(); + operand++; + _operationStack.Push(ValueFactory.Create(operand)); + NextInstruction(); + } + + private void NewInstance(int arg) { - var numToAdd = (int)_operationStack.Pop().AsNumber(); - var date = _operationStack.Pop().AsDate(); - _operationStack.Push(ValueFactory.Create(date.AddMonths(numToAdd))); - NextInstruction(); - } - - private void CurrentDate(int arg) - { - var date = DateTime.Now; - date = date.AddTicks(-(date.Ticks % TimeSpan.TicksPerSecond)); - _operationStack.Push(ValueFactory.Create(date)); - NextInstruction(); - } - - private void Integer(int arg) - { - var num = Math.Truncate(_operationStack.Pop().AsNumber()); - _operationStack.Push(ValueFactory.Create(num)); - NextInstruction(); - } - - private void Round(int arg) - { - decimal num; - int digits; - int mode; - if (arg == 1) - { - num = _operationStack.Pop().AsNumber(); - digits = 0; - mode = 0; - } - else if (arg == 2) - { - digits = (int)_operationStack.Pop().AsNumber(); - num = _operationStack.Pop().AsNumber(); - mode = 0; - } - else - { - mode = (int)_operationStack.Pop().AsNumber(); - mode = mode == 0 ? 0 : 1; - digits = (int)_operationStack.Pop().AsNumber(); - num = _operationStack.Pop().AsNumber(); - } - - decimal scale = (decimal)Math.Pow(10.0, digits); - decimal scaled = Math.Abs(num) * scale; - - var director = (int)((scaled - (long)scaled) * 10 % 10); - - decimal round; - if (director == 5) - round = Math.Floor(scaled + mode * 0.5m * Math.Sign(digits)); - else if (director > 5) - round = Math.Ceiling(scaled); - else - round = Math.Floor(scaled); - - decimal result; - - if(digits >= 0) - result = (Math.Sign(num) * round / scale); - else - result = (Math.Sign(num) * round * scale); - - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private void Log(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Log((double) num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void Log10(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Log10((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void Sin(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Sin((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void Cos(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Cos((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void Tan(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Tan((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void ASin(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Asin((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void ACos(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Acos((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void ATan(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Atan((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - private void Exp(int arg) - { - var num = _operationStack.Pop().AsNumber(); - var result = Math.Exp((double)num); - _operationStack.Push(ValueFactory.Create((decimal)result)); - NextInstruction(); - } - - private void Pow(int arg) - { - var powPower = _operationStack.Pop().AsNumber(); - var powBase = _operationStack.Pop().AsNumber(); - - int exp = (int)powPower; - decimal result; - if (exp >= 0 && exp == powPower) - result = PowInt(powBase, (uint)exp); - else - result = (decimal)Math.Pow((double)powBase, (double)powPower); - - _operationStack.Push(ValueFactory.Create(result)); - NextInstruction(); - } - - private decimal PowInt(decimal bas, uint exp) - { - decimal pow = 1; - - while (true) - { - if ((exp & 1) == 1) pow *= bas; - exp >>= 1; - if (exp == 0) break; - bas *= bas; - } - - return pow; - } - - private void Sqrt(int arg) - { - var num = (double)_operationStack.Pop().AsNumber(); - var root = Math.Sqrt(num); - _operationStack.Push(ValueFactory.Create((decimal)root)); - NextInstruction(); - } - - private void Min(int argCount) - { - System.Diagnostics.Debug.Assert(argCount > 0); - - IValue min = _operationStack.Pop(); - while (--argCount > 0) - { - var current = _operationStack.Pop(); - if (current.CompareTo(min) < 0) - min = current; - } - - _operationStack.Push(BreakVariableLink(min)); - - NextInstruction(); - } - - private void Max(int argCount) - { - System.Diagnostics.Debug.Assert(argCount > 0); - - IValue max = _operationStack.Pop(); - while (--argCount > 0) - { - var current = _operationStack.Pop(); - if (current.CompareTo(max) > 0) - max = current; - } - - _operationStack.Push(BreakVariableLink(max)); - NextInstruction(); - } - - private void Format(int arg) - { - var formatString = _operationStack.Pop().AsString(); - var valueToFormat = _operationStack.Pop(); - - var formatted = ValueFormatter.Format(valueToFormat, formatString); - - _operationStack.Push(ValueFactory.Create(formatted)); - NextInstruction(); - - } - - private void ExceptionInfo(int arg) - { - if (_currentFrame.LastException != null) - { - ExceptionInfoContext excInfo; - if (_currentFrame.LastException is ParametrizedRuntimeException) - excInfo = new ExceptionInfoContext((ParametrizedRuntimeException)_currentFrame.LastException); - else - excInfo = new ExceptionInfoContext(_currentFrame.LastException); - - _operationStack.Push(ValueFactory.Create(excInfo)); - } - else - { - _operationStack.Push(ValueFactory.Create()); - } - NextInstruction(); - } - - private void ExceptionDescr(int arg) - { - if (_currentFrame.LastException != null) - { - var excInfo = new ExceptionInfoContext(_currentFrame.LastException); - _operationStack.Push(ValueFactory.Create(excInfo.MessageWithoutCodeFragment)); - } - else - { - _operationStack.Push(ValueFactory.Create("")); - } - NextInstruction(); - } - - private void ModuleInfo(int arg) - { - var currentScript = this.CurrentScript; - if (currentScript != null) - { - _operationStack.Push(currentScript); - } - else - { - _operationStack.Push(ValueFactory.Create()); - } - NextInstruction(); - } - - #endregion - - #endregion - - private LoadedModule CompileExpressionModule(string expression) - { - var ctx = ExtractCompilerContext(); - - ICodeSource stringSource = new StringBasedSource(expression); - var parser = new Lexer(); - parser.Code = stringSource.Code; - var compiler = new Compiler.Compiler(); - ctx.PushScope(new SymbolScope()); // скоуп выражения - var modImg = compiler.CompileExpression(parser, ctx); - modImg.ModuleInfo = new ModuleInformation(); - modImg.ModuleInfo.Origin = ""; - modImg.ModuleInfo.ModuleName = ""; - var code = new LoadedModule(modImg); - return code; - } - - private LoadedModule CompileExecutionBatchModule(string execBatch) - { - var ctx = ExtractCompilerContext(); - var entryId = CurrentCodeEntry().ToString(); - - ICodeSource stringSource = new StringBasedSource(execBatch); - var parser = new Lexer(); - parser.Code = stringSource.Code; - var compiler = new Compiler.Compiler(); - ctx.PushScope(new SymbolScope()); // скоуп выражения - var modImg = compiler.CompileExecBatch(parser, ctx); - modImg.ModuleInfo = new ModuleInformation(); - modImg.ModuleInfo.Origin = $"{entryId}:"; - modImg.ModuleInfo.ModuleName = $"{entryId}:"; - modImg.ModuleInfo.CodeIndexer = parser.Iterator; - var code = new LoadedModule(modImg); - return code; - } - - private CompilerContext ExtractCompilerContext() - { - var ctx = new CompilerContext(); - foreach (var scope in _scopes) - { - var symbolScope = new SymbolScope(); - foreach (var methodInfo in scope.Methods) - { - symbolScope.DefineMethod(methodInfo); - } - foreach (var variable in scope.Variables) - { - symbolScope.DefineVariable(variable.Name); - } - - ctx.PushScope(symbolScope); - } - - var locals = new SymbolScope(); - foreach (var variable in _currentFrame.Locals) - { - locals.DefineVariable(variable.Name); - } - - ctx.PushScope(locals); - return ctx; - } - - private void NextInstruction() - { - _currentFrame.InstructionPointer++; - } - - private IValue BreakVariableLink(IValue value) - { - return value.GetRawValue(); - } - - public IList GetExecutionFrames() - { - return _fullCallstackCache; - } - - public IList GetFrameLocals(int frameId) - { - System.Diagnostics.Debug.Assert(_fullCallstackCache != null); - if (frameId < 0 || frameId >= _fullCallstackCache.Count) - return new IVariable[0]; - - var frame = _fullCallstackCache[frameId]; - return frame.FrameObject.Locals; - } - - private ExecutionFrameInfo FrameInfo(LoadedModule module, ExecutionFrame frame) - { - return new ExecutionFrameInfo() - { - LineNumber = frame.LineNumber, - MethodName = frame.MethodName, - Source = module.ModuleInfo.Origin, - FrameObject = frame + int argCount = (int)_operationStack.Pop().AsNumber(); + IValue[] argValues = new IValue[argCount]; + // fact args + for (int i = argCount - 1; i >= 0; i--) + { + var argValue = _operationStack.Pop(); + if(!argValue.IsSkippedArgument()) + argValues[i] = RawValue(argValue); + } + + var typeName = _module.Identifiers[arg]; + _operationStack.Push(CreateInstance(typeName, argValues)); + NextInstruction(); + } + + private void PushIterator(int arg) + { + var collection = PopRawValue(); + if (collection is ICollectionContext context) + { + var iterator = new CollectionEnumerator(context.GetEnumerator(_process)); + _currentFrame.LocalFrameStack.Push(iterator); + NextInstruction(); + + } + else + { + throw RuntimeException.IteratorIsNotDefined(); + } + } + + private void IteratorNext(int arg) + { + var iterator = _currentFrame.LocalFrameStack.Peek() as CollectionEnumerator ?? throw new WrongStackConditionException(); + var hasNext = iterator.MoveNext(); + if (hasNext) + { + _operationStack.Push(iterator.Current); + } + _operationStack.Push(ValueFactory.Create(hasNext)); + NextInstruction(); + } + + private void StopIterator(int arg) + { + var iterator = _currentFrame.LocalFrameStack.Pop() as CollectionEnumerator ?? throw new WrongStackConditionException(); + iterator.Dispose(); + NextInstruction(); + } + + private void BeginTry(int exceptBlockAddress) + { + var info = new ExceptionJumpInfo + { + HandlerAddress = exceptBlockAddress, + HandlerFrame = _currentFrame, + StackSize = _operationStack.Count + }; + + _exceptionsStack.Push(info); + NextInstruction(); + } + + private void EndTry(int arg) + { + if (_exceptionsStack.Count != 0) + { + var jmpInfo = _exceptionsStack.Peek(); + if (jmpInfo.HandlerFrame == _currentFrame && arg == jmpInfo.HandlerAddress) + _exceptionsStack.Pop(); + } + _currentFrame.LastException = null; + NextInstruction(); + } + + private void RaiseException(int arg) + { + if (arg < 0) + { + if (_currentFrame.LastException == null) + { + // Если в блоке Исключение была еще одна Попытка, то она затерла lastException + // 1С в этом случае бросает новое пустое исключение + //throw new RuntimeException(""); + throw new RuntimeException(""); + } + + throw _currentFrame.LastException; + } + else + { + var exceptionValue = PopRawValue(); + if (exceptionValue is ExceptionInfoContext { IsErrorTemplate: true } excTemplateInfo) + { + throw new ParametrizedRuntimeException( + excTemplateInfo.Description, + excTemplateInfo.Parameters, + excTemplateInfo.InnerException + ); + } + else if (exceptionValue is ExceptionInfoContext { IsErrorTemplate: false } excInfo) + { + throw new ParametrizedRuntimeException( + excInfo.Description, + ValueFactory.Create(), + excInfo + ); + } + else + { + throw new RuntimeException(RawBslValue(exceptionValue).ToString(_process)); + } + } + } + + private void LineNum(int arg) + { + if (_currentFrame.LineNumber != arg) + { + _currentFrame.LineNumber = arg; + CodeStat_LineReached(); + } + + EmitStopEventIfNecessary(); + + NextInstruction(); + } + + private void EmitStopOnException() + { + if (_debugEnabled) + { + CreateFullCallstack(); + _stopManager.NotifyStop(MachineStopReason.Exception, ""); + } + } + + private void EmitStopEventIfNecessary() + { + if (_debugEnabled && _stopManager.ShouldStopAtThisLine(_module.Source.Location, _currentFrame)) + { + CreateFullCallstack(); + _stopManager.NotifyStop(); + _stopManager.LastStopErrorMessage = string.Empty; + } + } + + private void CreateFullCallstack() + { + var result = _callStack.Select(x => FrameInfo(x.Module, x)).ToList(); + _fullCallstackCache = result; + } + + private void MakeRawValue(int arg) + { + var value = PopRawValue(); + _operationStack.Push(value); + NextInstruction(); + } + + private void MakeBool(int arg) + { + var value = _operationStack.Pop().AsBoolean(); + _operationStack.Push(ValueFactory.Create(value)); + NextInstruction(); + } + + private void PushTmp(int arg) + { + var value = _operationStack.Pop(); + _currentFrame.LocalFrameStack.Push(value); + NextInstruction(); + } + + private void PopTmp(int arg) + { + var tmpVal = _currentFrame.LocalFrameStack.Pop(); + + if (arg == 0) + _operationStack.Push(tmpVal); + + NextInstruction(); + } + + private void Execute(int arg) + { + var code = PopRawBslValue().ToString(_process); + var module = CompileCached(code, CompileExecutionBatchModule); + if (module.Methods.Count == 0) + { + NextInstruction(); + return; + } + + IAttachableContext localScope = new EvalExecLocalContext(_currentFrame.Locals); + var scopes = CreateFrameScopes(_currentFrame.Scopes, localScope); + + var mi = (MachineMethodInfo)module.Methods[0]; + var method = mi.GetRuntimeMethod(); + var frame = new ExecutionFrame + { + Module = module, + MethodName = mi.Name, + ThisScope = localScope, + Scopes = scopes, + Locals = new IVariable[method.LocalVariables.Length], + InstructionPointer = 0, + IsReentrantCall = true + }; + var locals = frame.Locals; + for (int i = 0; i < locals.Length; i++) + { + locals[i] = Variable.Create(ValueFactory.Create(), method.LocalVariables[i]); + } + + PushFrame(frame); + ExecuteCode(); + PopFrame(); + + NextInstruction(); + } + + private void Eval(int arg) + { + IValue value = Evaluate(PopRawBslValue().ToString(_process)); + _operationStack.Push(value); + NextInstruction(); + } + + private void AddHandler(int arg) + { + PrepareHandlerOperationArgs( + arg == 0, + out var handlerMethod, + out var handlerTarget, + out var eventName, + out var eventSource); + + EventProcessor?.AddHandler(eventSource, eventName, handlerTarget, handlerMethod); + + NextInstruction(); + } + + private void RemoveHandler(int arg) + { + PrepareHandlerOperationArgs( + arg == 0, + out var handlerMethod, + out var handlerTarget, + out var eventName, + out var eventSource); + + EventProcessor?.RemoveHandler(eventSource, eventName, handlerTarget, handlerMethod); + + NextInstruction(); + } + + private void PrepareHandlerOperationArgs(bool useExportMode, + out string handlerMethod, + out IRuntimeContextInstance handlerTarget, + out string eventName, + out IRuntimeContextInstance eventSource) + { + if (useExportMode) + { + handlerMethod = PopRawBslValue().ToString(_process); + handlerTarget = _operationStack.Pop().AsObject(); + eventName = PopRawBslValue().ToString(_process); + eventSource = _operationStack.Pop().AsObject(); + + // Выбросит исключение, если не найден такой метод + handlerTarget.GetMethodNumber(handlerMethod); + } + else + { + handlerMethod = PopRawBslValue().ToString(_process); + handlerTarget = _currentFrame.ThisScope; + eventName = PopRawBslValue().ToString(_process); + eventSource = _operationStack.Pop().AsObject(); + } + } + + private void ExitTry(int arg) + { + while (arg-- > 0) + _exceptionsStack.Pop(); + + NextInstruction(); + } + + #endregion + + #region Built-in functions + + private void Bool(int arg) + { + bool value = _operationStack.Pop().AsBoolean(); + _operationStack.Push(ValueFactory.Create(value)); + NextInstruction(); + } + + private void Number(int arg) + { + decimal value = _operationStack.Pop().AsNumber(); + _operationStack.Push(ValueFactory.Create(value)); + NextInstruction(); + } + + private void Str(int arg) + { + var value = PopRawBslValue(); + _operationStack.Push(ValueFactory.Create(value.ToString(_process))); + NextInstruction(); + } + + private void Date(int arg) + { + if (arg == 1) + { + var strDate = PopRawBslValue().ToString(_process); + _operationStack.Push(ValueFactory.Parse(strDate, DataType.Date)); + } + else if (arg >= 3 && arg <= 6) + { + int[] factArgs = new int[6]; + + for (int i = arg - 1; i >= 0; i--) + { + factArgs[i] = (int)_operationStack.Pop().AsNumber(); + } + + var date = new DateTime( + factArgs[0], + factArgs[1], + factArgs[2], + factArgs[3], + factArgs[4], + factArgs[5]); + + _operationStack.Push(ValueFactory.Create(date)); + + } + else + { + throw new RuntimeException("Неверное количество параметров"); + } + + NextInstruction(); + } + + private void Type(int arg) + { + var typeName = PopRawBslValue().ToString(_process); + var type = _typeManager.GetTypeByName(typeName); + var value = new BslTypeValue(type); + _operationStack.Push(value); + NextInstruction(); + } + + private void ValType(int arg) + { + var value = _operationStack.Pop(); + var valueType = new BslTypeValue(value.SystemType); + _operationStack.Push(valueType); + NextInstruction(); + } + + private void StrLen(int arg) + { + var str = PopRawBslValue().ToString(_process); + _operationStack.Push(ValueFactory.Create(str.Length)); + NextInstruction(); + } + + private void TrimL(int arg) + { + var str = PopRawBslValue().ToString(_process).TrimStart(); + _operationStack.Push(ValueFactory.Create(str)); + NextInstruction(); + } + + private void TrimR(int arg) + { + var str = PopRawBslValue().ToString(_process); + + int lastIdx = str.Length-1; + for (int i = lastIdx; i >= 0; i--) + { + if (!Char.IsWhiteSpace(str[i])) + { + var trimmed = str.Substring(0, i+1); + _operationStack.Push(ValueFactory.Create(trimmed)); + NextInstruction(); + return; + } + } + + _operationStack.Push(ValueFactory.Create("")); + NextInstruction(); + } + + private void TrimLR(int arg) + { + var str = PopRawBslValue().ToString(_process).Trim(); + _operationStack.Push(ValueFactory.Create(str)); + NextInstruction(); + } + + private void Left(int arg) + { + var len = (int)_operationStack.Pop().AsNumber(); + var str = PopRawBslValue().ToString(_process); + + if (len > str.Length) + len = str.Length; + else if (len < 0) + { + _operationStack.Push(ValueFactory.Create("")); + NextInstruction(); + return; + } + + _operationStack.Push(ValueFactory.Create(str[..len])); + NextInstruction(); + } + + private void Right(int arg) + { + var len = (int)_operationStack.Pop().AsNumber(); + var str = PopRawBslValue().ToString(_process); + + if (len > str.Length) + len = str.Length; + else if (len < 0) + { + _operationStack.Push(ValueFactory.Create("")); + NextInstruction(); + return; + } + + int startIdx = str.Length - len; + _operationStack.Push(ValueFactory.Create(str.Substring(startIdx, len))); + + NextInstruction(); + } + + private void Mid(int arg) + { + string str; + int start; + int len; + if (arg == 2) + { + start = (int)_operationStack.Pop().AsNumber(); + str = PopRawBslValue().ToString(_process); + len = str.Length-start+1; + } + else + { + len = (int)_operationStack.Pop().AsNumber(); + start = (int)_operationStack.Pop().AsNumber(); + str = PopRawBslValue().ToString(_process); + } + + if (start < 1) + start = 1; + + if (start+len > str.Length || len < 0) + len = str.Length-start+1; + + string result; + + if (start > str.Length || len == 0) + { + result = ""; + } + else + { + result = str.Substring(start - 1, len); + } + + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void StrPos(int arg) + { + var needle = PopRawBslValue().ToString(_process); + var haystack = PopRawBslValue().ToString(_process); + + var result = haystack.IndexOf(needle, StringComparison.Ordinal) + 1; + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void UCase(int arg) + { + var result = PopRawBslValue().ToString(_process).ToUpper(); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void LCase(int arg) + { + var result = PopRawBslValue().ToString(_process).ToLower(); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void TCase(int arg) + { + var argValue = PopRawBslValue().ToString(_process); + + char[] array = argValue.ToCharArray(); + // Handle the first letter in the string. + bool inWord = false; + if (array.Length >= 1) + { + if (char.IsLetter(array[0])) + inWord = true; + + if(char.IsLower(array[0])) + { + array[0] = char.ToUpper(array[0]); + } + } + // Scan through the letters, checking for spaces. + // ... Uppercase the lowercase letters following spaces. + for (int i = 1; i < array.Length; i++) + { + if (inWord && Char.IsLetter(array[i])) + array[i] = Char.ToLower(array[i]); + else if (Char.IsSeparator(array[i]) || Char.IsPunctuation(array[i])) + inWord = false; + else if(!inWord && Char.IsLetter(array[i])) + { + inWord = true; + if (char.IsLower(array[i])) + { + array[i] = char.ToUpper(array[i]); + } + } + } + + var result = new string(array); + + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void Chr(int arg) + { + var code = (int)_operationStack.Pop().AsNumber(); + + var result = (code >= 0 && code < 65536) ? new String((char)code, 1) : String.Empty; + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void ChrCode(int arg) + { + string strChar; + int position; + + if(arg == 2) + { + position = (int)_operationStack.Pop().AsNumber()-1; + strChar = PopRawBslValue().ToString(_process); + } + else if(arg == 1) + { + strChar = PopRawBslValue().ToString(_process); + position = 0; + } + else + { + throw new WrongStackConditionException(); + } + + int result = (position >= 0 && position < strChar.Length) ? strChar[position] : -1; + + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void EmptyStr(int arg) + { + var str = PopRawBslValue().ToString(_process); + + _operationStack.Push(ValueFactory.Create(String.IsNullOrWhiteSpace(str))); + NextInstruction(); + } + + private void StrReplace(int arg) + { + var newVal = PopRawBslValue().ToString(_process); + var searchVal = PopRawBslValue().ToString(_process); + var sourceString = PopRawBslValue().ToString(_process); + + var result = !string.IsNullOrEmpty(searchVal) ? sourceString.Replace(searchVal, newVal) : sourceString; + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void StrGetLine(int arg) + { + var lineNumber = (int)_operationStack.Pop().AsNumber(); + var strArg =PopRawBslValue().ToString(_process); + string result = ""; + if (lineNumber >= 1) + { + string[] subStrVals = strArg.Split(new Char[] { '\n' }, lineNumber + 1); + result = subStrVals[lineNumber - 1]; + } + + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void StrLineCount(int arg) + { + var strArg = PopRawBslValue().ToString(_process); + int pos = 0; + int lineCount = 1; + while (pos >= 0 && pos < strArg.Length) + { + pos = strArg.IndexOf('\n', pos); + if (pos >= 0) + { + lineCount++; + pos++; + } + } + + _operationStack.Push(ValueFactory.Create(lineCount)); + NextInstruction(); + } + + private void StrEntryCount(int arg) + { + var what = PopRawBslValue().ToString(_process); + var where = PopRawBslValue().ToString(_process); + + var pos = where.IndexOf(what); + var entryCount = 0; + while(pos >= 0) + { + entryCount++; + var nextIndex = pos + what.Length; + if (nextIndex >= where.Length) + break; + + pos = where.IndexOf(what, nextIndex); + } + + _operationStack.Push(ValueFactory.Create(entryCount)); + NextInstruction(); + } + + private void Year(int arg) + { + var date = _operationStack.Pop().AsDate().Year; + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private void Month(int arg) + { + var date = _operationStack.Pop().AsDate().Month; + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private void Day(int arg) + { + var date = _operationStack.Pop().AsDate().Day; + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private void Hour(int arg) + { + var date = _operationStack.Pop().AsDate().Hour; + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private void Minute(int arg) + { + var date = _operationStack.Pop().AsDate().Minute; + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private void Second(int arg) + { + var date = _operationStack.Pop().AsDate().Second; + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private static DateTime DropTimeFraction(in DateTime date) + { + return new DateTime(date.Year, date.Month, date.Day); + } + + private void BegOfWeek(int arg) + { + var date = DropTimeFraction(_operationStack.Pop().AsDate()); + + var numDayOfWeek = (int)date.DayOfWeek; + if (numDayOfWeek == 0) + { + numDayOfWeek = 7; + } + + var desiredDate = date.AddDays(-(numDayOfWeek - 1)); + _operationStack.Push(ValueFactory.Create(desiredDate)); + + NextInstruction(); + } + + private void BegOfYear(int arg) + { + var year = _operationStack.Pop().AsDate().Year; + _operationStack.Push(ValueFactory.Create(new DateTime(year,1,1))); + NextInstruction(); + } + + private void BegOfMonth(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, 1); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void BegOfDay(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, date.Day); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void BegOfHour(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, 0, 0); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void BegOfMinute(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 0); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void BegOfQuarter(int arg) + { + //1,4,7,10 + var date = _operationStack.Pop().AsDate(); + + int quarterMonth; + if (date.Month >= 1 && date.Month <= 3) + { + quarterMonth = 1; + } + else if (date.Month >= 4 && date.Month <= 6) + { + quarterMonth = 4; + } + else if (date.Month >= 7 && date.Month <= 9) + { + quarterMonth = 7; + } + else + { + quarterMonth = 10; + } + var result = new DateTime(date.Year, quarterMonth, 1); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void EndOfYear(int arg) + { + var year = _operationStack.Pop().AsDate().Year; + _operationStack.Push(ValueFactory.Create(new DateTime(year, 12, DateTime.DaysInMonth(year,12), 23, 59, 59))); + NextInstruction(); + } + + private void EndOfMonth(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month), 23, 59, 59); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void EndOfDay(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, date.Day, 23, 59, 59); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void EndOfHour(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, 59, 59); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void EndOfMinute(int arg) + { + var date = _operationStack.Pop().AsDate(); + var result = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, 59); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void EndOfQuarter(int arg) + { + //1,4,7,10 + var date = _operationStack.Pop().AsDate(); + + int quarterMonth; + if (date.Month >= 1 && date.Month <= 3) + { + quarterMonth = 3; + } + else if (date.Month >= 4 && date.Month <= 6) + { + quarterMonth = 6; + } + else if (date.Month >= 7 && date.Month <= 9) + { + quarterMonth = 9; + } + else + { + quarterMonth = 12; + } + var result = new DateTime(date.Year, quarterMonth, DateTime.DaysInMonth(date.Year, quarterMonth), 23, 59, 59); + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void EndOfWeek(int arg) + { + var date = DropTimeFraction(_operationStack.Pop().AsDate()); + + var numDayOfWeek = (int)date.DayOfWeek; + if (numDayOfWeek == 0) + { + numDayOfWeek = 7; + } + + var desiredDate = date.AddDays(7 - numDayOfWeek); + _operationStack.Push(ValueFactory.Create(new DateTime(desiredDate.Year, desiredDate.Month, desiredDate.Day, 23, 59, 59))); + + NextInstruction(); + } + + private void WeekOfYear(int arg) + { + var date = _operationStack.Pop().AsDate(); + var cal = new System.Globalization.GregorianCalendar(); + + _operationStack.Push(ValueFactory.Create(cal.GetWeekOfYear(date, System.Globalization.CalendarWeekRule.FirstDay, System.DayOfWeek.Monday))); + NextInstruction(); + } + + private void DayOfYear(int arg) + { + var date = _operationStack.Pop().AsDate().DayOfYear; + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private void DayOfWeek(int arg) + { + var day = (int)_operationStack.Pop().AsDate().DayOfWeek; + + if (day == 0) + { + day = 7; + } + + _operationStack.Push(ValueFactory.Create(day)); + NextInstruction(); + } + + private void AddMonth(int arg) + { + var numToAdd = (int)_operationStack.Pop().AsNumber(); + var date = _operationStack.Pop().AsDate(); + _operationStack.Push(ValueFactory.Create(date.AddMonths(numToAdd))); + NextInstruction(); + } + + private void CurrentDate(int arg) + { + var date = DateTime.Now; + date = date.AddTicks(-(date.Ticks % TimeSpan.TicksPerSecond)); + _operationStack.Push(ValueFactory.Create(date)); + NextInstruction(); + } + + private void Integer(int arg) + { + var num = Math.Truncate(_operationStack.Pop().AsNumber()); + _operationStack.Push(ValueFactory.Create(num)); + NextInstruction(); + } + + private void Round(int arg) + { + decimal num; + int digits; + int mode = 1; // по умолчанию Окр15как20 + if (arg == 1) + { + num = _operationStack.Pop().AsNumber(); + digits = 0; + } + else if (arg == 2) + { + digits = (int)_operationStack.Pop().AsNumber(); + num = _operationStack.Pop().AsNumber(); + } + else + { + mode = (int)_operationStack.Pop().AsNumber(); + mode = mode == 0 ? 0 : 1; + digits = (int)_operationStack.Pop().AsNumber(); + num = _operationStack.Pop().AsNumber(); + } + + decimal result; + if (digits >= 0) + { + result = Math.Round(num, digits, MidpointRounding.AwayFromZero); + if (mode == 0) + { + int scale = (int)Math.Pow(10, digits); + // для.Net Core 3+, 5+ можно использовать MidpointRounding.ToZero + var diff = (result - num) * scale; + if (diff == 0.5m) + result -= 1m / scale; + else if (diff == -0.5m) + result += 1m / scale; + } + } + else + { + int scale = (int)Math.Pow(10, -digits); + num /= scale; + result = Math.Round(num, MidpointRounding.AwayFromZero); + if (mode == 0) + { + var diff = result - num; + if (diff == 0.5m) + result -= 1m; + else if (diff == -0.5m) + result += 1m; + } + result *= scale; + } + + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private void Log(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Log((double) num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void Log10(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Log10((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void Sin(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Sin((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void Cos(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Cos((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void Tan(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Tan((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void ASin(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Asin((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void ACos(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Acos((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void ATan(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Atan((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + private void Exp(int arg) + { + var num = _operationStack.Pop().AsNumber(); + var result = Math.Exp((double)num); + _operationStack.Push(ValueFactory.Create((decimal)result)); + NextInstruction(); + } + + private void Pow(int arg) + { + var powPower = _operationStack.Pop().AsNumber(); + var powBase = _operationStack.Pop().AsNumber(); + + int exp = (int)powPower; + decimal result; + if (exp >= 0 && exp == powPower) + result = PowInt(powBase, (uint)exp); + else + result = (decimal)Math.Pow((double)powBase, (double)powPower); + + _operationStack.Push(ValueFactory.Create(result)); + NextInstruction(); + } + + private static decimal PowInt(decimal bas, uint exp) + { + decimal pow = 1; + + while (true) + { + if ((exp & 1) == 1) pow *= bas; + exp >>= 1; + if (exp == 0) break; + bas *= bas; + } + + return pow; + } + + private void Sqrt(int arg) + { + var num = (double)_operationStack.Pop().AsNumber(); + var root = Math.Sqrt(num); + _operationStack.Push(ValueFactory.Create((decimal)root)); + NextInstruction(); + } + + private void Min(int argCount) + { + System.Diagnostics.Debug.Assert(argCount > 0); + + IValue min = PopRawValue(); + while (--argCount > 0) + { + var current = PopRawValue(); + if (current.CompareTo(min) < 0) + min = current; + } + + _operationStack.Push(min); + + NextInstruction(); + } + + private void Max(int argCount) + { + System.Diagnostics.Debug.Assert(argCount > 0); + + IValue max = PopRawValue(); + while (--argCount > 0) + { + var current = PopRawValue(); + if (current.CompareTo(max) > 0) + max = current; + } + + _operationStack.Push(max); + NextInstruction(); + } + + private void Format(int arg) + { + var formatString = PopRawBslValue().ToString(_process); + var valueToFormat = PopRawValue(); + + var formatted = ValueFormatter.Format((BslValue)valueToFormat, formatString); + + _operationStack.Push(ValueFactory.Create(formatted)); + NextInstruction(); + + } + + private void ExceptionInfo(int arg) + { + if (_currentFrame.LastException != null) + { + var excInfo = new ExceptionInfoContext(_currentFrame.LastException); + _operationStack.Push(excInfo); + } + else + { + _operationStack.Push(ExceptionInfoContext.EmptyExceptionInfo()); + } + NextInstruction(); + } + + private void ExceptionDescr(int arg) + { + if (_currentFrame.LastException != null) + { + var excInfo = new ExceptionInfoContext(_currentFrame.LastException); + _operationStack.Push(ValueFactory.Create(excInfo.MessageWithoutCodeFragment)); + } + else + { + _operationStack.Push(ValueFactory.Create("")); + } + NextInstruction(); + } + + private void ModuleInfo(int arg) + { + if (_debugInfo != null) + { + _operationStack.Push(_debugInfo); + } + else + { + var currentScript = this.CurrentScript; + if (currentScript != null) + { + _operationStack.Push(currentScript); + } + else + { + _operationStack.Push(ValueFactory.Create()); + } + } + NextInstruction(); + } + + private void NewFunc(int argCount) + { + IValue[] argValues; + + if (argCount == 0) + argValues = Array.Empty(); + else + { + var valueFromStack = PopRawValue(); + if (valueFromStack is IValueArray array) + argValues = array.ToArray(); + else + argValues = Array.Empty(); + } + + var typeName = PopRawBslValue().ToString(_process); + _operationStack.Push(CreateInstance(typeName, argValues)); + NextInstruction(); + } + + private IValue CreateInstance(string typeName, IValue[] args) + { + if (!_typeManager.TryGetType(typeName, out var type)) + { + throw RuntimeException.TypeIsNotDefined(typeName); + } + + // TODO убрать cast после рефакторинга ITypeFactory + var factory = (TypeFactory)_typeManager.GetFactoryFor(type); + var context = new TypeActivationContext + { + TypeName = typeName, + TypeManager = _typeManager, + Services = _process.Services, + CurrentProcess = _process }; - } - - // multithreaded instance - [ThreadStatic] - private static MachineInstance _currentThreadWorker; - - private static void SetCurrentMachineInstance(MachineInstance inst) - { - _currentThreadWorker = inst; - } - - public static MachineInstance Current - { - get - { - if(_currentThreadWorker == null) - _currentThreadWorker = new MachineInstance(); - - return _currentThreadWorker; - } - } - } -} + return factory.Activate(context, args); + } + + #endregion + + #endregion + + private StackRuntimeModule CompileExpressionModule(string expression) + { + var entryId = CurrentCodeEntry().ToString(); + + var stringSource = SourceCodeBuilder.Create() + .FromString(expression) + .WithName($"{entryId}:") + .Build(); + + var compiler = _process.Services.Resolve(); + compiler.SharedSymbols = ExtractCompilerContext(); + var module = (StackRuntimeModule)compiler.CompileExpression(stringSource); + return module; + } + + private StackRuntimeModule CompileExecutionBatchModule(string execBatch) + { + var entryId = CurrentCodeEntry().ToString(); + + var stringSource = SourceCodeBuilder.Create() + .FromString(execBatch) + .WithName($"{entryId}:") + .Build(); + + var compiler = _process.Services.Resolve(); + compiler.SharedSymbols = ExtractCompilerContext(); + var module = (StackRuntimeModule)compiler.CompileBatch(stringSource); + + return module; + } + + private SymbolTable ExtractCompilerContext() + { + var ctx = new SymbolTable(); + var scopes = _currentFrame.Scopes ?? Array.Empty(); + var scopeCount = scopes.Count; + var thisScope = _currentFrame.ThisScope; + + // Добавляем все контексты из scopes (глобальные + локальные из предыдущих кадров) + for (int index = 0; index < scopeCount; index++) + { + var scope = scopes[index]; + + var symbolScope = new SymbolScope(); + + // Добавляем методы + for (int i = 0; i < scope.MethodsCount; i++) + { + var methodInfo = scope.GetMethod(i); + symbolScope.DefineMethod(methodInfo.ToSymbol()); + } + + // Добавляем переменные + for (int i = 0; i < scope.VariablesCount; i++) + { + var variable = scope.GetVariable(i); + + string alias = null; + if (scope is IRuntimeContextInstance runtimeContext) + { + try + { + var propInfo = runtimeContext.GetPropertyInfo(i); + alias = propInfo.Alias; + } + catch + { + // Алиас остается пустым + } + } + + if (alias != null) + { + symbolScope.DefineVariable(new AliasedVariableSymbol(variable.Name, alias)); + } + else + { + symbolScope.DefineVariable(new LocalVariableSymbol(variable.Name)); + } + } + + ctx.PushScope(symbolScope, ScopeBindingDescriptor.FrameScope(index)); + } + + // Локальные переменные текущего фрейма + var locals = new SymbolScope(); + foreach (var variable in _currentFrame.Locals) + { + locals.DefineVariable(new LocalVariableSymbol(variable.Name)); + } + + ctx.PushScope(locals, ScopeBindingDescriptor.ThisScope()); + return ctx; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private IAttachableContext ResolveBindingTarget(ModuleSymbolBinding binding) + { + return binding.ResolveTarget(_currentFrame); + } + + private void NextInstruction() + { + _currentFrame.InstructionPointer++; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IValue RawValue(IValue val) + { + if (val is IValueReference r) + { + return r.Value; + } + + return val; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BslValue RawBslValue(IValue val) + { + if (val is IValueReference r) + { + return r.BslValue; + } + + return (BslValue)val; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private IValue PopRawValue() + { + return RawValue(_operationStack.Pop()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private BslValue PopRawBslValue() + { + return RawBslValue(_operationStack.Pop()); + } + + public IList GetExecutionFrames() + { + CreateFullCallstack(); + return _fullCallstackCache; + } + + public IList GetFrameLocals(int frameId) + { + Debug.Assert(_fullCallstackCache != null); + if (frameId < 0 || frameId >= _fullCallstackCache.Count) + return Array.Empty(); + + var frame = _fullCallstackCache[frameId]; + return frame.FrameObject.Locals; + } + + public IList GetModuleVariables(int frameId) + { + Debug.Assert(_fullCallstackCache != null); + if (frameId < 0 || frameId >= _fullCallstackCache.Count) + return Array.Empty(); + + var frame = _fullCallstackCache[frameId].FrameObject; + var thisScope = frame.ThisScope; + + var variables = new List(thisScope.VariablesCount); + for (int i = 0; i < thisScope.VariablesCount; i++) + { + variables.Add(thisScope.GetVariable(i)); + } + + return variables; + } + + private static ExecutionFrameInfo FrameInfo(StackRuntimeModule module, ExecutionFrame frame) + => new ExecutionFrameInfo() + { + LineNumber = frame.LineNumber, + MethodName = frame.MethodName, + Source = module.Source.Location, + FrameObject = frame + }; + } +} diff --git a/src/ScriptEngine/Machine/MachineMethod.cs b/src/ScriptEngine/Machine/MachineMethod.cs new file mode 100644 index 000000000..c83f51b47 --- /dev/null +++ b/src/ScriptEngine/Machine/MachineMethod.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 ScriptEngine.Machine +{ + public struct MachineMethod + { + public MethodSignature Signature { get; set; } + + public int EntryPoint { get; set; } + + public string[] LocalVariables { get; set; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/MachineMethodInfo.cs b/src/ScriptEngine/Machine/MachineMethodInfo.cs new file mode 100644 index 000000000..6a4dd85cf --- /dev/null +++ b/src/ScriptEngine/Machine/MachineMethodInfo.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +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.Runtime.CompilerServices; +using OneScript.Contexts; + +namespace ScriptEngine.Machine +{ + internal class MachineMethodInfo : BslScriptMethodInfo + { + private MachineMethod _method; + + internal void SetRuntimeParameters(int entryPoint, string[] locals) + { + _method = new MachineMethod + { + EntryPoint = entryPoint, + LocalVariables = locals, + Signature = this.MakeSignature() + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal MachineMethod GetRuntimeMethod() => _method; + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/MachineStopManager.cs b/src/ScriptEngine/Machine/MachineStopManager.cs index 11f5eafc1..bdb180da0 100644 --- a/src/ScriptEngine/Machine/MachineStopManager.cs +++ b/src/ScriptEngine/Machine/MachineStopManager.cs @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the using System; using System.Linq; +using ScriptEngine.Machine.Debugger; namespace ScriptEngine.Machine { @@ -19,8 +20,6 @@ internal enum DebugState SteppingOut } - - internal class MachineStopManager { private struct StopPoint @@ -32,23 +31,37 @@ private struct StopPoint private DebugState _currentState = DebugState.Running; private readonly IBreakpointManager _breakpoints; private readonly MachineInstance _machine; + private readonly IThreadEventsListener _threadManager; private ExecutionFrame[] _stopFrames; private StopPoint _lastStopPoint; - public MachineStopManager(MachineInstance runner, IBreakpointManager breakpoints) + public MachineStopManager(MachineInstance runner, IThreadEventsListener threadManager, IBreakpointManager breakpoints) { _machine = runner ?? throw new ArgumentNullException(nameof(runner)); + _threadManager = threadManager; _breakpoints = breakpoints ?? throw new ArgumentNullException(nameof(runner)); } public IBreakpointManager Breakpoints => _breakpoints; public MachineStopReason LastStopReason { get; internal set; } + public string LastStopErrorMessage { get; internal set; } public DebugState CurrentState => _currentState; + public void NotifyStop(MachineStopReason reason, string errMessage) + { + _threadManager.ThreadStopped(_machine.Process.VirtualThreadId, reason, errMessage); + } + + public void NotifyStop() + { + _threadManager.ThreadStopped(_machine.Process.VirtualThreadId, LastStopReason, LastStopErrorMessage); + } + public bool ShouldStopAtThisLine(string module, ExecutionFrame currentFrame) { bool mustStop = false; + switch (_currentState) { case DebugState.Running: @@ -71,13 +84,35 @@ public bool ShouldStopAtThisLine(string module, ExecutionFrame currentFrame) if (mustStop) { - // здесь мы уже останавливались + // здесь мы уже останавливались? if (_lastStopPoint.frame != currentFrame || _lastStopPoint.line != currentFrame.LineNumber) { if (_currentState == DebugState.Running) + { LastStopReason = MachineStopReason.Breakpoint; + + // Проверим существование условия остановки + var condition = Breakpoints.GetCondition(module, currentFrame.LineNumber); + + if (!string.IsNullOrEmpty(condition)) + { + try + { + mustStop = _machine.EvaluateInFrame(condition, currentFrame).AsBoolean(); + } + catch (Exception ex) + { + // Остановим и сообщим, что остановка произошла не по условию, а в результате ошибки вычисления + mustStop = true; + LastStopReason = MachineStopReason.BreakpointConditionError; + LastStopErrorMessage = $"Не удалось выполнить условие точки останова: {ex.Message}"; + } + } + } else + { LastStopReason = MachineStopReason.Step; + } _lastStopPoint = new StopPoint() { @@ -98,7 +133,7 @@ public bool ShouldStopAtThisLine(string module, ExecutionFrame currentFrame) private bool HitBreakpointOnLine(string module, ExecutionFrame currentFrame) { - return _breakpoints.Find(module, currentFrame.LineNumber); + return _breakpoints.FindBreakpoint(module, currentFrame.LineNumber); } private bool FrameIsInStopList(ExecutionFrame currentFrame) @@ -106,7 +141,7 @@ private bool FrameIsInStopList(ExecutionFrame currentFrame) return _stopFrames != null && _stopFrames.Contains(currentFrame); } - public void StepOver(ExecutionFrame currentFrame) + public void StepOver() { _currentState = DebugState.SteppingOver; _stopFrames = _machine.GetExecutionFrames().Select(x => x.FrameObject).ToArray(); @@ -118,15 +153,10 @@ public void StepIn() _currentState = DebugState.SteppingIn; } - internal void StepOut(ExecutionFrame currentFrame) + internal void StepOut() { _currentState = DebugState.SteppingOut; _stopFrames = _machine.GetExecutionFrames().Select(x => x.FrameObject).Skip(1).ToArray(); } - - internal void Continue() - { - _lastStopPoint = default(StopPoint); - } } } diff --git a/src/ScriptEngine/Machine/MachineStopReason.cs b/src/ScriptEngine/Machine/MachineStopReason.cs new file mode 100644 index 000000000..99284dbef --- /dev/null +++ b/src/ScriptEngine/Machine/MachineStopReason.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace ScriptEngine.Machine +{ + public enum MachineStopReason + { + Breakpoint, + BreakpointConditionError, + Step, + Exception + } +} diff --git a/src/ScriptEngine/Machine/MachineStoppedEventArgs.cs b/src/ScriptEngine/Machine/MachineStoppedEventArgs.cs deleted file mode 100644 index 330b5e27c..000000000 --- a/src/ScriptEngine/Machine/MachineStoppedEventArgs.cs +++ /dev/null @@ -1,40 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Threading; - -namespace ScriptEngine.Machine -{ - public class MachineStoppedEventArgs : EventArgs - { - public MachineStoppedEventArgs(MachineStopReason reason) - { - Reason = reason; - ThreadId = Thread.CurrentThread.ManagedThreadId; - } - - public MachineStoppedEventArgs(MachineStopReason reason, int threadId) - { - Reason = reason; - ThreadId = threadId; - } - - public MachineStopReason Reason { get; } - - public int ThreadId { get; } - - } - - public enum MachineStopReason - { - Breakpoint, - Step, - Exception - } - -} diff --git a/src/ScriptEngine/Machine/MethodSignature.cs b/src/ScriptEngine/Machine/MethodSignature.cs new file mode 100644 index 000000000..5d4f1e0fd --- /dev/null +++ b/src/ScriptEngine/Machine/MethodSignature.cs @@ -0,0 +1,102 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.CompilerServices; + +namespace ScriptEngine.Machine +{ + [Serializable] + public struct MethodSignature + { + public string Name; + public string Alias; + public ParameterDefinition[] Params; + public AnnotationDefinition[] Annotations; + public MethodFlags Flags; + + public bool IsFunction + { + get => IsSet(MethodFlags.IsFunction); + set => SetFlag(MethodFlags.IsFunction, value); + } + + public bool IsExport + { + get => IsSet(MethodFlags.IsExported); + set => SetFlag(MethodFlags.IsExported, value); + } + + public bool IsAsync + { + get => IsSet(MethodFlags.IsAsync); + set => SetFlag(MethodFlags.IsAsync, value); + } + + public bool IsDeprecated + { + get => IsSet(MethodFlags.IsDeprecated); + set => SetFlag(MethodFlags.IsDeprecated, value); + } + + public bool ThrowOnUseDeprecated + { + get => IsSet(MethodFlags.ThrowOnUsage); + set => SetFlag(MethodFlags.ThrowOnUsage, value); + } + + public int ArgCount => Params?.Length ?? 0; + public int AnnotationsCount => Annotations?.Length ?? 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetFlag(MethodFlags flag) => Flags |= flag; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SetFlag(MethodFlags flag, bool value) + { + if(value) SetFlag(flag); + else ClearFlag(flag); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ClearFlag(MethodFlags flag) => Flags &= ~flag; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IsSet(MethodFlags flag) => (Flags & flag) != 0; + } + + [Serializable] + [Flags] + public enum MethodFlags + { + Default = 0, + IsFunction = 1, + IsExported = 2, + IsDeprecated = 4, + ThrowOnUsage = 8, + IsAsync = 16 + } + + [Serializable] + public struct ParameterDefinition + { + public string Name; + public bool IsByValue; + public bool HasDefaultValue; + public int DefaultValueIndex; + public AnnotationDefinition[] Annotations; + + public int AnnotationsCount => Annotations?.Length ?? 0; + + public const int UNDEFINED_VALUE_INDEX = -1; + + public bool IsDefaultValueDefined() + { + return HasDefaultValue && DefaultValueIndex != UNDEFINED_VALUE_INDEX; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/ModuleSymbolBinding.cs b/src/ScriptEngine/Machine/ModuleSymbolBinding.cs new file mode 100644 index 000000000..cecb8e329 --- /dev/null +++ b/src/ScriptEngine/Machine/ModuleSymbolBinding.cs @@ -0,0 +1,62 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Compilation.Binding; + +namespace ScriptEngine.Machine +{ + public struct ModuleSymbolBinding : IEquatable + { + public IAttachableContext Target { get; set; } + + public int MemberNumber { get; set; } + + public ScopeBindingKind Kind { get; set; } + + public int ScopeIndex { get; set; } + + internal IAttachableContext ResolveTarget(ExecutionFrame frame) + { + return Kind switch + { + ScopeBindingKind.FrameScope => ResolveFrameScope(frame), + ScopeBindingKind.ThisScope => frame.ThisScope, + _ => Target + }; + } + + private IAttachableContext ResolveFrameScope(ExecutionFrame frame) + { + if (frame?.Scopes == null) + throw new InvalidOperationException("Frame scopes are not available"); + if (ScopeIndex < 0 || ScopeIndex >= frame.Scopes.Count) + throw new InvalidOperationException($"Invalid scope index {ScopeIndex}"); + + return frame.Scopes[ScopeIndex]; + } + + public bool Equals(ModuleSymbolBinding other) + { + return ReferenceEquals(Target, other.Target) + && MemberNumber == other.MemberNumber + && Kind == other.Kind + && ScopeIndex == other.ScopeIndex; + } + + public override bool Equals(object obj) + { + return obj is ModuleSymbolBinding other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Target, MemberNumber, Kind, ScopeIndex); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/ParametrizedRuntimeException.cs b/src/ScriptEngine/Machine/ParametrizedRuntimeException.cs index 561f9c843..df038d0fb 100644 --- a/src/ScriptEngine/Machine/ParametrizedRuntimeException.cs +++ b/src/ScriptEngine/Machine/ParametrizedRuntimeException.cs @@ -5,15 +5,19 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Exceptions; + namespace ScriptEngine.Machine { public class ParametrizedRuntimeException : RuntimeException { - public ParametrizedRuntimeException(string msg, IValue parameter) : base(msg) + public ParametrizedRuntimeException(string msg, IValue parameter, IValue cause = null) : base(msg) { Parameter = parameter; + Cause = cause; } public IValue Parameter { get; private set; } + public IValue Cause { get; private set; } } } diff --git a/src/ScriptEngine/Machine/PropertyBag.cs b/src/ScriptEngine/Machine/PropertyBag.cs index b27138db6..0cf2bbb4f 100644 --- a/src/ScriptEngine/Machine/PropertyBag.cs +++ b/src/ScriptEngine/Machine/PropertyBag.cs @@ -5,56 +5,61 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; +using OneScript.Contexts; using ScriptEngine.Machine.Contexts; namespace ScriptEngine.Machine { - class PropertyBag : DynamicPropertiesAccessor, IAttachableContext + internal class PropertyBag : DynamicPropertiesAccessor, IAttachableContext { - private struct PropertyAccessFlags - { - public bool CanRead; - public bool CanWrite; - } - private readonly List _values = new List(); - private readonly List _accessFlags = new List(); - + public void Insert(IValue value, string identifier) { Insert(value, identifier, true, true); } - public void Insert(IValue value, string identifier, bool canRead, bool canWrite) + public int Insert(IValue value, string identifier, bool canRead, bool canWrite) { - var num = RegisterProperty(identifier); + var num = RegisterProperty(identifier, canRead, canWrite); if (num == _values.Count) { _values.Add(null); - _accessFlags.Add(new PropertyAccessFlags() { CanRead = canRead, CanWrite = canWrite }); } - if (value == null) + value ??= ValueFactory.Create(); + + SetPropValue(num, value); + + return num; + } + + public int Insert(IValue value, BslPropertyInfo definition) + { + var num = RegisterProperty(definition); + if (num == _values.Count) { - value = ValueFactory.Create(); + _values.Add(null); } - SetPropValue(num, value); + value ??= ValueFactory.Create(); + + _values[num] = value; + return num; } public override bool IsPropReadable(int propNum) { - return _accessFlags[propNum].CanRead; + return GetPropertyInfo(propNum).CanRead; } public override bool IsPropWritable(int propNum) { - return _accessFlags[propNum].CanWrite; + return GetPropertyInfo(propNum).CanWrite; } public override IValue GetPropValue(int propNum) @@ -66,30 +71,24 @@ public override void SetPropValue(int propNum, IValue newVal) { _values[propNum] = newVal; } + + public int Count => _values.Count; - public int Count + public override int GetMethodsCount() { - get - { - return _values.Count; - } + return 0; } #region IAttachableContext Members - public void OnAttach(MachineInstance machine, out IVariable[] variables, out MethodInfo[] methods) - { - variables = new IVariable[this.Count]; - var props = GetProperties().OrderBy(x => x.Value).Select(x=>x.Key).ToArray(); - Debug.Assert(props.Length == variables.Length); + IVariable IAttachableContext.GetVariable(int index) => + Variable.CreateContextPropertyReference(this, index, GetPropertyName(index)); + + BslMethodInfo IAttachableContext.GetMethod(int index) => throw new ArgumentOutOfRangeException(); - for (var i = 0; i < variables.Length; i++) - { - variables[i] = Variable.CreateContextPropertyReference(this, i, props[i]); - } - - methods = new MethodInfo[0]; - } + int IAttachableContext.VariablesCount => this.Count; + + int IAttachableContext.MethodsCount => 0; #endregion } diff --git a/src/ScriptEngine/Machine/RciHelperExtensions.cs b/src/ScriptEngine/Machine/RciHelperExtensions.cs new file mode 100644 index 000000000..d4943a4f3 --- /dev/null +++ b/src/ScriptEngine/Machine/RciHelperExtensions.cs @@ -0,0 +1,64 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Exceptions; + +namespace ScriptEngine.Machine +{ + [Obsolete] + public static class RciHelperExtensions + { + public static IEnumerable GetMethods(this IRuntimeContextInstance context) + { + var methods = new BslMethodInfo[context.GetMethodsCount()]; + for (int i = 0; i < methods.Length; i++) + { + methods[i] = context.GetMethodInfo(i); + } + + return methods; + } + + public static IEnumerable GetProperties(this IRuntimeContextInstance context) + { + var infos = new BslPropertyInfo[context.GetPropCount()]; + for (var i = 0; i < infos.Length; i++) + { + infos[i] = context.GetPropertyInfo(i); + } + + return infos; + } + + public static IValue GetPropValue(this IRuntimeContextInstance context, string propName) + { + int propNum = context.GetPropertyNumber(propName); + + if (propNum == -1) + { + throw RuntimeException.InvalidArgumentValue(propName); + } + + return context.GetPropValue(propNum); + } + + public static void SetPropValue(this IRuntimeContextInstance context, string propName, IValue value) + { + int propNum = context.GetPropertyNumber(propName); + + if (propNum == -1) + { + throw RuntimeException.InvalidArgumentValue(propName); + } + + context.SetPropValue(propNum, value); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Rcw/RcwMemberMetadata.cs b/src/ScriptEngine/Machine/Rcw/RcwMemberMetadata.cs deleted file mode 100644 index 6c145d250..000000000 --- a/src/ScriptEngine/Machine/Rcw/RcwMemberMetadata.cs +++ /dev/null @@ -1,45 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Rcw -{ - public class RcwMethodMetadata : RcwMemberMetadata - { - public bool? IsFunction { get; } - - public RcwMethodMetadata(string name, int dispId, bool? isFunc) : base(name, dispId) - { - IsFunction = isFunc; - } - } - - public class RcwPropertyMetadata : RcwMemberMetadata - { - public bool IsReadable { get; internal set; } - - public bool IsWritable { get; internal set; } - - public RcwPropertyMetadata(string name, int dispId) : base(name, dispId) - { - IsReadable = true; - IsWritable = true; - } - } - - public abstract class RcwMemberMetadata - { - public int DispatchId { get; } - - public string Name { get; } - - protected RcwMemberMetadata(string name, int dispId) - { - Name = name; - DispatchId = dispId; - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Rcw/RcwMembersMetadataCollection.cs b/src/ScriptEngine/Machine/Rcw/RcwMembersMetadataCollection.cs deleted file mode 100644 index 198439b36..000000000 --- a/src/ScriptEngine/Machine/Rcw/RcwMembersMetadataCollection.cs +++ /dev/null @@ -1,52 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; - -namespace ScriptEngine.Machine.Rcw -{ - public class RcwMembersMetadataCollection where T : RcwMemberMetadata - { - private readonly List _collection; - private readonly Dictionary _dispIds; - private readonly Dictionary _names; - - public IReadOnlyDictionary DispatchIds => new ReadOnlyDictionary(_dispIds); - - public IReadOnlyDictionary Names => new ReadOnlyDictionary(_names); - - public T this[int index] => _collection[index]; - - public RcwMembersMetadataCollection() - { - _collection = new List(); - _dispIds = new Dictionary(); - _names = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - } - - public int IndexOf(T item) => _collection.IndexOf(item); - - public void Add(T item) - { - if (_dispIds.ContainsKey(item.DispatchId)) - { - _names.Add(item.Name, _dispIds[item.DispatchId]); - return; - } - - _collection.Add(item); - _dispIds.Add(item.DispatchId, item); - _names.Add(item.Name, item); - } - - public bool Contains(T item) => _collection.Contains(item); - - public int Count => _collection.Count; - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Reflection/ClassBuilder.cs b/src/ScriptEngine/Machine/Reflection/ClassBuilder.cs deleted file mode 100644 index ed3b158d3..000000000 --- a/src/ScriptEngine/Machine/Reflection/ClassBuilder.cs +++ /dev/null @@ -1,256 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using SysReflection = System.Reflection; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.Machine.Reflection -{ - public class ClassBuilder : IReflectedClassBuilder where T: ContextIValueImpl - { - private List _methods = new List(); - private List _properties = new List(); - private List _fields = new List(); - private List _constructors = new List(); - - public string TypeName { get; set; } - public LoadedModule Module { get; set; } - - public IReflectedClassBuilder SetTypeName(string typeName) - { - TypeName = typeName; - return this; - } - - public IReflectedClassBuilder SetModule(LoadedModule module) - { - Module = module; - return this; - } - - public IReflectedClassBuilder ExportClassMethod(string methodName) - { - var mi = typeof(T).GetMethod(methodName); - if(mi == null) - throw new InvalidOperationException($"Method '{methodName}' not found in {typeof(T)}"); - - ExportClassMethod(mi); - return this; - } - - public IReflectedClassBuilder ExportClassMethod(SysReflection.MethodInfo nativeMethod) - { - if(nativeMethod == null) - throw new ArgumentNullException(nameof(nativeMethod)); - - if (nativeMethod.ReflectedType != typeof(T)) - throw new InvalidOperationException($"Method '{nativeMethod.Name}' not found in {typeof(T)}"); - - if(MarkedAsContextMethod(nativeMethod, true)) - _methods.Add(new WrappedMethodInfo(nativeMethod)); - else - _methods.Add(nativeMethod); - - return this; - } - - public IReflectedClassBuilder ExportProperty(string propName) - { - var mi = typeof(T).GetProperty(propName); - if (mi == null) - throw new InvalidOperationException($"Method '{propName}' not found in {typeof(T)}"); - - return this; - } - - public IReflectedClassBuilder ExportMethods(bool includeDeprecations = false) - { - var methods = typeof(T).GetMethods() - .Where(x => MarkedAsContextMethod(x, includeDeprecations)) - .Select(x=>new WrappedMethodInfo(x)); - - _methods.AddRange(methods); - return this; - } - - public IReflectedClassBuilder ExportProperties(bool includeDeprecations = false) - { - var props = typeof(T).GetProperties() - .Where(MarkedAsContextProperty); - _properties.AddRange(props); - return this; - } - - private bool MarkedAsContextMethod(SysReflection.MemberInfo member, bool includeDeprecations) - { - return member - .GetCustomAttributes(typeof(ContextMethodAttribute), false) - .Any(x => includeDeprecations || (x as ContextMethodAttribute)?.IsDeprecated == false); - } - - private bool MarkedAsContextProperty(SysReflection.MemberInfo member) - { - return member.GetCustomAttributes(typeof(ContextPropertyAttribute), false).Any(); - } - - public IReflectedClassBuilder ExportConstructor(SysReflection.ConstructorInfo info) - { - if (info.DeclaringType != typeof(T)) - { - throw new ArgumentException("info must belong to the current class"); - } - - _constructors.Add(info); - return this; - } - - public IReflectedClassBuilder ExportConstructor(Func creator) - { - var info = new ReflectedConstructorInfo(creator); - info.SetDeclaringType(typeof(T)); - _constructors.Add(info); - return this; - } - - public IReflectedClassBuilder ExportScriptVariables() - { - if(Module == null) - throw new InvalidOperationException("Module is not set"); - - foreach (var variable in Module.Variables) - { - var exported = Module.ExportedProperies.FirstOrDefault(x => x.SymbolicName.Equals(variable.Identifier) || x.SymbolicName.Equals(variable.Alias)); - bool exportFlag = exported.SymbolicName != null; - if(exportFlag) - System.Diagnostics.Debug.Assert(variable.Index == exported.Index, "indices of vars and exports are equal"); - - var fieldInfo = new ReflectedFieldInfo(variable, exportFlag); - if (variable.Annotations != null) - { - foreach (var annotation in variable.Annotations) - { - fieldInfo.AddAnnotation(annotation); - } - } - - _fields.Add(fieldInfo); - } - - return this; - } - - public IReflectedClassBuilder ExportScriptMethods() - { - if (Module == null) - throw new InvalidOperationException("Module is not set"); - - for (int i = 0; i < Module.Methods.Length; i++) - { - var methodDescriptor = Module.Methods[i]; - if(methodDescriptor.Signature.Name == Compiler.Compiler.BODY_METHOD_NAME) - continue; - - var methInfo = CreateMethodInfo(methodDescriptor.Signature); - _methods.Add(methInfo); - } - - return this; - } - - private ReflectedMethodInfo CreateMethodInfo(MethodInfo methInfo) - { - var reflectedMethod = new ReflectedMethodInfo(methInfo.Name); - reflectedMethod.SetPrivate(!methInfo.IsExport); - reflectedMethod.IsFunction = methInfo.IsFunction; - - var unknownVal = ValueFactory.CreateInvalidValueMarker(); - - for (int i = 0; i < methInfo.Params.Length; i++) - { - var currentParam = methInfo.Params[i]; - var reflectedParam = new ReflectedParamInfo(currentParam.Name, currentParam.IsByValue); - reflectedParam.SetOwner(reflectedMethod); - reflectedParam.SetPosition(i); - if (currentParam.HasDefaultValue) - { - - } - - reflectedParam.SetDefaultValue(unknownVal); - if (currentParam.Annotations != null) - { - foreach (var annotation in currentParam.Annotations) - { - reflectedParam.AddAnnotation(annotation); - } - } - - reflectedMethod.Parameters.Add(reflectedParam); - } - - if(methInfo.Annotations != null) - { - foreach (var annotation in methInfo.Annotations) - { - reflectedMethod.AddAnnotation(annotation); - } - } - - return reflectedMethod; - - } - - public IReflectedClassBuilder ExportScriptConstructors() - { - var statics = typeof(T).GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public) - .Where(x => x.GetCustomAttributes(false).Any(y => y is ScriptConstructorAttribute)); - - foreach (var staticConstructor in statics) - { - var action = new Func((parameters) => - { - return (IRuntimeContextInstance)staticConstructor.Invoke(null, SysReflection.BindingFlags.InvokeMethod, null, parameters, CultureInfo.CurrentCulture); - }); - - ExportConstructor(action); - } - - return this; - } - - public IReflectedClassBuilder ExportDefaults() - { - ExportMethods(); - ExportProperties(); - if (Module != null) - { - ExportScriptVariables(); - ExportScriptMethods(); - ExportScriptConstructors(); - } - - return this; - } - - public Type Build() - { - var classDelegator = new ReflectedClassType(); - classDelegator.SetName(TypeName); - classDelegator.SetFields(_fields); - classDelegator.SetProperties(_properties); - classDelegator.SetMethods(_methods); - classDelegator.SetConstructors(_constructors); - - return classDelegator; - } - - } - -} diff --git a/src/ScriptEngine/Machine/Reflection/IReflectedClassBuilder.cs b/src/ScriptEngine/Machine/Reflection/IReflectedClassBuilder.cs deleted file mode 100644 index 7974c443f..000000000 --- a/src/ScriptEngine/Machine/Reflection/IReflectedClassBuilder.cs +++ /dev/null @@ -1,42 +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 ScriptEngine.Machine.Reflection -{ - public interface IReflectedClassBuilder - { - IReflectedClassBuilder SetTypeName(string typeName); - - IReflectedClassBuilder SetModule(LoadedModule module); - - IReflectedClassBuilder ExportClassMethod(string methodName); - - IReflectedClassBuilder ExportClassMethod(System.Reflection.MethodInfo nativeMethod); - - IReflectedClassBuilder ExportProperty(string propName); - - IReflectedClassBuilder ExportMethods(bool includeDeprecations = false); - - IReflectedClassBuilder ExportProperties(bool includeDeprecations = false); - - IReflectedClassBuilder ExportConstructor(System.Reflection.ConstructorInfo info); - - IReflectedClassBuilder ExportConstructor(Func creator); - - IReflectedClassBuilder ExportScriptVariables(); - - IReflectedClassBuilder ExportScriptMethods(); - - IReflectedClassBuilder ExportScriptConstructors(); - - IReflectedClassBuilder ExportDefaults(); - - Type Build(); - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Reflection/ReflectedClassType.cs b/src/ScriptEngine/Machine/Reflection/ReflectedClassType.cs deleted file mode 100644 index 5be92f646..000000000 --- a/src/ScriptEngine/Machine/Reflection/ReflectedClassType.cs +++ /dev/null @@ -1,175 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.Machine.Reflection -{ - public class ReflectedClassType : TypeDelegator where T : ContextIValueImpl - { - private string _typeName; - private PropertyInfo[] _properties; - private System.Reflection.MethodInfo[] _methods; - private FieldInfo[] _fields; - private ConstructorInfo[] _constructors; - - public ReflectedClassType() - :base(typeof(T)) - { - } - - public void SetName(string name) - { - _typeName = name; - } - - public void SetFields(IEnumerable source) - { - _fields = source.Select(x => - { - if (x is ReflectedFieldInfo refl) - { - refl.SetDeclaringType(this); - } - - return x; - }).ToArray(); - } - - public void SetProperties(IEnumerable source) - { - _properties = source.ToArray(); - } - - public void SetMethods(IEnumerable source) - { - _methods = source.Select(x => - { - if (x is ReflectedMethodInfo refl) - { - refl.SetDeclaringType(this); - } - - return x; - }).ToArray(); - } - - public void SetConstructors(IEnumerable source) - { - _constructors = source.Select(x => - { - if(x is ReflectedConstructorInfo refl) - refl.SetDeclaringType(this); - return x; - - }).ToArray(); - } - - public override string Name => _typeName; - public override string FullName => Namespace + "." + Name; - public override Assembly Assembly => Assembly.GetExecutingAssembly(); - public override bool ContainsGenericParameters => false; - public override string AssemblyQualifiedName => Assembly.CreateQualifiedName(Assembly.FullName, Name); - public override Type UnderlyingSystemType => typeof(T); - public override Type BaseType => typeof(T).BaseType; - public override IEnumerable CustomAttributes => null; - public override string Namespace => GetType().Namespace + ".dyn"; - - public override FieldInfo[] GetFields(BindingFlags bindingAttr) - { - IEnumerable result; - bool showPublic = bindingAttr.HasFlag(BindingFlags.Public); - bool showPrivate = bindingAttr.HasFlag(BindingFlags.NonPublic); - if (showPublic && showPrivate) - result = _fields; - else - result = _fields.Where(x => x.IsPublic && showPublic || x.IsPrivate && showPrivate); - - return result.ToArray(); - } - - public override FieldInfo GetField(string name, BindingFlags bindingAttr) - { - if (bindingAttr.HasFlag(BindingFlags.Public)) - { - return _fields.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0 || x.IsPublic); - } - - return _fields.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0); - } - - protected override System.Reflection.MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) - { - return _methods.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0); - } - - public override System.Reflection.MethodInfo[] GetMethods(BindingFlags bindingAttr) - { - bool showPrivate = bindingAttr.HasFlag(BindingFlags.NonPublic); - bool showPublic = bindingAttr.HasFlag(BindingFlags.Public); - return _methods.Where(x=>x.IsPublic && showPublic || x.IsPrivate && showPrivate).ToArray(); - } - - public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) - { - bool showPrivate = bindingAttr.HasFlag(BindingFlags.NonPublic); - bool showPublic = bindingAttr.HasFlag(BindingFlags.Public); - - return _properties.Where(x => - { - var isPublic = (x.GetMethod?.IsPublic) == true || (x.SetMethod?.IsPublic) == true; - return isPublic && showPublic || !isPublic && showPrivate; - - }).ToArray(); - } - - protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) - { - return _properties.FirstOrDefault(x => StringComparer.OrdinalIgnoreCase.Compare(x.Name, name) == 0); - } - - public override MemberInfo[] GetMembers(BindingFlags bindingAttr) - { - var mems = new MemberInfo[_properties.Length + _fields.Length + _methods.Length]; - - Array.Copy(_fields, mems, _fields.Length); - Array.Copy(_properties, 0, mems, _fields.Length, _properties.Length); - Array.Copy(_methods, 0, mems, _properties.Length + _fields.Length, _methods.Length); - - return mems; - } - - public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) - { - if(name == null) - throw new ArgumentNullException(); - - switch (type) - { - case MemberTypes.Field: - return new MemberInfo[] { GetField(name, bindingAttr) }; - case MemberTypes.Method: - return new MemberInfo[] { GetMethod(name, bindingAttr) }; - case MemberTypes.Property: - return new MemberInfo[] { GetProperty(name, bindingAttr) }; - default: - return new MemberInfo[0]; - } - } - - public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) - { - return _constructors; - } - - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Reflection/ReflectedConstructorInfo.cs b/src/ScriptEngine/Machine/Reflection/ReflectedConstructorInfo.cs deleted file mode 100644 index bd782ec90..000000000 --- a/src/ScriptEngine/Machine/Reflection/ReflectedConstructorInfo.cs +++ /dev/null @@ -1,96 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Globalization; -using System.Reflection; - -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Reflection -{ - public class ReflectedConstructorInfo : ConstructorInfo - { - private Type _declaringType; - private Func _constructorImplementation; - - public ReflectedConstructorInfo(Func constructor) - { - _constructorImplementation = constructor; - } - - public void SetDeclaringType(Type declaringType) - { - _declaringType = declaringType; - } - - public override object[] GetCustomAttributes(bool inherit) - { - return new object[0]; - } - - public override bool IsDefined(Type attributeType, bool inherit) - { - return false; - } - - public override ParameterInfo[] GetParameters() - { - return new ParameterInfo[0]; - } - - public override MethodImplAttributes GetMethodImplementationFlags() - { - return MethodImplAttributes.Managed; - } - - public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) - { - return Invoke(invokeAttr, binder, parameters, culture); - } - - public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) - { - return _constructorImplementation(parameters); - } - - public override string Name - { - get { return DeclaringType.Name; } - } - - public override Type DeclaringType - { - get { return _declaringType; } - } - - public override Type ReflectedType - { - get { return _declaringType; } - } - - public override RuntimeMethodHandle MethodHandle - { - get { throw new NotImplementedException(); } - } - - public override MethodAttributes Attributes - { - get { return MethodAttributes.Public; } - } - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - { - return new object[0]; - } - - } -} diff --git a/src/ScriptEngine/Machine/Reflection/ReflectedFieldInfo.cs b/src/ScriptEngine/Machine/Reflection/ReflectedFieldInfo.cs deleted file mode 100644 index 62e590082..000000000 --- a/src/ScriptEngine/Machine/Reflection/ReflectedFieldInfo.cs +++ /dev/null @@ -1,113 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.Machine.Reflection -{ - public class ReflectedFieldInfo : FieldInfo - { - private Type _declaringType; - private bool _isPublic; - private readonly VariableInfo _info; - - private readonly List _attributes = new List(); - - public ReflectedFieldInfo(VariableInfo info, bool isPublic) - { - _info = info; - _isPublic = isPublic; - } - - public void SetDeclaringType(Type declType) - { - _declaringType = declType; - } - - private IEnumerable GetCustomAttributesInternal(bool inherit) - { - return _attributes; - } - - public override object[] GetCustomAttributes(bool inherit) - { - return GetCustomAttributesInternal(inherit).ToArray(); - } - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - { - var attribs = GetCustomAttributesInternal(inherit); - return attribs.Where(x => x.GetType() == attributeType).ToArray(); - } - - public override bool IsDefined(Type attributeType, bool inherit) - { - return GetCustomAttributes(inherit).Any(x => x.GetType() == attributeType); - } - - public override object GetValue(object obj) - { - var irc = obj as IRuntimeContextInstance; - if(irc == null) - throw new ArgumentException(); - - return ContextValuesMarshaller.ConvertReturnValue(irc.GetPropValue(_info.Index)); - } - - public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture) - { - var irc = obj as IRuntimeContextInstance; - if (irc == null) - throw new ArgumentException(); - - irc.SetPropValue(_info.Index, (IValue) value); - } - - public override string Name - { - get { return _info.Identifier; } - } - - public override Type DeclaringType - { - get { return _declaringType; } - } - - public override Type ReflectedType - { - get { return _declaringType; } - } - - public override Type FieldType - { - get { return typeof(IValue); } - } - - public override FieldAttributes Attributes - { - get { return _isPublic ? FieldAttributes.Public : FieldAttributes.Private; } - } - - public override RuntimeFieldHandle FieldHandle - { - get { throw new NotImplementedException(); } - } - - public void AddAnnotation(AnnotationDefinition annotation) - { - _attributes.Add(new UserAnnotationAttribute() - { - Annotation = annotation - }); - } - } -} diff --git a/src/ScriptEngine/Machine/Reflection/ReflectedMethodInfo.cs b/src/ScriptEngine/Machine/Reflection/ReflectedMethodInfo.cs deleted file mode 100644 index d6921dcca..000000000 --- a/src/ScriptEngine/Machine/Reflection/ReflectedMethodInfo.cs +++ /dev/null @@ -1,206 +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/. -----------------------------------------------------------*/ -//#if !__MonoCS__ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; - -using ScriptEngine.Machine.Reflection; - -namespace ScriptEngine.Machine.Contexts -{ - - public class ReflectedMethodInfo : System.Reflection.MethodInfo - { - readonly string _name; - int _dispId; - bool _isPrivate; - - private Type _declaringType; - - private readonly List _parameters; - private List _annotations; - - public ReflectedMethodInfo(string name) - { - _name = name; - _parameters = new List(); - _annotations = new List(); - } - - public void SetDeclaringType(Type declaringType) - { - _declaringType = declaringType; - } - - public void SetDispId(int p) - { - _dispId = p; - } - - public void SetPrivate(bool makePrivate) - { - _isPrivate = makePrivate; - } - - public List Parameters => _parameters; - - public bool IsFunction { get; set; } - - public override System.Reflection.MethodInfo GetBaseDefinition() - { - return this; - } - - public override Type ReturnType - { - get - { - if (IsFunction) - return typeof(IValue); - else - return typeof(void); - } - } - - public override IEnumerable CustomAttributes - { - get - { - return new CustomAttributeData[0]; - } - } - - - public override ICustomAttributeProvider ReturnTypeCustomAttributes - { - get { throw new NotImplementedException(); } - } - - public override MethodAttributes Attributes - { - get - { - if (_isPrivate) - { - return MethodAttributes.Private; - } - else - { - return MethodAttributes.Public; - } - } - } - - public override MethodImplAttributes GetMethodImplementationFlags() - { - return MethodImplAttributes.Managed; - } - - public override ParameterInfo[] GetParameters() - { - return _parameters.ToArray(); - } - - public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, System.Globalization.CultureInfo culture) - { - IRuntimeContextInstance inst = obj as IRuntimeContextInstance; - if (inst == null) - throw new ArgumentException("Wrong argument type"); - - IValue[] engineParameters = parameters.Select(x => COMWrapperContext.CreateIValue(x)).ToArray(); - IValue retVal = null; - - inst.CallAsFunction(GetDispatchIndex(inst), engineParameters, out retVal); - - return COMWrapperContext.MarshalIValue(retVal); - } - - /// - /// Прямой вызов скриптового кода, минуя медленные интерфейсы Reflection - /// - /// - /// - /// - public IValue InvokeDirect(IRuntimeContextInstance instance, IValue[] parameters) - { - instance.CallAsFunction(GetDispatchIndex(instance), parameters, out var retVal); - return retVal; - } - - private int GetDispatchIndex(IRuntimeContextInstance obj) - { - if (_dispId != -1) - return obj.FindMethod(Name); - - return _dispId; - - } - - public override RuntimeMethodHandle MethodHandle - { - get { throw new NotImplementedException(); } - } - - public override Type DeclaringType - { - get { return _declaringType; } - } - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - { - if (attributeType == typeof(DispIdAttribute)) - { - DispIdAttribute[] attribs = new DispIdAttribute[1]; - attribs[0] = new DispIdAttribute(_dispId); - return attribs; - } - - if(attributeType == typeof(UserAnnotationAttribute)) - { - return _annotations.ToArray(); - } - - return new object[0]; - } - - public override object[] GetCustomAttributes(bool inherit) - { - List data = new List(); - data.Add(new DispIdAttribute(_dispId)); - - data.AddRange(_annotations); - return data.ToArray(); - } - - public override bool IsDefined(Type attributeType, bool inherit) - { - return attributeType == typeof(DispIdAttribute) || _annotations.Count > 0 && attributeType == typeof(UserAnnotationAttribute); - } - - public override string Name - { - get { return _name; } - } - - public override Type ReflectedType - { - get { return _declaringType; } - } - - public void AddAnnotation(AnnotationDefinition annotation) - { - _annotations.Add(new UserAnnotationAttribute() - { - Annotation = annotation - }); - } - } -} -//#endif \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Reflection/ReflectedParamInfo.cs b/src/ScriptEngine/Machine/Reflection/ReflectedParamInfo.cs deleted file mode 100644 index 1280c9858..000000000 --- a/src/ScriptEngine/Machine/Reflection/ReflectedParamInfo.cs +++ /dev/null @@ -1,88 +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/. -----------------------------------------------------------*/ -//#if !__MonoCS__ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using ScriptEngine.Machine.Reflection; - -namespace ScriptEngine.Machine.Contexts -{ - public class ReflectedParamInfo : ParameterInfo - { - private readonly List _attributes; - - public ReflectedParamInfo(string name, bool isByVal) - { - _attributes = new List(); - NameImpl = name; - AttrsImpl = ParameterAttributes.In; - if (!isByVal) - { - _attributes.Add(new ByRefAttribute()); - } - - ClassImpl = typeof(IValue); - } - - internal void SetOwner(MemberInfo owner) - { - MemberImpl = owner; - } - - internal void SetPosition(int index) - { - PositionImpl = index; - } - - public void AddAnnotation(AnnotationDefinition annotation) - { - _attributes.Add(new UserAnnotationAttribute() - { - Annotation = annotation - }); - } - - public void SetDefaultValue(IValue val) - { - DefaultValueImpl = val; - } - - public override object DefaultValue => DefaultValueImpl; - - public override bool HasDefaultValue => DefaultValue != null; - - private IEnumerable GetCustomAttributesInternal(bool inherit) - { - return _attributes; - } - - public override object[] GetCustomAttributes(bool inherit) - { - return GetCustomAttributesInternal(inherit).ToArray(); - } - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - { - var attribs = GetCustomAttributesInternal(inherit); - if (attributeType == null) - return attribs.ToArray(); - - return attribs.Where(x => attributeType.IsAssignableFrom(x.GetType())) - .ToArray(); - } - - public override bool IsDefined(Type attributeType, bool inherit) - { - return GetCustomAttributes(inherit).Any(x => x.GetType() == attributeType); - } - } -} -//#endif \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Reflection/UserAnnotationAttribute.cs b/src/ScriptEngine/Machine/Reflection/UserAnnotationAttribute.cs deleted file mode 100644 index 3d2e17a85..000000000 --- a/src/ScriptEngine/Machine/Reflection/UserAnnotationAttribute.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 ScriptEngine.Machine.Reflection -{ - [AttributeUsage(AttributeTargets.Method|AttributeTargets.Parameter, AllowMultiple = true)] - public class UserAnnotationAttribute : Attribute - { - public AnnotationDefinition Annotation { get; set; } - } -} diff --git a/src/ScriptEngine/Machine/Reflection/WrappedMethodInfo.cs b/src/ScriptEngine/Machine/Reflection/WrappedMethodInfo.cs deleted file mode 100644 index 8dcab689f..000000000 --- a/src/ScriptEngine/Machine/Reflection/WrappedMethodInfo.cs +++ /dev/null @@ -1,79 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Globalization; -using System.Linq; -using System.Reflection; - -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine.Machine.Reflection -{ - public sealed class WrappedMethodInfo : System.Reflection.MethodInfo - { - private readonly System.Reflection.MethodInfo _realMethod; - private readonly ContextMethodAttribute _scriptMark; - - public WrappedMethodInfo(System.Reflection.MethodInfo realMethod) - { - _realMethod = realMethod; - _scriptMark = (ContextMethodAttribute)GetCustomAttributes(typeof(ContextMethodAttribute), false).First(); - } - - public override Type ReturnType => _realMethod.ReturnType; - - public override ParameterInfo ReturnParameter => _realMethod.ReturnParameter; - - public override object[] GetCustomAttributes(bool inherit) - { - return _realMethod.GetCustomAttributes(inherit); - } - - public override bool IsDefined(Type attributeType, bool inherit) - { - return _realMethod.IsDefined(attributeType, inherit); - } - - public override ParameterInfo[] GetParameters() - { - return _realMethod.GetParameters(); - } - - public override MethodImplAttributes GetMethodImplementationFlags() - { - return _realMethod.GetMethodImplementationFlags(); - } - - public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) - { - return _realMethod.Invoke(obj, invokeAttr, binder, parameters, culture); - } - - public override System.Reflection.MethodInfo GetBaseDefinition() - { - return _realMethod.GetBaseDefinition(); - } - - public override ICustomAttributeProvider ReturnTypeCustomAttributes => _realMethod.ReturnTypeCustomAttributes; - - public override string Name => _scriptMark.GetName(); - - public override Type DeclaringType => _realMethod.DeclaringType; - - public override Type ReflectedType => _realMethod.ReflectedType; - - public override RuntimeMethodHandle MethodHandle => _realMethod.MethodHandle; - - public override MethodAttributes Attributes => _realMethod.Attributes; - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - { - return _realMethod.GetCustomAttributes(attributeType, inherit); - } - } -} diff --git a/src/ScriptEngine/Machine/RuntimeExceptions.cs b/src/ScriptEngine/Machine/RuntimeExceptions.cs index 66d8e9523..20aea0343 100644 --- a/src/ScriptEngine/Machine/RuntimeExceptions.cs +++ b/src/ScriptEngine/Machine/RuntimeExceptions.cs @@ -1,217 +1,70 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using OneScript.Language; - -namespace ScriptEngine.Machine -{ - public class RuntimeException : ScriptException - { - private List _frames; - - public RuntimeException() : base() - { - } - - public RuntimeException(string msg) : base(msg) - { - } - - public RuntimeException(string msg, Exception inner) - : base(new CodePositionInfo(), msg, inner) - { - } - - public IEnumerable GetStackTrace() - { - return _frames.AsReadOnly(); - } - - internal IList CallStackFrames => _frames; - - internal void InitCallStackFrames(IEnumerable src) - { - _frames = src == null ? new List() : new List(src); - } - - public static RuntimeException DeprecatedMethodCall(string name) - { - return new RuntimeException($"Вызов безнадёжно устаревшего метода {name}"); - } - - public static RuntimeException ConvertToNumberException() - { - return new RuntimeException("Преобразование к типу 'Число' не поддерживается"); - } - - public static RuntimeException ConvertToBooleanException() - { - return new RuntimeException("Преобразование к типу 'Булево' не поддерживается"); - } - - public static RuntimeException ConvertToDateException() - { - return new RuntimeException("Преобразование к типу 'Дата' не поддерживается"); - } - - public static RuntimeException PropIsNotReadableException(string prop) - { - return PropertyAccessException.GetPropIsNotReadableException(prop); - } - - public static RuntimeException PropIsNotWritableException(string prop) - { - return PropertyAccessException.GetPropIsNotWritableException(prop); - } - - public static RuntimeException PropNotFoundException(string prop) - { - return PropertyAccessException.GetPropNotFoundException(prop); - } - - public static RuntimeException MethodNotFoundException(string methodName) - { - return new RuntimeException($"Метод объекта не обнаружен ({methodName})"); - } - - public static RuntimeException MethodNotFoundException(string methodName, string objectName) - { - return new RuntimeException($"Метод объекта не обнаружен ({{{objectName}}}::{methodName})"); - } - - public static RuntimeException ValueIsNotObjectException() - { - return new RuntimeException("Значение не является значением объектного типа"); - } - - public static RuntimeException TooManyArgumentsPassed() - { - return new RuntimeException("Слишком много фактических параметров"); - } - - public static RuntimeException TooFewArgumentsPassed() - { - return new RuntimeException("Недостаточно фактических параметров"); - } - - public static RuntimeException InvalidArgumentType() - { - return new RuntimeException("Неверный тип аргумента"); - } - - public static RuntimeException InvalidArgumentType(string argName) - { - return new RuntimeException(String.Format("Неверный тип аргумента '{0}'", argName)); - } - - public static RuntimeException InvalidNthArgumentType(int argNum) - { - return new RuntimeException(String.Format("Неверный тип аргумента номер {0}", argNum)); - } - - public static RuntimeException InvalidArgumentType(int argNum, string argName ) - { - return new RuntimeException(String.Format("Неверный тип аргумента номер {0} '{1}'", argNum, argName )); - } - - public static RuntimeException InvalidArgumentValue() - { - return new RuntimeException("Неверное значение аргумента"); - } - - public static RuntimeException InvalidNthArgumentValue(int argNum) - { - return new RuntimeException(String.Format("Неверное значение аргумента номер {0}", argNum)); - } - - public static RuntimeException InvalidArgumentValue(object value) - { - return new RuntimeException("Неверное значение аргумента {"+value.ToString()+"}"); - } - - public static RuntimeException ComparisonNotSupportedException() - { - return new RuntimeException("Сравнение на больше/меньше для данного типа не поддерживается"); - } - - public static RuntimeException IndexedAccessIsNotSupportedException() - { - return new RuntimeException("Объект не поддерживает доступ по индексу"); - } - - public static RuntimeException IteratorIsNotDefined() - { - return new RuntimeException("Итератор не определен"); - } - - public static RuntimeException UseProcAsAFunction() - { - return new RuntimeException("Использование процедуры, как функции"); - } - - public static RuntimeException DivideByZero() - { - return new RuntimeException("Деление на ноль"); - } - - } - - public class WrongStackConditionException : ApplicationException - { - public WrongStackConditionException() - : base("Внутренняя ошибка: неверное состояние стека") - { - - } - } - - public class PropertyAccessException : RuntimeException - { - private PropertyAccessException(string msg) : base (msg) - { - - } - - public static PropertyAccessException GetPropIsNotReadableException(string prop) - { - return new PropertyAccessException(string.Format("Свойство {0} недоступно для чтения", prop)); - } - - public static PropertyAccessException GetPropIsNotWritableException(string prop) - { - return new PropertyAccessException(string.Format("Свойство {0} недоступно для записи", prop)); - } - - public static PropertyAccessException GetPropNotFoundException(string prop) - { - return new PropertyAccessException(string.Format("Свойство объекта не обнаружено ({0})", prop)); - } - - } - - public class ScriptInterruptionException : ApplicationException - { - public ScriptInterruptionException(int exitCode) : base("Script interrupted") - { - ExitCode = exitCode; - } - - public int ExitCode { get; private set; } - } - - public class ValueMarshallingException : RuntimeException - { - public ValueMarshallingException() : this("Неклассифицированная ошибка маршаллинга значений") - { - } - - public ValueMarshallingException(string message) : base(message) - { - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Commons; +using OneScript.Exceptions; + +namespace ScriptEngine.Machine +{ + public class WrongStackConditionException : ApplicationException + { + public WrongStackConditionException() + : base("Внутренняя ошибка: неверное состояние стека") + { + + } + } + + public class ValueMarshallingException : RuntimeException + { + public ValueMarshallingException() : this("Неклассифицированная ошибка маршаллинга значений") + { + } + + public ValueMarshallingException(string message) : base(message) + { + } + + public static ValueMarshallingException TypeNotSupported(Type type) + { + return new ValueMarshallingException(string.Format(Locale.NStr + ("ru='Возвращаемый тип {0} не поддерживается'; en='Return type {0} is not supported'"), type)); + } + + public static ValueMarshallingException InvalidEnum(Type type) + { + return new ValueMarshallingException(string.Format(Locale.NStr + ("ru = 'Некорректный тип конвертируемого перечисления: {0}'; en = 'Invalid enum return type: {0}'"), type)); + } + + public static ValueMarshallingException EnumWithNoAttribute(Type type) + { + return new ValueMarshallingException(string.Format(Locale.NStr + ("ru='Значение перечисления {0} должно быть помечено атрибутом EnumItemAttribute';" + + "en='An enumeration value {0} must be marked with the EnumItemAttribute attribute"), type)); + } + + public static ValueMarshallingException NoConversionToCLR(Type type) + { + return new ValueMarshallingException(string.Format(Locale.NStr + ("ru='Тип {0} не поддерживает преобразование в CLR-объект';" + +"en='Type {0} does not support conversion to CLR object'"), type)); + } + public static ValueMarshallingException InvalidNullValue() + { + return new ValueMarshallingException(Locale.NStr + ("ru = 'Значение не может быть null'; en = 'Value cannot be null'")); + } + + public static ValueMarshallingException InvalidNullIndex() + { + return new ValueMarshallingException(Locale.NStr + ("ru = 'Индекс не может быть null'; en = 'Index cannot be null'")); + } + + } +} diff --git a/src/ScriptEngine/Machine/Scope.cs b/src/ScriptEngine/Machine/Scope.cs deleted file mode 100644 index a7116f230..000000000 --- a/src/ScriptEngine/Machine/Scope.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/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine -{ - struct Scope - { - public IVariable[] Variables; - public MethodInfo[] Methods; - public IRuntimeContextInstance Instance; - } - -} diff --git a/src/ScriptEngine/Machine/ScriptInterruptionException.cs b/src/ScriptEngine/Machine/ScriptInterruptionException.cs new file mode 100644 index 000000000..0844ee594 --- /dev/null +++ b/src/ScriptEngine/Machine/ScriptInterruptionException.cs @@ -0,0 +1,20 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; + +namespace ScriptEngine.Machine +{ + public class ScriptInterruptionException : ApplicationException + { + public ScriptInterruptionException(int exitCode) : base("Script interrupted") + { + ExitCode = exitCode; + } + + public int ExitCode { get; private set; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/StackMachineExecutor.cs b/src/ScriptEngine/Machine/StackMachineExecutor.cs new file mode 100644 index 000000000..fb6aa2614 --- /dev/null +++ b/src/ScriptEngine/Machine/StackMachineExecutor.cs @@ -0,0 +1,79 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Values; +using ScriptEngine.Machine.Debugger; + +namespace ScriptEngine.Machine +{ + public class StackMachineExecutor : IExecutorProvider + { + private MachineInstance _machine; + + public Type SupportedModuleType => typeof(StackRuntimeModule); + + public Invoker GetInvokeDelegate() + { + return Executor; + } + + public void BeforeProcessStart(IBslProcess process) + { + _machine = new MachineInstance(); + _machine.Setup(process); + + var debugger = process.Services.TryResolve(); + if (debugger?.IsEnabled == true) + { + var session = debugger.GetSession(); + if (session.IsActive) + { + _machine.SetDebugMode(session.ThreadManager, session.BreakpointManager); + session.ThreadManager.ThreadStarted(process.VirtualThreadId, _machine); + } + } + + process.Services.Resolve().Machine = _machine; + } + + public void AfterProcessExit(IBslProcess process) + { + var debugger = process.Services.TryResolve(); + if (debugger?.IsEnabled != true) + return; + + var session = debugger.GetSession(); + if (session.IsActive) + { + session.ThreadManager.ThreadExited(process.VirtualThreadId); + } + } + + private BslValue Executor(IBslProcess process, BslObjectValue target, IExecutableModule module, BslScriptMethodInfo method, IValue[] arguments) + { + if (!(method is MachineMethodInfo scriptMethodInfo)) + { + throw new InvalidOperationException($"Method has type {method?.GetType()} but expected {typeof(MachineMethodInfo)}"); + } + + if (!(target is IRunnable runnable)) + { + throw new InvalidOperationException($"Target must implement {typeof(IRunnable)}"); + } + + if (_machine?.Process == default) + { + throw new InvalidOperationException("Machine is not initialized by process"); + } + + return (BslValue)_machine.ExecuteMethod(runnable, scriptMethodInfo, arguments); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/StackMachineProvider.cs b/src/ScriptEngine/Machine/StackMachineProvider.cs new file mode 100644 index 000000000..e988ccadd --- /dev/null +++ b/src/ScriptEngine/Machine/StackMachineProvider.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 ScriptEngine.Machine +{ + /// + /// Сервис предоставляющий экземпляр машины в текущем bsl-процессе + /// Экземпляр представляется в разрезе IBslProcess. + /// + public class StackMachineProvider + { + public MachineInstance Machine { get; internal set; } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/StackRuntimeAdoptionExtensions.cs b/src/ScriptEngine/Machine/StackRuntimeAdoptionExtensions.cs new file mode 100644 index 000000000..f8d09b83a --- /dev/null +++ b/src/ScriptEngine/Machine/StackRuntimeAdoptionExtensions.cs @@ -0,0 +1,88 @@ +/*---------------------------------------------------------- +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.Linq; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Values; + +namespace ScriptEngine.Machine +{ + public static class StackRuntimeAdoptionExtensions + { + public static MethodSignature MakeSignature(this BslMethodInfo method) + { + var signature = new MethodSignature + { + Name = method.Name, + Alias = method.Alias, + IsFunction = method.IsFunction(), + IsExport = method.IsPublic, + Annotations = MakeAnnotations(method), + Params = method.GetParameters().Select(ToMachineDefinition).ToArray() + }; + + return signature; + } + + public static BslAnnotationAttribute[] GetAnnotations(this ICustomAttributeProvider member) + { + return member.GetCustomAttributes(typeof(BslAnnotationAttribute), false) + .Cast() + .ToArray(); + } + + public static AnnotationDefinition ToMachineDefinition(this BslAnnotationAttribute attribute) + { + return new AnnotationDefinition + { + Name = attribute.Name, + Parameters = attribute.Parameters.Select(ToMachineDefinition).ToArray() + }; + } + + public static AnnotationParameter ToMachineDefinition(this BslAnnotationParameter parameter) + { + return new AnnotationParameter + { + Name = parameter.Name, + RuntimeValue = parameter.Value, + }; + } + + public static ParameterDefinition ToMachineDefinition(this ParameterInfo parameter) + { + return new ParameterDefinition + { + Name = parameter.Name, + IsByValue = !parameter.IsByRef(), + DefaultValueIndex = parameter is BslParameterInfo p? p.ConstantValueIndex : -1, + HasDefaultValue = parameter.HasDefaultValue, + Annotations = MakeAnnotations(parameter) + }; + } + + private static AnnotationDefinition[] MakeAnnotations(ICustomAttributeProvider member) + { + return member.GetCustomAttributes(typeof(BslAnnotationAttribute), false) + .Cast() + .Select(ToMachineDefinition).ToArray(); + } + + public static BslAnnotationAttribute MakeBslAttribute(this in AnnotationDefinition annotation) + { + var attribute = new BslAnnotationAttribute(annotation.Name); + if (annotation.ParamCount > 0) + { + attribute.SetParameters(annotation.Parameters.Select(p => + new BslAnnotationParameter(p.Name, (BslPrimitiveValue) p.RuntimeValue))); + } + + return attribute; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/StackRuntimeModule.cs b/src/ScriptEngine/Machine/StackRuntimeModule.cs new file mode 100644 index 000000000..ea4d02803 --- /dev/null +++ b/src/ScriptEngine/Machine/StackRuntimeModule.cs @@ -0,0 +1,65 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Sources; +using OneScript.Values; + +namespace ScriptEngine.Machine +{ + public class StackRuntimeModule : IExecutableModule + { + public StackRuntimeModule(Type ownerType) + { + ClassType = ownerType; + } + + public Type ClassType { get; } + + public int EntryMethodIndex { get; set; } = -1; + + public List Constants { get; } = new List(); + + internal List Identifiers { get; } = new List(); + + internal List VariableRefs { get; } = new List(); + + internal List MethodRefs { get; } = new List(); + + #region IExecutableModule members + + public BslScriptMethodInfo ModuleBody + { + get + { + if (EntryMethodIndex == -1) + return null; + + return Methods[MethodRefs[EntryMethodIndex].MemberNumber]; + } + } + + public IList ModuleAttributes { get; } = new List(); + + public IList Fields { get; } = new List(); + + public IList Properties { get; } = new List(); + + public IList Methods { get; } = new List(); + + public List Code { get; } = new List(512); + + public SourceCode Source { get; set; } + + public IDictionary Interfaces { get; } = new Dictionary(); + + #endregion + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/TypeFactory.cs b/src/ScriptEngine/Machine/TypeFactory.cs index fdb93069f..6bef446da 100644 --- a/src/ScriptEngine/Machine/TypeFactory.cs +++ b/src/ScriptEngine/Machine/TypeFactory.cs @@ -8,47 +8,76 @@ This Source Code Form is subject to the terms of the using ScriptEngine.Machine.Contexts; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Linq.Expressions; +using OneScript.Types; +using OneScript.Contexts; +using OneScript.Exceptions; +using ScriptEngine.Types; using Refl = System.Reflection; namespace ScriptEngine.Machine { - public delegate IValue InstanceConstructor(string typeName, IValue[] arguments); - - public class TypeFactory + internal delegate IValue InstanceConstructor(TypeActivationContext context, IValue[] arguments); + + public class TypeFactory : ITypeFactory { - private readonly Type _clrType; + private readonly TypeDescriptor _systemType; private Dictionary _constructorsCache = new Dictionary(); + private static readonly Refl.MethodInfo CopyMethod = typeof(TypeFactory) + .GetMethod(nameof(CaptureVariantArgs), Refl.BindingFlags.Static | Refl.BindingFlags.InvokeMethod | Refl.BindingFlags.NonPublic); - public TypeFactory(Type clrType) + public TypeFactory(TypeDescriptor type) + { + _systemType = type; + } + + public TypeFactory(Type type) : this(type.GetTypeFromClassMarkup()) { - _clrType = clrType; } - public InstanceConstructor GetConstructor(string typeName, IValue[] arguments) + private Type ClrType => _systemType.ImplementingClass; + + public IValue Activate(TypeActivationContext context, IValue[] arguments) + { + var constructor = GetConstructor(arguments); + if (constructor == default) + { + throw RuntimeException.ConstructorNotFound(context.TypeName); + } + + var instance = constructor(context, arguments); + if (instance is ISystemTypeAcceptor typeAcceptor) + { + typeAcceptor.AssignType(_systemType); + } + + return instance; + } + + private InstanceConstructor GetConstructor(IValue[] arguments) { if (_constructorsCache.TryGetValue(arguments.Length, out var constructor)) { return constructor; } - constructor = CreateConstructor(typeName, arguments); + constructor = CreateConstructor(arguments); if(constructor != null) _constructorsCache[arguments.Length] = constructor; return constructor; - } - private InstanceConstructor CreateConstructor(string typeName, IValue[] arguments) + private InstanceConstructor CreateConstructor(IValue[] arguments) { - var definition = FindConstructor(arguments); - if (definition == null) + var (success, definition) = FindConstructor(arguments); + if (!success) return null; - var methodInfo = definition.Value.CtorInfo; + var methodInfo = definition.CtorInfo; if (!typeof(IValue).IsAssignableFrom(methodInfo.ReturnType)) { return FallbackConstructor(methodInfo); @@ -57,47 +86,62 @@ private InstanceConstructor CreateConstructor(string typeName, IValue[] argument var argsParam = Expression.Parameter(typeof(IValue[]), "args"); var parameters = methodInfo.GetParameters(); var argsToPass = new List(); - var typeNameParam = Expression.Parameter(typeof(string), "typeName"); + var contextParam = Expression.Parameter(typeof(TypeActivationContext), "context"); + int paramIndex = 0; - if (definition.Value.Parametrized && parameters.Length > 0) + if (definition.Parametrized && parameters.Length > 0) { - argsToPass.Add(typeNameParam); + if (definition.InjectContext) + { + argsToPass.Add(contextParam); + } + else + { + var propAccess = Expression.PropertyOrField(contextParam, nameof(TypeActivationContext.TypeName)); + argsToPass.Add(propAccess); + } ++paramIndex; } - var typeCast = typeof(ContextValuesMarshaller).GetMethod("ConvertParam", new[]{typeof(IValue),typeof(Type)}); - System.Diagnostics.Debug.Assert(typeCast != null); - + var bslProcessParameter = + Expression.PropertyOrField(contextParam, nameof(TypeActivationContext.CurrentProcess)); + for (int i = 0; i < arguments.Length; i++) { if (parameters[paramIndex].ParameterType.IsArray) { // capture all - - var copyMethod = typeof(TypeFactory).GetMethod("CaptureVariantArgs", Refl.BindingFlags.Static | Refl.BindingFlags.InvokeMethod | Refl.BindingFlags.NonPublic); - System.Diagnostics.Debug.Assert(copyMethod != null); - - argsToPass.Add(Expression.Call(copyMethod, argsParam, Expression.Constant(i))); + argsToPass.Add(Expression.Call(CopyMethod, argsParam, Expression.Constant(i))); ++paramIndex; break; } - if (parameters[paramIndex].ParameterType == typeof(IValue)) argsToPass.Add(Expression.ArrayIndex(argsParam, Expression.Constant(i))); else { - var conversionArg = Expression.ArrayIndex(argsParam, - Expression.Constant(i) - ); - var marshalledArg = Expression.Call(typeCast, conversionArg, Expression.Constant(parameters[paramIndex].ParameterType)); - argsToPass.Add( - Expression.Convert(marshalledArg, parameters[paramIndex].ParameterType) - ); + var conversionArg = Expression.ArrayIndex(argsParam, Expression.Constant(i)); + var targetType = parameters[paramIndex].ParameterType; + var convertMethod = ContextValuesMarshaller.BslGenericParameterConverter.MakeGenericMethod(targetType); + + Expression marshalledArg; + if (parameters[paramIndex].HasDefaultValue) + { + var defaultArg = Expression.Constant(parameters[paramIndex].DefaultValue, targetType); + marshalledArg = Expression.Call(convertMethod, conversionArg, defaultArg, bslProcessParameter); + } + else + { + marshalledArg = Expression.Call( + convertMethod, + conversionArg, + ContextValuesMarshaller.GetDefaultBslValueConstant(targetType), + bslProcessParameter); + } + argsToPass.Add(Expression.Convert(marshalledArg, targetType)); } ++paramIndex; - } for (int i = paramIndex; i < parameters.Length; i++) @@ -113,7 +157,7 @@ private InstanceConstructor CreateConstructor(string typeName, IValue[] argument } var constructorCallExpression = Expression.Call(methodInfo, argsToPass); - var callLambda = Expression.Lambda(constructorCallExpression, typeNameParam, argsParam).Compile(); + var callLambda = Expression.Lambda(constructorCallExpression, contextParam, argsParam).Compile(); return callLambda; } @@ -124,7 +168,7 @@ private InstanceConstructor CreateConstructor(string typeName, IValue[] argument // private InstanceConstructor FallbackConstructor(Refl.MethodInfo methodInfo) { - return (typeName, args) => + return (context, args) => { var methArgs = new IValue[methodInfo.GetParameters().Length]; for (int i = 0; i < methArgs.Length; i++) @@ -132,11 +176,19 @@ private InstanceConstructor FallbackConstructor(Refl.MethodInfo methodInfo) if (i < args.Length) methArgs[i] = args[i]; } - return (IValue) methodInfo.Invoke(null, methArgs); + + try + { + return (IValue) methodInfo.Invoke(null, methArgs); + } + catch (Refl.TargetInvocationException e) + { + Debug.Assert(e.InnerException != null, "e.InnerException != null"); + throw e.InnerException; + } }; } - // ReSharper disable once UnusedMember.Global internal static IValue[] CaptureVariantArgs(IValue[] sourceArgs, int startingFrom) { var newArray = new IValue[sourceArgs.Length - startingFrom]; @@ -144,29 +196,17 @@ internal static IValue[] CaptureVariantArgs(IValue[] sourceArgs, int startingFro return newArray; } - private ConstructorDefinition? FindConstructor(IValue[] arguments) + private (bool, ConstructorDefinition) FindConstructor(IValue[] arguments) { - var ctors = _clrType.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public) - .Where(x => x.GetCustomAttributes(false).Any(y => y is ScriptConstructorAttribute)) - .Select(x => new ConstructorDefinition - { - CtorInfo = x, - Parametrized = ((ScriptConstructorAttribute)x.GetCustomAttributes(typeof(ScriptConstructorAttribute), false)[0]).ParametrizeWithClassName - }); - + var ctors = GetMarkedConstructors(ClrType); int argCount = arguments.Length; foreach (var ctor in ctors) { var parameters = ctor.CtorInfo.GetParameters(); - if (ctor.Parametrized && parameters.Length > 0) + if (ctor.Parametrized) { - if (parameters[0].ParameterType != typeof(string)) - { - throw new InvalidOperationException("Type parametrized constructor must have first argument of type String"); - } - parameters = parameters.Skip(1).ToArray(); } @@ -174,7 +214,7 @@ internal static IValue[] CaptureVariantArgs(IValue[] sourceArgs, int startingFro || (parameters.Length > 0 && parameters[0].ParameterType.IsArray); if (success) - return ctor; + return (true, ctor); if (parameters.Length > 0 && parameters.Length < argCount && !parameters[parameters.Length - 1].ParameterType.IsArray) @@ -213,19 +253,47 @@ internal static IValue[] CaptureVariantArgs(IValue[] sourceArgs, int startingFro } if (success) - return ctor; - + return (true, ctor); } - return null; + return (false, default); } + private IEnumerable GetMarkedConstructors(Type type) + { + var staticMethods = ClrType.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); + var constructors = new List(4); + + foreach (var method in staticMethods) + { + var attribute = (ScriptConstructorAttribute) method.GetCustomAttributes(false) + .FirstOrDefault(y => y is ScriptConstructorAttribute); + if(attribute == default) + continue; + + var parameters = method.GetParameters(); + var injectContext = parameters.Length > 0 && + parameters[0].ParameterType == typeof(TypeActivationContext); + + var definition = new ConstructorDefinition + { + CtorInfo = method, + Parametrized = injectContext, + InjectContext = injectContext + }; + + constructors.Add(definition); + } + + return constructors; + } + private struct ConstructorDefinition { public Refl.MethodInfo CtorInfo { get; set; } public bool Parametrized { get; set; } + + public bool InjectContext { get; set; } } - } - } diff --git a/src/ScriptEngine/Machine/TypeManager.cs b/src/ScriptEngine/Machine/TypeManager.cs deleted file mode 100644 index 2c9521b04..000000000 --- a/src/ScriptEngine/Machine/TypeManager.cs +++ /dev/null @@ -1,328 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Machine.Values; - -namespace ScriptEngine.Machine -{ - public interface ITypeManager - { - Type GetImplementingClass(int typeId); - TypeDescriptor GetTypeByName(string name); - TypeDescriptor GetTypeById(int id); - TypeDescriptor GetTypeByFrameworkType(Type type); - TypeDescriptor RegisterType(string name, Type implementingClass); - TypeDescriptor GetTypeDescriptorFor(IValue typeTypeValue); - void RegisterAliasFor(TypeDescriptor td, string alias); - bool IsKnownType(Type type); - bool IsKnownType(string typeName); - Type NewInstanceHandler { get; set; } - } - - class StandartTypeManager : ITypeManager - { - private readonly Dictionary _knownTypesIndexes = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - private readonly List _knownTypes = new List(); - private Type _dynamicFactory; - - private struct KnownType - { - public Type SystemType; - public TypeDescriptor Descriptor; - } - - public StandartTypeManager() - { - foreach (var item in Enum.GetValues(typeof(DataType))) - { - DataType typeEnum = (DataType)item; - string name; - string alias; - switch (typeEnum) - { - case DataType.Undefined: - name = "Неопределено"; - alias = "Undefined"; - break; - case DataType.Boolean: - name = "Булево"; - alias = "Boolean"; - break; - case DataType.String: - name = "Строка"; - alias = "String"; - break; - case DataType.Date: - name = "Дата"; - alias = "Date"; - break; - case DataType.Number: - name = "Число"; - alias = "Number"; - break; - case DataType.Type: - name = "Тип"; - alias = "Type"; - break; - case DataType.Object: - name = "Object"; - alias = null; - break; - default: - continue; - } - - var td = new TypeDescriptor() - { - Name = name, - ID = (int)typeEnum - }; - - RegisterType(td, typeof(DataType)); - - if (alias != null) - { - RegisterAliasFor(td, alias); - } - - } - - RegisterType("Null", typeof(NullValue)); - } - - #region ITypeManager Members - - public Type GetImplementingClass(int typeId) - { - var kt = _knownTypes.First(x => x.Descriptor.ID == typeId); - return kt.SystemType; - } - - public TypeDescriptor GetTypeByName(string name) - { - if (_knownTypesIndexes.ContainsKey(name)) - { - return _knownTypes[_knownTypesIndexes[name]].Descriptor; - } - var clrType = Type.GetType(name, throwOnError: false, ignoreCase: true); - if (clrType != null) - { - var td = RegisterType(name, typeof(COMWrapperContext)); - return td; - } - throw new RuntimeException(String.Format("Тип не зарегистрирован ({0})", name)); - } - - public TypeDescriptor GetTypeById(int id) - { - return _knownTypes[id].Descriptor; - } - - public TypeDescriptor RegisterType(string name, Type implementingClass) - { - if (_knownTypesIndexes.ContainsKey(name)) - { - var td = GetTypeByName(name); - if (GetImplementingClass(td.ID) != implementingClass) - { - throw new InvalidOperationException(string.Format("Name `{0}` is already registered", name)); - } - - return td; - } - else - { - var nextId = _knownTypes.Count; - var typeDesc = new TypeDescriptor() - { - ID = nextId, - Name = name - }; - - RegisterType(typeDesc, implementingClass); - return typeDesc; - } - - } - - private void RegisterType(TypeDescriptor td, Type implementingClass) - { - _knownTypesIndexes.Add(td.Name, td.ID); - _knownTypes.Add(new KnownType() - { - Descriptor = td, - SystemType = implementingClass - }); - } - - public void RegisterAliasFor(TypeDescriptor td, string alias) - { - _knownTypesIndexes[alias] = td.ID; - } - - public TypeDescriptor GetTypeByFrameworkType(Type type) - { - var kt = _knownTypes.First(x => x.SystemType == type); - return kt.Descriptor; - } - - public bool IsKnownType(Type type) - { - return _knownTypes.Any(x => x.SystemType == type); - } - - public bool IsKnownType(string typeName) - { - var nameToUpper = typeName.ToUpperInvariant(); - return _knownTypes.Any(x => x.Descriptor.Name.ToUpperInvariant() == nameToUpper); - } - - public Type NewInstanceHandler - { - get - { - return _dynamicFactory; - } - - set - { - if (value.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public) - .Where(x => x.GetCustomAttributes(false).Any(y => y is ScriptConstructorAttribute)) - .Any()) - { - _dynamicFactory = value; - } - else - { - throw new InvalidOperationException("Class " + value.ToString() + " can't be registered as New handler"); - } - } - } - - public TypeDescriptor GetTypeDescriptorFor(IValue typeTypeValue) - { - if (typeTypeValue.DataType != DataType.Type) - throw RuntimeException.InvalidArgumentType(); - - var ttVal = typeTypeValue.GetRawValue() as TypeTypeValue; - - System.Diagnostics.Debug.Assert(ttVal != null, "value must be of type TypeTypeValue"); - - return ttVal.Value; - - } - - #endregion - - } - - public static class TypeManager - { - private static ITypeManager _instance; - private static Dictionary _factories; - - internal static void Initialize(ITypeManager instance) - { - _instance = instance; - _factories = new Dictionary(); - } - - public static ITypeManager Instance => _instance; - - public static Type GetImplementingClass(int typeId) - { - return _instance.GetImplementingClass(typeId); - } - - public static TypeDescriptor GetTypeByName(string name) - { - return _instance.GetTypeByName(name); - } - - public static TypeDescriptor RegisterType(string name, Type implementingClass) - { - return _instance.RegisterType(name, implementingClass); - } - - public static void RegisterAliasFor(TypeDescriptor td, string alias) - { - _instance.RegisterAliasFor(td, alias); - } - - public static TypeDescriptor GetTypeById(int id) - { - var type = _instance.GetTypeById(id); - return type; - } - - public static bool IsKnownType(Type type) - { - return _instance.IsKnownType(type); - } - - public static bool IsKnownType(string typeName) - { - return _instance.IsKnownType(typeName); - } - - public static TypeDescriptor GetTypeByFrameworkType(Type type) - { - return _instance.GetTypeByFrameworkType(type); - } - - public static TypeDescriptor GetTypeDescriptorFor(IValue typeTypeValue) - { - return _instance.GetTypeDescriptorFor(typeTypeValue); - } - - public static Type NewInstanceHandler - { - get - { - return _instance.NewInstanceHandler; - } - set - { - _instance.NewInstanceHandler = value; - } - } - - public static TypeFactory GetFactoryFor(string typeName) - { - int typeId; - Type clrType; - try - { - typeId = TypeManager.GetTypeByName(typeName).ID; - clrType = TypeManager.GetImplementingClass(typeId); - } - catch (RuntimeException e) - { - if (NewInstanceHandler != null) - { - clrType = NewInstanceHandler; - } - else - { - throw new RuntimeException("Конструктор не найден (" + typeName + ")", e); - } - } - - if(!_factories.TryGetValue(clrType, out var factory)) - { - factory = new TypeFactory(clrType); - _factories[clrType] = factory; - } - - return factory; - } - } - -} diff --git a/src/ScriptEngine/Machine/ValueAdoptionExtensions.cs b/src/ScriptEngine/Machine/ValueAdoptionExtensions.cs new file mode 100644 index 000000000..462c154f5 --- /dev/null +++ b/src/ScriptEngine/Machine/ValueAdoptionExtensions.cs @@ -0,0 +1,79 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Runtime.CompilerServices; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine.Contexts; + +namespace ScriptEngine.Machine +{ + public static class ValueAdoptionExtensions + { + public static bool AsBoolean(this BslValue val) => (bool) val; + public static DateTime AsDate(this BslValue val) => (DateTime) val; + public static decimal AsNumber(this BslValue val) => (decimal) val; + + public static IRuntimeContextInstance AsObject(this BslValue val) + => val is IRuntimeContextInstance ctx? ctx : throw BslExceptions.ValueIsNotObjectException(); + + public static bool AsBoolean(this IValue val) => (bool) UnwrapReference(val); + public static DateTime AsDate(this IValue val) => (DateTime) UnwrapReference(val); + public static decimal AsNumber(this IValue val) => (decimal) UnwrapReference(val); + + // Метод для совместимости внешних компонент + [Obsolete("Use overload with IBslProcess")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string AsString(this IValue val) => AsString(val, ForbiddenBslProcess.Instance); + + public static string AsString(this IValue val, IBslProcess process) => ((BslValue)val).ToString(process); + + // Метод для совместимости внешних компонент + [Obsolete("Just don't use. IValue is always marshalled as raw value.")] + public static IValue GetRawValue(this IValue val) + { + return UnwrapReference(val); + } + + public static string ExplicitString(this IValue val) + { + if (val == null) + return ""; + + if (val.SystemType == BasicTypes.String) + return UnwrapReference(val).ToString(); + + throw RuntimeException.InvalidArgumentType(); + } + + public static IRuntimeContextInstance AsObject(this IValue val) + => UnwrapReference(val) as IRuntimeContextInstance ?? throw BslExceptions.ValueIsNotObjectException(); + + public static object UnwrapToClrObject(this IValue value) + { + return ContextValuesMarshaller.ConvertToClrObject(value); + } + + public static bool IsSkippedArgument(this IValue val) + { + return ReferenceEquals(val, BslSkippedParameterValue.Instance); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static BslValue UnwrapReference(IValue v) + { + if (v is IValueReference r) + return r.BslValue; + + return (BslValue)v; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/ValueBinder.cs b/src/ScriptEngine/Machine/ValueBinder.cs index 25630d217..92af601ba 100644 --- a/src/ScriptEngine/Machine/ValueBinder.cs +++ b/src/ScriptEngine/Machine/ValueBinder.cs @@ -34,9 +34,9 @@ public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] m public override object ChangeType(object value, Type type, CultureInfo culture) { - if (value is IValue) + if (value is IValue iValue) { - return ContextValuesMarshaller.ConvertParam((IValue) value, type); + return ContextValuesMarshaller.ConvertParam(iValue, type); } return Type.DefaultBinder.ChangeType(value, type, culture); } diff --git a/src/ScriptEngine/Machine/ValueFactory.cs b/src/ScriptEngine/Machine/ValueFactory.cs index e96c7dd17..52101d3ec 100644 --- a/src/ScriptEngine/Machine/ValueFactory.cs +++ b/src/ScriptEngine/Machine/ValueFactory.cs @@ -5,8 +5,11 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using System.Globalization; -using ScriptEngine.Machine.Values; +using System.Diagnostics; +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Values; namespace ScriptEngine.Machine { @@ -14,47 +17,42 @@ public static class ValueFactory { public static IValue Create() { - return UndefinedValue.Instance; + return BslUndefinedValue.Instance; } public static IValue Create(string value) { - return StringValue.Create(value); + return BslStringValue.Create(value); } public static IValue Create(bool value) { - return value ? BooleanValue.True : BooleanValue.False; + return BslBooleanValue.Create(value); } public static IValue Create(decimal value) { - return NumberValue.Create(value); + return BslNumericValue.Create(value); } public static IValue Create(int value) { - return NumberValue.Create(value); + return BslNumericValue.Create(value); } public static IValue Create(DateTime value) { - return new DateValue(value); + return BslDateValue.Create(value); } public static IValue CreateInvalidValueMarker() { - return InvalidValue.Instance; + return BslSkippedParameterValue.Instance; } public static IValue CreateNullValue() { - return NullValue.Instance; - } - - public static IValue Create(IRuntimeContextInstance instance) - { - return (IValue)instance; + return BslNullValue.Instance; } public static IValue Parse(string presentation, DataType type) @@ -63,70 +61,21 @@ public static IValue Parse(string presentation, DataType type) switch (type) { case DataType.Boolean: - - if (String.Compare(presentation, "истина", StringComparison.OrdinalIgnoreCase) == 0 - || String.Compare(presentation, "true", StringComparison.OrdinalIgnoreCase) == 0 - || String.Compare(presentation, "да", StringComparison.OrdinalIgnoreCase) == 0) - result = ValueFactory.Create(true); - else if (String.Compare(presentation, "ложь", StringComparison.OrdinalIgnoreCase) == 0 - || String.Compare(presentation, "false", StringComparison.OrdinalIgnoreCase) == 0 - || String.Compare(presentation, "нет", StringComparison.OrdinalIgnoreCase) == 0) - result = ValueFactory.Create(false); - else - throw RuntimeException.ConvertToBooleanException(); - + result = BslBooleanValue.Parse(presentation); break; case DataType.Date: - string format; - if (presentation.Length == 14) - format = "yyyyMMddHHmmss"; - else if (presentation.Length == 8) - format = "yyyyMMdd"; - else if (presentation.Length == 12) - format = "yyyyMMddHHmm"; - else - throw RuntimeException.ConvertToDateException(); - - if (presentation == "00000000" - || presentation == "000000000000" - || presentation == "00000000000000") - { - result = ValueFactory.Create(new DateTime()); - } - else - try - { - result = ValueFactory.Create(DateTime.ParseExact(presentation, format, System.Globalization.CultureInfo.InvariantCulture)); - } - catch (FormatException) - { - throw RuntimeException.ConvertToDateException(); - } - + result = BslDateValue.Parse(presentation); break; case DataType.Number: - var numInfo = NumberFormatInfo.InvariantInfo; - var numStyle = NumberStyles.AllowDecimalPoint - |NumberStyles.AllowLeadingSign - |NumberStyles.AllowLeadingWhite - |NumberStyles.AllowTrailingWhite; - - try - { - result = ValueFactory.Create(Decimal.Parse(presentation, numStyle, numInfo)); - } - catch (FormatException) - { - throw RuntimeException.ConvertToNumberException(); - } + result = BslNumericValue.Parse(presentation); break; case DataType.String: - result = ValueFactory.Create(presentation); + result = BslStringValue.Create(presentation); break; case DataType.Undefined: - result = ValueFactory.Create(); + result = BslUndefinedValue.Instance; break; - case DataType.GenericValue: + case DataType.Null: if (string.Compare(presentation, "null", StringComparison.OrdinalIgnoreCase) == 0) result = ValueFactory.CreateNullValue(); else @@ -140,83 +89,19 @@ public static IValue Parse(string presentation, DataType type) return result; } - class InvalidValue : IValue + public static IValue Add(IValue op1, IValue op2, IBslProcess process) { - private static IValue _instance = new InvalidValue(); - - internal static IValue Instance => _instance; - - #region IValue Members - - public DataType DataType => DataType.NotAValidValue; - - public TypeDescriptor SystemType => throw new NotImplementedException(); - - public decimal AsNumber() + // принимаем только RawValue + Debug.Assert(!(op1 is IVariable || op2 is IVariable)); + + if (op1 is BslStringValue s) { - throw new NotImplementedException(); + return Create(s + ((BslValue)op2).ToString(process)); } - public DateTime AsDate() + if (op1 is BslDateValue date) { - throw new NotImplementedException(); - } - - public bool AsBoolean() - { - throw new NotImplementedException(); - } - - public string AsString() - { - throw new NotImplementedException(); - } - - public IRuntimeContextInstance AsObject() - { - throw new NotImplementedException(); - } - - public IValue GetRawValue() - { - return this; - } - - #endregion - - #region IComparable Members - - public int CompareTo(IValue other) - { - throw new NotImplementedException(); - } - - #endregion - - #region IEquatable Members - - public bool Equals(IValue other) - { - return ReferenceEquals(other, this); - } - - #endregion - } - - - public static IValue Add(IValue op1, IValue op2) - { - var type1 = op1.DataType; - - if (type1 == DataType.String) - { - return Create(op1.AsString() + op2.AsString()); - } - - if (type1 == DataType.Date && op2.DataType == DataType.Number) - { - var date = op1.AsDate(); - return Create(date.AddSeconds((double)op2.AsNumber())); + return Create(date + op2.AsNumber()); } // все к числовому типу. @@ -225,20 +110,22 @@ public static IValue Add(IValue op1, IValue op2) public static IValue Sub(IValue op1, IValue op2) { - if (op1.DataType == DataType.Number) - { - return Create(op1.AsNumber() - op2.AsNumber()); - } - if (op1.DataType == DataType.Date && op2.DataType == DataType.Number) + // принимаем только RawValue + Debug.Assert(!(op1 is IVariable || op2 is IVariable)); + + if (op1 is BslNumericValue n) { - var date = op1.AsDate(); - var result = date.AddSeconds(-(double)op2.AsNumber()); - return Create(result); + return Create(n - op2.AsNumber()); } - if (op1.DataType == DataType.Date && op2.DataType == DataType.Date) + + if (op1 is BslDateValue date) { - var span = op1.AsDate() - op2.AsDate(); - return Create((decimal)span.TotalSeconds); + if (op2 is BslDateValue d2) + { + return Create(date - d2); + } + + return Create(date - op2.AsNumber()); } // все к числовому типу. @@ -247,11 +134,17 @@ public static IValue Sub(IValue op1, IValue op2) public static IValue Mul(IValue op1, IValue op2) { + // принимаем только RawValue + Debug.Assert(!(op1 is IVariable || op2 is IVariable)); + return Create(op1.AsNumber() * op2.AsNumber()); } public static IValue Div(IValue op1, IValue op2) { + // принимаем только RawValue + Debug.Assert(!(op1 is IVariable || op2 is IVariable)); + if (op2.AsNumber() == 0) { throw RuntimeException.DivideByZero(); @@ -261,6 +154,9 @@ public static IValue Div(IValue op1, IValue op2) public static IValue Mod(IValue op1, IValue op2) { + // принимаем только RawValue + Debug.Assert(!(op1 is IVariable || op2 is IVariable)); + if (op2.AsNumber() == 0) { throw RuntimeException.DivideByZero(); @@ -270,6 +166,9 @@ public static IValue Mod(IValue op1, IValue op2) public static IValue Neg(IValue op1) { + // принимаем только RawValue + Debug.Assert(!(op1 is IVariable)); + return Create(op1.AsNumber() * -1); } } diff --git a/src/ScriptEngine/Machine/Values/BooleanValue.cs b/src/ScriptEngine/Machine/Values/BooleanValue.cs deleted file mode 100644 index a99beaf7b..000000000 --- a/src/ScriptEngine/Machine/Values/BooleanValue.cs +++ /dev/null @@ -1,63 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Values -{ - public class BooleanValue : GenericValue - { - public static readonly BooleanValue True = new BooleanValue(true); - public static readonly BooleanValue False = new BooleanValue(false); - - private static string _trueString; - private static string _falseString; - - private bool _value = false; - - private BooleanValue(bool value) - { - _value = value; - DataType = DataType.Boolean; - } - - static BooleanValue() - { - RefreshLocalizedStrings(); - Locale.SystemLocaleChanged += RefreshLocalizedStrings; - } - - private static void RefreshLocalizedStrings() - { - _trueString = Locale.NStr("ru = 'Да'; en = 'True'"); - _falseString = Locale.NStr("ru = 'Нет'; en = 'False'"); - } - - public override bool AsBoolean() - { - return _value; - } - - public override decimal AsNumber() - { - return _value ? 1 : 0; - } - - public override string AsString() - { - return _value ? _trueString : _falseString; - } - - public override int CompareTo(IValue other) - { - if (other.DataType == DataType.Number || other.DataType == DataType.Boolean) - { - return AsNumber().CompareTo(other.AsNumber()); - } - - return base.CompareTo(other); - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Values/DateValue.cs b/src/ScriptEngine/Machine/Values/DateValue.cs deleted file mode 100644 index aee522dbf..000000000 --- a/src/ScriptEngine/Machine/Values/DateValue.cs +++ /dev/null @@ -1,48 +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 ScriptEngine.Machine.Values -{ - public class DateValue : GenericValue - { - private readonly DateTime _value; - - public DateValue(DateTime value) - { - _value = value; - DataType = DataType.Date; - } - - public override DateTime AsDate() - { - return _value; - } - - public override string AsString() - { - return _value.ToString(); - } - - public override int CompareTo(IValue other) - { - if(other.DataType == DataType.Date) - return _value.CompareTo(other.AsDate()); - - return base.CompareTo(other); - } - - public override bool Equals(IValue other) - { - if (other == null) - return false; - - return other.DataType == DataType.Date && _value.Equals(other.AsDate()); - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Values/GenericValue.cs b/src/ScriptEngine/Machine/Values/GenericValue.cs deleted file mode 100644 index cbb799cc7..000000000 --- a/src/ScriptEngine/Machine/Values/GenericValue.cs +++ /dev/null @@ -1,60 +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 ScriptEngine.Machine.Values -{ - public abstract class GenericValue : IValue - { - public virtual int CompareTo(IValue other) - { - throw RuntimeException.ComparisonNotSupportedException(); - } - - public virtual bool Equals(IValue other) - { - return ReferenceEquals(this, other?.GetRawValue()); - } - - public DataType DataType { get; protected set; } - - public virtual TypeDescriptor SystemType => TypeDescriptor.FromDataType(DataType); - - public virtual decimal AsNumber() - { - throw RuntimeException.ConvertToNumberException(); - } - - public virtual DateTime AsDate() - { - throw RuntimeException.ConvertToDateException(); - } - - public virtual bool AsBoolean() - { - throw RuntimeException.ConvertToBooleanException(); - } - - public virtual string AsString() => DataType.ToString(); - - public virtual IRuntimeContextInstance AsObject() - { - throw RuntimeException.ValueIsNotObjectException(); - } - - public IValue GetRawValue() - { - return this; - } - - public override string ToString() - { - return AsString(); - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Values/NullValue.cs b/src/ScriptEngine/Machine/Values/NullValue.cs deleted file mode 100644 index dddc4fae9..000000000 --- a/src/ScriptEngine/Machine/Values/NullValue.cs +++ /dev/null @@ -1,31 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Values -{ - public class NullValue : GenericValue - { - public static readonly NullValue Instance; - - static NullValue() - { - Instance = new NullValue(); - } - - public NullValue() - { - DataType = DataType.GenericValue; - } - - public override TypeDescriptor SystemType => TypeManager.GetTypeByFrameworkType(typeof(NullValue)); - - public override string AsString() - { - return string.Empty; - } - } -} diff --git a/src/ScriptEngine/Machine/Values/NumberValue.cs b/src/ScriptEngine/Machine/Values/NumberValue.cs deleted file mode 100644 index f1d9676f9..000000000 --- a/src/ScriptEngine/Machine/Values/NumberValue.cs +++ /dev/null @@ -1,96 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Values -{ - public class NumberValue : GenericValue - { - private readonly decimal _value; - - private static readonly NumberValue[] _popularValues = new NumberValue[10]; - - static NumberValue() - { - for (int i = 0; i < 10; i++) - { - _popularValues[i] = new NumberValue(i); - } - } - - public static NumberValue Create(decimal value) - { - switch (value) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - case 9: - return _popularValues[(int)value]; - default: - return new NumberValue(value); - } - } - - public static NumberValue Create(double value) - { - return Create((decimal)value); - } - - public static NumberValue Create(int value) - { - return Create((decimal)value); - } - - private NumberValue(decimal value) - { - _value = value; - DataType = DataType.Number; - } - - public override bool AsBoolean() - { - return _value != 0; - } - - public override decimal AsNumber() - { - return _value; - } - - public override string AsString() - { - return AsNumber().ToString(System.Globalization.NumberFormatInfo.InvariantInfo); - } - - public override int CompareTo(IValue other) - { - if (other.DataType == DataType.Boolean || other.DataType == DataType.Number) - { - return _value.CompareTo(other.AsNumber()); - } - - return base.CompareTo(other); - } - - public override bool Equals(IValue other) - { - if (other == null) - return false; - - if (other.DataType == DataType.Number || other.DataType == DataType.Boolean) - return _value == other.AsNumber(); - - return false; - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Values/StringValue.cs b/src/ScriptEngine/Machine/Values/StringValue.cs deleted file mode 100644 index 5437278c4..000000000 --- a/src/ScriptEngine/Machine/Values/StringValue.cs +++ /dev/null @@ -1,68 +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 ScriptEngine.Machine.Values -{ - public class StringValue : GenericValue - { - public static StringValue Empty { get; } = new StringValue(String.Empty); - - private readonly string _value; - - public static StringValue Create(string value) - { - return value == string.Empty ? Empty : new StringValue(value); - } - - private StringValue(string value) - { - _value = value ?? throw new ArgumentNullException(); - DataType = DataType.String; - } - - public override string AsString() - { - return _value; - } - - public override decimal AsNumber() - { - return ValueFactory.Parse(_value, DataType.Number).AsNumber(); - } - - public override DateTime AsDate() - { - return ValueFactory.Parse(_value, DataType.Date).AsDate(); - } - - public override bool AsBoolean() - { - return ValueFactory.Parse(_value, DataType.Boolean).AsBoolean(); - } - - public override int CompareTo(IValue other) - { - if(other?.DataType == DataType.String) - return String.Compare(_value, other.AsString(), StringComparison.Ordinal); - - throw RuntimeException.ComparisonNotSupportedException(); - } - - public override bool Equals(IValue other) - { - if (other?.DataType == DataType) - { - var scv = other.AsString(); - return scv == _value; - } - - return false; - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Values/TypeTypeValue.cs b/src/ScriptEngine/Machine/Values/TypeTypeValue.cs deleted file mode 100644 index 545c6a661..000000000 --- a/src/ScriptEngine/Machine/Values/TypeTypeValue.cs +++ /dev/null @@ -1,58 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Values -{ - public class TypeTypeValue : GenericValue - { - readonly TypeDescriptor _instance; - - public TypeTypeValue(string name) - { - _instance = TypeManager.GetTypeByName(name); - DataType = DataType.Type; - } - - public TypeTypeValue(TypeDescriptor type) - { - _instance = type; - DataType = DataType.Type; - } - - public override TypeDescriptor SystemType => TypeDescriptor.FromDataType(DataType.Type); - - public override string AsString() - { - return _instance.ToString(); - } - - public override bool Equals(IValue other) - { - if(other?.DataType == DataType) - { - var otherVal = other.GetRawValue() as TypeTypeValue; - return otherVal._instance.ID == this._instance.ID; - } - - return false; - } - - public override bool Equals(object obj) - { - if (obj is TypeTypeValue value) - return Equals(value); - return false; - } - - public override int GetHashCode() - { - return _instance.GetHashCode(); - } - - public TypeDescriptor Value => _instance; - } -} diff --git a/src/ScriptEngine/Machine/Values/UndefinedValue.cs b/src/ScriptEngine/Machine/Values/UndefinedValue.cs deleted file mode 100644 index 96bb0a7c4..000000000 --- a/src/ScriptEngine/Machine/Values/UndefinedValue.cs +++ /dev/null @@ -1,32 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace ScriptEngine.Machine.Values -{ - public class UndefinedValue : GenericValue - { - public static UndefinedValue Instance { get; } = new UndefinedValue(); - - private UndefinedValue() - { - DataType = DataType.Undefined; - } - - public override int CompareTo(IValue other) - { - if(other.DataType == DataType) - return 0; - - return base.CompareTo(other); - } - - public override string AsString() - { - return string.Empty; - } - } -} \ No newline at end of file diff --git a/src/ScriptEngine/Machine/Variables.cs b/src/ScriptEngine/Machine/Variables.cs deleted file mode 100644 index 3f3737d2a..000000000 --- a/src/ScriptEngine/Machine/Variables.cs +++ /dev/null @@ -1,296 +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 ScriptEngine.Machine -{ - public interface IVariable : IValue - { - IValue Value { get; set; } - string Name { get; } - } - - public class Variable : IVariable - { - private IValue _val; - - public string Name { get; private set; } - - private Variable() - { - - } - - #region Factory - - public static IVariable Create(IValue val, string symbol) - { - return new Variable() - { - _val = val, - Name = symbol - }; - } - - public static IVariable Create(IValue val, VariableInfo metadata) - { - return new Variable() - { - _val = val, - Name = metadata.Identifier - }; - } - - public static IVariable CreateReference(IVariable variable, string refName) - { - return new VariableReference(variable, refName); - } - - public static IVariable CreateContextPropertyReference(IRuntimeContextInstance context, int propertyNumber, string refName) - { - return new VariableReference(context, propertyNumber, refName); - } - - public static IVariable CreateIndexedPropertyReference(IRuntimeContextInstance context, IValue index, string refName) - { - return new VariableReference(context, index, refName); - } - - #endregion - - #region IVariable Members - - public IValue Value - { - get - { - return _val; - } - set - { - _val = value; - } - } - - #endregion - - #region IValue Members - - public DataType DataType - { - get { return Value.DataType; } - } - - public TypeDescriptor SystemType - { - get { return Value.SystemType; } - } - - public decimal AsNumber() - { - return Value.AsNumber(); - } - - public DateTime AsDate() - { - return Value.AsDate(); - } - - public bool AsBoolean() - { - return Value.AsBoolean(); - } - - public string AsString() - { - return Value.AsString(); - } - - public IRuntimeContextInstance AsObject() - { - return Value.AsObject(); - } - - public IValue GetRawValue() - { - return Value; - } - - #endregion - - #region IComparable Members - - public int CompareTo(IValue other) - { - return Value.CompareTo(other); - } - - #endregion - - #region IEquatable Members - - public bool Equals(IValue other) - { - return Value.Equals(other); - } - - #endregion - - #region Reference - - private class VariableReference : IVariable - { - private ReferenceType _refType; - private IVariable _referencedValue; - private IRuntimeContextInstance _context; - private int _contextPropertyNumber; - private IValue _index; - - public string Name { get; private set; } - - public VariableReference(IVariable var, string name) - { - _refType = ReferenceType.Simple; - _referencedValue = var; - Name = name; - } - - public VariableReference(IRuntimeContextInstance context, int propertyNumber, string name) - { - _refType = ReferenceType.ContextProperty; - _context = context; - _contextPropertyNumber = propertyNumber; - Name = name; - } - - public VariableReference(IRuntimeContextInstance context, IValue index, string name) - { - _refType = ReferenceType.IndexedProperty; - _context = context; - _index = index; - Name = name; - } - - private enum ReferenceType - { - Simple, - ContextProperty, - IndexedProperty - } - - #region IVariable Members - - public IValue Value - { - get - { - if (_refType == ReferenceType.Simple) - { - return _referencedValue.Value; - } - else if (_refType == ReferenceType.ContextProperty) - { - if (_context.IsPropReadable(_contextPropertyNumber)) - return _context.GetPropValue(_contextPropertyNumber); - else - throw RuntimeException.PropIsNotReadableException(""); - } - else - { - return _context.GetIndexedValue(_index); - } - } - set - { - if (_refType == ReferenceType.Simple) - { - _referencedValue.Value = value; - } - else if (_refType == ReferenceType.ContextProperty) - { - if(_context.IsPropWritable(_contextPropertyNumber)) - _context.SetPropValue(_contextPropertyNumber, value); - else - throw RuntimeException.PropIsNotWritableException(""); - } - else - { - _context.SetIndexedValue(_index, value); - } - } - } - - #endregion - - #region IValue Members - - public DataType DataType - { - get { return Value.DataType; } - } - - public TypeDescriptor SystemType - { - get { return Value.SystemType; } - } - - public decimal AsNumber() - { - return Value.AsNumber(); - } - - public DateTime AsDate() - { - return Value.AsDate(); - } - - public bool AsBoolean() - { - return Value.AsBoolean(); - } - - public string AsString() - { - return Value.AsString(); - } - - public IRuntimeContextInstance AsObject() - { - return Value.AsObject(); - } - - public IValue GetRawValue() - { - return Value.GetRawValue(); - } - - #endregion - - #region IComparable Members - - public int CompareTo(IValue other) - { - return Value.CompareTo(other); - } - - #endregion - - #region IEquatable Members - - public bool Equals(IValue other) - { - return Value.Equals(other); - } - - #endregion - - } - - #endregion - } - -} diff --git a/src/ScriptEngine/ModuleImage.cs b/src/ScriptEngine/ModuleImage.cs deleted file mode 100644 index 7df7f7cc8..000000000 --- a/src/ScriptEngine/ModuleImage.cs +++ /dev/null @@ -1,57 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using ScriptEngine.Machine; -using ScriptEngine.Environment; - -namespace ScriptEngine -{ - [Serializable] - public class ModuleImage - { - public ModuleImage() - { - EntryMethodIndex = -1; - Code = new List(); - VariableRefs = new List(); - MethodRefs = new List(); - Methods = new List(); - Constants = new List(); - ExportedProperties = new List(); - ExportedMethods = new List(); - Variables = new VariablesFrame(); - } - - public VariablesFrame Variables { get; } - public int EntryMethodIndex { get; set; } - public IList Code { get; set; } - public IList VariableRefs { get; set; } - public IList MethodRefs { get; set; } - public IList Methods { get; set; } - public IList Constants { get; set; } - public IList ExportedProperties { get; set; } - public IList ExportedMethods { get; set; } - public int LoadAddress { get; set; } - public ModuleInformation ModuleInfo { get; set; } - } - - [Serializable] - public struct MethodDescriptor - { - public MethodInfo Signature; - public VariablesFrame Variables; - public int EntryPoint; - } - - [Serializable] - public struct ExportedSymbol - { - public string SymbolicName; - public int Index; - } -} diff --git a/src/ScriptEngine/NullDependencyResolver.cs b/src/ScriptEngine/NullDependencyResolver.cs new file mode 100644 index 000000000..763690c88 --- /dev/null +++ b/src/ScriptEngine/NullDependencyResolver.cs @@ -0,0 +1,27 @@ +/*---------------------------------------------------------- +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/. +----------------------------------------------------------*/ + +#nullable enable +using OneScript.Compilation; +using OneScript.Execution; +using OneScript.Sources; + +namespace ScriptEngine +{ + // ReSharper disable once ClassNeverInstantiated.Global + internal class NullDependencyResolver : IDependencyResolver + { + public PackageInfo? Resolve(SourceCode module, string libraryName, IBslProcess process) + { + return null; + } + + public void Initialize(ScriptingEngine engine) + { + } + } +} diff --git a/src/ScriptEngine/OneScriptCoreOptions.cs b/src/ScriptEngine/OneScriptCoreOptions.cs new file mode 100644 index 000000000..7d90b0793 --- /dev/null +++ b/src/ScriptEngine/OneScriptCoreOptions.cs @@ -0,0 +1,83 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Text; +using OneScript.Commons; +using OneScript.Native.Compiler; +using ScriptEngine.Hosting; + +namespace ScriptEngine +{ + public class OneScriptCoreOptions + { + private const string FILE_READER_ENCODING = "encoding.script"; + private const string SYSTEM_LANGUAGE_KEY = "SystemLanguage"; + private const string PREPROCESSOR_DEFINITIONS_KEY = "preprocessor.define"; + private const string DEFAULT_RUNTIME_KEY = "runtime.default"; + private const string EXPLICIT_IMPORT = "lang.explicitImports"; + + public OneScriptCoreOptions(KeyValueConfig config) + { + SystemLanguage = config[SYSTEM_LANGUAGE_KEY]; + FileReaderEncoding = SetupEncoding(config[FILE_READER_ENCODING]); + PreprocessorDefinitions = SetupDefinitions(config[PREPROCESSOR_DEFINITIONS_KEY]); + UseNativeAsDefaultRuntime = SetupDefaultRuntime(config[DEFAULT_RUNTIME_KEY]); + ExplicitImports = SetupExplicitImports(config[EXPLICIT_IMPORT]); + } + + public string SystemLanguage { get; } + + public Encoding FileReaderEncoding { get; } + + public bool UseNativeAsDefaultRuntime { get; } + + public IEnumerable PreprocessorDefinitions { get; } + + public ExplicitImportsBehavior ExplicitImports { get; } + + private static IEnumerable SetupDefinitions(string s) + { + return s?.Split(',') ?? Array.Empty(); + } + + private static Encoding SetupEncoding(string openerEncoding) + { + if (string.IsNullOrWhiteSpace(openerEncoding)) + return Encoding.UTF8; + + return StringComparer.InvariantCultureIgnoreCase.Compare(openerEncoding, "default") == 0 ? + FileOpener.SystemSpecificEncoding() : + Encoding.GetEncoding(openerEncoding); + } + + private static bool SetupDefaultRuntime(string runtimeId) + { + return runtimeId == NativeRuntimeAnnotationHandler.NativeDirectiveName; + } + + private static ExplicitImportsBehavior SetupExplicitImports(string keyValue) + { + switch (keyValue) + { + case "on": + return ExplicitImportsBehavior.Enabled; + case "off": + return ExplicitImportsBehavior.Disabled; + case "warn": + return ExplicitImportsBehavior.Warn; + case "dev": + case null: + return ExplicitImportsBehavior.Development; + default: + SystemLogger.Write($"Unknown value for {EXPLICIT_IMPORT}: {keyValue}"); + return ExplicitImportsBehavior.Warn; + } + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/RuntimeEnvironment.cs b/src/ScriptEngine/RuntimeEnvironment.cs index 775de4f73..bb450db38 100644 --- a/src/ScriptEngine/RuntimeEnvironment.cs +++ b/src/ScriptEngine/RuntimeEnvironment.cs @@ -1,162 +1,187 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using ScriptEngine.Compiler; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; - -namespace ScriptEngine -{ - public class RuntimeEnvironment - { - private readonly List _objects = new List(); - private readonly CompilerContext _symbolScopes = new CompilerContext(); - private SymbolScope _globalScope; - private PropertyBag _injectedProperties; - - private readonly List _externalLibs = new List(); - - public void InjectObject(IAttachableContext context) - { - InjectObject(context, false); - } - - public void InjectObject(IAttachableContext context, bool asDynamicScope) - { - RegisterSymbolScope(context, asDynamicScope); - RegisterObject(context); - } - - public void InjectGlobalProperty(IValue value, string identifier, bool readOnly) - { - if(!Utils.IsValidIdentifier(identifier)) - { - throw new ArgumentException("Invalid identifier", "identifier"); - } - - if (_globalScope == null) - { - _globalScope = new SymbolScope(); - TypeManager.RegisterType("__globalPropertiesHolder", typeof(PropertyBag)); - _injectedProperties = new PropertyBag(); - _symbolScopes.PushScope(_globalScope); - RegisterObject(_injectedProperties); - } - - _globalScope.DefineVariable(identifier, SymbolType.ContextProperty); - _injectedProperties.Insert(value, identifier, true, !readOnly); - } - - public void SetGlobalProperty(string propertyName, IValue value) - { - int propId = _injectedProperties.FindProperty(propertyName); - _injectedProperties.SetPropValue(propId, value); - } - - public IValue GetGlobalProperty(string propertyName) - { - int propId = _injectedProperties.FindProperty(propertyName); - return _injectedProperties.GetPropValue(propId); - } - - internal CompilerContext SymbolsContext - { - get - { - return _symbolScopes; - } - } - - internal IList AttachedContexts - { - get - { - return _objects; - } - } - - // public void NotifyClassAdded(ModuleImage module, string symbol, string libraryName) - // { - // _externalScripts.Add(new UserAddedScript() - // { - // Type = UserAddedScriptType.Class, - // Symbol = symbol, - // Image = module, - // LibraryName = libraryName - // }); - // } - // - // public void NotifyModuleAdded(ModuleImage module, string symbol, string libraryName) - // { - // var script = new UserAddedScript() - // { - // Type = UserAddedScriptType.Module, - // Symbol = symbol, - // Image = module, - // LibraryName = libraryName - // }; - // - // _externalScripts.Add(script); - // } - - public IEnumerable GetUserAddedScripts() - { - return _externalLibs.ToArray(); - } - - private void RegisterSymbolScope(IRuntimeContextInstance provider, bool asDynamicScope) - { - var scope = new SymbolScope(); - scope.IsDynamicScope = asDynamicScope; - - _symbolScopes.PushScope(scope); - foreach (var item in provider.GetProperties()) - { - _symbolScopes.DefineVariable(item.Identifier); - } - - foreach (var item in provider.GetMethods()) - { - _symbolScopes.DefineMethod(item); - } - } - - private void RegisterObject(IAttachableContext context) - { - _objects.Add(context); - } - - public void LoadMemory(MachineInstance machine) - { - machine.Cleanup(); - foreach (var item in AttachedContexts) - { - machine.AttachContext(item); - } - machine.ContextsAttached(); - } - - public void InitExternalLibrary(ScriptingEngine runtime, ExternalLibraryDef library) - { - var loadedObjects = new ScriptDrivenObject[library.Modules.Count]; - int i = 0; - foreach (var module in library.Modules) - { - var loaded = runtime.LoadModuleImage(module.Image); - var instance = runtime.CreateUninitializedSDO(loaded); - SetGlobalProperty(module.Symbol, instance); - module.InjectOrder = _injectedProperties.FindProperty(module.Symbol); - loadedObjects[i++] = instance; - } - - _externalLibs.Add(library); - loadedObjects.ForEach(runtime.InitializeSDO); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.Contexts; +using OneScript.Execution; +using ScriptEngine.Libraries; +using ScriptEngine.Machine; +using SymbolScope = OneScript.Compilation.Binding.SymbolScope; + +namespace ScriptEngine +{ + [Obsolete("Use interface IRuntimeEnvironment")] + public class RuntimeEnvironment : IRuntimeEnvironment, ILibraryManager + { + private readonly SymbolTable _symbols = new SymbolTable(); + private SymbolScope _scopeOfGlobalProperties; + + private readonly PropertyBag _injectedProperties; + + private readonly List _contexts = new List(); + + private readonly ILibraryManager _libraryManager; + + public RuntimeEnvironment() + { + _injectedProperties = new PropertyBag(); + _libraryManager = new LibraryManager(_injectedProperties); + } + + private void CreateGlobalScopeIfNeeded() + { + if (_scopeOfGlobalProperties != null) + return; + + lock (_injectedProperties) + { + _scopeOfGlobalProperties ??= _symbols.PushContext(_injectedProperties); + _contexts.Add(_injectedProperties); + } + } + + public void InjectObject(IAttachableContext context) + { + RegisterObject(context); + } + + public void InjectGlobalProperty(IValue value, string identifier, string alias, bool readOnly) + { + InjectPropertyInternal(value, identifier, alias, readOnly, null); + } + + public void InjectGlobalProperty(IValue value, string identifier, bool readOnly) + { + InjectGlobalProperty(value, identifier, default, readOnly); + } + + public void InjectGlobalProperty(IValue value, string identifier, PackageInfo ownerPackage) + { + InjectPropertyInternal(value, identifier, default, true, ownerPackage); + } + + private void InjectPropertyInternal( + IValue value, + string identifier, + string alias, + bool readOnly, + PackageInfo ownerPackage) + { + ArgumentNullException.ThrowIfNull(value); + if(!Utils.IsValidIdentifier(identifier)) + { + throw new ArgumentException("Invalid identifier", nameof(identifier)); + } + + if (alias != default && !Utils.IsValidIdentifier(alias)) + { + throw new ArgumentException("Invalid identifier", nameof(alias)); + } + CreateGlobalScopeIfNeeded(); + var num = _injectedProperties.Insert(value, identifier, true, !readOnly); + + var bslPropertyInfo = _injectedProperties.GetPropertyInfo(num); + IVariableSymbol registeredSymbol; + if (ownerPackage == null) + { + registeredSymbol = new WrappedPropertySymbol(bslPropertyInfo) + { + Name = identifier, + Alias = alias + }; + } + else + { + registeredSymbol = new WrappedLibraryPropertySymbol(bslPropertyInfo, ownerPackage) + { + Name = identifier, + Alias = alias + }; + } + + _scopeOfGlobalProperties.DefineVariable(registeredSymbol); + } + + public void InjectGlobalProperty(IValue value, BslPropertyInfo definition) + { + CreateGlobalScopeIfNeeded(); + _injectedProperties.Insert(value, definition); + + var symbol = new WrappedPropertySymbol(definition) + { + Name = definition.Name, + Alias = definition.Alias + }; + + _scopeOfGlobalProperties.DefineVariable(symbol); + } + + private void RegisterObject(IAttachableContext context) + { + _symbols.PushContext(context); + _contexts.Add(context); + } + + public void SetGlobalProperty(string propertyName, IValue value) + { + _symbols.FindVariable(propertyName, out var binding); + + var context = _contexts[binding.ScopeNumber]; + context.SetPropValue(binding.MemberNumber, value); + } + + public IValue GetGlobalProperty(string propertyName) + { + _symbols.FindVariable(propertyName, out var binding); + + var context = _contexts[binding.ScopeNumber]; + return context.GetPropValue(binding.MemberNumber); + } + + public SymbolTable GetSymbolTable() => _symbols; + + public IReadOnlyList AttachedContexts => _contexts; + + public void InitExternalLibrary(ScriptingEngine runtime, ExternalLibraryInfo library, IBslProcess process) + { + _libraryManager.InitExternalLibrary(runtime, library, process); + } + + private class WrappedPropertySymbol : IPropertySymbol + { + public WrappedPropertySymbol(BslPropertyInfo propInfo) + { + Property = propInfo; + } + + public string Name { get; set; } + public string Alias { get; set; } + public Type Type => Property.PropertyType; + public BslPropertyInfo Property { get; } + } + + private class WrappedLibraryPropertySymbol : IPropertySymbol, IPackageSymbol + { + public WrappedLibraryPropertySymbol(BslPropertyInfo propInfo, PackageInfo ownerPackage) + { + Property = propInfo; + Package = ownerPackage; + } + + public string Name { get; set; } + public string Alias { get; set; } + public Type Type => Property.PropertyType; + public BslPropertyInfo Property { get; } + private PackageInfo Package { get; } + + public PackageInfo GetPackageInfo() => Package; + } + } +} diff --git a/src/ScriptEngine/ScriptEngine.csproj b/src/ScriptEngine/ScriptEngine.csproj index ca990352f..f81d68951 100644 --- a/src/ScriptEngine/ScriptEngine.csproj +++ b/src/ScriptEngine/ScriptEngine.csproj @@ -3,8 +3,8 @@ - net452;netstandard2.0;netstandard2.1 - Debug;Release + $(TargetFrameworkVersion) + Debug;Release;LinuxDebug AnyCPU @@ -26,17 +26,28 @@ 1701;1702;1705;1591 + + TRACE;TINYIOC_INTERNAL + + + + TRACE;TINYIOC_INTERNAL + + + + TRACE;TINYIOC_INTERNAL;DEBUG; + true + false + + + + - - - - - \ No newline at end of file diff --git a/src/ScriptEngine/ScriptSourceFactory.cs b/src/ScriptEngine/ScriptSourceFactory.cs new file mode 100644 index 000000000..572bd7d20 --- /dev/null +++ b/src/ScriptEngine/ScriptSourceFactory.cs @@ -0,0 +1,49 @@ +/*---------------------------------------------------------- +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.Text; +using OneScript.Sources; + +namespace ScriptEngine +{ + public class ScriptSourceFactory + { + public ScriptSourceFactory() + { + ReaderEncoding = Encoding.UTF8; + } + + public SourceCode FromString(string code) + { + return SourceCodeBuilder.Create() + .FromString(code) + .Build(); + } + + public SourceCode FromFile(string path) + { + return SourceCodeBuilder + .Create() + .FromFile(path, ReaderEncoding) + .Build(); + } + + /// + /// Создаёт SourceCode из файла с указанием пакета-владельца. + /// + public SourceCode FromFile(string path, string ownerPackageId) + { + return SourceCodeBuilder + .Create() + .FromFile(path, ReaderEncoding) + .WithOwnerPackage(ownerPackageId) + .Build(); + } + + public Encoding ReaderEncoding { get; set; } + } +} diff --git a/src/ScriptEngine/ScriptingEngine.cs b/src/ScriptEngine/ScriptingEngine.cs index 4162b1243..5b8340900 100644 --- a/src/ScriptEngine/ScriptingEngine.cs +++ b/src/ScriptEngine/ScriptingEngine.cs @@ -1,196 +1,202 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; - -using ScriptEngine.Environment; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using ScriptEngine.Compiler; - -namespace ScriptEngine -{ - public class ScriptingEngine : IDisposable - { - // TODO выпилить инстанс машины отсюда, т.к. он привязан к потоку, а не к engine - private readonly MachineInstance _machine; - private readonly ScriptSourceFactory _scriptFactory; - private AttachedScriptsFactory _attachedScriptsFactory; - private IDebugController _debugController; - - public ScriptingEngine() - { - _machine = MachineInstance.Current; - - TypeManager.Initialize(new StandartTypeManager()); - TypeManager.RegisterType("Сценарий", typeof(UserScriptContextInstance)); - - GlobalsManager.Reset(); - AttachAssembly(System.Reflection.Assembly.GetExecutingAssembly()); - - _scriptFactory = new ScriptSourceFactory(); - DirectiveResolvers = new DirectiveMultiResolver(); - } - - public CodeGenerationFlags ProduceExtraCode { get; set; } - - public void AttachAssembly(System.Reflection.Assembly asm) - { - ContextDiscoverer.DiscoverClasses(asm); - } - - public void AttachAssembly(System.Reflection.Assembly asm, RuntimeEnvironment globalEnvironment) - { - ContextDiscoverer.DiscoverClasses(asm); - ContextDiscoverer.DiscoverGlobalContexts(globalEnvironment, asm); - } - - public void AttachExternalAssembly(System.Reflection.Assembly asm, RuntimeEnvironment globalEnvironment) - { - ContextDiscoverer.DiscoverClasses(asm); - - var lastCount = globalEnvironment.AttachedContexts.Count(); - ContextDiscoverer.DiscoverGlobalContexts(globalEnvironment, asm); - - var newCount = globalEnvironment.AttachedContexts.Count(); - while (lastCount < newCount) - { - _machine.AttachContext(globalEnvironment.AttachedContexts[lastCount]); - ++lastCount; - } - } - - public RuntimeEnvironment Environment { get; set; } - - public void Initialize() - { - SetDefaultEnvironmentIfNeeded(); - - UpdateContexts(); - - _attachedScriptsFactory = new AttachedScriptsFactory(this); - AttachedScriptsFactory.SetInstance(_attachedScriptsFactory); - } - - public void UpdateContexts() - { - Environment.LoadMemory(_machine); - } - - private void SetDefaultEnvironmentIfNeeded() - { - if (Environment == null) - Environment = new RuntimeEnvironment(); - } - - public ICodeSourceFactory Loader - { - get - { - return _scriptFactory; - } - } - - public IList DirectiveResolvers { get; } - - public CompilerService GetCompilerService() - { - var cs = new CompilerService(Environment.SymbolsContext); - switch (System.Environment.OSVersion.Platform) - { - case PlatformID.Unix: - cs.DefinePreprocessorValue("Linux"); - break; - case PlatformID.MacOSX: - cs.DefinePreprocessorValue("MacOS"); - break; - case PlatformID.Win32NT: - cs.DefinePreprocessorValue("Windows"); - break; - } - - cs.ProduceExtraCode = ProduceExtraCode; - cs.DirectiveResolver = (IDirectiveResolver)DirectiveResolvers; - return cs; - } - - public IRuntimeContextInstance NewObject(LoadedModule module, ExternalContextData externalContext = null) - { - var scriptContext = CreateUninitializedSDO(module, externalContext); - InitializeSDO(scriptContext); - - return scriptContext; - } - - public ScriptDrivenObject CreateUninitializedSDO(LoadedModule module, ExternalContextData externalContext = null) - { - var scriptContext = new UserScriptContextInstance(module); - scriptContext.AddProperty("ЭтотОбъект", "ThisObject", scriptContext); - if (externalContext != null) - { - foreach (var item in externalContext) - { - scriptContext.AddProperty(item.Key, item.Value); - } - } - - scriptContext.InitOwnData(); - return scriptContext; - } - - public LoadedModule LoadModuleImage(ModuleImage moduleImage) - { - return new LoadedModule(moduleImage); - } - - public void InitializeSDO(ScriptDrivenObject sdo) - { - sdo.Initialize(); - } - - public void ExecuteModule(LoadedModule module) - { - var scriptContext = new UserScriptContextInstance(module); - InitializeSDO(scriptContext); - } - - public MachineInstance Machine => _machine; - - public AttachedScriptsFactory AttachedScriptsFactory => _attachedScriptsFactory; - - public IDebugController DebugController - { - get => _debugController; - set - { - _debugController = value; - if (value != null) - { - ProduceExtraCode = CodeGenerationFlags.DebugCode; - _machine.SetDebugMode(_debugController.BreakpointManager); - } - } - } - - public void SetCodeStatisticsCollector(ICodeStatCollector collector) - { - ProduceExtraCode = CodeGenerationFlags.CodeStatistics; - _machine.SetCodeStatisticsCollector(collector); - } - - #region IDisposable Members - - public void Dispose() - { - AttachedScriptsFactory.SetInstance(null); - GlobalsManager.Reset(); - } - - #endregion - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.Compilation; +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Compiler; +using ScriptEngine.Libraries; +using ScriptEngine.Machine.Debugger; + +namespace ScriptEngine +{ + public class ScriptingEngine : IDisposable + { + private AttachedScriptsFactory _attachedScriptsFactory; + private IRuntimeEnvironment _runtimeEnvironment; + + private readonly ILibraryManager _libraryManager; + + public ScriptingEngine(ITypeManager types, + IGlobalsManager globals, + RuntimeEnvironment env, + OneScriptCoreOptions options, + IDebugger debugger, + IServiceContainer services) + { + TypeManager = types; + // FIXME: Пока потребители не отказались от статических инстансов, они будут жить и здесь + + GlobalsManager = globals; + _runtimeEnvironment = env; + _libraryManager = env; + + Loader = new ScriptSourceFactory(); + Services = services; + ContextDiscoverer = new ContextDiscoverer(types, globals, services); + + Debugger = debugger; + + if (debugger.IsEnabled) + { + ProduceExtraCode |= CodeGenerationFlags.DebugCode; + } + + Loader.ReaderEncoding = options.FileReaderEncoding; + } + + public IServiceContainer Services { get; } + + private ContextDiscoverer ContextDiscoverer { get; } + + public IRuntimeEnvironment Environment => _runtimeEnvironment; + + public ILibraryManager LibraryManager => _libraryManager; + + public ITypeManager TypeManager { get; } + + public IGlobalsManager GlobalsManager { get; } + + private CodeGenerationFlags ProduceExtraCode { get; set; } + + public void AttachAssembly(System.Reflection.Assembly asm, Predicate filter = null) + { + ContextDiscoverer.DiscoverClasses(asm, filter); + ContextDiscoverer.DiscoverGlobalContexts(Environment, asm, filter); + } + + public void AttachExternalAssembly(System.Reflection.Assembly asm, IRuntimeEnvironment globalEnvironment) + { + ContextDiscoverer.DiscoverClasses(asm); + + //var lastCount = globalEnvironment.AttachedContexts.Count(); + ContextDiscoverer.DiscoverGlobalContexts(globalEnvironment, asm); + + //var newCount = globalEnvironment.AttachedContexts.Count(); + // while (lastCount < newCount) + // { + // MachineInstance.Current.AttachContext(globalEnvironment.AttachedContexts[lastCount]); + // ++lastCount; + // } + } + + public void AttachExternalAssembly(System.Reflection.Assembly asm) + { + AttachExternalAssembly(asm, Environment); + } + + public void Initialize() + { + SetDefaultEnvironmentIfNeeded(); + EnableCodeStatistics(); + //UpdateContexts(); + + _attachedScriptsFactory = new AttachedScriptsFactory(this); + AttachedScriptsFactory.SetInstance(_attachedScriptsFactory); + } + + // public void UpdateContexts() + // { + // lock (this) + // { + // ExecutionDispatcher.Current ??= Services.Resolve(); + // } + // MachineInstance.Current.SetMemory(Services.Resolve()); + // } + + private void SetDefaultEnvironmentIfNeeded() + { + _runtimeEnvironment ??= new RuntimeEnvironment(); + } + + public ScriptSourceFactory Loader { get; } + + public ICompilerFrontend GetCompilerService() + { + using var scope = Services.CreateScope(); + var compiler = scope.Resolve(); + compiler.SharedSymbols = _runtimeEnvironment.GetSymbolTable(); + + switch (System.Environment.OSVersion.Platform) + { + case PlatformID.Unix: + compiler.PreprocessorDefinitions.Add("Linux"); + break; + case PlatformID.MacOSX: + compiler.PreprocessorDefinitions.Add("MacOS"); + break; + case PlatformID.Win32NT: + compiler.PreprocessorDefinitions.Add("Windows"); + break; + } + + compiler.GenerateDebugCode = ProduceExtraCode.HasFlag(CodeGenerationFlags.DebugCode); + compiler.GenerateCodeStat = ProduceExtraCode.HasFlag(CodeGenerationFlags.CodeStatistics); + return compiler; + } + + public UserScriptContextInstance NewObject(IExecutableModule module, IBslProcess process, + ExternalContextData externalContext = null) + { + var scriptContext = CreateUninitializedSDO(module, externalContext); + InitializeSDO(scriptContext, process); + + return scriptContext; + } + + public UserScriptContextInstance CreateUninitializedSDO(IExecutableModule module, ExternalContextData externalContext = null) + { + var scriptContext = new UserScriptContextInstance(module, true); + if (externalContext != null) + { + foreach (var item in externalContext) + { + scriptContext.AddProperty(item.Key, item.Value); + } + } + + scriptContext.InitOwnData(); + return scriptContext; + } + + public void InitializeSDO(ScriptDrivenObject sdo, IBslProcess process) + { + sdo.Initialize(process); + } + + public AttachedScriptsFactory AttachedScriptsFactory => _attachedScriptsFactory; + + public IDebugger Debugger { get; } + + private void EnableCodeStatistics() + { + var collector = Services.TryResolve(); + if (collector == default) + return; + + ProduceExtraCode |= CodeGenerationFlags.CodeStatistics; + } + + #region IDisposable Members + + public void Dispose() + { + AttachedScriptsFactory.SetInstance(null); + } + + #endregion + + /// + /// Инициализирует новый процесс + /// + public IBslProcess NewProcess() => Services.Resolve().NewProcess(); + } +} diff --git a/src/ScriptEngine/SystemLogger.cs b/src/ScriptEngine/SystemLogger.cs index a943033c6..2e0eff04a 100644 --- a/src/ScriptEngine/SystemLogger.cs +++ b/src/ScriptEngine/SystemLogger.cs @@ -24,6 +24,11 @@ public static void SetWriter(ISystemLogWriter writer) _writer = writer; } + public static void Reset() + { + _writer = new NullWriter(); + } + public static void Write(string text) { _writer.Write(text); diff --git a/src/ScriptEngine/Types/TypeFactoryCache.cs b/src/ScriptEngine/Types/TypeFactoryCache.cs new file mode 100644 index 000000000..aba6d3600 --- /dev/null +++ b/src/ScriptEngine/Types/TypeFactoryCache.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +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 OneScript.Types; +using ScriptEngine.Machine; + +namespace ScriptEngine.Types +{ + public class TypeFactoryCache + { + private readonly Dictionary _factories = new Dictionary(); + + public TypeFactory GetFactoryFor(TypeDescriptor type) + { + if (!_factories.TryGetValue(type, out var factory)) + { + factory = new TypeFactory(type); + _factories[type] = factory; + } + + return factory; + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Types/TypeHandlingExtensions.cs b/src/ScriptEngine/Types/TypeHandlingExtensions.cs new file mode 100644 index 000000000..36b06b3cb --- /dev/null +++ b/src/ScriptEngine/Types/TypeHandlingExtensions.cs @@ -0,0 +1,51 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using OneScript.Types; + +namespace ScriptEngine.Types +{ + public static class TypeHandlingExtensions + { + public static TypeDescriptor RegisterClass(this ITypeManager manager, Type classType) + { + var attribData = classType.GetCustomAttributes(typeof(ContextClassAttribute), false); + if (attribData.Length == 0) + { + throw new InvalidOperationException("Class is not marked as context"); + } + + var attr = (ContextClassAttribute)attribData[0]; + + var type = new TypeDescriptor( + classType, + attr.Name, + attr.Alias); + + manager.RegisterType(type); + return type; + } + + public static TypeDescriptor GetTypeFromClassMarkup(this Type classType) + { + var attribData = classType.GetCustomAttributes(typeof(ContextClassAttribute), false); + if (attribData.Length == 0) + { + throw new InvalidOperationException("Class is not marked as context"); + } + + var attr = (ContextClassAttribute)attribData[0]; + + return new TypeDescriptor( + classType, + attr.Name, + attr.Alias); + } + } +} \ No newline at end of file diff --git a/src/ScriptEngine/Utils.cs b/src/ScriptEngine/Utils.cs deleted file mode 100644 index f133d39d8..000000000 --- a/src/ScriptEngine/Utils.cs +++ /dev/null @@ -1,39 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; - -namespace ScriptEngine -{ - public static class Utils - { - public static bool IsValidIdentifier(string name) - { - if (name == null || name.Length == 0) - return false; - - if (!(Char.IsLetter(name[0]) || name[0] == '_')) - return false; - - for (int i = 1; i < name.Length; i++) - { - if (!(Char.IsLetterOrDigit(name[i]) || name[i] == '_')) - return false; - } - - return true; - } - - public static void ForEach(this IEnumerable input, Action action) - { - foreach (var data in input) - { - action(data); - } - } - } -} diff --git a/src/ScriptEngine/VariablesFrame.cs b/src/ScriptEngine/VariablesFrame.cs deleted file mode 100644 index cfdd53c2b..000000000 --- a/src/ScriptEngine/VariablesFrame.cs +++ /dev/null @@ -1,95 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections; -using System.Collections.Generic; -using ScriptEngine.Machine; - -namespace ScriptEngine -{ - [Serializable] - public class VariablesFrame : IList - { - private readonly List _data; - - public VariablesFrame() - { - _data = new List(); - } - - public VariablesFrame(IEnumerable src) - { - _data = new List(src); - } - - public IEnumerator GetEnumerator() - { - return _data.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable) _data).GetEnumerator(); - } - - public void Add(VariableInfo item) - { - _data.Add(item); - } - - public void Clear() - { - _data.Clear(); - } - - public bool Contains(VariableInfo item) - { - return _data.Contains(item); - } - - public void CopyTo(VariableInfo[] array, int arrayIndex) - { - _data.CopyTo(array, arrayIndex); - } - - public bool Remove(VariableInfo item) - { - return _data.Remove(item); - } - - public int Count - { - get { return _data.Count; } - } - - public bool IsReadOnly - { - get { return false; } - } - - public int IndexOf(VariableInfo item) - { - return _data.IndexOf(item); - } - - public void Insert(int index, VariableInfo item) - { - _data.Insert(index, item); - } - - public void RemoveAt(int index) - { - _data.RemoveAt(index); - } - - public VariableInfo this[int index] - { - get { return _data[index]; } - set { _data[index] = value; } - } - } -} diff --git a/src/StandaloneRunner/CompiledCodeIndexer.cs b/src/StandaloneRunner/CompiledCodeIndexer.cs index cb66f0f82..25b0d53fd 100644 --- a/src/StandaloneRunner/CompiledCodeIndexer.cs +++ b/src/StandaloneRunner/CompiledCodeIndexer.cs @@ -5,8 +5,8 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using OneScript.Commons; using OneScript.Language.LexicalAnalysis; -using ScriptEngine; namespace StandaloneRunner { diff --git a/src/StandaloneRunner/InternalTemplate.cs b/src/StandaloneRunner/InternalTemplate.cs index cdb8ca869..e8fbebf79 100644 --- a/src/StandaloneRunner/InternalTemplate.cs +++ b/src/StandaloneRunner/InternalTemplate.cs @@ -6,8 +6,8 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.IO; +using OneScript.StandardLibrary.Binary; using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library.Binary; namespace StandaloneRunner { @@ -44,7 +44,7 @@ public string GetFilename() _tempFileName = Path.GetTempFileName(); using (var fs = new FileStream(_tempFileName, FileMode.OpenOrCreate)) { - fs.Write(_data.Buffer,0,_data.Buffer.Length); + _data.CopyTo(fs); } } diff --git a/src/StandaloneRunner/ProcessLoader.cs b/src/StandaloneRunner/ProcessLoader.cs index cf457d350..9266c2a44 100644 --- a/src/StandaloneRunner/ProcessLoader.cs +++ b/src/StandaloneRunner/ProcessLoader.cs @@ -9,11 +9,14 @@ This Source Code Form is subject to the terms of the using System.IO; using System.Linq; using System.Runtime.Serialization.Formatters.Binary; +using OneScript.Commons; +using OneScript.Sources; +using OneScript.StandardLibrary.Collections; using ScriptEngine; using ScriptEngine.Compiler; using ScriptEngine.HostedScript; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; +using ScriptEngine.HostedScript.Extensions; +using ScriptEngine.Hosting; namespace StandaloneRunner { @@ -22,19 +25,30 @@ public class ProcessLoader public Process CreateProcess(Stream sourceStream, IHostApplication host) { var appDump = DeserializeAppDump(sourceStream); - var engine = new HostedScriptEngine(); - var src = new BinaryCodeSource(); - var templateStorage = new TemplateStorage(new StandaloneTemplateFactory()); + + var engineBuilder = DefaultEngineBuilder + .Create() + .SetupEnvironment(e => + { + e.AddAssembly(typeof(ArrayImpl).Assembly); + e.UseTemplateFactory(new StandaloneTemplateFactory()); + }) + .SetupConfiguration(p => p.UseEnvironmentVariableConfig("OSCRIPT_CONFIG")); + var engine = new HostedScriptEngine(engineBuilder.Build()); + var src = SourceCodeBuilder.Create() + .FromSource(new BinaryCodeSource()) + .WithName("Compiler source") + .Build(); + engine.SetGlobalEnvironment(host, src); engine.InitializationCallback = (e, env) => { - e.Environment.InjectObject(templateStorage); - GlobalsManager.RegisterInstance(templateStorage); + var storage = e.GlobalsManager.GetInstance(); + LoadResources(storage, appDump.Resources); }; engine.Initialize(); - LoadResources(templateStorage, appDump.Resources); LoadScripts(engine, appDump.Scripts); var process = engine.CreateProcess(host, appDump.Scripts[0].Image, src); diff --git a/src/StandaloneRunner/Program.cs b/src/StandaloneRunner/Program.cs index 3b69613f8..b058d3904 100644 --- a/src/StandaloneRunner/Program.cs +++ b/src/StandaloneRunner/Program.cs @@ -10,12 +10,13 @@ This Source Code Form is subject to the terms of the using System.IO; using System.Linq; using System.Reflection; -using System.Text; namespace StandaloneRunner { internal static class Program { + private static Dictionary _loadedAssemblies = new Dictionary(); + private static int Main(string[] args) { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; @@ -59,23 +60,33 @@ private static int LoadAndRun(FileStream sourceStream, string[] args) { CommandLineArguments = args }; - + return process.LoadAndRun(codeStream); } - + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { - var resourceName = "StandaloneRunner." + new AssemblyName(args.Name).Name + ".dll"; + var assemblyShortName = new AssemblyName(args.Name).Name; + var resourceName = "StandaloneRunner." + assemblyShortName + ".dll"; + Assembly assembly; + + if (_loadedAssemblies.TryGetValue(assemblyShortName, out assembly)) + { + return assembly; + } using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { var asmData = new byte[stream.Length]; stream.Read(asmData, 0, asmData.Length); - return Assembly.Load(asmData); + assembly = Assembly.Load(asmData); + + _loadedAssemblies.Add(assemblyShortName, assembly); + return assembly; } } - + private static Stream LocateCode(Stream sourceStream) { const int SIGN_SIZE = 8; diff --git a/src/StandaloneRunner/StandaloneApplicationHost.cs b/src/StandaloneRunner/StandaloneApplicationHost.cs index 7bdce230d..0792d887a 100644 --- a/src/StandaloneRunner/StandaloneApplicationHost.cs +++ b/src/StandaloneRunner/StandaloneApplicationHost.cs @@ -6,9 +6,9 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using OneScript.StandardLibrary; using oscript; using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; namespace StandaloneRunner { @@ -26,9 +26,9 @@ public void ShowExceptionInfo(Exception exc) ConsoleHostImpl.ShowExceptionInfo(exc); } - public bool InputString(out string result, int maxLen) + public bool InputString(out string result, string prompt, int maxLen, bool multiline) { - return ConsoleHostImpl.InputString(out result, maxLen); + return ConsoleHostImpl.InputString(out result, prompt, maxLen, multiline); } public string[] GetCommandLineArguments() diff --git a/src/StandaloneRunner/StandaloneProcess.cs b/src/StandaloneRunner/StandaloneProcess.cs index 9fa393481..f94f4a880 100644 --- a/src/StandaloneRunner/StandaloneProcess.cs +++ b/src/StandaloneRunner/StandaloneProcess.cs @@ -6,20 +6,9 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Reflection; -using System.Runtime.Serialization.Formatters.Binary; - -using oscript; -using ScriptEngine; -using ScriptEngine.Compiler; -using ScriptEngine.Environment; -using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; namespace StandaloneRunner { @@ -51,14 +40,10 @@ public int LoadAndRun(Stream codeStream) } } - internal class BinaryCodeSource : ICodeSource + internal class BinaryCodeSource : OneScript.Sources.ICodeSource { - #region ICodeSource Members - - public string SourceDescription => Assembly.GetExecutingAssembly().Location; - - public string Code => ""; - - #endregion + public string Location => Assembly.GetExecutingAssembly().Location; + + public string GetSourceCode() => ""; } } \ No newline at end of file diff --git a/src/StandaloneRunner/StandaloneRunner.csproj b/src/StandaloneRunner/StandaloneRunner.csproj index 03cd55417..dd3b39009 100644 --- a/src/StandaloneRunner/StandaloneRunner.csproj +++ b/src/StandaloneRunner/StandaloneRunner.csproj @@ -1,16 +1,14 @@  - net452 + net5.0 AnyCPU;x86 Debug;Release Exe StandaloneRunner.Program - - - + ConsoleHostImpl.cs @@ -24,22 +22,10 @@ - - - - - - - + + + + - - - - - - - - - \ No newline at end of file diff --git a/src/TestApp/App.xaml.cs b/src/TestApp/App.xaml.cs index 4deb19560..444aab9fb 100644 --- a/src/TestApp/App.xaml.cs +++ b/src/TestApp/App.xaml.cs @@ -4,11 +4,7 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Data; -using System.Linq; + using System.Windows; namespace TestApp diff --git a/src/TestApp/Controls/CodeControl.xaml.cs b/src/TestApp/Controls/CodeControl.xaml.cs index 55d0c23d5..c418dfda7 100644 --- a/src/TestApp/Controls/CodeControl.xaml.cs +++ b/src/TestApp/Controls/CodeControl.xaml.cs @@ -4,27 +4,19 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Windows; using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; +using System.Windows.Threading; using System.Xml; using ICSharpCode.AvalonEdit.Folding; using ICSharpCode.AvalonEdit.Highlighting; -using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Search; -using System.Windows.Threading; - namespace V8Reader.Controls { /// @@ -54,7 +46,7 @@ public CodeControl() SearchPanel.Install(editor.TextArea); editor.ShowLineNumbers = true; - foldingManager = FoldingManager.Install(editor.TextArea); + _foldingManager = FoldingManager.Install(editor.TextArea); editor.TextChanged += editor_TextChanged; editor.TextArea.Options.EnableHyperlinks = false; @@ -63,10 +55,10 @@ public CodeControl() editor.TextArea.SelectionCornerRadius = 0; editor.TextArea.Caret.PositionChanged += Caret_PositionChanged; - foldingUpdateTimer = new DispatcherTimer(DispatcherPriority.ContextIdle); - foldingUpdateTimer.Interval = TimeSpan.FromSeconds(2); - foldingUpdateTimer.Tick += foldingUpdateTimer_Tick; - foldingUpdateTimer.Start(); + _foldingUpdateTimer = new DispatcherTimer(DispatcherPriority.ContextIdle); + _foldingUpdateTimer.Interval = TimeSpan.FromSeconds(2); + _foldingUpdateTimer.Tick += foldingUpdateTimer_Tick; + _foldingUpdateTimer.Start(); } @@ -156,50 +148,42 @@ private int FindInChunk(int curLine, int chunkStart, int chunkEnd) void editor_TextChanged(object sender, EventArgs e) { // m_ModifyFlag = true; - if (foldingUpdateTimer!= null && !foldingUpdateTimer.IsEnabled) + if (_foldingUpdateTimer!= null && !_foldingUpdateTimer.IsEnabled) { - foldingUpdateTimer.Start(); + _foldingUpdateTimer.Start(); } } void foldingUpdateTimer_Tick(object sender, EventArgs e) { ((DispatcherTimer)sender).Stop(); - foldingStrategy.UpdateFoldings(foldingManager, editor.Document); - _procList = ((V8ModuleFoldingStrategy)foldingStrategy).ProcedureList; + _foldingStrategy.UpdateFoldings(_foldingManager, editor.Document); + _procList = _foldingStrategy.ProcedureList; - if (_procList != null) - { - cbProcList.Items.Clear(); + if (_procList == null) return; + cbProcList.Items.Clear(); - var Names = from lst in _procList select lst.Name; - foreach (var item in Names) - { - cbProcList.Items.Add(item); - } - - UpdateCurrentProc(); + var names = from lst in _procList select lst.Name; + foreach (var item in names) + { + cbProcList.Items.Add(item); } + + UpdateCurrentProc(); //m_ModifyFlag = false; } - public String Text + public string Text { - get - { - return editor.Text; - } - set - { - editor.Text = value; - } + get => editor.Text; + set => editor.Text = value; } - FoldingManager foldingManager; - AbstractFoldingStrategy foldingStrategy = new V8ModuleFoldingStrategy(); + FoldingManager _foldingManager; + V8ModuleFoldingStrategy _foldingStrategy = new V8ModuleFoldingStrategy(); //bool m_ModifyFlag; - DispatcherTimer foldingUpdateTimer; + DispatcherTimer _foldingUpdateTimer; List _procList; bool _userMethodSelect = false; @@ -259,9 +243,9 @@ private void btnExpandNodes_Click(object sender, RoutedEventArgs e) private void PerformNodeFolding(bool SetCollapsed) { - if (foldingManager != null) + if (_foldingManager != null) { - foreach (var folding in foldingManager.AllFoldings) + foreach (var folding in _foldingManager.AllFoldings) { folding.IsFolded = SetCollapsed; } @@ -319,267 +303,4 @@ public override string ToString() return Name; } } - - class V8ModuleFoldingStrategy : AbstractFoldingStrategy - { - - public V8ModuleFoldingStrategy() - { - - } - - /// - /// Create s for the specified document. - /// - public override IEnumerable CreateNewFoldings(TextDocument document, out int firstErrorOffset) - { - firstErrorOffset = -1; - return CreateNewFoldings(document); - } - - /// - /// Create s for the specified document. - /// - - private struct TextFragment - { - public int offset; - public int len; - } - - private List _procList; - - public List ProcedureList - { - get - { - return _procList; - } - } - - public IEnumerable CreateNewFoldings(ITextSource document) - { - List newFoldings = new List(); - _procList = new List(); - - int startPos = 0; - int len = document.TextLength; - - int currentStart = 0; - - bool MethodIsOpen = false; - string MethodName =""; - string EndToken = null; - - int PreCommentStart = -1; - - //var Reader = document.CreateReader(); - - string FullText = document.Text; - int DocLine = 0; - int MethodStart = 0; - - const string kProcStart = "ПРОЦЕДУРА"; - const string kProcEnd = "КОНЕЦПРОЦЕДУРЫ"; - const string kFuncStart = "ФУНКЦИЯ"; - const string kFuncEnd = "КОНЕЦФУНКЦИИ"; - - char[] trimArr = new char[]{' ','\t'}; - - do - { - int prev_start = startPos; - string lineText = ReadLine(FullText, ref startPos); - - DocLine++; - - if (lineText == null) - { - break; - } - - TextFragment tf = new TextFragment(); - tf.offset = prev_start; - tf.len = lineText.Length; - - //startPos += lineText.Length + 2; - - if (!MethodIsOpen) - { - bool CommentBreak = false; - - if (lineText.StartsWith("//")) - { - if (PreCommentStart < 0) - { - PreCommentStart = tf.offset + tf.len; - } - } - else - { - CommentBreak = true; - } - - if (LineIsKeyword(lineText.TrimStart(trimArr), kProcStart)) - { - MethodIsOpen = true; - MethodName = ScanForParamList(FullText, prev_start+kProcStart.Length); - EndToken = kProcEnd; - MethodStart = DocLine; - } - else if(LineIsKeyword(lineText.TrimStart(trimArr), kFuncStart)) - { - MethodIsOpen = true; - MethodName = ScanForParamList(FullText, prev_start + kFuncStart.Length); - EndToken = kFuncEnd; - MethodStart = DocLine; - } - - if (MethodIsOpen) - { - currentStart = tf.offset + tf.len; - - if (PreCommentStart >= 0) - { - var Folding = new NewFolding(PreCommentStart, tf.offset-1); - newFoldings.Add(Folding); - PreCommentStart = -1; - } - } - else if(CommentBreak) - { - PreCommentStart = -1; - } - - } - else if (LineIsKeyword(lineText.TrimStart(trimArr), EndToken)) - { - var Folding = new NewFolding(currentStart, tf.offset + tf.len); - newFoldings.Add(Folding); - - if (MethodName != "") - { - ProcListItem pli = new ProcListItem(); - pli.Name = MethodName; - pli.StartLine = MethodStart; - pli.EndLine = DocLine; - pli.ListIndex = _procList.Count; - - _procList.Add(pli); - - MethodName = ""; - } - - MethodIsOpen = false; - } - - } - while (true); - - return newFoldings; - } - - private string ScanForParamList(string FullText, int Start) - { - int nameLen = 0; - int i = Start; - bool found = false; - bool ltrFound = false; - - while (i < FullText.Length) - { - nameLen++; - char currentLtr = FullText[i++]; - - if (!Char.IsLetterOrDigit(currentLtr)) - { - if (ltrFound && !Char.IsWhiteSpace(currentLtr) && currentLtr != '(') - { - break; - } - } - else - { - ltrFound = true; - } - - if (currentLtr == '(') - { - found = true; - nameLen--; - break; - } - - } - - if (found) - { - return FullText.Substring(Start, nameLen).Trim(); - } - else - { - return ""; - } - } - - private bool LineIsKeyword(string Line, string Keyword) - { - if (Line.StartsWith(Keyword, StringComparison.OrdinalIgnoreCase)) - { - if (Line.Length > Keyword.Length) - { - return (Char.IsWhiteSpace(Line[Keyword.Length])); - } - else - { - return true; - } - } - else - { - return false; - } - } - - private string ReadLine(string Content, ref int Position) - { - if (Position >= Content.Length) - { - Position = -1; - return null; - } - - int StartPoint = Position; - int EndPoint = Position; - - while (Position < Content.Length) - { - if (Content[Position] == '\n') - { - Position++; - break; - } - - if (Content[Position] != '\r') - { - EndPoint = Position; - } - - Position++; - - } - - int len = EndPoint == StartPoint ? 0 : EndPoint - StartPoint + 1; - - string result = Content.Substring(StartPoint, len); - return result; - - } - - - - - - } - } diff --git a/src/TestApp/Controls/ImageArray.cs b/src/TestApp/Controls/ImageArray.cs index 09ebebdbf..b368e6958 100644 --- a/src/TestApp/Controls/ImageArray.cs +++ b/src/TestApp/Controls/ImageArray.cs @@ -4,10 +4,9 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Drawing; using System.Windows.Media; using System.Windows.Media.Imaging; diff --git a/src/TestApp/Controls/MDConstants.cs b/src/TestApp/Controls/MDConstants.cs index b06801458..52bc85f0b 100644 --- a/src/TestApp/Controls/MDConstants.cs +++ b/src/TestApp/Controls/MDConstants.cs @@ -4,11 +4,9 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows; using System.Windows.Data; using System.Windows.Media; diff --git a/src/TestApp/Controls/ProcedureListWnd.xaml.cs b/src/TestApp/Controls/ProcedureListWnd.xaml.cs index 8e93ea193..5df7d6bb8 100644 --- a/src/TestApp/Controls/ProcedureListWnd.xaml.cs +++ b/src/TestApp/Controls/ProcedureListWnd.xaml.cs @@ -4,18 +4,13 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Windows; using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; namespace V8Reader.Controls { diff --git a/src/TestApp/Controls/V8ModuleFoldingStrategy.cs b/src/TestApp/Controls/V8ModuleFoldingStrategy.cs new file mode 100644 index 000000000..11698fb07 --- /dev/null +++ b/src/TestApp/Controls/V8ModuleFoldingStrategy.cs @@ -0,0 +1,283 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Folding; + +namespace V8Reader.Controls +{ + class V8ModuleFoldingStrategy + { + + public V8ModuleFoldingStrategy() + { + + } + + public void UpdateFoldings(FoldingManager manager, TextDocument document) + { + int firstErrorOffset; + var foldings = CreateNewFoldings(document, out firstErrorOffset); + manager.UpdateFoldings(foldings, firstErrorOffset); + } + + /// + /// Create s for the specified document. + /// + public IEnumerable CreateNewFoldings(TextDocument document, out int firstErrorOffset) + { + firstErrorOffset = -1; + return CreateNewFoldings(document); + } + + /// + /// Create s for the specified document. + /// + + private struct TextFragment + { + public int offset; + public int len; + } + + private List _procList; + + public List ProcedureList + { + get + { + return _procList; + } + } + + public IEnumerable CreateNewFoldings(ITextSource document) + { + List newFoldings = new List(); + _procList = new List(); + + int startPos = 0; + int len = document.TextLength; + + int currentStart = 0; + + bool MethodIsOpen = false; + string MethodName =""; + string EndToken = null; + + int PreCommentStart = -1; + + //var Reader = document.CreateReader(); + + string FullText = document.Text; + int DocLine = 0; + int MethodStart = 0; + + const string kProcStart = "ПРОЦЕДУРА"; + const string kProcEnd = "КОНЕЦПРОЦЕДУРЫ"; + const string kFuncStart = "ФУНКЦИЯ"; + const string kFuncEnd = "КОНЕЦФУНКЦИИ"; + + char[] trimArr = new char[]{' ','\t'}; + + do + { + int prev_start = startPos; + string lineText = ReadLine(FullText, ref startPos); + + DocLine++; + + if (lineText == null) + { + break; + } + + TextFragment tf = new TextFragment(); + tf.offset = prev_start; + tf.len = lineText.Length; + + //startPos += lineText.Length + 2; + + if (!MethodIsOpen) + { + bool CommentBreak = false; + + if (lineText.StartsWith("//")) + { + if (PreCommentStart < 0) + { + PreCommentStart = tf.offset + tf.len; + } + } + else + { + CommentBreak = true; + } + + if (LineIsKeyword(lineText.TrimStart(trimArr), kProcStart)) + { + MethodIsOpen = true; + MethodName = ScanForParamList(FullText, prev_start+kProcStart.Length); + EndToken = kProcEnd; + MethodStart = DocLine; + } + else if(LineIsKeyword(lineText.TrimStart(trimArr), kFuncStart)) + { + MethodIsOpen = true; + MethodName = ScanForParamList(FullText, prev_start + kFuncStart.Length); + EndToken = kFuncEnd; + MethodStart = DocLine; + } + + if (MethodIsOpen) + { + currentStart = tf.offset + tf.len; + + if (PreCommentStart >= 0) + { + var Folding = new NewFolding(PreCommentStart, tf.offset-1); + newFoldings.Add(Folding); + PreCommentStart = -1; + } + } + else if(CommentBreak) + { + PreCommentStart = -1; + } + + } + else if (LineIsKeyword(lineText.TrimStart(trimArr), EndToken)) + { + var Folding = new NewFolding(currentStart, tf.offset + tf.len); + newFoldings.Add(Folding); + + if (MethodName != "") + { + ProcListItem pli = new ProcListItem(); + pli.Name = MethodName; + pli.StartLine = MethodStart; + pli.EndLine = DocLine; + pli.ListIndex = _procList.Count; + + _procList.Add(pli); + + MethodName = ""; + } + + MethodIsOpen = false; + } + + } + while (true); + + return newFoldings; + } + + private string ScanForParamList(string FullText, int Start) + { + int nameLen = 0; + int i = Start; + bool found = false; + bool ltrFound = false; + + while (i < FullText.Length) + { + nameLen++; + char currentLtr = FullText[i++]; + + if (!Char.IsLetterOrDigit(currentLtr)) + { + if (ltrFound && !Char.IsWhiteSpace(currentLtr) && currentLtr != '(') + { + break; + } + } + else + { + ltrFound = true; + } + + if (currentLtr == '(') + { + found = true; + nameLen--; + break; + } + + } + + if (found) + { + return FullText.Substring(Start, nameLen).Trim(); + } + else + { + return ""; + } + } + + private bool LineIsKeyword(string Line, string Keyword) + { + if (Line.StartsWith(Keyword, StringComparison.OrdinalIgnoreCase)) + { + if (Line.Length > Keyword.Length) + { + return (Char.IsWhiteSpace(Line[Keyword.Length])); + } + else + { + return true; + } + } + else + { + return false; + } + } + + private string ReadLine(string Content, ref int Position) + { + if (Position >= Content.Length) + { + Position = -1; + return null; + } + + int StartPoint = Position; + int EndPoint = Position; + + while (Position < Content.Length) + { + if (Content[Position] == '\n') + { + Position++; + break; + } + + if (Content[Position] != '\r') + { + EndPoint = Position; + } + + Position++; + + } + + int len = EndPoint == StartPoint ? 0 : EndPoint - StartPoint + 1; + + string result = Content.Substring(StartPoint, len); + return result; + + } + + + + + + } +} \ No newline at end of file diff --git a/src/TestApp/EditedFileSource.cs b/src/TestApp/EditedFileSource.cs index 03851c3fd..57a6cb74f 100644 --- a/src/TestApp/EditedFileSource.cs +++ b/src/TestApp/EditedFileSource.cs @@ -5,13 +5,8 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; - -using ScriptEngine.Environment; +using OneScript.Language.Sources; namespace TestApp { @@ -28,17 +23,11 @@ public EditedFileSource(string code, string path) _code = code; } - private string GetCodeString() + public string GetSourceCode() { return _code; } - #region ICodeSource Members - - string ICodeSource.Code => GetCodeString(); - - string ICodeSource.SourceDescription => _path != "" ? _path : ""; - - #endregion + public string Location => _path != "" ? _path : ""; } } \ No newline at end of file diff --git a/src/TestApp/MainWindow.xaml.cs b/src/TestApp/MainWindow.xaml.cs index c69a355c4..5c2adc40c 100644 --- a/src/TestApp/MainWindow.xaml.cs +++ b/src/TestApp/MainWindow.xaml.cs @@ -4,18 +4,23 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ + using System; +using System.Collections.Generic; using System.IO; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; +using OneScript.Commons; +using OneScript.Sources; +using OneScript.StandardLibrary; +using OneScript.Web.Server; using ScriptEngine.HostedScript; -using System.Collections.Generic; using ScriptEngine; using ScriptEngine.Compiler; -using ScriptEngine.Environment; -using ScriptEngine.HostedScript.Library; +using ScriptEngine.HostedScript.Extensions; +using ScriptEngine.Hosting; namespace TestApp { @@ -43,6 +48,9 @@ public bool IsModified public MainWindow() { InitializeComponent(); +#if NETCOREAPP + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +#endif } // Определяем путь к AppData\Local @@ -124,10 +132,35 @@ private void RestoreLastCode() } } + private HostedScriptEngine CreateEngine() + { + var builder = DefaultEngineBuilder + .Create() + .SetDefaultOptions() + .UseNativeRuntime() + .UseImports() + .SetupEnvironment(e => + { + e.AddStandardLibrary(); + e.AddWebServer(); + }) + .SetupConfiguration(x => + { + x.UseSystemConfigFile() + .UseEntrypointConfigFile(_currentDocPath); + }); + + builder.UseFileSystemLibraries(); + var engine = builder.Build(); + var mainEngine = new HostedScriptEngine(engine); + + return mainEngine; + } + private void Button_Click(object sender, RoutedEventArgs e) { - var hostedScript = new HostedScriptEngine(); - hostedScript.CustomConfig = CustomConfigPath(_currentDocPath); + var hostedScript = CreateEngine(); + hostedScript.Initialize(); var src = hostedScript.Loader.FromString(txtCode.Text); using (var writer = new StringWriter()) @@ -135,12 +168,10 @@ private void Button_Click(object sender, RoutedEventArgs e) try { var cs = hostedScript.GetCompilerService(); - if(GenerateExtraCode.IsChecked) - cs.ProduceExtraCode |= CodeGenerationFlags.CodeStatistics; - if(GenerateDebugCode.IsChecked) - cs.ProduceExtraCode |= CodeGenerationFlags.DebugCode; + cs.GenerateCodeStat = GenerateExtraCode.IsChecked; + cs.GenerateDebugCode = GenerateDebugCode.IsChecked; - var moduleWriter = new ScriptEngine.Compiler.ModuleWriter(cs); + var moduleWriter = new ModuleDumpWriter(cs, hostedScript.Engine.NewProcess()); moduleWriter.Write(writer, src); result.Text = writer.GetStringBuilder().ToString(); } @@ -174,11 +205,12 @@ private void Button_Click_1(object sender, RoutedEventArgs e) var host = new Host(result, l_args.ToArray()); SystemLogger.SetWriter(host); - var hostedScript = new HostedScriptEngine(); - hostedScript.CustomConfig = CustomConfigPath(_currentDocPath); - SetEncodingFromConfig(hostedScript); - - var src = new EditedFileSource(txtCode.Text, _currentDocPath); + var hostedScript = CreateEngine(); + + var src = SourceCodeBuilder.Create() + .FromSource(new EditedFileSource(txtCode.Text, _currentDocPath)) + .WithName(_currentDocPath) + .Build(); Process process = null; try @@ -200,23 +232,10 @@ private void Button_Click_1(object sender, RoutedEventArgs e) result.AppendText("\nError detected. Exit code = " + returnCode.ToString()); } result.AppendText("\nScript completed: " + DateTime.Now.ToString()); - result.AppendText("\nDuration: " + sw.Elapsed.ToString()); + result.AppendText("\nDuration: " + sw.Elapsed.ToString() + "\n"); } - public static string CustomConfigPath(string scriptPath) - { - if (scriptPath == null || !File.Exists(scriptPath)) - return null; - - var dir = Path.GetDirectoryName(scriptPath); - var cfgPath = Path.Combine(dir, HostedScriptEngine.ConfigFileName); - if (File.Exists(cfgPath)) - return cfgPath; - else - return null; - } - private static string GetFileDialogFilter() { return "Поддерживаемые файлы|*.os;*.txt|Все файлы|*.*"; @@ -246,14 +265,11 @@ private void Open_Execute(object sender, ExecutedRoutedEventArgs e) dlg.Multiselect = false; if (dlg.ShowDialog() == true ) { - var hostedScript = new HostedScriptEngine(); - hostedScript.CustomConfig = CustomConfigPath(dlg.FileName); - SetEncodingFromConfig(hostedScript); - + _currentDocPath = dlg.FileName; + using (var fs = FileOpener.OpenReader(dlg.FileName)) { txtCode.Text = fs.ReadToEnd(); - _currentDocPath = dlg.FileName; this.Title = _currentDocPath; } } @@ -264,17 +280,6 @@ private void Save_Execute(object sender, ExecutedRoutedEventArgs e) SaveFile(); } - private void SetEncodingFromConfig(HostedScriptEngine engine) - { - var cfg = engine.GetWorkingConfig(); - - string openerEncoding = cfg["encoding.script"]; - if (!String.IsNullOrWhiteSpace(openerEncoding) && StringComparer.InvariantCultureIgnoreCase.Compare(openerEncoding, "default") != 0) - { - engine.Loader.ReaderEncoding = Encoding.GetEncoding(openerEncoding); - } - } - private bool SaveFile() { if (_currentDocPath == "") @@ -301,8 +306,8 @@ private bool AskForFilenameAndSave() dlg.DefaultExt = ".os"; if (!String.IsNullOrEmpty(_currentDocPath)) { - dlg.InitialDirectory = System.IO.Path.GetDirectoryName(_currentDocPath); - dlg.FileName = System.IO.Path.GetFileName(_currentDocPath); + dlg.InitialDirectory = Path.GetDirectoryName(_currentDocPath); + dlg.FileName = Path.GetFileName(_currentDocPath); } if (dlg.ShowDialog() == true ) @@ -417,8 +422,11 @@ public Host(TextBox output, string [] arguments = null) public void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary) { - _output.AppendText(str + '\n'); - _output.ScrollToEnd(); + _output.Dispatcher.BeginInvoke(new Action(() => + { + _output.AppendText(str + '\n'); + _output.ScrollToEnd(); + })); } public void ShowExceptionInfo(Exception exc) @@ -426,7 +434,7 @@ public void ShowExceptionInfo(Exception exc) Echo(exc.Message); } - public bool InputString(out string result, int maxLen) + public bool InputString(out string result, string prompt, int maxLen, bool multiline) { result = "строка введена"; return true; diff --git a/src/TestApp/TestApp.csproj b/src/TestApp/TestApp.csproj index 23d60a775..241a30b6c 100644 --- a/src/TestApp/TestApp.csproj +++ b/src/TestApp/TestApp.csproj @@ -3,55 +3,41 @@ - net452 + net8.0-windows logo.ico WinExe TestApp.App x86;AnyCPU - Debug;Release + Debug;Release;LinuxDebug + true + false + Major 1Script ui host + + true + false + + - - - - - - - - - MSBuild:Compile - Designer - - - - Designer - MSBuild:Compile - - - + - - - - - - + + - \ No newline at end of file diff --git a/src/TestWebApp/ApplicationInsights.config b/src/TestWebApp/ApplicationInsights.config deleted file mode 100644 index ee9ea3c04..000000000 --- a/src/TestWebApp/ApplicationInsights.config +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - System.Web.Handlers.TransferRequestHandler - Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler - System.Web.StaticFileHandler - System.Web.Handlers.AssemblyResourceLoader - System.Web.Optimization.BundleHandler - System.Web.Script.Services.ScriptHandlerFactory - System.Web.Handlers.TraceHandler - System.Web.Services.Discovery.DiscoveryRequestHandler - System.Web.HttpDebugHandler - - - - - - - - 5 - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/TestWebApp/Default.os b/src/TestWebApp/Default.os deleted file mode 100644 index 892ad4a94..000000000 --- a/src/TestWebApp/Default.os +++ /dev/null @@ -1,56 +0,0 @@ - -// Это предопределенная функция - обработчик запроса HTTP-сервиса -// -Функция ОбработкаВызоваHTTPСервиса(Запрос) Экспорт - - Если Запрос.HTTPМетод = "GET" Тогда - - Возврат ПоказатьГлавнуюСтраницу(Запрос); - - Иначе - - Возврат ПоказатьИсходныйКод(Запрос); - - КонецЕсли; - -КонецФункции - -Функция ПоказатьГлавнуюСтраницу(Запрос) - - Контекст = Запрос.Контекст; - - ИмяФайлаШаблона = СтрЗаменить(Контекст.ФизическийПуть, "Default.os", "Default.ospt"); - СтрокаШаблон = ПолучитьМакетСтраницы(ИмяФайлаШаблона); - СтрокаТело = СтрЗаменить(СтрокаШаблон, "{ClientAddress}", Контекст.АдресКлиента); - Ответ = Новый HTTPСервисОтвет(200); - Ответ.УстановитьТелоИзСтроки(СтрокаТело); - - Возврат Ответ; - -КонецФункции - -Функция ПоказатьИсходныйКод(Запрос) - - Контекст = Запрос.Контекст; - - СтрокаИсходныйКод = ПолучитьМакетСтраницы(Контекст.ФизическийПуть); - - ИмяФайлаШаблона = СтрЗаменить(Контекст.ФизическийПуть, "Default.os", "Source.ospt"); - СтрокаШаблон = ПолучитьМакетСтраницы(ИмяФайлаШаблона); - - СтрокаТело = СтрЗаменить(СтрокаШаблон, "{SourceCode}", СтрокаИсходныйКод); - - Ответ = Новый HTTPСервисОтвет(200); - Ответ.УстановитьТелоИзСтроки(СтрокаТело); - - Возврат Ответ; - -КонецФункции - -Функция ПолучитьМакетСтраницы(ПутьКФайлу) - - Документ = Новый ТекстовыйДокумент; - Документ.Прочитать(ПутьКФайлу); - Возврат Документ.ПолучитьТекст(); - -КонецФункции \ No newline at end of file diff --git a/src/TestWebApp/Default.ospt b/src/TestWebApp/Default.ospt deleted file mode 100644 index 892c1dce3..000000000 --- a/src/TestWebApp/Default.ospt +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - OneScript HTTP Service Test Page - - - -
-

Привет ({ClientAddress})!

-

Эта страница создана HTTP сервисом OneScript.

-
-
-
-
- - -
-
-
- - - \ No newline at end of file diff --git a/src/TestWebApp/Properties/AssemblyInfo.cs b/src/TestWebApp/Properties/AssemblyInfo.cs deleted file mode 100644 index 92489d081..000000000 --- a/src/TestWebApp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,42 +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.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TestWebApp")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("TestWebApp")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("6de97986-8304-45ae-bd47-63a5563f9c3a")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/TestWebApp/TestWebApp.csproj b/src/TestWebApp/TestWebApp.csproj deleted file mode 100644 index 08425e27e..000000000 --- a/src/TestWebApp/TestWebApp.csproj +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - Debug - AnyCPU - - - 2.0 - {6DE97986-8304-45AE-BD47-63A5563F9C3A} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - TestWebApp - TestWebApp - v4.5.2 - true - - enabled - disabled - true - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\ - TRACE - prompt - 4 - - - - - PreserveNewest - - - - - - Web.config - - - Web.config - - - - - - - - - Designer - - - - - - - - {b7cd7f52-e387-490e-8f77-e1fb060401b5} - HTTPServices - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - True - True - 50950 - / - http://localhost:50685/ - False - False - - - False - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file diff --git a/src/TestWebApp/Web.Debug.config b/src/TestWebApp/Web.Debug.config deleted file mode 100644 index 2e302f9f9..000000000 --- a/src/TestWebApp/Web.Debug.config +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/TestWebApp/Web.Release.config b/src/TestWebApp/Web.Release.config deleted file mode 100644 index c35844462..000000000 --- a/src/TestWebApp/Web.Release.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/TestWebApp/Web.config b/src/TestWebApp/Web.config deleted file mode 100644 index ff22f5f58..000000000 --- a/src/TestWebApp/Web.config +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/TestWebApp/images/home.png b/src/TestWebApp/images/home.png deleted file mode 100644 index a1686704c..000000000 Binary files a/src/TestWebApp/images/home.png and /dev/null differ diff --git a/src/TestWebApp/images/viewsrc.jpg b/src/TestWebApp/images/viewsrc.jpg deleted file mode 100644 index 98b37e43a..000000000 Binary files a/src/TestWebApp/images/viewsrc.jpg and /dev/null differ diff --git a/src/TestWebApp/packages.config b/src/TestWebApp/packages.config deleted file mode 100644 index dc83cae95..000000000 --- a/src/TestWebApp/packages.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/TestWebApp/scripts/highlight.pack.js b/src/TestWebApp/scripts/highlight.pack.js deleted file mode 100644 index 2b63060fe..000000000 --- a/src/TestWebApp/scripts/highlight.pack.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ -!function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("1c",function(s){var x="[A-Za-zА-Яа-яёЁ_][A-Za-zА-Яа-яёЁ_0-9]+",o="далее ",m="возврат вызватьисключение выполнить для если и из или иначе иначеесли исключение каждого конецесли конецпопытки конеццикла не новый перейти перем по пока попытка прервать продолжить тогда цикл экспорт ",t=o+m,l="загрузитьизфайла ",e="вебклиент вместо внешнеесоединение клиент конецобласти мобильноеприложениеклиент мобильноеприложениесервер наклиенте наклиентенасервере наклиентенасерверебезконтекста насервере насерверебезконтекста область перед после сервер толстыйклиентобычноеприложение толстыйклиентуправляемоеприложение тонкийклиент ",n=l+e,a="разделительстраниц разделительстрок символтабуляции ",d="ansitooem oemtoansi ввестивидсубконто ввестиперечисление ввестипериод ввестиплансчетов выбранныйплансчетов датагод датамесяц датачисло заголовоксистемы значениевстроку значениеизстроки каталогиб каталогпользователя кодсимв конгода конецпериодаби конецрассчитанногопериодаби конецстандартногоинтервала конквартала конмесяца коннедели лог лог10 максимальноеколичествосубконто названиеинтерфейса названиенабораправ назначитьвид назначитьсчет найтиссылки началопериодаби началостандартногоинтервала начгода начквартала начмесяца начнедели номерднягода номерднянедели номернеделигода обработкаожидания основнойжурналрасчетов основнойплансчетов основнойязык очиститьокносообщений периодстр получитьвремята получитьдатута получитьдокументта получитьзначенияотбора получитьпозициюта получитьпустоезначение получитьта префиксавтонумерации пропись пустоезначение разм разобратьпозициюдокумента рассчитатьрегистрына рассчитатьрегистрыпо симв создатьобъект статусвозврата стрколичествострок сформироватьпозициюдокумента счетпокоду текущеевремя типзначения типзначениястр установитьтана установитьтапо фиксшаблон шаблон ",i="acos asin atan base64значение base64строка cos exp log log10 pow sin sqrt tan xmlзначение xmlстрока xmlтип xmlтипзнч активноеокно безопасныйрежим безопасныйрежимразделенияданных булево ввестидату ввестизначение ввестистроку ввестичисло возможностьчтенияxml вопрос восстановитьзначение врег выгрузитьжурналрегистрации выполнитьобработкуоповещения выполнитьпроверкуправдоступа вычислить год данныеформывзначение дата день деньгода деньнедели добавитьмесяц заблокироватьданныедляредактирования заблокироватьработупользователя завершитьработусистемы загрузитьвнешнююкомпоненту закрытьсправку записатьjson записатьxml записатьдатуjson записьжурналарегистрации заполнитьзначениясвойств запроситьразрешениепользователя запуститьприложение запуститьсистему зафиксироватьтранзакцию значениевданныеформы значениевстрокувнутр значениевфайл значениезаполнено значениеизстрокивнутр значениеизфайла изxmlтипа импортмоделиxdto имякомпьютера имяпользователя инициализироватьпредопределенныеданные информацияобошибке каталогбиблиотекимобильногоустройства каталогвременныхфайлов каталогдокументов каталогпрограммы кодироватьстроку кодлокализацииинформационнойбазы кодсимвола командасистемы конецгода конецдня конецквартала конецмесяца конецминуты конецнедели конецчаса конфигурациябазыданныхизмененадинамически конфигурацияизменена копироватьданныеформы копироватьфайл краткоепредставлениеошибки лев макс местноевремя месяц мин минута монопольныйрежим найти найтинедопустимыесимволыxml найтиокнопонавигационнойссылке найтипомеченныенаудаление найтипоссылкам найтифайлы началогода началодня началоквартала началомесяца началоминуты началонедели началочаса начатьзапросразрешенияпользователя начатьзапускприложения начатькопированиефайла начатьперемещениефайла начатьподключениевнешнейкомпоненты начатьподключениерасширенияработыскриптографией начатьподключениерасширенияработысфайлами начатьпоискфайлов начатьполучениекаталогавременныхфайлов начатьполучениекаталогадокументов начатьполучениерабочегокаталогаданныхпользователя начатьполучениефайлов начатьпомещениефайла начатьпомещениефайлов начатьсозданиедвоичныхданныхизфайла начатьсозданиекаталога начатьтранзакцию начатьудалениефайлов начатьустановкувнешнейкомпоненты начатьустановкурасширенияработыскриптографией начатьустановкурасширенияработысфайлами неделягода необходимостьзавершениясоединения номерсеансаинформационнойбазы номерсоединенияинформационнойбазы нрег нстр обновитьинтерфейс обновитьнумерациюобъектов обновитьповторноиспользуемыезначения обработкапрерыванияпользователя объединитьфайлы окр описаниеошибки оповестить оповеститьобизменении отключитьобработчикзапросанастроекклиенталицензирования отключитьобработчикожидания отключитьобработчикоповещения открытьзначение открытьиндекссправки открытьсодержаниесправки открытьсправку открытьформу открытьформумодально отменитьтранзакцию очиститьжурналрегистрации очиститьнастройкипользователя очиститьсообщения параметрыдоступа перейтипонавигационнойссылке переместитьфайл подключитьвнешнююкомпоненту подключитьобработчикзапросанастроекклиенталицензирования подключитьобработчикожидания подключитьобработчикоповещения подключитьрасширениеработыскриптографией подключитьрасширениеработысфайлами подробноепредставлениеошибки показатьвводдаты показатьвводзначения показатьвводстроки показатьвводчисла показатьвопрос показатьзначение показатьинформациюобошибке показатьнакарте показатьоповещениепользователя показатьпредупреждение полноеимяпользователя получитьcomобъект получитьxmlтип получитьадреспоместоположению получитьблокировкусеансов получитьвремязавершенияспящегосеанса получитьвремязасыпанияпассивногосеанса получитьвремяожиданияблокировкиданных получитьданныевыбора получитьдополнительныйпараметрклиенталицензирования получитьдопустимыекодылокализации получитьдопустимыечасовыепояса получитьзаголовокклиентскогоприложения получитьзаголовоксистемы получитьзначенияотборажурналарегистрации получитьидентификаторконфигурации получитьизвременногохранилища получитьимявременногофайла получитьимяклиенталицензирования получитьинформациюэкрановклиента получитьиспользованиежурналарегистрации получитьиспользованиесобытияжурналарегистрации получитькраткийзаголовокприложения получитьмакетоформления получитьмаскувсефайлы получитьмаскувсефайлыклиента получитьмаскувсефайлысервера получитьместоположениепоадресу получитьминимальнуюдлинупаролейпользователей получитьнавигационнуюссылку получитьнавигационнуюссылкуинформационнойбазы получитьобновлениеконфигурациибазыданных получитьобновлениепредопределенныхданныхинформационнойбазы получитьобщиймакет получитьобщуюформу получитьокна получитьоперативнуюотметкувремени получитьотключениебезопасногорежима получитьпараметрыфункциональныхопцийинтерфейса получитьполноеимяпредопределенногозначения получитьпредставлениянавигационныхссылок получитьпроверкусложностипаролейпользователей получитьразделительпути получитьразделительпутиклиента получитьразделительпутисервера получитьсеансыинформационнойбазы получитьскоростьклиентскогосоединения получитьсоединенияинформационнойбазы получитьсообщенияпользователю получитьсоответствиеобъектаиформы получитьсоставстандартногоинтерфейсаodata получитьструктурухранениябазыданных получитьтекущийсеансинформационнойбазы получитьфайл получитьфайлы получитьформу получитьфункциональнуюопцию получитьфункциональнуюопциюинтерфейса получитьчасовойпоясинформационнойбазы пользователиос поместитьвовременноехранилище поместитьфайл поместитьфайлы прав праводоступа предопределенноезначение представлениекодалокализации представлениепериода представлениеправа представлениеприложения представлениесобытияжурналарегистрации представлениечасовогопояса предупреждение прекратитьработусистемы привилегированныйрежим продолжитьвызов прочитатьjson прочитатьxml прочитатьдатуjson пустаястрока рабочийкаталогданныхпользователя разблокироватьданныедляредактирования разделитьфайл разорватьсоединениесвнешнимисточникомданных раскодироватьстроку рольдоступна секунда сигнал символ скопироватьжурналрегистрации смещениелетнеговремени смещениестандартноговремени соединитьбуферыдвоичныхданных создатькаталог создатьфабрикуxdto сокрл сокрлп сокрп сообщить состояние сохранитьзначение сохранитьнастройкипользователя сред стрдлина стрзаканчиваетсяна стрзаменить стрнайти стрначинаетсяс строка строкасоединенияинформационнойбазы стрполучитьстроку стрразделить стрсоединить стрсравнить стрчисловхождений стрчислострок стршаблон текущаядата текущаядатасеанса текущаяуниверсальнаядата текущаяуниверсальнаядатавмиллисекундах текущийвариантинтерфейсаклиентскогоприложения текущийвариантосновногошрифтаклиентскогоприложения текущийкодлокализации текущийрежимзапуска текущийязык текущийязыксистемы тип типзнч транзакцияактивна трег удалитьданныеинформационнойбазы удалитьизвременногохранилища удалитьобъекты удалитьфайлы универсальноевремя установитьбезопасныйрежим установитьбезопасныйрежимразделенияданных установитьблокировкусеансов установитьвнешнююкомпоненту установитьвремязавершенияспящегосеанса установитьвремязасыпанияпассивногосеанса установитьвремяожиданияблокировкиданных установитьзаголовокклиентскогоприложения установитьзаголовоксистемы установитьиспользованиежурналарегистрации установитьиспользованиесобытияжурналарегистрации установитькраткийзаголовокприложения установитьминимальнуюдлинупаролейпользователей установитьмонопольныйрежим установитьнастройкиклиенталицензирования установитьобновлениепредопределенныхданныхинформационнойбазы установитьотключениебезопасногорежима установитьпараметрыфункциональныхопцийинтерфейса установитьпривилегированныйрежим установитьпроверкусложностипаролейпользователей установитьрасширениеработыскриптографией установитьрасширениеработысфайлами установитьсоединениесвнешнимисточникомданных установитьсоответствиеобъектаиформы установитьсоставстандартногоинтерфейсаodata установитьчасовойпоясинформационнойбазы установитьчасовойпояссеанса формат цел час часовойпояс часовойпояссеанса число числопрописью этоадресвременногохранилища ",c="wsссылки библиотекакартинок библиотекамакетовоформлениякомпоновкиданных библиотекастилей бизнеспроцессы внешниеисточникиданных внешниеобработки внешниеотчеты встроенныепокупки главныйинтерфейс главныйстиль документы доставляемыеуведомления журналыдокументов задачи информацияобинтернетсоединении использованиерабочейдаты историяработыпользователя константы критерииотбора метаданные обработки отображениерекламы отправкадоставляемыхуведомлений отчеты панельзадачос параметрзапуска параметрысеанса перечисления планывидоврасчета планывидовхарактеристик планыобмена планысчетов полнотекстовыйпоиск пользователиинформационнойбазы последовательности проверкавстроенныхпокупок рабочаядата расширенияконфигурации регистрыбухгалтерии регистрынакопления регистрырасчета регистрысведений регламентныезадания сериализаторxdto справочники средствагеопозиционирования средствакриптографии средствамультимедиа средстваотображениярекламы средствапочты средствателефонии фабрикаxdto файловыепотоки фоновыезадания хранилищанастроек хранилищевариантовотчетов хранилищенастроекданныхформ хранилищеобщихнастроек хранилищепользовательскихнастроекдинамическихсписков хранилищепользовательскихнастроекотчетов хранилищесистемныхнастроек ",r=a+d+i+c,p="webцвета windowsцвета windowsшрифты библиотекакартинок рамкистиля символы цветастиля шрифтыстиля ",b="автоматическоесохранениеданныхформывнастройках автонумерациявформе автораздвижениесерий анимациядиаграммы вариантвыравниванияэлементовизаголовков вариантуправлениявысотойтаблицы вертикальнаяпрокруткаформы вертикальноеположение вертикальноеположениеэлемента видгруппыформы виддекорацииформы виддополненияэлементаформы видизмененияданных видкнопкиформы видпереключателя видподписейкдиаграмме видполяформы видфлажка влияниеразмеранапузырекдиаграммы горизонтальноеположение горизонтальноеположениеэлемента группировкаколонок группировкаподчиненныхэлементовформы группыиэлементы действиеперетаскивания дополнительныйрежимотображения допустимыедействияперетаскивания интервалмеждуэлементамиформы использованиевывода использованиеполосыпрокрутки используемоезначениеточкибиржевойдиаграммы историявыборапривводе источникзначенийоситочекдиаграммы источникзначенияразмерапузырькадиаграммы категориягруппыкоманд максимумсерий начальноеотображениедерева начальноеотображениесписка обновлениетекстаредактирования ориентациядендрограммы ориентациядиаграммы ориентацияметокдиаграммы ориентацияметоксводнойдиаграммы ориентацияэлементаформы отображениевдиаграмме отображениевлегендедиаграммы отображениегруппыкнопок отображениезаголовкашкалыдиаграммы отображениезначенийсводнойдиаграммы отображениезначенияизмерительнойдиаграммы отображениеинтерваладиаграммыганта отображениекнопки отображениекнопкивыбора отображениеобсужденийформы отображениеобычнойгруппы отображениеотрицательныхзначенийпузырьковойдиаграммы отображениепанелипоиска отображениеподсказки отображениепредупрежденияприредактировании отображениеразметкиполосырегулирования отображениестраницформы отображениетаблицы отображениетекстазначениядиаграммыганта отображениеуправленияобычнойгруппы отображениефигурыкнопки палитрацветовдиаграммы поведениеобычнойгруппы поддержкамасштабадендрограммы поддержкамасштабадиаграммыганта поддержкамасштабасводнойдиаграммы поисквтаблицепривводе положениезаголовкаэлементаформы положениекартинкикнопкиформы положениекартинкиэлементаграфическойсхемы положениекоманднойпанелиформы положениекоманднойпанелиэлементаформы положениеопорнойточкиотрисовки положениеподписейкдиаграмме положениеподписейшкалызначенийизмерительнойдиаграммы положениесостоянияпросмотра положениестрокипоиска положениетекстасоединительнойлинии положениеуправленияпоиском положениешкалывремени порядокотображенияточекгоризонтальнойгистограммы порядоксерийвлегендедиаграммы размеркартинки расположениезаголовкашкалыдиаграммы растягиваниеповертикалидиаграммыганта режимавтоотображениясостояния режимвводастроктаблицы режимвыборанезаполненного режимвыделениядаты режимвыделениястрокитаблицы режимвыделениятаблицы режимизмененияразмера режимизменениясвязанногозначения режимиспользованиядиалогапечати режимиспользованияпараметракоманды режиммасштабированияпросмотра режимосновногоокнаклиентскогоприложения режимоткрытияокнаформы режимотображениявыделения режимотображениягеографическойсхемы режимотображениязначенийсерии режимотрисовкисеткиграфическойсхемы режимполупрозрачностидиаграммы режимпробеловдиаграммы режимразмещениянастранице режимредактированияколонки режимсглаживаниядиаграммы режимсглаживанияиндикатора режимсписказадач сквозноевыравнивание сохранениеданныхформывнастройках способзаполнениятекстазаголовкашкалыдиаграммы способопределенияограничивающегозначениядиаграммы стандартнаягруппакоманд стандартноеоформление статусоповещенияпользователя стильстрелки типаппроксимациилиниитрендадиаграммы типдиаграммы типединицышкалывремени типимпортасерийслоягеографическойсхемы типлиниигеографическойсхемы типлиниидиаграммы типмаркерагеографическойсхемы типмаркерадиаграммы типобластиоформления типорганизацииисточникаданныхгеографическойсхемы типотображениясериислоягеографическойсхемы типотображенияточечногообъектагеографическойсхемы типотображенияшкалыэлементалегендыгеографическойсхемы типпоискаобъектовгеографическойсхемы типпроекциигеографическойсхемы типразмещенияизмерений типразмещенияреквизитовизмерений типрамкиэлементауправления типсводнойдиаграммы типсвязидиаграммыганта типсоединениязначенийпосериямдиаграммы типсоединенияточекдиаграммы типсоединительнойлинии типстороныэлементаграфическойсхемы типформыотчета типшкалырадарнойдиаграммы факторлиниитрендадиаграммы фигуракнопки фигурыграфическойсхемы фиксациявтаблице форматдняшкалывремени форматкартинки ширинаподчиненныхэлементовформы ",w="виддвижениябухгалтерии виддвижениянакопления видпериодарегистрарасчета видсчета видточкимаршрутабизнеспроцесса использованиеагрегатарегистранакопления использованиегруппиэлементов использованиережимапроведения использованиесреза периодичностьагрегатарегистранакопления режимавтовремя режимзаписидокумента режимпроведениядокумента ",h="авторегистрацияизменений допустимыйномерсообщения отправкаэлементаданных получениеэлементаданных ",j="использованиерасшифровкитабличногодокумента ориентациястраницы положениеитоговколоноксводнойтаблицы положениеитоговстроксводнойтаблицы положениетекстаотносительнокартинки расположениезаголовкагруппировкитабличногодокумента способчтениязначенийтабличногодокумента типдвустороннейпечати типзаполненияобластитабличногодокумента типкурсоровтабличногодокумента типлиниирисункатабличногодокумента типлинииячейкитабличногодокумента типнаправленияпереходатабличногодокумента типотображениявыделениятабличногодокумента типотображениялинийсводнойтаблицы типразмещениятекстатабличногодокумента типрисункатабличногодокумента типсмещениятабличногодокумента типузоратабличногодокумента типфайлатабличногодокумента точностьпечати чередованиерасположениястраниц ",z="отображениевремениэлементовпланировщика ",f="типфайлаформатированногодокумента ",k="обходрезультатазапроса типзаписизапроса ",u="видзаполнениярасшифровкипостроителяотчета типдобавленияпредставлений типизмеренияпостроителяотчета типразмещенияитогов ",y="доступкфайлу режимдиалогавыборафайла режимоткрытияфайла ",N="типизмеренияпостроителязапроса ",g="видданныханализа методкластеризации типединицыинтервалавременианализаданных типзаполнениятаблицырезультатаанализаданных типиспользованиячисловыхзначенийанализаданных типисточникаданныхпоискаассоциаций типколонкианализаданныхдереворешений типколонкианализаданныхкластеризация типколонкианализаданныхобщаястатистика типколонкианализаданныхпоискассоциаций типколонкианализаданныхпоискпоследовательностей типколонкимоделипрогноза типмерырасстоянияанализаданных типотсеченияправилассоциации типполяанализаданных типстандартизациианализаданных типупорядочиванияправилассоциациианализаданных типупорядочиванияшаблоновпоследовательностейанализаданных типупрощениядереварешений ",E="wsнаправлениепараметра вариантxpathxs вариантзаписидатыjson вариантпростоготипаxs видгруппымоделиxs видфасетаxdto действиепостроителяdom завершенностьпростоготипаxs завершенностьсоставноготипаxs завершенностьсхемыxs запрещенныеподстановкиxs исключениягруппподстановкиxs категорияиспользованияатрибутаxs категорияограниченияидентичностиxs категорияограниченияпространствименxs методнаследованияxs модельсодержимогоxs назначениетипаxml недопустимыеподстановкиxs обработкапробельныхсимволовxs обработкасодержимогоxs ограничениезначенияxs параметрыотбораузловdom переносстрокjson позициявдокументеdom пробельныесимволыxml типатрибутаxml типзначенияjson типканоническогоxml типкомпонентыxs типпроверкиxml типрезультатаdomxpath типузлаdom типузлаxml формаxml формапредставленияxs форматдатыjson экранированиесимволовjson ",M="видсравнениякомпоновкиданных действиеобработкирасшифровкикомпоновкиданных направлениесортировкикомпоновкиданных расположениевложенныхэлементоврезультатакомпоновкиданных расположениеитоговкомпоновкиданных расположениегруппировкикомпоновкиданных расположениеполейгруппировкикомпоновкиданных расположениеполякомпоновкиданных расположениереквизитовкомпоновкиданных расположениересурсовкомпоновкиданных типбухгалтерскогоостаткакомпоновкиданных типвыводатекстакомпоновкиданных типгруппировкикомпоновкиданных типгруппыэлементовотборакомпоновкиданных типдополненияпериодакомпоновкиданных типзаголовкаполейкомпоновкиданных типмакетагруппировкикомпоновкиданных типмакетаобластикомпоновкиданных типостаткакомпоновкиданных типпериодакомпоновкиданных типразмещениятекстакомпоновкиданных типсвязинаборовданныхкомпоновкиданных типэлементарезультатакомпоновкиданных расположениелегендыдиаграммыкомпоновкиданных типпримененияотборакомпоновкиданных режимотображенияэлементанастройкикомпоновкиданных режимотображениянастроеккомпоновкиданных состояниеэлементанастройкикомпоновкиданных способвосстановлениянастроеккомпоновкиданных режимкомпоновкирезультата использованиепараметракомпоновкиданных автопозицияресурсовкомпоновкиданных вариантиспользованиягруппировкикомпоновкиданных расположениересурсоввдиаграммекомпоновкиданных фиксациякомпоновкиданных использованиеусловногооформлениякомпоновкиданных ",_="важностьинтернетпочтовогосообщения обработкатекстаинтернетпочтовогосообщения способкодированияинтернетпочтовоговложения способкодированиянеasciiсимволовинтернетпочтовогосообщения типтекстапочтовогосообщения протоколинтернетпочты статусразборапочтовогосообщения ",v="режимтранзакциизаписижурналарегистрации статустранзакциизаписижурналарегистрации уровеньжурналарегистрации ",A="расположениехранилищасертификатовкриптографии режимвключениясертификатовкриптографии режимпроверкисертификатакриптографии типхранилищасертификатовкриптографии ",C="кодировкаименфайловвzipфайле методсжатияzip методшифрованияzip режимвосстановленияпутейфайловzip режимобработкиподкаталоговzip режимсохраненияпутейzip уровеньсжатияzip ",L="звуковоеоповещение направлениепереходакстроке позициявпотоке порядокбайтов режимблокировкиданных режимуправленияблокировкойданных сервисвстроенныхпокупок состояниефоновогозадания типподписчикадоставляемыхуведомлений уровеньиспользованиязащищенногосоединенияftp ",Z="направлениепорядкасхемызапроса типдополненияпериодамисхемызапроса типконтрольнойточкисхемызапроса типобъединениясхемызапроса типпараметрадоступнойтаблицысхемызапроса типсоединениясхемызапроса ",$="httpметод автоиспользованиеобщегореквизита автопрефиксномеразадачи вариантвстроенногоязыка видиерархии видрегистранакопления видтаблицывнешнегоисточникаданных записьдвиженийприпроведении заполнениепоследовательностей индексирование использованиебазыпланавидоврасчета использованиебыстроговыбора использованиеобщегореквизита использованиеподчинения использованиеполнотекстовогопоиска использованиеразделяемыхданныхобщегореквизита использованиереквизита назначениеиспользованияприложения назначениерасширенияконфигурации направлениепередачи обновлениепредопределенныхданных оперативноепроведение основноепредставлениевидарасчета основноепредставлениевидахарактеристики основноепредставлениезадачи основноепредставлениепланаобмена основноепредставлениесправочника основноепредставлениесчета перемещениеграницыприпроведении периодичностьномерабизнеспроцесса периодичностьномерадокумента периодичностьрегистрарасчета периодичностьрегистрасведений повторноеиспользованиевозвращаемыхзначений полнотекстовыйпоискпривводепостроке принадлежностьобъекта проведение разделениеаутентификацииобщегореквизита разделениеданныхобщегореквизита разделениерасширенийконфигурацииобщегореквизита режимавтонумерацииобъектов режимзаписирегистра режимиспользованиямодальности режимиспользованиясинхронныхвызововрасширенийплатформыивнешнихкомпонент режимповторногоиспользованиясеансов режимполученияданныхвыборапривводепостроке режимсовместимости режимсовместимостиинтерфейса режимуправленияблокировкойданныхпоумолчанию сериикодовпланавидовхарактеристик сериикодовпланасчетов сериикодовсправочника созданиепривводе способвыбора способпоискастрокипривводепостроке способредактирования типданныхтаблицывнешнегоисточникаданных типкодапланавидоврасчета типкодасправочника типмакета типномерабизнеспроцесса типномерадокумента типномеразадачи типформы удалениедвижений ",q="важностьпроблемыприменениярасширенияконфигурации вариантинтерфейсаклиентскогоприложения вариантмасштабаформклиентскогоприложения вариантосновногошрифтаклиентскогоприложения вариантстандартногопериода вариантстандартнойдатыначала видграницы видкартинки видотображенияполнотекстовогопоиска видрамки видсравнения видцвета видчисловогозначения видшрифта допустимаядлина допустимыйзнак использованиеbyteordermark использованиеметаданныхполнотекстовогопоиска источникрасширенийконфигурации клавиша кодвозвратадиалога кодировкаxbase кодировкатекста направлениепоиска направлениесортировки обновлениепредопределенныхданных обновлениеприизмененииданных отображениепанелиразделов проверказаполнения режимдиалогавопрос режимзапускаклиентскогоприложения режимокругления режимоткрытияформприложения режимполнотекстовогопоиска скоростьклиентскогосоединения состояниевнешнегоисточникаданных состояниеобновленияконфигурациибазыданных способвыборасертификатаwindows способкодированиястроки статуссообщения типвнешнейкомпоненты типплатформы типповеденияклавишиenter типэлементаинформацииовыполненииобновленияконфигурациибазыданных уровеньизоляциитранзакций хешфункция частидаты",B=p+b+w+h+j+z+f+k+u+y+N+g+E+M+_+v+A+C+L+Z+$+q,I="comобъект ftpсоединение httpзапрос httpсервисответ httpсоединение wsопределения wsпрокси xbase анализданных аннотацияxs блокировкаданных буфердвоичныхданных включениеxs выражениекомпоновкиданных генераторслучайныхчисел географическаясхема географическиекоординаты графическаясхема группамоделиxs данныерасшифровкикомпоновкиданных двоичныеданные дендрограмма диаграмма диаграммаганта диалогвыборафайла диалогвыборацвета диалогвыборашрифта диалограсписаниярегламентногозадания диалогредактированиястандартногопериода диапазон документdom документhtml документацияxs доставляемоеуведомление записьdom записьfastinfoset записьhtml записьjson записьxml записьzipфайла записьданных записьтекста записьузловdom запрос защищенноесоединениеopenssl значенияполейрасшифровкикомпоновкиданных извлечениетекста импортxs интернетпочта интернетпочтовоесообщение интернетпочтовыйпрофиль интернетпрокси интернетсоединение информациядляприложенияxs использованиеатрибутаxs использованиесобытияжурналарегистрации источникдоступныхнастроеккомпоновкиданных итераторузловdom картинка квалификаторыдаты квалификаторыдвоичныхданных квалификаторыстроки квалификаторычисла компоновщикмакетакомпоновкиданных компоновщикнастроеккомпоновкиданных конструктормакетаоформлениякомпоновкиданных конструкторнастроеккомпоновкиданных конструкторформатнойстроки линия макеткомпоновкиданных макетобластикомпоновкиданных макетоформлениякомпоновкиданных маскаxs менеджеркриптографии наборсхемxml настройкикомпоновкиданных настройкисериализацииjson обработкакартинок обработкарасшифровкикомпоновкиданных обходдереваdom объявлениеатрибутаxs объявлениенотацииxs объявлениеэлементаxs описаниеиспользованиясобытиядоступжурналарегистрации описаниеиспользованиясобытияотказвдоступежурналарегистрации описаниеобработкирасшифровкикомпоновкиданных описаниепередаваемогофайла описаниетипов определениегруппыатрибутовxs определениегруппымоделиxs определениеограниченияидентичностиxs определениепростоготипаxs определениесоставноготипаxs определениетипадокументаdom определенияxpathxs отборкомпоновкиданных пакетотображаемыхдокументов параметрвыбора параметркомпоновкиданных параметрызаписиjson параметрызаписиxml параметрычтенияxml переопределениеxs планировщик полеанализаданных полекомпоновкиданных построительdom построительзапроса построительотчета построительотчетаанализаданных построительсхемxml поток потоквпамяти почта почтовоесообщение преобразованиеxsl преобразованиекканоническомуxml процессорвыводарезультатакомпоновкиданныхвколлекциюзначений процессорвыводарезультатакомпоновкиданныхвтабличныйдокумент процессоркомпоновкиданных разыменовательпространствименdom рамка расписаниерегламентногозадания расширенноеимяxml результатчтенияданных своднаядиаграмма связьпараметравыбора связьпотипу связьпотипукомпоновкиданных сериализаторxdto сертификатклиентаwindows сертификатклиентафайл сертификаткриптографии сертификатыудостоверяющихцентровwindows сертификатыудостоверяющихцентровфайл сжатиеданных системнаяинформация сообщениепользователю сочетаниеклавиш сравнениезначений стандартнаядатаначала стандартныйпериод схемаxml схемакомпоновкиданных табличныйдокумент текстовыйдокумент тестируемоеприложение типданныхxml уникальныйидентификатор фабрикаxdto файл файловыйпоток фасетдлиныxs фасетколичестваразрядовдробнойчастиxs фасетмаксимальноговключающегозначенияxs фасетмаксимальногоисключающегозначенияxs фасетмаксимальнойдлиныxs фасетминимальноговключающегозначенияxs фасетминимальногоисключающегозначенияxs фасетминимальнойдлиныxs фасетобразцаxs фасетобщегоколичестваразрядовxs фасетперечисленияxs фасетпробельныхсимволовxs фильтрузловdom форматированнаястрока форматированныйдокумент фрагментxs хешированиеданных хранилищезначения цвет чтениеfastinfoset чтениеhtml чтениеjson чтениеxml чтениеzipфайла чтениеданных чтениетекста чтениеузловdom шрифт элементрезультатакомпоновкиданных ",P="comsafearray деревозначений массив соответствие списокзначений структура таблицазначений фиксированнаяструктура фиксированноесоответствие фиксированныймассив ",T=I+P,W="null истина ложь неопределено",D=s.inherit(s.NM),F={cN:"string",b:'"|\\|',e:'"|$',c:[{b:'""'}]},G={b:"'",e:"'",eB:!0,eE:!0,c:[{cN:"number",b:"\\d{4}([\\.\\\\/:-]?\\d{2}){0,5}"}]},H=s.inherit(s.CLCM),J={cN:"meta",l:x,b:"#|&",e:"$",k:{"meta-keyword":t+n},c:[H]},K={cN:"symbol",b:"~",e:";|:",eE:!0},O={cN:"function",l:x,v:[{b:"процедура|функция",e:"\\)",k:"процедура функция"},{b:"конецпроцедуры|конецфункции",k:"конецпроцедуры конецфункции"}],c:[{b:"\\(",e:"\\)",endsParent:!0,c:[{cN:"params",l:x,b:x,e:",",eE:!0,eW:!0,k:{keyword:"знач",literal:W},c:[D,F,G]},H]},s.inherit(s.TM,{b:x})]};return{cI:!0,l:x,k:{keyword:t,built_in:r,"class":B,type:T,literal:W},c:[J,O,H,K,D,F,G]}}); \ No newline at end of file diff --git a/src/TestWebApp/source.ospt b/src/TestWebApp/source.ospt deleted file mode 100644 index 462b2ab24..000000000 --- a/src/TestWebApp/source.ospt +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - OneScript HTTP Service Test Page - - - - - - - - -
- - Go to W3Schools! - -
-

-            {SourceCode}
-        
- - - \ No newline at end of file diff --git a/src/TestWebApp/styles/github.css b/src/TestWebApp/styles/github.css deleted file mode 100644 index 791932b87..000000000 --- a/src/TestWebApp/styles/github.css +++ /dev/null @@ -1,99 +0,0 @@ -/* - -github.com style (c) Vasily Polovnyov - -*/ - -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - color: #333; - background: #f8f8f8; -} - -.hljs-comment, -.hljs-quote { - color: #998; - font-style: italic; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-subst { - color: #333; - font-weight: bold; -} - -.hljs-number, -.hljs-literal, -.hljs-variable, -.hljs-template-variable, -.hljs-tag .hljs-attr { - color: #008080; -} - -.hljs-string, -.hljs-doctag { - color: #d14; -} - -.hljs-title, -.hljs-section, -.hljs-selector-id { - color: #900; - font-weight: bold; -} - -.hljs-subst { - font-weight: normal; -} - -.hljs-type, -.hljs-class .hljs-title { - color: #458; - font-weight: bold; -} - -.hljs-tag, -.hljs-name, -.hljs-attribute { - color: #000080; - font-weight: normal; -} - -.hljs-regexp, -.hljs-link { - color: #009926; -} - -.hljs-symbol, -.hljs-bullet { - color: #990073; -} - -.hljs-built_in, -.hljs-builtin-name { - color: #0086b3; -} - -.hljs-meta { - color: #999; - font-weight: bold; -} - -.hljs-deletion { - background: #fdd; -} - -.hljs-addition { - background: #dfd; -} - -.hljs-emphasis { - font-style: italic; -} - -.hljs-strong { - font-weight: bold; -} diff --git a/src/Tests/DocumenterTests/DocumenterTests.csproj b/src/Tests/DocumenterTests/DocumenterTests.csproj new file mode 100644 index 000000000..b039de817 --- /dev/null +++ b/src/Tests/DocumenterTests/DocumenterTests.csproj @@ -0,0 +1,27 @@ + + + + $(TargetFrameworkVersion) + false + true + 8 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Tests/DocumenterTests/MarkdownWriterTests.cs b/src/Tests/DocumenterTests/MarkdownWriterTests.cs new file mode 100644 index 000000000..b134b8a3e --- /dev/null +++ b/src/Tests/DocumenterTests/MarkdownWriterTests.cs @@ -0,0 +1,66 @@ +/*---------------------------------------------------------- +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.IO; +using OneScriptDocumenter.Cli; +using Xunit; + +namespace DocumenterTests +{ + public class MarkdownWriterTests + { + [Fact] + public void TestListIndentZeroIndent() + { + var stringWriter = new StringWriter(); + var mdWriter = new MarkdownWriter(stringWriter); + + mdWriter.BeginList(); + mdWriter.ListItem("One"); + mdWriter.ListItem("Two"); + mdWriter.ListItem("Three"); + mdWriter.EndList(); + + stringWriter.Flush(); + + var result = stringWriter.ToString(); + + Assert.Equal("* One\r\n" + + "* Two\r\n" + + "* Three\r\n", result); + + } + + [Fact] + public void TestListIndentTwoLevelList() + { + var stringWriter = new StringWriter(); + var mdWriter = new MarkdownWriter(stringWriter); + + mdWriter.BeginList(); + mdWriter.ListItem("One"); + mdWriter.ListItem("Two"); + mdWriter.BeginList(); + mdWriter.ListItem("Three"); + mdWriter.ListItem("Four"); + mdWriter.EndList(); + mdWriter.ListItem("Five"); + mdWriter.EndList(); + + stringWriter.Flush(); + + var result = stringWriter.ToString(); + + Assert.Equal("* One\r\n" + + "* Two\r\n" + + " * Three\r\n" + + " * Four\r\n" + + "* Five\r\n", result); + + } + } +} \ No newline at end of file diff --git a/src/Tests/DocumenterTests/Resources/SimpleMarkup.xml b/src/Tests/DocumenterTests/Resources/SimpleMarkup.xml new file mode 100644 index 000000000..1409d1e01 --- /dev/null +++ b/src/Tests/DocumenterTests/Resources/SimpleMarkup.xml @@ -0,0 +1,12 @@ + + + Возвращает соответствие переменных среды. Ключом является имя переменной, а значением - значение переменной + + Расположение переменной среды + + Для Каждого Переменная Из ПеременныеСреды() Цикл + Сообщить(Переменная.Ключ + " = " + Переменная.Значение); + КонецЦикла; + + Соответствие соответствие Имя-Значение переменных среды + \ No newline at end of file diff --git a/src/Tests/DocumenterTests/XmlDocConversionTest.cs b/src/Tests/DocumenterTests/XmlDocConversionTest.cs new file mode 100644 index 000000000..fe57cbbc6 --- /dev/null +++ b/src/Tests/DocumenterTests/XmlDocConversionTest.cs @@ -0,0 +1,76 @@ +/*---------------------------------------------------------- +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.Linq; +using System.Reflection; +using System.Xml.Linq; +using Moq; +using OneScriptDocumenter.Secondary; +using Xunit; + +namespace DocumenterTests +{ + public class XmlDocConversionTest + { + [Fact] + public void TestConversionOfTextBlock() + { + using var xmlStream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream("DocumenterTests.Resources.SimpleMarkup.xml"); + Assert.NotNull(xmlStream); + + var doc = XDocument.Load(xmlStream); + var exampleNode = doc.Root.Elements("example").First(); + + var converter = new XmlDocConverter(Mock.Of()); + var text = converter.ConvertTextBlock(exampleNode); + var expected = +@"Для Каждого Переменная Из ПеременныеСреды() Цикл + Сообщить(Переменная.Ключ + "" = "" + Переменная.Значение); +КонецЦикла;"; + + Assert.Equal(expected, text); + } + + [Fact] + public void TestConversionOfInlineCode() + { + using var xmlStream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream("DocumenterTests.Resources.SimpleMarkup.xml"); + Assert.NotNull(xmlStream); + + var doc = XDocument.Load(xmlStream); + var exampleNode = doc.Root.Elements("returns").First(); + + var converter = new XmlDocConverter(Mock.Of()); + var text = converter.ConvertTextBlock(exampleNode); + var expected = "`Соответствие` соответствие Имя-Значение переменных среды"; + + Assert.Equal(expected, text); + } + + [Fact] + public void CanExtractLinkFromXmlDoc() + { + var resolver = new Mock(); + resolver.Setup(r => r.Resolve(It.IsAny(), It.IsAny())) + .Returns((linkTarget, linkText) => $"[{linkText}]({linkTarget})"); + + var converter = new XmlDocConverter(resolver.Object); + var elementWithLink = new XElement("summary", + new XText("Это будет "), + new XElement("see", new XAttribute("cref", "referenceForSomeType"), + new XText("гиперссылка")), + new XText(" на другой документ")); + + var text = converter.ConvertTextBlock(elementWithLink); + Assert.Equal("Это будет [гиперссылка](referenceForSomeType) на другой документ", text); + resolver.Verify(r => + r.Resolve(It.Is(a => a == "referenceForSomeType"), It.Is(a => a == "гиперссылка"))); + } + } +} \ No newline at end of file diff --git a/src/Tests/NUnitTests/CompilerTests.cs b/src/Tests/NUnitTests/CompilerTests.cs new file mode 100644 index 000000000..8a9e507ec --- /dev/null +++ b/src/Tests/NUnitTests/CompilerTests.cs @@ -0,0 +1,552 @@ +/*---------------------------------------------------------- +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 NUnitTests +{ + [TestFixture] + public class CompilerTests + { + private EngineWrapperNUnit host; + + [OneTimeSetUp] + public void Init() + { + host = new EngineWrapperNUnit(); + host.StartEngine(); + var solutionRoot = Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", ".."); + host.Engine.InitExternalLibraries(Path.Combine(solutionRoot, "oscript-library", "src"), null); + } + + #region Отсутствие точки с запятой перед завершением блока + + [Test] + public void TestNoSemicolonBeforeEndProcedure() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Возврат + КонецПроцедуры"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestNoSemicolonBeforeEndFunction() + { + var moduleSource = host.Engine.Loader.FromString( + @"Функция Фун1() + Возврат 4 + КонецФункции"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestNoSemicolonBeforeEndDo() + { + var moduleSource = host.Engine.Loader.FromString( + @"Для Инд = 1 По 10 Цикл + Прервать + КонецЦикла"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestNoSemicolonBeforeElseOrEndIf() + { + var moduleSource = host.Engine.Loader.FromString( + @"Если Истина Тогда + Ф = 1 + ИначеЕсли Истина Тогда + Ф = 2 + Иначе + Ф = 3 + КонецЕсли"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestNoSemicolonBeforeExceptionOrEndTry() + { + var moduleSource = host.Engine.Loader.FromString( + @"Попытка + Ф = 1 + Исключение + ВызватьИсключение + КонецПопытки"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + + [Test] + public void TestNoSemicolonBeforeEndOfText() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Возврат + КонецПроцедуры + Ф = 0"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + #endregion + + #region Точка с запятой перед завершением блока + + [Test] + public void TestSemicolonBeforeEndProcedure() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Возврат; + КонецПроцедуры"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestSemicolonBeforeEndFunction() + { + var moduleSource = host.Engine.Loader.FromString( + @"Функция Фун1() + Возврат 4; + КонецФункции"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestSemicolonBeforeEndDo() + { + var moduleSource = host.Engine.Loader.FromString( + @"Для Инд = 1 По 10 Цикл + Прервать; + КонецЦикла"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestSemicolonBeforeElseOrEndIf() + { + var moduleSource = host.Engine.Loader.FromString( + @"Если Истина Тогда + Ф = 1; + ИначеЕсли Истина Тогда + Ф = 2; + Иначе + Ф = 3; + КонецЕсли"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test] + public void TestSemicolonBeforeExceptionOrEndTry() + { + var moduleSource = host.Engine.Loader.FromString( + @"Попытка + Ф = 1; + Исключение + ВызватьИсключение; + КонецПопытки"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + + [Test] + public void TestSemicolonBeforeEndOfText() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Возврат + КонецПроцедуры + Ф = 0;"); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + #endregion + + + #region Согласованное завершение структурных операторов + [Test] + public void TestEndFunctionDoesNotEndIf() + { + var moduleSource = host.Engine.Loader.FromString( + @"Если Истина Тогда + Ф = 1 + КонецФункции"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "КонецФункции закрыл Если!!!"); + } + + [Test] + public void TestEndDoDoesNotEndIf() + { + var moduleSource = host.Engine.Loader.FromString( + @"Если Истина Тогда + Ф = 1 + КонецЦикла"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "КонецЦикла закрыл Если!!!"); + } + + [Test] + public void TestEndIfDoesNotEndDo() + { + var moduleSource = host.Engine.Loader.FromString( + @"Пока Истина Цикл + Ф = 1 + КонецЕсли"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "КонецЕсли закрыл Пока!!!"); + } + [Test] + public void TestEndFunctionDoesNotEndProcedure() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц() + Возврат + КонецФункции"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "КонецФункции закрыл Процедуру!!!"); + } + + [Test] + public void TestEndProcedureDoesNotEndFunction() + { + var moduleSource = host.Engine.Loader.FromString( + @"Функция Функ() + Возврат 0 + КонецПроцедуры"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "КонецПроцедуры закрыл Функцию!!!"); + } + + [Test] + public void TestElseifDoesNotEndProcedure() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц() + Возврат + ИначеЕсли"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "ИначеЕсли закрыл Процедуру!!!"); + } + + [Test] + public void TestEndTryDoesNotEndProcedure() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц() + Возврат + КонецПопытки"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "КонецПопытки закрыл Процедуру!!!"); + } + #endregion + + [Test(Description = "Компилируется вызов метода с пропуском параметров")] + public void TestCompileMethodCallWithoutAllParams() + { + var moduleSource = host.Engine.Loader.FromString( + @"Функция Ф3(П1, П2, П3) + Возврат """" + П1 + П2 + П3 + КонецФункции + Функция Ф2(П1, П2, П3 = Неопределено) + Возврат """" + П1 + П2 + П3 + КонецФункции + Функция Ф1(П1, П2 = Неопределено, П3 = Неопределено) + Возврат """" + П1 + П2 + П3 + КонецФункции + Р = Ф3(,,); + Р = Ф2(,) + Ф2(,,); + Р = Ф1(,,) + Ф1(,); + "); + + host.Engine.GetCompilerService().Compile(moduleSource); + } + + [Test(Description = "Не компилируется вызов метода вообще без параметров")] + public void TestCantCompileCallWithoutParams() + { + var moduleSource = host.Engine.Loader.FromString( + @"Функция Ф1(П1) + Возврат П1; + КонецФункции + Р = Ф1(); + "); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "Не должно было скомпилироваться!"); + } + + [Test] + public void TestReturnBeforeException() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц() + Попытка + Возврат + Исключение + КонецПопытки + КонецПроцедуры"); + host.Engine.GetCompilerService().Compile(moduleSource); + } + + + [Test] + public void TestElseifDoesNotDelimitAddHandler() + { + var moduleSource = host.Engine.Loader.FromString( + @"Если Истина Тогда + ДобавитьОбработчик ЭтотОбъект.Событие ИначеЕсли ЭтотОбъект.Обработчик + КонецЕсли"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "ИначеЕсли разделяет параметры ДобавитьОбработчик!!!"); + } + + + [Test] + public void TestSemicolonBeforeProcedures() + { + var moduleSource = host.Engine.Loader.FromString( + @"Перем Глоб + Процедура Проц1() + Возврат + КонецПроцедуры"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "Нет точки с запятой между переменными модуля и процедурами!"); + } + + [Test] + public void TestSemicolonAfterLocals() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Перем Локал + Если Истина Тогда + Локал = 1 + КонецЕсли + КонецПроцедуры"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "Нет точки с запятой между локалиными переменными и операторами!"); + } + + [Test] + public void TestNoSemicolonBetweenProcedures() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Возврат + КонецПроцедуры; + Процедура Проц2() + Возврат + КонецПроцедуры"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "Точка с запятой между процедурами!"); + } + + [Test] + public void TestSemicolonBetweenStatements1() + { + var moduleSource = host.Engine.Loader.FromString( + @" + Ф = 1 + Если Ложь Тогда + Ф = 2 + КонецЕсли"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "Отсутствует точка с запятой между операторами!"); + } + + [Test] + public void TestSemicolonBetweenStatements2() + { + var moduleSource = host.Engine.Loader.FromString( + @" + Если Ложь Тогда + Ф = 2 + КонецЕсли + Ф = 1"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "Отсутствует точка с запятой между операторами!"); + } + + [Test] + public void TestSemicolonBetweenStatements3() + { + var moduleSource = host.Engine.Loader.FromString( + @"Если Истина Тогда + Ф = 1 + КонецЕсли + Если Ложь Тогда + Ф = 2 + КонецЕсли"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "Отсутствует точка с запятой между операторами!"); + } + + [Test] + public void TestLocalExportVar() + { + var moduleSource = host.Engine.Loader.FromString( + @"Процедура Проц1() + Перем Переменная Экспорт; + КонецПроцедуры"); + + bool exceptionThrown = false; + try + { + host.Engine.GetCompilerService().Compile(moduleSource); + } + catch (CompilerException) + { + exceptionThrown = true; + } + Assert.IsTrue(exceptionThrown, "В теле процедуры или функции не может быть объявлена экспортная переменная!"); + } + } +} \ No newline at end of file diff --git a/src/NUnitTests/ContextTests.cs b/src/Tests/NUnitTests/ContextTests.cs similarity index 92% rename from src/NUnitTests/ContextTests.cs rename to src/Tests/NUnitTests/ContextTests.cs index f96d5afc5..a479118ff 100644 --- a/src/NUnitTests/ContextTests.cs +++ b/src/Tests/NUnitTests/ContextTests.cs @@ -5,13 +5,6 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.IO; -using NUnit.Framework; -using ScriptEngine; -using ScriptEngine.Machine; - namespace NUnitTests { [TestFixture] @@ -20,7 +13,7 @@ public class ContextTests : ISystemLogWriter private EngineWrapperNUnit host; private readonly List _messages = new List(); - [OneTimeSetUp] + [SetUp] public void Init() { host = new EngineWrapperNUnit(); @@ -89,7 +82,7 @@ public void CheckUndefinedIsNull2() // Передача и возврат значения Class в функцию Class host.RunTestString( @"К = Новый ТестNullПреобразования; - Арг = Новый ПNullClass; + Арг = Новый ТестNullКласс; ВЗ = 5.7; ВЗ = К.ТестКласс(Арг); Если Не ВЗ = Арг Тогда @@ -130,23 +123,6 @@ public void CheckUndefinedIsNull4() КонецЕсли;"); } - [Test] - public void CheckUndefinedIsNull5() - { - SystemLogger.SetWriter(this); - _messages.Clear(); - - // Передача и возврат Неопределено в функцию Строка - host.RunTestString( - @"К = Новый ТестNullПреобразования; - Арг = Неопределено; - ВЗ = 5.7; - ВЗ = К.ТестNullString(Арг); - Если Не ВЗ = Неопределено Тогда - ВызватьИсключение ""Test string Func(string) -> Func(Unknown): return value is not equal of null""; - КонецЕсли;"); - } - [Test] public void CheckUndefinedIsNull6() { @@ -165,6 +141,7 @@ public void CheckUndefinedIsNull6() } [Test] + [Ignore("Зависит от принимающего объекта")] public void CheckUndefinedIsNull7() { SystemLogger.SetWriter(this); @@ -207,6 +184,7 @@ public void CheckUndefinedIsNull8() } [Test] + [Ignore("Зависит от принимающего объекта")] public void CheckUndefinedIsNull9() { SystemLogger.SetWriter(this); @@ -248,6 +226,7 @@ public void CheckUndefinedIsNull10() } [Test] + [Ignore("Зависит от принимающего объекта")] public void CheckUndefinedIsNull11() { SystemLogger.SetWriter(this); @@ -290,6 +269,7 @@ public void CheckUndefinedIsNull12() } [Test] + [Ignore("Зависит от принимающего объекта")] public void CheckUndefinedIsNull13() { SystemLogger.SetWriter(this); @@ -332,6 +312,7 @@ public void CheckUndefinedIsNull14() } [Test] + [Ignore("Зависит от принимающего объекта")] public void CheckUndefinedIsNull15() { SystemLogger.SetWriter(this); @@ -374,6 +355,7 @@ public void CheckUndefinedIsNull16() } [Test] + [Ignore("Зависит от принимающего объекта")] public void CheckUndefinedIsNull17() { SystemLogger.SetWriter(this); @@ -416,6 +398,7 @@ public void CheckUndefinedIsNull18() } [Test] + [Ignore("Зависит от принимающего объекта")] public void CheckUndefinedIsNull19() { SystemLogger.SetWriter(this); @@ -454,7 +437,7 @@ public void CheckUndefinedIsNull20() @"К = Новый ТестNullПреобразования; Арг = 3.5; К.ПIValue = Арг; - ВЗ = К.КПIValue; + ВЗ = К.ПIValue; Если Не ВЗ = Арг Тогда ВызватьИсключение ""Test IValue Prop <-> IValue: return value is not equal of argument""; КонецЕсли;"); @@ -529,23 +512,6 @@ public void CheckUndefinedIsNull24() КонецЕсли;"); } - [Test] - public void CheckUndefinedIsNull25() - { - SystemLogger.SetWriter(this); - _messages.Clear(); - - // Передача и возврат string - host.RunTestString( - @"К = Новый ТестNullПреобразования; - Арг = Неопределено; - К.ПNullString = Арг; - ВЗ = К.ПNullString; - Если Не ВЗ = Неопределено Тогда - ВызватьИсключение ""Test string Prop <-> Unknown: return value is not equal of Unknown""; - КонецЕсли;"); - } - [Test] public void TestICallGoodAndHaveNoWarning() { diff --git a/src/NUnitTests/NUnitTests.csproj b/src/Tests/NUnitTests/NUnitTests.csproj similarity index 92% rename from src/NUnitTests/NUnitTests.csproj rename to src/Tests/NUnitTests/NUnitTests.csproj index ce638654e..3de19e6da 100644 --- a/src/NUnitTests/NUnitTests.csproj +++ b/src/Tests/NUnitTests/NUnitTests.csproj @@ -1,150 +1,157 @@ - - - - - Debug - AnyCPU - {93ACC849-E7E1-4695-B59D-54B3737E48A6} - Library - Properties - NUnitTests - NUnitTests - v4.7.2 - 512 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll - True - - - - ..\packages\Moq.4.13.1\lib\net45\Moq.dll - True - - - ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll - True - - - - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - True - - - ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {727a498f-bf50-42f8-8523-40c0b5b1b233} - OneScript.DebugProtocol - - - {0b30df8b-bd15-4c63-9cc4-1e123566b5b3} - OneScript.Language - - - {f09a46bd-5737-45e7-ba60-a47c9f7821a9} - ScriptEngine.HostedScript - - - {f062d1d9-d307-492a-a56b-ff3c55f8f6c0} - ScriptEngine - - - {600ACA55-4077-4D29-8C51-DBA93EB34609} - ScriptEngine - - - - - - - - - - - - - False - - - False - - - False - - - False - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + + + Debug + AnyCPU + {93ACC849-E7E1-4695-B59D-54B3737E48A6} + Library + Properties + NUnitTests + NUnitTests + v4.6.2 + 512 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + full + false + DEBUG;TRACE + prompt + 4 + bin\LinuxDebug\ + + + + ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll + True + + + + ..\packages\Moq.4.13.1\lib\net45\Moq.dll + True + + + ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll + True + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + True + + + ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + True + + + + + + + + + + + + + + + + + + + + + + + {727a498f-bf50-42f8-8523-40c0b5b1b233} + OneScript.DebugProtocol + + + {0b30df8b-bd15-4c63-9cc4-1e123566b5b3} + OneScript.Language + + + {86cfec6c-2835-4eeb-9842-14b6a455a80c} + OneScript.StandardLibrary + + + {f09a46bd-5737-45e7-ba60-a47c9f7821a9} + ScriptEngine.HostedScript + + + {f062d1d9-d307-492a-a56b-ff3c55f8f6c0} + ScriptEngine + + + {600ACA55-4077-4D29-8C51-DBA93EB34609} + ScriptEngine + + + + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/Tests/NUnitTests/Properties/AssemblyInfo.cs b/src/Tests/NUnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..3933687cd --- /dev/null +++ b/src/Tests/NUnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +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/. +----------------------------------------------------------*/ + + + +// Управление общими сведениями о сборке осуществляется посредством следующего +// набора атрибутов. Измените значения этих атрибутов, чтобы изменить сведения, +// связанные со сборкой. +[assembly: AssemblyTitle("NUnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NUnitTests")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Задание значения false для атрибута ComVisible приведет к тому, что типы из этой сборки станут невидимыми +// для COM-компонентов. Если к одному из типов этой сборки необходимо обращаться из +// модели COM, задайте для атрибута ComVisible этого типа значение true. +[assembly: ComVisible(false)] + +// Если данный проект доступен для модели COM, следующий GUID используется в качестве идентификатора библиотеки типов +[assembly: Guid("93acc849-e7e1-4695-b59d-54b3737e48a6")] + +// Сведения о версии сборки состоят из следующих четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер сборки +// Редакция +// +// Можно задать все значения или принять номера сборки и редакции по умолчанию +// используя "*", как показано ниже: +// [сборка: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/NUnitTests/TestRunnerTest.cs b/src/Tests/NUnitTests/TestRunnerTest.cs similarity index 90% rename from src/NUnitTests/TestRunnerTest.cs rename to src/Tests/NUnitTests/TestRunnerTest.cs index 1dd55041e..6bf050292 100644 --- a/src/NUnitTests/TestRunnerTest.cs +++ b/src/Tests/NUnitTests/TestRunnerTest.cs @@ -1,104 +1,110 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.IO; - -using NUnit.Framework; -using ScriptEngine.Machine; - -namespace NUnitTests -{ - [TestFixture] - public class UnitTestWrapper - { - private const int TEST_STATE_FAILED = 3; - - private EngineWrapperNUnit host; - - private string solutionRoot; - - [OneTimeSetUp] - public void Initialize() - { - host = new EngineWrapperNUnit(); - host.StartEngine(); - solutionRoot = Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", ".."); - host.Engine.InitExternalLibraries(Path.Combine(solutionRoot, "oscript-library", "src"), null); - } - - private void RunSpecificTest(string testName) - { - var testRunnerPath = Path.Combine(solutionRoot, "tests", "testrunner.os"); - - Assert.IsTrue(File.Exists(testRunnerPath), - "Запускатель тестов отсутствует по пути " + testRunnerPath); - - var specificTestPath = Path.Combine(solutionRoot, "tests", testName); - var result = host.RunTestScriptFromPath(testRunnerPath, $"-run {specificTestPath}"); - - if (result == TEST_STATE_FAILED) - { - Assert.Fail("Есть непройденные тесты!"); - } - } - - [Test] - public void Test_Reflector() - { - RunSpecificTest(@"reflector.os"); - } - [Test] - public void Test_Zip() - { - RunSpecificTest(@"zip.os"); - } - - [Test] - public void Test_XmlWrite() - { - RunSpecificTest(@"xmlwrite.os"); - } - - [Test] - [Ignore("Внутри валится очень много тестов, надо чинить механизм.")] - public void RunEngineTests() - { - var testRunnerPath = Path.Combine(solutionRoot, "tests", "testrunner.os"); - - Assert.IsTrue(File.Exists(testRunnerPath), - "Запускатель тестов отсутствует по пути " + testRunnerPath); - - var result = host.RunTestScriptFromPath(testRunnerPath, "-runall " + new FileInfo(testRunnerPath).Directory.FullName); - - if (result == TEST_STATE_FAILED) - Assert.Fail("Есть непройденные тесты!"); - } - - [Test] - public void CheckIKnowThisObject() - { - var moduleSource = host.Engine.Loader.FromString( - @"Перем А Экспорт; - Функция ФЭтотОбъект(Знач Который = 0) Экспорт - Возврат ?(Который = 0, ЭтотОбъект, ThisObject); - КонецФункции - A = 333;"); - var module = host.Engine.GetCompilerService().Compile(moduleSource); - var loadedModule = host.Engine.EngineInstance.LoadModuleImage(module); - var instance = host.Engine.EngineInstance.NewObject(loadedModule); - var methodIndex = instance.FindMethod("ФЭтотОбъект"); - - IValue asRus, asEng; - - instance.CallAsFunction(methodIndex, new[]{ValueFactory.Create(0)}, out asRus); - instance.CallAsFunction(methodIndex, new[]{ValueFactory.Create(1)}, out asEng); - - Assert.AreEqual(asRus, asEng); // Тот же самый объект - } - } +/*---------------------------------------------------------- +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 NUnitTests +{ + [TestFixture] + public class UnitTestWrapper + { + private const int TEST_STATE_FAILED = 3; + + private EngineWrapperNUnit host; + + private string solutionRoot; + + [SetUp] // OneTimeSetUp не работает - между тестами где-то ломается состояние движка + public void Initialize() + { + host = new EngineWrapperNUnit(); + host.StartEngine(); + solutionRoot = Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", ".."); + host.Engine.InitExternalLibraries(Path.Combine(solutionRoot, "oscript-library", "src"), null); + } + + private void RunSpecificTest(string testName) + { + var testRunnerPath = Path.Combine(solutionRoot, "tests", "testrunner.os"); + + Assert.IsTrue(File.Exists(testRunnerPath), + "Запускатель тестов отсутствует по пути " + testRunnerPath); + + var specificTestPath = Path.Combine(solutionRoot, "tests", testName); + var result = host.RunTestScriptFromPath(testRunnerPath, $"-run {specificTestPath}"); + + if (result == TEST_STATE_FAILED) + { + Assert.Fail("Есть непройденные тесты!"); + } + } + + [Test] + public void Test_Reflector() + { + RunSpecificTest(@"reflector.os"); + } + [Test] + public void Test_Zip() + { + RunSpecificTest(@"zip.os"); + } + + [Test] + public void Test_XmlWrite() + { + RunSpecificTest(@"xmlwrite.os"); + } + + [Test] + public void Test_ValueTable() + { + RunSpecificTest(@"valuetable.os"); + } + + [Test] + public void Test_ValueTree() + { + RunSpecificTest(@"valuetree.os"); + } + + [Test] + [Ignore("Внутри валится очень много тестов, надо чинить механизм.")] + public void RunEngineTests() + { + var testRunnerPath = Path.Combine(solutionRoot, "tests", "testrunner.os"); + + Assert.IsTrue(File.Exists(testRunnerPath), + "Запускатель тестов отсутствует по пути " + testRunnerPath); + + var result = host.RunTestScriptFromPath(testRunnerPath, "-runall " + new FileInfo(testRunnerPath).Directory.FullName); + + if (result == TEST_STATE_FAILED) + Assert.Fail("Есть непройденные тесты!"); + } + + [Test] + public void CheckIKnowThisObject() + { + var moduleSource = host.Engine.Loader.FromString( + @"Перем А Экспорт; + Функция ФЭтотОбъект(Знач Который = 0) Экспорт + Возврат ?(Который = 0, ЭтотОбъект, ThisObject); + КонецФункции + A = 333;"); + var module = host.Engine.GetCompilerService().Compile(moduleSource); + var loadedModule = host.Engine.EngineInstance.LoadModuleImage(module); + var instance = host.Engine.EngineInstance.NewObject(loadedModule); + var methodIndex = instance.FindMethod("ФЭтотОбъект"); + + IValue asRus, asEng; + + instance.CallAsFunction(methodIndex, new[]{ValueFactory.Create(0)}, out asRus); + instance.CallAsFunction(methodIndex, new[]{ValueFactory.Create(1)}, out asEng); + + Assert.AreEqual(asRus, asEng); // Тот же самый объект + } + } } \ No newline at end of file diff --git a/src/Tests/NUnitTests/packages.config b/src/Tests/NUnitTests/packages.config new file mode 100644 index 000000000..a19504be9 --- /dev/null +++ b/src/Tests/NUnitTests/packages.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/ArgumentParserTests.cs b/src/Tests/OneScript.Core.Tests/ArgumentParserTests.cs new file mode 100644 index 000000000..247713773 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/ArgumentParserTests.cs @@ -0,0 +1,31 @@ +/*---------------------------------------------------------- +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 FluentAssertions; +using OneScript.StandardLibrary.Processes; +using Xunit; + +namespace OneScript.Core.Tests; + +public class ArgumentParserTests +{ + [Theory] + [InlineData("-c 'oscript -version'", "-c", "oscript -version")] + [InlineData("-c oscript -version", "-c", "oscript", "-version")] + [InlineData("-c '\"oscript\" -version'", "-c", "\"oscript\" -version")] + [InlineData("-c \"'oscript' -version\"", "-c", "'oscript' -version")] + [InlineData("'aaa\"", "aaa\"")] + [InlineData(" ")] + public void Should_Parse_Arguments(string input, params string[] expected) + { + var parser = new ArgumentsParser(input); + + var actual = parser.GetArguments(); + + actual.Should().Equal(expected); + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/CodeGenerationTests.cs b/src/Tests/OneScript.Core.Tests/CodeGenerationTests.cs new file mode 100644 index 000000000..c95a796d4 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/CodeGenerationTests.cs @@ -0,0 +1,108 @@ +/*---------------------------------------------------------- +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 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 OneScript.Values; +using ScriptEngine.Compiler; +using ScriptEngine.Machine; +using System.Linq; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class CodeGenerationTests + { + [Fact] + public void EmptyImageIsReturnedWithNoCode() + { + var code = ""; + var image = BuildModule(code, Mock.Of()); + image.Should().NotBeNull(); + image.Code.Should().BeEmpty(); + image.Constants.Should().BeEmpty(); + image.Methods.Should().BeEmpty(); + image.Fields.Should().BeEmpty(); + } + + [Fact] + public void Methods_Are_Registered_In_Image() + { + var code = "Procedure Foo() EndProcedure\n" + + "Function Bar() EndFunction"; + + var image = BuildModule(code, Mock.Of()); + image.Methods.Should().HaveCount(2); + } + + [Fact] + public void Variables_Are_Registered_In_Image() + { + var code = "Var A;\n" + + "Var B;"; + + var image = BuildModule(code, Mock.Of()); + image.Fields.Should().HaveCount(2); + } + + [Fact] + public void AnnotationsAsValuesInCode() { + var code = @" + &Аннотация(Параметр = &ТожеАннотация(&СТожеПараметромАннотацией, П2 = &СТожеПараметромАннотацией)) + Процедура Процедура1() Экспорт + КонецПроцедуры"; + var image = BuildModule(code, Mock.Of()); + image.Should().NotBeNull(); + image.Constants.Should().HaveCount(0); + image.Methods.Should().HaveCount(1); + image.Fields.Should().BeEmpty(); + + var method = image.Methods[0]; + method.GetAnnotations().Should().HaveCount(1); + + var annotation = method.GetAnnotations()[0]; + annotation.Parameters.Should().HaveCount(1); + + var annotationParameter = annotation.Parameters.First(); + annotationParameter.Value.Should().NotBeNull(); + annotationParameter.Value.Should().BeOfType(); + + var parameterValue = (BslAnnotationValue)annotationParameter.Value; + parameterValue.Parameters.Should().HaveCount(2); + parameterValue.Parameters.ElementAt(0).Value.Should().BeOfType(); + parameterValue.Parameters.ElementAt(1).Name.Should().Be("П2"); + parameterValue.Parameters.ElementAt(1).Value.Should().BeOfType(); + } + + + private static StackRuntimeModule BuildModule(string code, IBslProcess process) + { + 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, process); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/ConstructionTests.cs b/src/Tests/OneScript.Core.Tests/ConstructionTests.cs new file mode 100644 index 000000000..2f6563e99 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/ConstructionTests.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +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 FluentAssertions; +using OneScript.Types; +using ScriptEngine.Machine; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class ConstructionTests + { + [Fact] + public void CanCreate_With_ContextInjection() + { + var f = new TypeFactory(typeof(TestContextClass)); + var ctx = new TypeActivationContext + { + TypeName = "SomeType" + }; + + var args = new IValue[2] + { + default, + default + }; + + var instance = (TestContextClass)f.Activate(ctx, args); + instance.CreatedViaMethod.Should().Be("Constructor2-SomeType"); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/DynamicObjectTest.cs b/src/Tests/OneScript.Core.Tests/DynamicObjectTest.cs new file mode 100644 index 000000000..3f2390ad3 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/DynamicObjectTest.cs @@ -0,0 +1,72 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.StandardLibrary.Collections; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class DynamicObjectTest + { + [Fact] + public void CanCallMethodsOfStruct() + { + var structImpl = new StructureImpl(); + dynamic dyn = structImpl; + + dyn.Insert("Свойство", 1); + dyn.Вставить("Свойство2", "Привет"); + + Assert.Equal(2, structImpl.Count()); + Assert.True(structImpl.HasProperty("Свойство")); + Assert.True(structImpl.HasProperty("Свойство2")); + } + + [Fact] + public void CanAccessPropertiesOfStruct() + { + var structImpl = new StructureImpl(); + dynamic dyn = structImpl; + + dyn.Вставить("Свойство", 1); + dyn.Вставить("Свойство2", "Привет"); + + Assert.Equal(1, dyn.Свойство); + Assert.Equal("Привет",dyn.Свойство2); + } + + [Fact] + public void CanAccessIndexOfStruct() + { + var structImpl = new StructureImpl(); + dynamic dyn = structImpl; + + dyn.Вставить("Свойство", 1); + dyn.Вставить("Свойство2", "Привет"); + + Assert.Equal(1, dyn["Свойство"]); + Assert.Equal("Привет",dyn["Свойство2"]); + } + + [Fact] + public void CanAccessArraysByIndex() + { + var arr = new ArrayImpl(); + dynamic Массив = new ArrayImpl(); + + Массив.Добавить(1); + Массив.Добавить(2); + Массив.Добавить("Привет"); + + Assert.Equal(3, Массив.Количество()); + Assert.Equal(1, Массив[0]); + Assert.Equal(2, Массив[1]); + Assert.Equal("Привет", Массив[2]); + } + + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/ExplicitImportsTest.cs b/src/Tests/OneScript.Core.Tests/ExplicitImportsTest.cs new file mode 100644 index 000000000..586c4e0a4 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/ExplicitImportsTest.cs @@ -0,0 +1,340 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using FluentAssertions; +using Moq; +using OneScript.Compilation; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Sources; +using ScriptEngine; +using ScriptEngine.Hosting; +using ScriptEngine.Machine.Debugger; +using Xunit; + +namespace OneScript.Core.Tests +{ + [Collection("SystemLogger")] + public class ExplicitImportsTest : IDisposable + { + private readonly List _messages; + private readonly ISystemLogWriter _logWriter; + + // Названия для тестовых скриптов + private const string LibraryModuleName = "СкриптБиблиотеки"; + private const string LibraryPackageId = "testlib"; + private const string LibraryShortName = "TestLib"; + + public ExplicitImportsTest() + { + var mock = new Mock(); + mock.Setup(x => x.Write(It.IsAny())) + .Callback(str => _messages.Add(str)); + + _messages = new List(); + _logWriter = mock.Object; + SystemLogger.SetWriter(_logWriter); + } + + public void Dispose() + { + // Очищаем логгер после каждого теста + SystemLogger.Reset(); + } + + [Fact] + public void ExplicitImports_Disabled_NoWarnings() + { + var code = CompileClientScript(ExplicitImportsBehavior.Disabled, out var shouldCompile); + + shouldCompile.Should().BeTrue(); + _messages.Should().BeEmpty("в режиме Disabled не должно быть предупреждений"); + } + + [Fact] + public void ExplicitImports_Enabled_CompilationError() + { + Action act = () => CompileClientScript(ExplicitImportsBehavior.Enabled, out _); + + act.Should().Throw() + .WithMessage("*" + LibraryModuleName + " принадлежит пакету " + LibraryShortName + "*", "должна быть ошибка о неявном импорте"); + + _messages.Should().BeEmpty("в режиме Enabled выбрасывается исключение, а не пишется в лог"); + } + + [Fact] + public void ExplicitImports_Warn_HasWarning() + { + var code = CompileClientScript(ExplicitImportsBehavior.Warn, out var shouldCompile); + + shouldCompile.Should().BeTrue(); + _messages.Should().HaveCount(1, "должно быть одно предупреждение") + .And.Contain(x => + x.Contains(LibraryModuleName, StringComparison.InvariantCultureIgnoreCase) && + x.Contains(LibraryShortName, StringComparison.InvariantCultureIgnoreCase), + "предупреждение должно содержать имя библиотеки и символа"); + } + + [Fact] + public void ExplicitImports_Development_WithDebugger_HasWarning() + { + var debugSessionMock = new Mock(); + debugSessionMock.Setup(x => x.IsActive).Returns(false); + + var debuggerMock = new Mock(); + debuggerMock.Setup(x => x.IsEnabled).Returns(true); + debuggerMock.Setup(x => x.GetSession()).Returns(debugSessionMock.Object); + + var code = CompileClientScript( + ExplicitImportsBehavior.Development, + out var shouldCompile, + debuggerMock.Object); + + shouldCompile.Should().BeTrue(); + _messages.Should().HaveCount(1, "должно быть одно предупреждение") + .And.Contain(x => + x.Contains(LibraryModuleName, StringComparison.InvariantCultureIgnoreCase) && + x.Contains(LibraryShortName, StringComparison.InvariantCultureIgnoreCase), + "в режиме Development с включенным отладчиком должно быть предупреждение"); + } + + [Fact] + public void ExplicitImports_Development_WithoutDebugger_NoWarnings() + { + var debuggerMock = new Mock(); + debuggerMock.Setup(x => x.IsEnabled).Returns(false); + + var code = CompileClientScript( + ExplicitImportsBehavior.Development, + out var shouldCompile, + debuggerMock.Object); + + shouldCompile.Should().BeTrue(); + _messages.Should().BeEmpty("в режиме Development без отладчика не должно быть предупреждений"); + } + + [Fact] + public void ExplicitImports_Development_NoDebuggerRegistered_NoWarnings() + { + var code = CompileClientScript( + ExplicitImportsBehavior.Development, + out var shouldCompile); + + shouldCompile.Should().BeTrue(); + _messages.Should().BeEmpty("в режиме Development без зарегистрированного отладчика не должно быть предупреждений"); + } + + [Fact] + public void ExplicitImports_WithUseDirective_NoWarnings() + { + // Скрипт с явным импортом через директиву #Использовать + var codeWithImport = $"#Использовать {LibraryShortName}\n" + + $"А = {LibraryModuleName}.Метод();"; + + var instance = CompileUserScript( + codeWithImport, + ExplicitImportsBehavior.Enabled, + out var shouldCompile); + + shouldCompile.Should().BeTrue(); + _messages.Should().BeEmpty("при явном импорте через #Использовать не должно быть предупреждений"); + } + + [Fact] + public void ExplicitImports_SamePackage_NoWarnings() + { + // Библиотека использует себя - не должно быть предупреждений + var libraryCode = $"Функция Метод() Экспорт\n" + + $" А = {LibraryModuleName};\n" + + $" Возврат А;\n" + + $"КонецФункции"; + + var instance = CompileLibraryScript( + libraryCode, + ExplicitImportsBehavior.Enabled, + out var shouldCompile); + + shouldCompile.Should().BeTrue(); + _messages.Should().BeEmpty("модули из той же библиотеки не требуют явного импорта"); + } + + private IRuntimeContextInstance CompileClientScript( + ExplicitImportsBehavior behavior, + out bool success, + IDebugger debugger = null) + { + // Код клиентского скрипта, который использует библиотеку неявно + var clientCode = $"А = {LibraryModuleName}.Метод();"; + + var instance = CompileUserScript(clientCode, behavior, out success, debugger); + return instance; + } + + private IRuntimeContextInstance CompileLibraryScript( + string code, + ExplicitImportsBehavior behavior, + out bool success, + IDebugger debugger = null) + { + var instance = CompileUserScript( + code, + behavior, + out success, + debugger, + ownerPackageId: LibraryPackageId); + + return instance; + } + + private IRuntimeContextInstance CompileUserScript( + string code, + ExplicitImportsBehavior behavior, + out bool success, + IDebugger debugger = null, + string ownerPackageId = null) + { + success = true; + + try + { + var configValue = behavior switch + { + ExplicitImportsBehavior.Enabled => "on", + ExplicitImportsBehavior.Disabled => "off", + ExplicitImportsBehavior.Warn => "warn", + ExplicitImportsBehavior.Development => "dev", + _ => throw new ArgumentException(nameof(behavior)) + }; + + var builder = DefaultEngineBuilder + .Create() + .SetupConfiguration(p => + { + p.Add(new DictionaryConfigProvider(new Dictionary + { + { "lang.explicitImports", configValue } + })); + }) + .SetDefaultOptions(); + + // Регистрируем отладчик если передан + if (debugger != null) + { + builder.WithDebugger(debugger); + } + + // Регистрируем мок резолвера зависимостей + builder.SetupServices(services => + { + services.UseImports(_ => new TestDependencyResolver()); + }); + + var engine = builder.Build(); + + // Регистрируем библиотеку как глобальное свойство + RegisterLibrary(engine); + + var compiler = engine.GetCompilerService(); + var source = ownerPackageId != null + ? SourceCodeBuilder.Create().FromString(code).WithOwnerPackage(ownerPackageId).Build() + : engine.Loader.FromString(code); + + engine.Initialize(); + + var module = engine.AttachedScriptsFactory.CompileModuleFromSource( + compiler, + source, + null, + engine.NewProcess()); + + var instance = engine.NewObject(module, engine.NewProcess(), null); + return instance; + } + catch (CompilerException) + { + success = false; + throw; + } + } + + private void RegisterLibrary(ScriptingEngine engine) + { + // Создаем простой модуль библиотеки + var libraryCode = "Функция Метод() Экспорт\n" + + " Возврат 42;\n" + + "КонецФункции"; + + var compiler = engine.GetCompilerService(); + var source = SourceCodeBuilder.Create() + .FromString(libraryCode) + .WithOwnerPackage(LibraryPackageId) + .Build(); + engine.Initialize(); + + var libraryModule = engine.AttachedScriptsFactory.CompileModuleFromSource( + compiler, + source, + null, + engine.NewProcess()); + + var libraryInstance = engine.CreateUninitializedSDO(libraryModule); + + // Регистрируем библиотеку как глобальное свойство с PackageInfo + var packageInfo = new PackageInfo(LibraryPackageId, LibraryShortName); + engine.Environment.InjectGlobalProperty(libraryInstance, LibraryModuleName, packageInfo); + + // Инициализируем библиотеку + engine.InitializeSDO(libraryInstance, engine.NewProcess()); + } + + /// + /// Мок резолвера зависимостей для поддержки директивы #Использовать + /// + private class TestDependencyResolver : IDependencyResolver + { + public void Initialize(ScriptingEngine engine) + { + // Ничего не делаем + } + + public PackageInfo Resolve(SourceCode module, string libraryName, IBslProcess process) + { + // Возвращаем PackageInfo для нашей тестовой библиотеки + if (libraryName == LibraryShortName) + { + return new PackageInfo(LibraryPackageId, LibraryShortName); + } + + return null; + } + } + + private class DictionaryConfigProvider : IConfigProvider + { + private readonly Dictionary _config; + public string SourceId => "dictionary"; + + public DictionaryConfigProvider(Dictionary config) + { + _config = config; + } + + public IReadOnlyDictionary Load() + { + return _config; + } + + public string ResolveRelativePath(string path) + { + return path; + } + } + } +} + diff --git a/src/Tests/OneScript.Core.Tests/HelperClasses/DeprecatedEnum.cs b/src/Tests/OneScript.Core.Tests/HelperClasses/DeprecatedEnum.cs new file mode 100644 index 000000000..56eb3a7dd --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/HelperClasses/DeprecatedEnum.cs @@ -0,0 +1,23 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.Contexts.Enums; + +namespace OneScript.Core.Tests +{ + [DeprecatedName("СтароеИмя")] + [EnumerationType("НовоеПеречисление")] + public enum DeprecatedEnum + { + [EnumValue("Значение1")] + Value1, + [DeprecatedName("СтароеЗначение2")] + [EnumValue("Значение2")] + Value2 + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/HelperClasses/EngineWrapper.cs b/src/Tests/OneScript.Core.Tests/HelperClasses/EngineWrapper.cs new file mode 100644 index 000000000..eafbb2277 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/HelperClasses/EngineWrapper.cs @@ -0,0 +1,88 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +// +// using System; +// using System.Collections.Generic; +// using System.Linq; +// using System.Text; +// using OneScript.StandardLibrary; +// using ScriptEngine; +// using ScriptEngine.Environment; +// +// namespace NUnitTests +// { +// internal class EngineWrapper : IHostApplication +// { +// private string[] commandLineArgs; +// +// public HostedScriptEngine Engine { get; private set; } +// +// public HostedScriptEngine StartEngine() +// { +// Engine = new HostedScriptEngine(); +// Engine.Initialize(); +// +// commandLineArgs = new string[] +// { +// }; +// +// return Engine; +// } +// +// public int RunTestString(string source) +// { +// var process = Engine.CreateProcess(this, Engine.Loader.FromString(source)); +// return process.Start(); +// } +// +// private int RunTestScript(ICodeSource source, string resourceName) +// { +// var module = Engine.GetCompilerService().Compile(source); +// +// Engine.LoadUserScript(new UserAddedScript +// { +// Type = UserAddedScriptType.Class, +// Image = module, +// Symbol = resourceName +// }); +// +// var process = Engine.CreateProcess(this, source); +// return process.Start(); +// } +// +// internal int RunTestScriptFromPath(string scriptFilePath, string argsScript = "") +// { +// if (argsScript != "") +// commandLineArgs = argsScript.Split(' '); +// +// var sourceToCompile = Engine.Loader.FromFile(scriptFilePath); +// +// return RunTestScript(sourceToCompile, scriptFilePath); +// } +// +// public string[] GetCommandLineArguments() +// { +// return commandLineArgs; +// } +// +// public bool InputString(out string result, int maxLen) +// { +// result = ""; +// return false; +// } +// +// public void ShowExceptionInfo(Exception exc) +// { +// throw exc; +// } +// +// public void Echo(string str, MessageStatusEnum status = MessageStatusEnum.Ordinary) +// { +// Console.WriteLine(str); +// } +// } +// } \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/HelperClasses/NullConversionTestContext.cs b/src/Tests/OneScript.Core.Tests/HelperClasses/NullConversionTestContext.cs new file mode 100644 index 000000000..b5c2b04e8 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/HelperClasses/NullConversionTestContext.cs @@ -0,0 +1,250 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Contexts; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using Xunit.Sdk; + +namespace OneScript.Core.Tests +{ + [ContextClass("ТестNullПреобразования", "TestNullConversion")] + class NullConversionTestContext : AutoContext + { + IValue _pIValue; + string _pString; + TestNullClass _pClass; + + + [ScriptConstructor(Name = "Без параметров")] + public static NullConversionTestContext Constructor() + { + return new NullConversionTestContext(); + } + + [ContextMethod("ТестIValue", "IValueTest")] + public IValue TestIValue(IValue arg) + { + return arg; + } + + [ContextMethod("ТестIValueНеопределено", "IValueNullTest")] + public IValue TestIValueNull(IValue arg) + { + if (arg != ValueFactory.Create()) + throw new XunitException("Test IValue Func(IValue) -> Func(Unknown): argument value is different from null."); + + return arg; + } + + [ContextMethod("ТестКласс", "ClassTest")] + public TestNullClass TestClass(TestNullClass arg) + { + if (arg.GetType() != typeof(TestNullClass)) + throw new XunitException("Test Class Func(Class) -> Func(Class): argument type is different from Class."); + + return arg; + } + + [ContextMethod("ТестClassНеопределено", "ClassNullTest")] + public TestNullClass TestClassNull(TestNullClass arg) + { + if (arg != null) + throw new XunitException("Test Class Func(Class) -> Func(Unknown): argument value is different from null."); + + return arg; + } + + + + [ContextMethod("ТестString", "StringTest")] + public string TestString(string arg) + { + if (arg.GetType() != typeof(System.String)) + throw new XunitException("Test string Func(string) -> Func(string): argument type is different from string."); + + return arg; + } + + [ContextMethod("ТестNullString", "StringNullTest")] + public string TestStringNull(string arg) + { + if (arg != null) + throw new XunitException("Test string Func(string) -> Func(Unknown): argument value is different from null."); + + return arg; + } + + [ContextMethod("ТестInt", "IntTest")] + public int TestInt(int arg) + { + return arg; + } + + [ContextMethod("ТестUInt", "UIntTest")] + public uint TestUInt(uint arg) + { + return arg; + } + + [ContextMethod("ТестLong", "LongTest")] + public long TestLong(long arg) + { + return arg; + } + + [ContextMethod("ТестULong", "ULongTest")] + public ulong TestULong(ulong arg) + { + return arg; + } + + [ContextMethod("ТестDouble", "DoubleTest")] + public double TestDouble(double arg) + { + return arg; + } + + [ContextMethod("ТестDateTime", "DateTimeTest")] + public DateTime TestDateTime(DateTime arg) + { + return arg; + } + + [ContextMethod("ТестBool", "BoolTest")] + public bool TestBool(bool arg) + { + return arg; + } + + + [ContextProperty("ПInt", "PInt")] + public int PInt + { + get;set; + } + + [ContextProperty("ПUint", "PUint")] + public uint PUint + { + get;set; + } + + [ContextProperty("ПLong", "PLong")] + public long PLong + { + get;set; + } + + [ContextProperty("ПUlong", "PUlong")] + public ulong PUlong + { + get;set; + } + + [ContextProperty("ПDouble", "PDouble")] + public double PDouble + { + get;set; + } + + [ContextProperty("ПDateTime", "PDateTime")] + public DateTime PDateTime + { + get;set; + } + + [ContextProperty("ПBool", "PBool")] + public bool PBool + { + get;set; + } + + [ContextProperty("ПString", "PString")] + public string PString + { + get + { + return _pString; + } + set + { + if (value.GetType() != typeof(System.String)) + throw new XunitException("Test string Property = string: value type is different from string."); + + _pString = value; + } + } + + [ContextProperty("ПNullString", "PNullString")] + public string PNullString + { + get => _pString; + set => _pString = value; + } + + [ContextProperty("ПIValue", "PIValue")] + public IValue PIValue + { + get => _pIValue; + set => _pIValue = value; + } + + [ContextProperty("ПNullIValue", "PNullIValue")] + public IValue PNullIValue + { + get => _pIValue; + set => _pIValue = value; + } + + + + [ContextProperty("ПClass", "PClass")] + public TestNullClass PClass + { + get => _pClass; + set + { + if (value.GetType() != typeof(TestNullClass)) + throw new XunitException("Test TestNullClass Property = TestNullClass: value type is different from TestNullClass."); + + _pClass = value; + } + } + + [ContextProperty("ПNullClass", "PNullClass")] + public TestNullClass PNullClass + { + get => _pClass; + set + { + if (value != null) + throw new XunitException("Test TestNullClass Property = Unknown: value value is different from null."); + _pIValue = value; + } + } + + } + + [ContextClass("ТестNullКласс", "TestNullClass")] + public class TestNullClass : AutoContext + { + public TestNullClass() + { + + } + + [ScriptConstructor(Name = "Без параметров")] + public static TestNullClass Constructor() + { + return new TestNullClass(); + } + + } + +} diff --git a/src/Tests/OneScript.Core.Tests/HelperClasses/TestContextClass.cs b/src/Tests/OneScript.Core.Tests/HelperClasses/TestContextClass.cs new file mode 100644 index 000000000..0ce5e9f00 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/HelperClasses/TestContextClass.cs @@ -0,0 +1,81 @@ +/*---------------------------------------------------------- +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 OneScript.Contexts; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Types; + +namespace OneScript.Core.Tests +{ + [ContextClass("ТестовыйКласс", "TestClass")] + public class TestContextClass : AutoContext + { + private IDictionary _indexedValues = new Dictionary(); + + public TestContextClass() + { + DefineType(GetType().GetTypeFromClassMarkup()); + } + + public string CreatedViaMethod { get; private set; } + + [ContextMethod("УстаревшийМетод", "ObsoleteMethod", IsDeprecated = true, ThrowOnUse = false)] + public void ObsoleteMethod() + { + // Do nothing + } + + [DeprecatedName("ObsoleteAlias", throwOnUse: false)] + [DeprecatedName("VeryObsoleteAlias", throwOnUse: true)] + [ContextMethod("ХорошийМетод", "GoodMethod")] + public void GoodMethod() + { + // Do nothing + } + + [DeprecatedName("OldBslProp")] + [ContextProperty("СвойствоBsl","BslProp")] + public string BslProp { get; set; } + + [ContextProperty("УстаревшееСвойство","ObsoleteProperty", IsDeprecated = true)] + public string ObsoleteProp { get; set; } + + public override bool IsIndexed => true; + + public override IValue GetIndexedValue(IValue index) + { + return _indexedValues[(BslValue)index]; + } + + public override void SetIndexedValue(IValue index, IValue val) + { + _indexedValues[(BslValue)index] = (BslValue)val; + } + + [ScriptConstructor] + public static TestContextClass Constructor() + { + return new TestContextClass + { + CreatedViaMethod = "Constructor0" + }; + } + + [ScriptConstructor] + public static TestContextClass Constructor(TypeActivationContext context, IValue ctorParam1, IValue ctorParam2) + { + return new TestContextClass + { + CreatedViaMethod = $"Constructor2-{context.TypeName}" + }; + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/IocResolutionTests.cs b/src/Tests/OneScript.Core.Tests/IocResolutionTests.cs new file mode 100644 index 000000000..a8c59ecc2 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/IocResolutionTests.cs @@ -0,0 +1,42 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using FluentAssertions; +using OneScript.DependencyInjection; +using ScriptEngine; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class IocResolutionTests + { + private class TestService{} + + [Fact] + public void Scoped_Gets_Same_Instance_As_Global() + { + var privateType = typeof(ScriptingEngine).Assembly.GetTypes() + .First(t => t.Name == "TinyIocImplementation"); + + privateType.Should().NotBeNull(); + + var s = (IServiceDefinitions)Activator.CreateInstance(privateType); + + s.RegisterSingleton(); + + var parent = s.CreateContainer(); + var child = parent.CreateScope(); + + var parentInstance = parent.Resolve(); + var childInstance = child.Resolve(); + + parentInstance.Should().BeSameAs(childInstance); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs new file mode 100644 index 000000000..11ade2183 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/NativeCompilerTest.cs @@ -0,0 +1,1113 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using Moq; +using OneScript.Compilation.Binding; +using OneScript.DependencyInjection; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Collections.ValueList; +using OneScript.StandardLibrary.Collections.ValueTable; +using OneScript.StandardLibrary.Json; +using OneScript.StandardLibrary.Native; +using OneScript.StandardLibrary.Text; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using ScriptEngine.Types; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class NativeCompilerTest + { + private CompiledBlock GetCompiler(Action setup) + { + var tm = new DefaultTypeManager(); + var services = new TinyIocImplementation(); + services.Register(tm); + setup(tm, services); + + return new CompiledBlock(services.CreateContainer()); + } + + private CompiledBlock GetCompiler(ITypeManager tm) + { + var services = new TinyIocImplementation(); + services.Register(tm); + services.Register(); + var factoryMock = new Mock(); + factoryMock.Setup(f => f.NewProcess()).Returns(ForbiddenBslProcess.Instance); + services.Register(factoryMock.Object); + + return new CompiledBlock(services.CreateContainer()); + } + + [Fact] + public void CanInjectContext_As_Symbols() + { + var context = new GlobalJsonFunctions(); + var scope = SymbolScope.FromObject(context); + + scope.Methods.Should().HaveCount(3); + scope.Methods.IndexOf("ЗаписатьJSON").Should().BeGreaterOrEqualTo(0); + } + + [Fact] + public void Can_Compile_Assignment() + { + var blockOfCode = new CompiledBlock(default); + + blockOfCode.Parameters.Insert("MyVar", new BslTypeValue(BasicTypes.Number)); + blockOfCode.CodeBlock = "MyVar = 2"; + + var expr = blockOfCode.MakeExpression(); + expr.Body.As().Expressions.Should().HaveCount(2); // в конце всегда неявный return + expr.Body.As().Expressions[0].Should().BeAssignableTo(); + + expr.Parameters.Should().HaveCount(2); + } + + [Fact] + public void Can_Compile_Binary_Expressions() + { + var blockOfCode = new CompiledBlock(default); + + blockOfCode.Parameters.Insert("MyVar", new BslTypeValue(BasicTypes.Number)); + blockOfCode.CodeBlock = "MyVar = MyVar + 1"; + + var expr = blockOfCode.MakeExpression(); + + var body = expr.Body.As().Expressions; + + body[0].As().NodeType.Should().Be(ExpressionType.Assign); + body[0].As().Right.Should().BeAssignableTo(); + } + + [Fact] + public void Can_Compile_String_Additons() + { + var blockOfCode = new CompiledBlock(default); + + blockOfCode.CodeBlock = @"MyVar = ""А"" + ""Б"""; + + var expr = blockOfCode.MakeExpression(); + + var body = expr.Body.As().Expressions; + + body[0].As().NodeType.Should().Be(ExpressionType.Assign); + body[0].As().Right.Should().BeAssignableTo(); + } + + [Fact] + public void Can_Compile_String_Additons_With_No_String_at_Right() + { + var blockOfCode = new CompiledBlock(default); + + blockOfCode.CodeBlock = @"MyVar = ""А"" + 14"; + + var expr = blockOfCode.MakeExpression(); + + var body = expr.Body.As().Expressions; + + body[0].As().NodeType.Should().Be(ExpressionType.Assign); + body[0].As().Right.Should().BeAssignableTo(); + } + + [Theory] + [MemberData(nameof(ArgsForUnaryNumericOperators))] + public void Can_Compile_Numeric_Unary_Expressions(TypeDescriptor argType, Type expectedNodeType) + { + var blockOfCode = new CompiledBlock(default); + + blockOfCode.Parameters.Insert("MyVar", new BslTypeValue(argType)); + blockOfCode.CodeBlock = "MyVar = -MyVar; MyVar = +MyVar"; + + var expr = blockOfCode.MakeExpression(); + + var body = expr.Body.As().Expressions; + + body[0].As().NodeType.Should().Be(ExpressionType.Assign); + body[0].As().Right.Should().BeAssignableTo(expectedNodeType); + + body[1].As().NodeType.Should().Be(ExpressionType.Assign); + body[1].As().Right.Should().BeAssignableTo(expectedNodeType); + } + + [Theory] + [MemberData(nameof(ArgsForUnaryBooleanOperators))] + public void Can_Compile_Boolean_Unary_Expressions(TypeDescriptor argType, Type expectedNodeType) + { + var blockOfCode = new CompiledBlock(default); + + blockOfCode.Parameters.Insert("MyVar", new BslTypeValue(argType)); + blockOfCode.CodeBlock = "MyVar = Not MyVar"; + + var expr = blockOfCode.MakeExpression(); + + var body = expr.Body.As().Expressions; + + body[0].As().NodeType.Should().Be(ExpressionType.Assign); + body[0].As().Right.Should().BeAssignableTo(expectedNodeType); + } + + [Fact] + public void Can_Compile_Empty_Body() + { + var blockOfCode = new CompiledBlock(default); + var func = blockOfCode.CreateDelegate(); + var result = func(ForbiddenBslProcess.Instance, default); + Assert.Equal(BslUndefinedValue.Instance, result); + } + + [Fact] + public void Can_Compile_AcceptParameters_In_Array() + { + var blockOfCode = new CompiledBlock(default); + blockOfCode.Parameters.Insert("A", new BslTypeValue(BasicTypes.Number)); + blockOfCode.Parameters.Insert("Б", new BslTypeValue(BasicTypes.String)); + + var func = blockOfCode.CreateDelegate(); + var result = func(ForbiddenBslProcess.Instance, new BslValue[]{ BslNumericValue.Create(1), BslStringValue.Create("hello") }); + + Assert.Equal(BslUndefinedValue.Instance, result); + } + + [Fact] + public void Can_Compile_AcceptParameters() + { + var blockOfCode = new CompiledBlock(default); + blockOfCode.Parameters.Insert("A", new BslTypeValue(BasicTypes.Number)); + blockOfCode.Parameters.Insert("Б", new BslTypeValue(BasicTypes.String)); + + var func = blockOfCode.CreateDelegate>(); + var result = func(1, "привет"); + + Assert.Equal(BslUndefinedValue.Instance, result); + } + + [Fact] + public void Number_To_Number_Operations_Are_Available() + { + var block = new CompiledBlock(default); + block.CodeBlock = "А = 1+1*2/3%4"; + var assignment = block.MakeExpression() + .Body + .As() + .Expressions + .First(); + + assignment.NodeType.Should().Be(ExpressionType.Assign); + } + + [Fact] + public void Number_To_IValue_Operations_Are_Available() + { + var block = new CompiledBlock(default); + block.CodeBlock = "А = 1+(А*Б/3)%В"; + + block.Parameters.Insert("А", new BslTypeValue(BasicTypes.Number)); + block.Parameters.Insert("Б", new BslTypeValue(BasicTypes.Number)); + block.Parameters.Insert("В", new BslTypeValue(BasicTypes.Number)); + + var assignment = block.MakeExpression() + .Body + .As() + .Expressions + .First(); + + assignment.NodeType.Should().Be(ExpressionType.Assign); + + } + + [Fact] + public void Date_Seconds_Addition_Is_Availiable() + { + var block = new CompiledBlock(default); + block.CodeBlock = "А = '19840331'+(86400 * 37)"; + + var assignment = block.MakeExpression() + .Body + .As() + .Expressions + .First(); + + assignment.NodeType.Should().Be(ExpressionType.Assign); + var expr = assignment.As(); + + expr.Left.Type.Should().Be(); + expr.Right.Type.Should().Be(); + } + + [Fact] + public void Date_Seconds_Substraction_Is_Availiable() + { + var block = new CompiledBlock(default); + block.CodeBlock = "А = '19840331'-(86400 * 37)"; + + var assignment = block.MakeExpression() + .Body + .As() + .Expressions + .First(); + + assignment.NodeType.Should().Be(ExpressionType.Assign); + var expr = assignment.As(); + + expr.Left.Type.Should().Be(); + expr.Right.Type.Should().Be(); + } + + [Fact] + public void DateDiff_Available() + { + var block = new CompiledBlock(default); + block.CodeBlock = "F = (Сегодня - '19840331') / 86400 / 366"; + block.Parameters.Insert("Сегодня", new BslTypeValue(BasicTypes.Date)); + var assignment = block.MakeExpression() + .Body + .As() + .Expressions + .First(); + + assignment.NodeType.Should().Be(ExpressionType.Assign); + var expr = assignment.As(); + + expr.Left.Type.Should().Be(); + expr.Right.Type.Should().Be(); + } + + [Theory] + [MemberData(nameof(ArgsForComparisonOperators))] + public void StaticComparison_Available(TypeDescriptor argType, string literal) + { + var block = new CompiledBlock(default); + block.CodeBlock = + $"F = (Сегодня < {literal});\n" + + $"F = (Сегодня <= {literal});\n" + + $"F = (Сегодня > {literal});\n" + + $"F = (Сегодня >= {literal});\n"; + + block.Parameters.Insert("Сегодня", new BslTypeValue(argType)); + var statements = block.MakeExpression() + .Body + .As() + .Expressions; + + ExpressionType[] expectedOps = + { + ExpressionType.LessThan, + ExpressionType.LessThanOrEqual, + ExpressionType.GreaterThan, + ExpressionType.GreaterThanOrEqual + }; + int i = 0; + + foreach (var assignment in statements.Take(4)) + { + assignment.NodeType.Should().Be(ExpressionType.Assign); + var expr = assignment.As(); + expr.Left.Type.Should().Be(); + expr.Right.Type.Should().Be(); + expr.Right.NodeType.Should().Be(expectedOps[i++]); + } + } + + public static IEnumerable ArgsForComparisonOperators() + { + yield return new object[] { BasicTypes.Number, "14" }; + yield return new object[] { BasicTypes.Date, "'20010205'" }; + yield return new object[] { BasicTypes.String, "\"20010205\"" }; + } + + public static IEnumerable ArgsForUnaryNumericOperators() + { + yield return new object[] { BasicTypes.Number, typeof(UnaryExpression) }; + yield return new object[] { VariantType(), typeof(MethodCallExpression) }; + } + + public static IEnumerable ArgsForUnaryBooleanOperators() + { + yield return new object[] { BasicTypes.Boolean, typeof(UnaryExpression) }; + yield return new object[] { VariantType(), typeof(MethodCallExpression) }; + } + + private static TypeDescriptor VariantType() => new("TestValueType", "BslValue", null, typeof(BslValue)); + + [Theory] + [MemberData(nameof(TypesForTestEqualityOperators))] + public void EqualityOperators_Available(TypeDescriptor type) + { + var block = new CompiledBlock(default); + block.CodeBlock = + @"Равенство = (Значение1 = Значение2); + Неравенство = (Значение1 <> Значение2)"; + + block.Parameters.Insert("Значение1", new BslTypeValue(type)); + block.Parameters.Insert("Значение2", new BslTypeValue(type)); + var statements = block.MakeExpression() + .Body + .As() + .Expressions; + + statements[0].NodeType.Should().Be(ExpressionType.Assign); + statements[1].NodeType.Should().Be(ExpressionType.Assign); + } + + [Theory] + [MemberData(nameof(TypesForTestEqualityOperators))] + public void EqualityOperators_With_BslValue_On_Left(TypeDescriptor type) + { + var block = new CompiledBlock(default); + block.CodeBlock = + @"Равенство = (Значение1 = Значение2); + Неравенство = (Значение1 <> Значение2)"; + + var bslValueType = VariantType(); + + block.Parameters.Insert("Значение1", new BslTypeValue(bslValueType)); + block.Parameters.Insert("Значение2", new BslTypeValue(type)); + var statements = block.MakeExpression() + .Body + .As() + .Expressions; + + statements[0].NodeType.Should().Be(ExpressionType.Assign); + statements[1].NodeType.Should().Be(ExpressionType.Assign); + } + + [Theory] + [MemberData(nameof(TypesForTestEqualityOperators))] + public void EqualityOperators_With_BslValue_On_Right(TypeDescriptor type) + { + var block = new CompiledBlock(default); + block.CodeBlock = + @"Равенство = (Значение1 = Значение2); + Неравенство = (Значение1 <> Значение2)"; + + var bslValueType = VariantType(); + + block.Parameters.Insert("Значение1", new BslTypeValue(type)); + block.Parameters.Insert("Значение2", new BslTypeValue(bslValueType)); + var statements = block.MakeExpression() + .Body + .As() + .Expressions; + + statements[0].NodeType.Should().Be(ExpressionType.Assign); + statements[1].NodeType.Should().Be(ExpressionType.Assign); + } + + [Fact] + public void ClrIntegers_Can_Be_Used_As_Bsl_Numbers() + { + var block = new CompiledBlock(default); + block.CodeBlock = + @"Равенство = (Найти(""123"",""2"") = 0); + Неравенство = (КодСимвола(""А"") <> 0)"; + + var statements = block.MakeExpression() + .Body + .As() + .Expressions; + + statements[0].NodeType.Should().Be(ExpressionType.Assign); + statements[1].NodeType.Should().Be(ExpressionType.Assign); + } + + public static IEnumerable TypesForTestEqualityOperators() + { + yield return new object[] { BasicTypes.Undefined }; + yield return new object[] { BasicTypes.String }; + yield return new object[] { BasicTypes.Boolean }; + yield return new object[] { BasicTypes.Date }; + yield return new object[] { BasicTypes.Number }; + yield return new object[] { BasicTypes.Null }; + yield return new object[] { BasicTypes.Type }; + } + + [Fact] + public void Parameter_Passing_And_Return_Is_Available() + { + var block = new CompiledBlock(default); + block.CodeBlock = "Возврат (Сегодня - '19840331') / 86400 / 366"; + block.Parameters.Insert("Сегодня", new BslTypeValue(BasicTypes.Date)); + + var beaverAge = block.CreateDelegate>(); + var age = (decimal)(BslNumericValue)beaverAge(DateTime.Now); + + age.Should().BeGreaterThan(0); + } + + [Fact] + public void Can_Assign_To_Indexer() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + + var block = new CompiledBlock(default); + block.CodeBlock = "Arr[5] = 15"; + block.Parameters.Insert("Arr", new BslTypeValue(arrayType)); + + var expr = block.MakeExpression(); + var statement = expr.Body.As().Expressions.First(); + + if (statement.NodeType == ExpressionType.Assign) + { + var assign = statement.As(); + assign.Left.NodeType.Should().Be(ExpressionType.Index); + } + else if (statement.NodeType != ExpressionType.Dynamic) + { + statement.NodeType.Should().Be(ExpressionType.Call); + statement.As().Method.Name.Should().Be("BslIndexSetter"); + } + + var proc = expr.Compile(); + var array = new ArrayImpl(new IValue[6]); + proc.DynamicInvoke(ForbiddenBslProcess.Instance, array); + + array.Get(5).AsNumber().Should().Be(15M); + + } + + [Fact(Skip = "Будет переделываться на индексатор this")] + public void Can_Read_Special_StaticIndexer() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + + var block = new CompiledBlock(default); + block.CodeBlock = "А = Arr[5]; Возврат А;"; + block.Parameters.Insert("Arr", new BslTypeValue(arrayType)); + + var expr = block.MakeExpression(); + var statement = expr.Body.As().Expressions.First(); + + statement.NodeType.Should().Be(ExpressionType.Assign); + var assign = statement.As(); + + assign.Right.NodeType.Should().Be(ExpressionType.Call); + + var arr = new ArrayImpl(new IValue[6]); + arr.Set(5, ValueFactory.Create("Hello")); + + var func = expr.Compile(); + var result = (BslValue)func.DynamicInvoke(arr); + result.ToString().Should().Be("Hello"); + } + + [Fact] + public void Can_Do_While() + { + var block = new CompiledBlock(default); + block.Parameters.Insert("Результат", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = "Ф = 1;" + + "Пока Ф < 10 Цикл" + + "\tРезультат = Результат + Ф;" + + "\tФ = Ф + 1;" + + "\tЕсли Ф > 2 Тогда Прервать; КонецЕсли;" + + "КонецЦикла;" + + "Возврат Результат;"; + var func = block.MakeExpression().Compile(); + + var args = new object[] { ForbiddenBslProcess.Instance, decimal.One }; + var result = (decimal)(BslNumericValue)func.DynamicInvoke(args); + result.Should().Be(4); + } + + [Fact] + public void Can_Do_IfThen() + { + var block = new CompiledBlock(default); + block.CodeBlock = "Если Истина Тогда Ф=1; КонецЕсли"; + var loop = block.MakeExpression() + .Body + .As() + .Expressions + .First(); + loop.NodeType.Should().Be(ExpressionType.Conditional); + } + + [Fact] + public void Can_Do_IfThenElse() + { + var block = new CompiledBlock(default); + block.CodeBlock = "Если Истина Тогда Ф=1; Иначе Ф=2; КонецЕсли"; + var loop = block.MakeExpression() + .Body + .As() + .Expressions + .First(); + loop.NodeType.Should().Be(ExpressionType.Conditional); + } + + [Fact] + public void Can_Do_ElseIfElse() + { + var block = new CompiledBlock(default); + block.Parameters.Insert("П", new BslTypeValue(BasicTypes.Number)); + block.Parameters.Insert("Ф", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = + "Если П=1 Тогда Ф=1;" + + "ИначеЕсли П=2 Тогда Ф=2;" + + "ИначеЕсли П=3 Тогда Ф=3;" + + "Иначе Ф=0; КонецЕсли;" + + "Возврат Ф;"; + var expression = block.MakeExpression(); + var condition = expression + .Body + .As() + .Expressions + .First(); + condition.NodeType.Should().Be(ExpressionType.Conditional); + var func = expression.Compile(); + + for (decimal i = 0; i < 4; i++) + { + var args = new object[] {ForbiddenBslProcess.Instance, i, (decimal)0}; + var result = (BslNumericValue)func.DynamicInvoke(args); + ((decimal)result).Should().Be(i); + } + } + + [Fact] + public void Can_Do_ElseIf_WithoutElse() + { + var block = new CompiledBlock(default); + block.Parameters.Insert("П", new BslTypeValue(BasicTypes.Number)); + block.Parameters.Insert("Ф", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = + "Если П=1 Тогда Ф=1;" + + "ИначеЕсли П=2 Тогда Ф=2;" + + "ИначеЕсли П=3 Тогда Ф=3;" + + "ИначеЕсли П=4 Тогда Ф=4; КонецЕсли;" + + "Возврат Ф;"; + var expression = block.MakeExpression(); + var condition = expression + .Body + .As() + .Expressions + .First(); + condition.NodeType.Should().Be(ExpressionType.Conditional); + var func = expression.Compile(); + + for (decimal i = 0; i < 4; i++) + { + var args = new object[] {ForbiddenBslProcess.Instance, i, 0M}; + var result = (BslNumericValue)func.DynamicInvoke(args); + ((decimal)result).Should().Be(i); + } + } + + [Fact] + public void Can_ForLoop() + { + var block = new CompiledBlock(default); + block.Parameters.Insert("Результат", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = + "Для Ф = 1 По 2+2*2 Цикл " + + "Результат = Результат + Ф;" + + "Если Ф > 2 Тогда Прервать; КонецЕсли; " + + "Продолжить;" + + "КонецЦикла;" + + "Возврат Результат;"; + var expression = block.MakeExpression(); + var func = expression.Compile(); + var args = new object[] { ForbiddenBslProcess.Instance, decimal.Zero }; + var result = (decimal)(BslNumericValue)func.DynamicInvoke(args); + result.Should().Be(6); + } + + [Fact] + public void Can_Do_ForEachLoop() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Результат", new BslTypeValue(BasicTypes.Number)); + block.Parameters.Insert("П", new BslTypeValue(arrayType)); + + block.CodeBlock = + "Для Каждого Ф Из П Цикл " + + "Если Ф = 4 Тогда Продолжить; КонецЕсли; " + + "Если Ф = 5 Тогда Прервать; КонецЕсли;" + + "Результат = Результат + Ф;" + + "КонецЦикла;" + + "Возврат Результат;"; + var expression = block.MakeExpression(); + var func = expression.Compile(); + + var inArray = new ArrayImpl(); + + inArray.Add(ValueFactory.Create(1)); + inArray.Add(ValueFactory.Create(2)); + inArray.Add(ValueFactory.Create(3)); + inArray.Add(ValueFactory.Create(4)); + inArray.Add(ValueFactory.Create(5)); + + var args = new object[] { ForbiddenBslProcess.Instance, decimal.Zero, inArray }; + var result = (decimal)(BslNumericValue)func.DynamicInvoke(args); + result.Should().Be(6); + } + + [Fact] + public void Can_Do_TryExcept() + { + var services = new TinyIocImplementation(); + services.Register(); + var factoryMock = new Mock(); + factoryMock.Setup(f => f.NewProcess()).Returns(ForbiddenBslProcess.Instance); + services.Register(factoryMock.Object); + + var block = new CompiledBlock(services); + block.Parameters.Insert("Ф", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = + "Попытка Если Ф = 1 Тогда Возврат 1; КонецЕсли;" + + "ВызватьИсключение 123; " + + "Исключение Возврат 2; КонецПопытки;"; + var expression = block.MakeExpression(); + + expression.Body.As().Expressions[0].NodeType.Should().Be(ExpressionType.Try); + + var func = expression.Compile(); + + ((decimal)(BslNumericValue)func.DynamicInvoke(new object[] { ForbiddenBslProcess.Instance, decimal.One })).Should().Be(1); + ((decimal)(BslNumericValue)func.DynamicInvoke(new object[] { ForbiddenBslProcess.Instance, decimal.Zero })).Should().Be(2); + } + + [Fact] + public void CanCallGlobalFunctions() + { + var block = new CompiledBlock(default); + var context = new StandardGlobalContext(); + block.Symbols = new SymbolTable(); + block.Symbols.PushObject(context); + block.CodeBlock = "Возврат ТекущаяУниверсальнаяДатаВМиллисекундах();"; + + var func = block.CreateDelegate>(); + var time = (decimal)(BslNumericValue) func(); + time.Should().BeGreaterThan(0); + } + + [Fact] + public void Can_Do_Eratosthenes() + { + var code = @"времяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах(); + Для индекс = 2 По Н Цикл + Если Массив[индекс] Тогда + квадрат = индекс * индекс; + Если квадрат <= Н Тогда + м = квадрат; + Пока м <= Н Цикл + Массив[м] = Ложь; + м = м + индекс; + КонецЦикла; + КонецЕсли; + КонецЕсли; + КонецЦикла; + + времяОкончания = ТекущаяУниверсальнаяДатаВМиллисекундах(); + Возврат (времяОкончания - времяНачала)/1000"; + + var tm = new DefaultTypeManager(); + tm.RegisterClass(typeof(ArrayImpl)); + var blockCompiler = new CompiledBlock(default); + + var N = 50000; + var arr = new ArrayImpl(); + for (int i = 0; i < N; i++) + { + if(i < 2) + arr.Add(BslBooleanValue.False); + + arr.Add(BslBooleanValue.True); + } + + var arrayType = tm.GetTypeByFrameworkType(typeof(ArrayImpl)); + + blockCompiler.Parameters.Insert("Н", new BslTypeValue(BasicTypes.Number)); + blockCompiler.Parameters.Insert("Массив", new BslTypeValue(arrayType)); + blockCompiler.Symbols = new SymbolTable(); + blockCompiler.Symbols.PushObject(new StandardGlobalContext()); + blockCompiler.CodeBlock = code; + + blockCompiler.MakeExpression(); + var eratosphenes = blockCompiler.CreateDelegate>(); + var time = (decimal)(BslNumericValue)eratosphenes(N, arr); + time.Should().NotBe(0); + } + + [Fact] + public void Undefined_To_Object_Makes_NullObject() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + var blockCompiler = new CompiledBlock(default); + blockCompiler.Parameters.Insert("А", new BslTypeValue(arrayType)); + blockCompiler.CodeBlock = "А = Неопределено"; + + var lambda = blockCompiler.MakeExpression(); + var assignment = lambda.Body.As().Expressions[0].As(); + assignment.Right.Should().BeAssignableTo(); + assignment.Type.Should().Be(typeof(ArrayImpl)); + } + + [Fact] + public void Can_Call_Parameterless_Constructor() + { + var block = GetCompiler((tm, s) => tm.RegisterClass(typeof(ArrayImpl))); + block.CodeBlock = "Возврат Новый Массив"; + + var func = block.CreateDelegate>(); + var arrayBsl = func(); + + arrayBsl.Should().BeOfType(); + } + + [Fact] + public void Can_Call_Parameterized_Constructor_With_AllArgs() + { + var tm = new DefaultTypeManager(); + tm.RegisterClass(typeof(ArrayImpl)); + + var services = new TinyIocImplementation(); + services.Register(tm); + + var block = new CompiledBlock(services.CreateContainer()); + block.Parameters.Insert("Размер", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = "Возврат Новый Массив(Размер)"; + + var func = block.CreateDelegate>(); + var arrayBsl = func(15); + + arrayBsl.As().Count().Should().Be(15); + } + + [Fact] + public void Can_Call_Parameterized_Constructor_With_SkippedArgs() + { + var tm = new DefaultTypeManager(); + tm.RegisterClass(typeof(TextReadImpl)); + + var services = new TinyIocImplementation(); + services.Register(tm); + + var block = new CompiledBlock(services.CreateContainer()); + block.Parameters.Insert("Путь", new BslTypeValue(BasicTypes.String)); + block.CodeBlock = "Возврат Новый ЧтениеТекста(Путь,,,,Истина)"; + + var func = block.CreateDelegate>(); + var tempFile = System.IO.Path.GetTempFileName(); + try + { + using var reader = (TextReadImpl)func(tempFile); + reader.Close(); + } + finally + { + System.IO.File.Delete(tempFile); + } + } + + [Fact] + public void Can_Call_TypeConversions() + { + var block = new CompiledBlock(default); + block.CodeBlock = + @"БулевТип = Истина; + ЧисловойТип = 1; + БулевТип = Булево(ЧисловойТип);"; + + var l = block.MakeExpression(); + l.Body.As().Expressions[2].As() + .Right.NodeType + .Should() + .Be(ExpressionType.Convert); + } + + [Fact] + public void Can_Call_ParameterlessFunctions() + { + var block = new CompiledBlock(default); + block.CodeBlock = "А = ТекущаяДата()"; + + var l = block.MakeExpression(); + var statement = l.Body.As().Expressions[0].As(); + + statement.Right.NodeType.Should().Be(ExpressionType.Call); + } + + [Fact] + public void Can_Call_Member_Procedures() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Массив", new BslTypeValue(arrayType)); + block.CodeBlock = "Массив.Добавить(1); Массив.Добавить(2);"; + + var method = block.CreateDelegate>(); + var array = new ArrayImpl(); + method(array); + + array.Should().HaveCount(2); + } + + [Fact] + public void Can_Call_Member_Procedures_On_Dynamics() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + + var block = GetCompiler(tm); + block.Parameters.Insert("Массив", new BslTypeValue(arrayType)); + block.CodeBlock = "Массив.Добавить(Новый Массив); Массив[0].Добавить(2)"; + + var l = block.MakeExpression(); + var method = block.CreateDelegate>(); + var array = new ArrayImpl(); + method(array); + + array.Should().HaveCount(1); + array.Get(0).As().Should().HaveCount(1); + } + + [Fact] + public void Can_Call_Member_Procedures_With_Defaults() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + var services = new TinyIocImplementation(); + services.Register(tm); + var block = new CompiledBlock(services); + block.Parameters.Insert("Массив", new BslTypeValue(arrayType)); + block.CodeBlock = "Массив.Добавить();"; + + var method = block.CreateDelegate>(); + var array = new ArrayImpl(); + method(array); + + array.Should().HaveCount(1); + } + [Fact] + public void Can_Call_Member_Functions() + { + var tm = new DefaultTypeManager(); + var arrayType = tm.RegisterClass(typeof(ArrayImpl)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Массив", new BslTypeValue(arrayType)); + block.CodeBlock = "Массив.Добавить(1); Массив.Добавить(2); А = Массив.Количество()"; + + var lambda = block.MakeExpression(); + + var lastAssignment = lambda.Body.As().Expressions[^2].As(); + lastAssignment.Right.Type.Should().Be(typeof(decimal)); + } + + [Fact] + public void Can_Do_PropRead_Static() + { + var tm = new DefaultTypeManager(); + var objectType = tm.RegisterClass(typeof(ValueTable)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Ф", new BslTypeValue(objectType)); + block.CodeBlock = + "Возврат Ф.Колонки.Количество();"; + var expression = block.MakeExpression(); + + var func = expression.Compile(); + + var testData = new ValueTable(); + testData.Columns.Add("Колонка1"); + testData.Columns.Add("Колонка2"); + + ((decimal)(BslNumericValue)func.DynamicInvoke(new object[] { ForbiddenBslProcess.Instance, testData })).Should().Be(2M); + } + + [Fact] + public void Can_Do_PropRead_Dynamic() + { + var tm = new DefaultTypeManager(); + var objectType = tm.RegisterClass(typeof(StructureImpl)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Ф", new BslTypeValue(objectType)); + block.CodeBlock = + "Возврат Ф.Свойство1.ВложенноеСвойство1;"; + var expression = block.MakeExpression(); + + var func = expression.Compile(); + + var innerTestData = new StructureImpl(); + innerTestData.Insert("ВложенноеСвойство1", ValueFactory.Create(2M)); + + var testData = new StructureImpl(); + testData.Insert("Свойство1", innerTestData); + + ((decimal)(BslNumericValue)func.DynamicInvoke(new object[] { ForbiddenBslProcess.Instance, testData })).Should().Be(2M); + } + + [Fact] + public void Can_Do_PropWrite_Dynamic() + { + var tm = new DefaultTypeManager(); + var objectType = tm.RegisterClass(typeof(StructureImpl)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Ф", new BslTypeValue(objectType)); + block.Parameters.Insert("П", new BslTypeValue(objectType)); + block.Parameters.Insert("Ж", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = + "Ф.Свойство1 = П;" + + "Ф.Свойство1.ВложенноеСвойство1 = Ж;" + + "Возврат Ф.Свойство1.ВложенноеСвойство1;"; + var expression = block.MakeExpression(); + + var func = expression.Compile(); + + var innerTestData = new StructureImpl(); + innerTestData.Insert("ВложенноеСвойство1", ValueFactory.Create(1M)); + + var testData = new StructureImpl(); + testData.Insert("Свойство1", innerTestData); + + ((decimal)(BslNumericValue)func.DynamicInvoke(new object[] {ForbiddenBslProcess.Instance, testData, innerTestData, 2M })).Should().Be(2M); + } + + [Fact] + public void Can_Do_PropWrite_Static() + { + var tm = new DefaultTypeManager(); + tm.RegisterClass(typeof(ValueListImpl)); + var objectType = tm.RegisterClass(typeof(ValueListItem)); + + var block = new CompiledBlock(default); + block.Parameters.Insert("Ф", new BslTypeValue(objectType)); + block.Parameters.Insert("НовоеЗначение", new BslTypeValue(BasicTypes.Number)); + block.CodeBlock = + "Ф.Значение = НовоеЗначение; Возврат Ф.Значение"; + var expression = block.MakeExpression(); + + var func = expression.Compile(); + + var testStructure = new ValueListImpl(); + testStructure.Add(ValueFactory.Create(1M)); + + var testData = testStructure.FirstOrDefault(); + + ((decimal) (BslNumericValue) func.DynamicInvoke(new object[] {ForbiddenBslProcess.Instance, testData, 2M})) + .Should().Be(2M); + } + + [Fact] + public void ExceptionInfo_ReturnsUndefined_OutsideOfCatch() + { + var block = GetCompiler(new DefaultTypeManager()); + block.CodeBlock = "А = ИнформацияОбОшибке();"; + + var lambda = block.MakeExpression(); + lambda.Body.As().Expressions[0].Type.Should().BeAssignableTo(); + } + + [Fact] + public void ExceptionInfo_ReturnsClass_InCatch() + { + var block = GetCompiler(new DefaultTypeManager()); + block.CodeBlock = "Попытка\n" + + " ;\n" + + "Исключение\n" + + " А = ИнформацияОбОшибке();\n" + + "КонецПопытки;"; + + var lambda = block.MakeExpression(); + var tryBlock = lambda.Body.As().Expressions[0].As(); + + tryBlock.Handlers.First().Body.As().Expressions[0].Type + .Should() + .Be(typeof(BslObjectValue)); + } + + [Fact] + public void LazyBoolOr() + { + var tm = new DefaultTypeManager(); + var objectType = tm.RegisterClass(typeof(StructureImpl)); + var block = GetCompiler(new DefaultTypeManager()); + block.Parameters.Insert("Ф", new BslTypeValue(objectType)); + block.CodeBlock = "Возврат Истина Или Ф;"; // Ф не должен быть вычислен + + var lambda = block.MakeExpression(); + var func = lambda.Compile(); + + var testData = new StructureImpl(); + + ((bool)(BslBooleanValue)func.DynamicInvoke(new object[] { ForbiddenBslProcess.Instance, testData })).Should().Be(true); + } + + [Fact] + public void LazyBoolAnd() + { + var tm = new DefaultTypeManager(); + var objectType = tm.RegisterClass(typeof(StructureImpl)); + var block = GetCompiler(tm); + block.Parameters.Insert("Ф", new BslTypeValue(objectType)); + block.CodeBlock = "Возврат Ложь И Ф;"; // Ф не должен быть вычислен + + var lambda = block.MakeExpression(); + var func = lambda.Compile(); + + var testData = new StructureImpl(); + + ((bool)(BslBooleanValue)func.DynamicInvoke(new object[] { ForbiddenBslProcess.Instance, testData })).Should().Be(false); + } + + [Fact] + public void TypeFuncValue_Is_BslTypeValue() + { + var tm = new DefaultTypeManager(); + var objectType = tm.RegisterClass(typeof(StructureImpl)); + var block = GetCompiler(tm); + block.CodeBlock = "Возврат Тип(\"Структура\");"; + + var lambda = block.MakeExpression(); + var func = lambda.Compile(); + + var testType = new BslTypeValue(objectType); + var result = func.DynamicInvoke(ForbiddenBslProcess.Instance); + + result.Should().BeOfType().And.Be(testType); + } + + [Fact] + public void TypeOfFuncValue_Is_BslTypeValue() + { + var block = GetCompiler(new DefaultTypeManager()); + block.CodeBlock = "Возврат ТипЗнч(42);"; + + var lambda = block.MakeExpression(); + var func = lambda.Compile(); + + var testType = new BslTypeValue(BasicTypes.Number); + var result = func.DynamicInvoke(ForbiddenBslProcess.Instance); + + result.Should().BeOfType().And.Be(testType); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/ObsoleteMembersTest.cs b/src/Tests/OneScript.Core.Tests/ObsoleteMembersTest.cs new file mode 100644 index 000000000..f0ef06648 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/ObsoleteMembersTest.cs @@ -0,0 +1,170 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using FluentAssertions; +using Moq; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using Xunit; + +namespace OneScript.Core.Tests +{ + [Collection("SystemLogger")] + public class ObsoleteMembersTest : IDisposable + { + private readonly List _messages; + public ObsoleteMembersTest() + { + var mock = new Mock(); + mock.Setup(x => x.Write(It.IsAny())) + .Callback(str => _messages.Add(str)); + + _messages = new List(); + LogWriter = mock.Object; + SystemLogger.SetWriter(LogWriter); + } + + private ISystemLogWriter LogWriter { get; set; } + + public void Dispose() + { + // Очищаем логгер после каждого теста + SystemLogger.Reset(); + } + + [Fact] + public void TestLoggingOfObsoletePropAccess() + { + dynamic instance = new TestContextClass(); + var value = instance.ObsoleteProperty ?? ""; + instance.УстаревшееСвойство = value; + + _messages.Should().HaveCount(1, "must be only one warning"); + _messages.Should().Contain(item => + item.IndexOf("ObsoleteProperty", StringComparison.InvariantCultureIgnoreCase) >= 0 + || item.IndexOf("УстаревшееСвойство", StringComparison.InvariantCultureIgnoreCase) >= 0); + } + + [Fact] + public void TestLoggingOfObsoleteName() + { + dynamic instance = new TestContextClass(); + var value = instance.СвойствоBsl ?? ""; // обычное имя + instance.OldBslProp = value; + + _messages.Should().HaveCount(1, "must be only one warning"); + _messages.Should().Contain(item => item.IndexOf("OldBslProp", StringComparison.InvariantCultureIgnoreCase) >= 0); + } + + [Fact] + public void TestLoggingOfObsoleteCall() + { + dynamic instance = new TestContextClass(); + instance.УстаревшийМетод(); + instance.ObsoleteMethod(); + instance.УстаревшийМетод(); + + _messages.Should().HaveCount(1, "must be only one warning"); + _messages.Should().Contain(item => + item.IndexOf("УстаревшийМетод", StringComparison.InvariantCultureIgnoreCase) >= 0 + || item.IndexOf("ObsoleteMethod", StringComparison.InvariantCultureIgnoreCase) >= 0); + } + + [Fact] + public void CallGoodMethodsHasNoWarnings() + { + dynamic instance = new TestContextClass(); + instance.ХорошийМетод(); + instance.GoodMethod(); + + _messages.Should().BeEmpty(); + } + + [Fact] + public void TestICallDeprecatedAliasAndHaveWarning() + { + dynamic instance = new TestContextClass(); + instance.ObsoleteAlias(); + + _messages.Should().HaveCount(1) + .And.Contain(x => x.IndexOf("ObsoleteAlias", StringComparison.InvariantCultureIgnoreCase) >= 0); + } + + [Fact] + public void TestICallDeprecatedAliasAndHaveException() + { + var exceptionThrown = false; + + try + { + dynamic instance = new TestContextClass(); + instance.VeryObsoleteAlias(); + } + catch (RuntimeException) + { + exceptionThrown = true; + } + + exceptionThrown.Should().BeTrue("Безнадёжно устаревший метод должен вызвать исключение"); + } + + [Fact] + public void DeprecatedEnumClassHasWarning() + { + var host = DefaultEngineBuilder.Create() + .SetDefaultOptions() + .SetupEnvironment(e => e.AddAssembly(typeof(DeprecatedEnum).Assembly)) + .Build(); + + var source = host.Loader.FromString("А = СтароеИмя.Значение1"); + host.GetCompilerService().Compile(source, ForbiddenBslProcess.Instance); + + _messages.Should().HaveCount(1) + .And.Contain(x => x.Contains("СтароеИмя", StringComparison.InvariantCultureIgnoreCase)); + } + + [Fact] + public void DeprecatedEnumClassHasNoWarningOnNewName() + { + var host = DefaultEngineBuilder.Create() + .SetDefaultOptions() + .SetupEnvironment(e => e.AddAssembly(typeof(DeprecatedEnum).Assembly)) + .Build(); + + var source = host.Loader.FromString("А = НовоеПеречисление.Значение1"); + host.GetCompilerService().Compile(source, ForbiddenBslProcess.Instance); + + _messages.Should().HaveCount(0); + } + + [Fact] + public void DeprecatedEnumValueHasWarning() + { + var env = new RuntimeEnvironment(); + var discoverer = new ContextDiscoverer(new DefaultTypeManager(), Mock.Of(), new TinyIocImplementation()); + discoverer.DiscoverGlobalContexts(env, GetType().Assembly, t => t == typeof(DeprecatedEnum)); + + var enumInstance = env.GetGlobalProperty("НовоеПеречисление") as ClrEnumWrapper; + + enumInstance.Should().NotBeNull(); + enumInstance["Значение1"].Should().BeAssignableTo(); + enumInstance["Значение2"].Should().BeAssignableTo(); + enumInstance["СтароеЗначение2"].Should().BeAssignableTo(); + enumInstance["СтароеЗначение2"].Should().BeAssignableTo(); + + _messages.Should().HaveCount(1) + .And.Contain(x => x.Contains("СтароеЗначение2", StringComparison.InvariantCultureIgnoreCase)); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj b/src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj new file mode 100644 index 000000000..6cc2978cc --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/OneScript.Core.Tests.csproj @@ -0,0 +1,33 @@ + + + + $(TargetFrameworkVersion) + + false + + Debug;Release;LinuxDebug + 10.0 + AnyCPU + + + + true + false + + + + + + + + + + + + + + + + + + diff --git a/src/Tests/OneScript.Core.Tests/PredefinedInterfacesTest.cs b/src/Tests/OneScript.Core.Tests/PredefinedInterfacesTest.cs new file mode 100644 index 000000000..3570a11c9 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/PredefinedInterfacesTest.cs @@ -0,0 +1,154 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using FluentAssertions; +using Moq; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Localization; +using Xunit; + +namespace OneScript.Core.Tests; + +public class PredefinedInterfacesTest +{ + [Fact] + public void Test_CheckerReactsOnModuleAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnModule(annotationToSearch), + }); + + var module = MockModule(mock => + { + mock.SetupGet(x => x.ModuleAttributes).Returns(new[] + { + new BslAnnotationAttribute("MyAnno") + }); + + mock.SetupGet(x => x.Methods).Returns(Array.Empty()); + }); + + checker.Setup(x => x.Validate(module)).Callback((m) => + { + m.Interfaces.Add(typeof(string), "Registered"); + }); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + module.Interfaces.Should().Contain(KeyValuePair.Create(typeof(string), (object)"Registered")); + } + + [Fact] + public void Test_CheckerDoesNotReactsOnModuleAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnModule(annotationToSearch), + }); + + var module = MockModule(mock => + { + mock.SetupGet(x => x.ModuleAttributes).Returns(Array.Empty()); + mock.SetupGet(x => x.Methods).Returns(Array.Empty()); + }); + + checker.Setup(x => x.Validate(module)).Callback((m) => + { + m.Interfaces.Add(typeof(string), "Registered"); + }); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + module.Interfaces.Should().BeEmpty(); + } + + [Fact] + public void Test_CheckerReactsOnlyOnceOnManyAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnModule(annotationToSearch), + PredefinedInterfaceRegistration.OnMethod(annotationToSearch, new BilingualString("MyMethod")), + }); + + var module = MockModule(mock => + { + mock.SetupGet(x => x.ModuleAttributes).Returns(new[] + { + new BslAnnotationAttribute("MyAnno") + }); + + var method = BslMethodBuilder.Create() + .Name("MyMethod") + .SetAnnotations(new[] { new BslAnnotationAttribute("MyAnno") }) + .Build(); + + mock.SetupGet(x => x.Methods).Returns(new[] { method }); + }); + + checker.Setup(x => x.Validate(module)).Verifiable(); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + checker.Verify(x => x.Validate(It.IsAny()), Times.Once); + } + + [Fact] + public void Test_CheckerReactsOnlyOnMethodAnnotations() + { + var annotationToSearch = new BilingualString("MyAnno"); + var checker = new Mock(); + checker.Setup(x => x.GetRegistrations()).Returns(new[] + { + PredefinedInterfaceRegistration.OnMethod(annotationToSearch, new BilingualString("MyMethod")), + }); + + var module = MockModule(mock => + { + var method = BslMethodBuilder.Create() + .Name("MyMethod") + .SetAnnotations(new[] { new BslAnnotationAttribute("MyAnno") }) + .Build(); + + mock.SetupGet(x => x.Methods).Returns(new[] { method }); + }); + + checker.Setup(x => x.Validate(module)).Callback((m) => + { + m.Interfaces.Add(typeof(string), "Registered"); + }); + + var resolver = new PredefinedInterfaceResolver(new[] { checker.Object }); + resolver.Resolve(module); + + module.Interfaces.Should().Contain(KeyValuePair.Create(typeof(string), (object)"Registered")); + } + + private static IExecutableModule MockModule(Action> setup) + { + var dict = new Dictionary(); + var moduleMock = new Mock(); + moduleMock.SetupGet(x => x.Interfaces).Returns(dict); + + setup(moduleMock); + + return moduleMock.Object; + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/SymbolsRegistrationTests.cs b/src/Tests/OneScript.Core.Tests/SymbolsRegistrationTests.cs new file mode 100644 index 000000000..15e84434c --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/SymbolsRegistrationTests.cs @@ -0,0 +1,60 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class SymbolsRegistrationTests + { + [Fact] + public void GlobalProperty_In_Shared_PropertyBag_IsSettable() + { + var env = new RuntimeEnvironment(); + var prop = new TestContextClass(); + + env.InjectGlobalProperty(BslUndefinedValue.Instance, "TestClass", false); + env.SetGlobalProperty("TestClass", prop); + + var storedProp = env.GetGlobalProperty("TestClass"); + Assert.Same(prop, storedProp); + } + + [Fact] + public void GlobalProperty_In_Separate_InjectedContext_IsSettable() + { + var engine = DefaultEngineBuilder + .Create() + .SetDefaultOptions() + .Build(); + + var compiler = engine.GetCompilerService(); + compiler.FillSymbols(typeof(UserScriptContextInstance)); + var module = compiler.Compile(engine.Loader.FromString("Перем А Экспорт; Перем Б Экспорт;\n" + + "А = 1; Б = 2;"), engine.NewProcess()); + engine.Initialize(); + var propertyHolder = (UserScriptContextInstance)engine.NewObject(module, engine.NewProcess()); + engine.Dispose(); + + var env = new RuntimeEnvironment(); + var propToPropBag = new TestContextClass(); + + env.InjectObject(propertyHolder); + env.InjectGlobalProperty(propToPropBag, "TestClass", false); + + Assert.Same(propToPropBag, env.GetGlobalProperty("TestClass")); + Assert.Equal(ValueFactory.Create(1), env.GetGlobalProperty("А")); + Assert.Equal(ValueFactory.Create(2), env.GetGlobalProperty("Б")); + + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/TestNullConversions.cs b/src/Tests/OneScript.Core.Tests/TestNullConversions.cs new file mode 100644 index 000000000..4656dc859 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/TestNullConversions.cs @@ -0,0 +1,40 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using FluentAssertions; +using ScriptEngine.Machine; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class TestNullConversions + { + private dynamic Instance { get; set; } + + public TestNullConversions() + { + Instance = new NullConversionTestContext(); + } + + [Fact] + public void CheckUndefinedIsNullOnPassingArg() + { + var undef = ValueFactory.Create(); + var result = (IValue)Instance.ТестIValueНеопределено(undef); + + result.Should().BeNull(); + } + + [Fact] + public void CheckDefinedValueIsNotNullOnPassingArg() + { + var result = (decimal)Instance.ТестIValue(7.5); + + result.Should().Be(7.5m); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/TestTypes_Registration.cs b/src/Tests/OneScript.Core.Tests/TestTypes_Registration.cs new file mode 100644 index 000000000..65861a9c9 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/TestTypes_Registration.cs @@ -0,0 +1,103 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Moq; +using OneScript.Contexts; +using OneScript.StandardLibrary.Collections; +using OneScript.Types; +using ScriptEngine; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Types; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class TestTypesRegistration + { + [Fact] + public void All_Classes_Have_Unique_Types() + { + var tm = new DefaultTypeManager(); + var discoverer = new ContextDiscoverer(tm, Mock.Of(), default); + + discoverer.DiscoverClasses(typeof(ArrayImpl).Assembly); + + var manualDiscover = typeof(ArrayImpl).Assembly.GetTypes() + .Where(t => t.IsDefined(typeof(ContextClassAttribute), false)) + .ToArray(); + + var alreadySearchedTypes = new HashSet(); + + foreach (var type in manualDiscover) + { + TypeDescriptor definition; + try + { + definition = type.GetTypeFromClassMarkup(); + } + catch (InvalidOperationException) + { + definition = tm.GetTypeByFrameworkType(type); // если не упало, то зарегистрировалось + var typeData = $"name: {definition.Name}\n" + + $"id: {definition.Id}\n" + + $"class: {definition.ImplementingClass.Name}\n" + + $"discoveredClass: {type.Name}"; + + alreadySearchedTypes.Add(definition).Should().BeTrue(typeData + "\nshould not be in hashset"); + continue; + } + + var registeredType = tm.GetTypeByName(definition.Name); + Assert.Equal(type, registeredType.ImplementingClass); + Assert.Equal(definition, registeredType); + + alreadySearchedTypes.Add(definition).Should().BeTrue(definition.Name + "\nshould not be in hashset"); + } + } + + [Fact] + public void TypeEqualityById() + { + var guid = "826429B0-F662-4CCD-BF15-384A10B53611"; + var type1 = new TypeDescriptor(guid, "TheType"); + var type2 = new TypeDescriptor(guid, "TheType"); + + Assert.Equal(type1, type2); + Assert.True(type1 == type2); // operator== + Assert.False(type1 != type2); // operator != + } + + [Fact] + public void TypeInequalityForNull() + { + var guid = "826429B0-F662-4CCD-BF15-384A10B53611"; + var type1 = new TypeDescriptor(guid, "TheType"); + TypeDescriptor type2 = null; + + Assert.NotEqual(type1, type2); + Assert.False(type1 == type2); // operator== + Assert.True(type1 != type2); // operator != + } + + [Fact] + public void TypeEqualityForNull() + { + TypeDescriptor type1 = null; + TypeDescriptor type2 = null; + + Assert.Equal(type1, type2); + Assert.True(type1 == type2); // operator== + Assert.False(type1 != type2); // operator != + } + + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/TestUserScriptProperties.cs b/src/Tests/OneScript.Core.Tests/TestUserScriptProperties.cs new file mode 100644 index 000000000..90ad989b7 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/TestUserScriptProperties.cs @@ -0,0 +1,78 @@ +/*---------------------------------------------------------- +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.Linq; +using FluentAssertions; +using OneScript.Contexts; +using OneScript.Values; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class TestUserScriptProperties + { + [Fact] + public void ExportVariables_And_Properties_Are_Accessible() + { + var code = "Перем МояПеременная Экспорт;"; + + var instance = CompileUserScript(code); + + var thisObjIndex = instance.GetPropertyNumber("ThisObject"); + var thisPropVal = instance.GetPropValue(thisObjIndex); + thisPropVal.Should().BeSameAs((IValue)instance); + + var myVarPropertyIndex = instance.GetPropertyNumber("МояПеременная"); + var myVarValue = instance.GetPropValue(myVarPropertyIndex); + myVarValue.Should().Be(BslUndefinedValue.Instance); + + thisObjIndex.Should().Be(0); + instance.GetPropertyNumber("МояПеременная").Should().Be(1); + } + + [Fact] + public void ExternalVariables_And_Properties_Are_Accessible() + { + var context = new ExternalContextData(); + context.Add("ContextVariable", ValueFactory.Create(18)); + + var code = "Перем МояПеременная Экспорт;"; + + var instance = CompileUserScript(code, context); + var externalVar = instance.GetPropertyNumber("ContextVariable"); + var value = instance.GetPropValue(externalVar); + value.AsNumber().Should().Be(18); + + var allNames = instance.GetProperties() + .Select(x => x.Name) + .ToArray(); + + allNames[0].Should().BeOneOf("ThisObject", "ЭтотОбъект"); + allNames[1].Should().Be("ContextVariable"); + allNames[2].Should().Be("МояПеременная"); + } + + private static IRuntimeContextInstance CompileUserScript(string code, ExternalContextData context = null) + { + var engine = DefaultEngineBuilder + .Create() + .SetDefaultOptions() + .Build(); + + var compiler = engine.GetCompilerService(); + + var source = engine.Loader.FromString(code); + engine.Initialize(); + var module = engine.AttachedScriptsFactory.CompileModuleFromSource(compiler, source, context, engine.NewProcess()); + + var instance = engine.NewObject(module, engine.NewProcess(), context); + return instance; + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/ThisObjectPropertyTest.cs b/src/Tests/OneScript.Core.Tests/ThisObjectPropertyTest.cs new file mode 100644 index 000000000..1c18e7207 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/ThisObjectPropertyTest.cs @@ -0,0 +1,39 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using FluentAssertions; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class ThisObjectPropertyTest + { + [Fact] + public void ThisObjectCanBeFoundForUserScript() + { + var builder = DefaultEngineBuilder + .Create() + .SetDefaultOptions(); + var engine = builder.Build(); + engine.Initialize(); + var instance = engine.AttachedScriptsFactory.LoadFromString(engine.GetCompilerService(), "", engine.NewProcess()); + + instance.Should().BeOfType(); + instance.GetPropertyNumber("ЭтотОбъект").Should().NotBe(-1); + instance.GetPropertyNumber("ThisObject").Should().NotBe(-1); + + var id = instance.GetPropertyNumber("ThisObject"); + instance.GetPropValue(id).Should().BeSameAs((IValue)instance); + instance.IsPropReadable(id).Should().BeTrue(); + instance.IsPropWritable(id).Should().BeFalse(); + instance.GetPropName(id).Should().BeOneOf("ЭтотОбъект", "ThisObject"); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Core.Tests/TypeReflectionTests.cs b/src/Tests/OneScript.Core.Tests/TypeReflectionTests.cs new file mode 100644 index 000000000..9b119e8eb --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/TypeReflectionTests.cs @@ -0,0 +1,246 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Reflection; +using OneScript.Contexts; +using OneScript.Execution; +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.Hosting; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Machine; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class TypeReflectionTests + { + private ScriptingEngine host; + + public TypeReflectionTests() + { + var builder = DefaultEngineBuilder + .Create() + .SetDefaultOptions(); + + host = builder.Build(); + } + + private IExecutableModule LoadFromString(string code) + { + var codeSrc = host.Loader.FromString(code); + var cmp = host.GetCompilerService(); + return cmp.Compile(codeSrc, host.NewProcess()); + } + + [Fact] + public void CheckIfTypeHasReflectedWithName() + { + string script = "Перем А;"; + + var reflected = CreateDummyType(script); + Assert.Equal("Dummy", reflected.Name); + Assert.Equal("OneScript.Contexts.dyn.Dummy", reflected.FullName); + + } + + private Type CreateDummyType(string script) + { + var module = LoadFromString(script); + var builder = new ClassBuilder(typeof(UserScriptContextInstance)); + var reflected = builder.SetModule(module) + .SetTypeName("Dummy") + .ExportDefaults() + .Build(); + + return reflected; + } + + [Fact] + public void CheckNonExportVarsArePrivateFields() + { + string script = "Перем А; Перем Б Экспорт;"; + + var reflected = CreateDummyType(script); + + var props = reflected.GetFields(BindingFlags.NonPublic); + Assert.Single(props); + Assert.Equal("А", props[0].Name); + Assert.Equal(typeof(BslValue), props[0].FieldType); + } + + [Fact] + public void CheckExportVarsArePublicFields() + { + string script = "Перем А; Перем Б Экспорт;"; + + var reflected = CreateDummyType(script); + + var props = reflected.GetFields(BindingFlags.Public); + Assert.Single(props); + Assert.Equal("Б", props[0].Name); + Assert.Equal(typeof(BslValue), props[0].FieldType); + + } + + [Fact] + public void CheckDefaultGetMethodsArePublic() + { + string script = "Процедура Внутренняя()\n" + + "КонецПроцедуры\n\n" + + "Процедура Внешняя() Экспорт\n" + + "КонецПроцедуры"; + + var reflected = CreateDummyType(script); + + var defaultGet = reflected.GetMethods(); + Assert.Single(defaultGet); + Assert.Equal("Внешняя", defaultGet[0].Name); + } + + [Fact] + public void CheckExplicitPublicMethodsCanBeRetrieved() + { + string script = "Процедура Внутренняя()\n" + + "КонецПроцедуры\n\n" + + "Процедура Внешняя() Экспорт\n" + + "КонецПроцедуры"; + + var reflected = CreateDummyType(script); + + var defaultGet = reflected.GetMethods(BindingFlags.Public); + Assert.Single(defaultGet); + Assert.Equal("Внешняя", defaultGet[0].Name); + } + + [Fact] + public void CheckPrivateMethodsCanBeRetrieved() + { + string script = "Процедура Внутренняя()\n" + + "КонецПроцедуры\n\n" + + "Процедура Внешняя() Экспорт\n" + + "КонецПроцедуры"; + + var reflected = CreateDummyType(script); + + var defaultGet = reflected.GetMethods(BindingFlags.NonPublic); + Assert.Single(defaultGet); + Assert.Equal("Внутренняя", defaultGet[0].Name); + } + + [Fact] + public void CheckAllMethodsCanBeRetrieved() + { + string script = "Процедура Внутренняя()\n" + + "КонецПроцедуры\n\n" + + "Процедура Внешняя() Экспорт\n" + + "КонецПроцедуры"; + + var reflected = CreateDummyType(script); + + var defaultGet = reflected.GetMethods(BindingFlags.Public|BindingFlags.NonPublic); + Assert.Equal(2, defaultGet.Length); + } + + [Fact] + public void ClassCanBeCreatedViaConstructor() + { + var cb = new ClassBuilder(typeof(UserScriptContextInstance)); + var module = LoadFromString(""); + cb.SetTypeName("testDrive") + .SetModule(module) + .ExportDefaults() + .ExportConstructor((c, p) => new UserScriptContextInstance(module)); + var type = cb.Build(); + + var instance = type.GetConstructors()[0].Invoke(new object[0]); + Assert.IsAssignableFrom(instance); + } + + [Fact] + public void ClassCanExposeNativeMethodByName() + { + var cb = new ClassBuilder(typeof(UserScriptContextInstance)); + var module = LoadFromString(""); + cb.SetTypeName("testDrive") + .SetModule(module) + .ExportClassMethod("GetMethodsCount"); + var type = cb.Build(); + + Assert.NotNull(type.GetMethod("GetMethodsCount")); + } + + [Fact] + public void ClassCanExposeNativeMethodDirectly() + { + var cb = new ClassBuilder(typeof(UserScriptContextInstance)); + var module = LoadFromString(""); + var nativeMethod = typeof(UserScriptContextInstance) + .GetMethod("AddProperty", + BindingFlags.Public | BindingFlags.Instance, + null, + new Type[] + { + typeof(string), + typeof(IValue) + }, null); + cb.SetTypeName("testDrive") + .SetModule(module) + .ExportClassMethod(nativeMethod); + var type = cb.Build(); + + Assert.NotNull(type.GetMethod("AddProperty")); + } + + [Fact] + public void CheckMethodBodyIsNotReflected() + { + string script = "Процедура Внутренняя()\n" + + "КонецПроцедуры\n\n" + + "Процедура Внешняя() Экспорт\n" + + "КонецПроцедуры\n" + + "ТелоМодуля = 2;"; + + var reflected = CreateDummyType(script); + + var defaultGet = reflected.GetMethods(BindingFlags.Public | BindingFlags.NonPublic); + Assert.Equal(2, defaultGet.Length); + } + + [Fact] + public void CheckMethodAnnotationsReflected() + { + string script = "&Аннотация\n" + + "&ДругаяАннотация\n" + + "Процедура Внешняя() Экспорт\n" + + "КонецПроцедуры"; + + var reflected = CreateDummyType(script); + var method = reflected.GetMethod("Внешняя"); + Assert.NotNull(method); + Assert.Equal(2, method.GetCustomAttributes(typeof(BslAnnotationAttribute), false).Length); + + var first = (BslAnnotationAttribute)method.GetCustomAttributes(typeof(BslAnnotationAttribute), false)[0]; + Assert.Equal("Аннотация", first.Name); + } + + [Fact] + public void CheckParametersAnnotationsReflected() + { + string script = "Процедура Внешняя(&Аннотация Параметр, ПараметрБезАннотации) Экспорт\n" + + "КонецПроцедуры"; + + var reflected = CreateDummyType(script); + var method = reflected.GetMethod("Внешняя"); + Assert.NotNull(method); + var param = method.GetParameters()[0]; + + var first = (BslAnnotationAttribute)param.GetCustomAttributes(typeof(BslAnnotationAttribute), false)[0]; + Assert.Equal("Аннотация", first.Name); + } + } +} diff --git a/src/Tests/OneScript.Core.Tests/ValueReferenceTests.cs b/src/Tests/OneScript.Core.Tests/ValueReferenceTests.cs new file mode 100644 index 000000000..3ab118dc0 --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/ValueReferenceTests.cs @@ -0,0 +1,70 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.Values; +using Xunit; +using FluentAssertions; + +namespace OneScript.Core.Tests +{ + public class ValueReferenceTests + { + [Fact] + public void TestCanReferenceSimpleValue() + { + var context = new TestContextClass(); + var reference = new ValueReference(() => BslStringValue.Create(context.BslProp), v => context.BslProp = v.ToString()); + + context.BslProp = "HELLO"; + reference.Value.ToString().Should().Be("HELLO"); + reference.Value = BslStringValue.Create("NEWVAL"); + context.BslProp.Should().Be("NEWVAL"); + } + + [Fact] + public void TestCanReferenceContextProperties() + { + var context = new TestContextClass(); + var referenceEn = new PropertyValueReference(context, "BslProp"); + var referenceRu = new PropertyValueReference(context, "СвойствоBsl"); + + context.BslProp = "HELLO"; + referenceEn.Value.ToString().Should().Be("HELLO"); + referenceRu.Value.ToString().Should().Be("HELLO"); + + // ссылки эквивалентны только если ссылаются на тот же объект + referenceRu.Should().Be(referenceEn); + var simpleReference = new ValueReference(() => default, v => {}); + referenceEn.Should().NotBe(simpleReference); + + referenceEn.Value = BslStringValue.Create("NEWVAL"); + referenceRu.Value.ToString().Should().Be("NEWVAL"); + context.BslProp.Should().Be("NEWVAL"); + } + + [Fact] + public void TestCanReferenceIndexedValues() + { + var context = new TestContextClass(); + context.SetIndexedValue(BslNumericValue.Create(1), BslBooleanValue.True); + context.SetIndexedValue(BslNumericValue.Create(2), BslBooleanValue.False); + + var ref1 = new IndexedValueReference(context, BslNumericValue.Create(1)); + var ref1Dup = new IndexedValueReference(context, BslNumericValue.Create(1)); + var ref2 = new IndexedValueReference(context, BslNumericValue.Create(2)); + + ref1.Should().Be(ref1Dup); + + ref1.Value.Should().Be(context.GetIndexedValue(BslNumericValue.Create(1))); + ref2.Value.Should().Be(context.GetIndexedValue(BslNumericValue.Create(2))); + + ref2.Value = BslUndefinedValue.Instance; + context.GetIndexedValue(BslNumericValue.Create(2)).Should().Be(BslUndefinedValue.Instance); + context.SetIndexedValue(BslNumericValue.Create(2), BslNumericValue.Create(10)); + ref2.Value.Should().Be(BslNumericValue.Create(10)); + } + } +} diff --git a/src/Tests/OneScript.Core.Tests/ValuesTest.cs b/src/Tests/OneScript.Core.Tests/ValuesTest.cs new file mode 100644 index 000000000..5e22c455a --- /dev/null +++ b/src/Tests/OneScript.Core.Tests/ValuesTest.cs @@ -0,0 +1,326 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using FluentAssertions; +using Moq; +using OneScript.Commons; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary; +using OneScript.Types; +using OneScript.Values; +using ScriptEngine; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using Xunit; + +namespace OneScript.Core.Tests +{ + public class ValuesTest + { + [Fact] + public void BooleanEquality() + { + Assert.True(BslBooleanValue.True.AsBoolean()); + Assert.False(BslBooleanValue.False.AsBoolean()); + + Assert.Same(BslBooleanValue.True, ValueFactory.Create(true)); + Assert.Same(BslBooleanValue.False, ValueFactory.Create(false)); + Assert.NotEqual(BslBooleanValue.False, BslBooleanValue.True); + Assert.Equal(0, BslBooleanValue.False.AsNumber()); + Assert.Equal(1, BslBooleanValue.True.AsNumber()); + + Assert.True(BslBooleanValue.True.CompareTo(BslBooleanValue.False) > 0); + + Assert.ThrowsAny(() => BslBooleanValue.True.AsDate()); + Assert.ThrowsAny(() => BslBooleanValue.True.AsObject()); + } + + [Theory] + [InlineData("ru", "Да", "Нет")] + [InlineData("en", "True", "False")] + [InlineData("jp", "True", "False")] + public void BooleanStringLocales(string locale, string trueString, string falseString) + { + Locale.SystemLanguageISOName = locale; + Assert.Equal(trueString, BslBooleanValue.True.ToString()); + Assert.Equal(falseString, BslBooleanValue.False.ToString()); + } + + [Fact] + public void NumbersEquality() + { + var num1 = ValueFactory.Create(12.5M); + var num2 = ValueFactory.Create(12.5M); + var num3 = ValueFactory.Create(7); + var num4 = ValueFactory.Create(0); + + Assert.Equal(num1, num2); + Assert.True(num1.Equals(num2)); + Assert.NotEqual(num1, num3); + + Assert.Equal(0, num4.AsNumber()); + Assert.Equal(7, num3.AsNumber()); + + Assert.True(num1.AsBoolean()); + Assert.True(num2.AsBoolean()); + Assert.True(num3.AsBoolean()); + Assert.False(num4.AsBoolean()); + + Assert.True(num1.CompareTo(num2) == 0); + Assert.True(num1.CompareTo(num3) > 0); + Assert.True(num4.CompareTo(num3) < 0); + + Assert.Equal("12.5", num1.ToString()); + Assert.ThrowsAny(() => num1.AsDate()); + Assert.ThrowsAny(() => num1.AsObject()); + } + + [Fact] + public void PopularNumbersReferenceEquality() + { + for (int i = 0; i < 10; i++) + { + var n1 = ValueFactory.Create(i); + var n2 = ValueFactory.Create(i); + + Assert.Same(n1, n2); + } + } + + [Fact] + public void StringValueTests() + { + var trueString = ValueFactory.Create("ИстИНа"); + Assert.True(trueString.SystemType == BasicTypes.String); + Assert.True(trueString.AsBoolean()); + Assert.True(trueString.ToString() == "ИстИНа"); + + var falseString = ValueFactory.Create("лОжЬ"); + Assert.False(falseString.AsBoolean()); + Assert.True(falseString.ToString() == "лОжЬ"); + + var dateString = ValueFactory.Create("20140101"); + DateTime jan_01_14 = new DateTime(2014,01,01); + Assert.True(dateString.AsDate() == jan_01_14); + + var numString = ValueFactory.Create("012.12"); + Assert.True(numString.AsNumber() == 12.12m); + + Assert.ThrowsAny(() => dateString.AsObject()); + Assert.ThrowsAny(() => trueString.AsNumber()); + } + + [Fact] + public void Undefined_Value_Test() + { + var value = ValueFactory.Create(); + Assert.True(value.SystemType == BasicTypes.Undefined); + Assert.True(value.ToString() == ""); + + Assert.ThrowsAny(() => value.AsNumber()); + Assert.ThrowsAny(() => value.AsBoolean()); + Assert.ThrowsAny(() => value.AsObject()); + Assert.ThrowsAny(() => value.AsDate()); + } + + [Fact] + public void Null_Value_Test() + { + var value = ValueFactory.CreateNullValue(); + Assert.True(value is BslNullValue); + Assert.True(ReferenceEquals(value, BslNullValue.Instance)); + Assert.True(value.ToString() == ""); + + Assert.ThrowsAny(() => value.AsNumber()); + Assert.ThrowsAny(() => value.AsBoolean()); + Assert.ThrowsAny(() => value.AsObject()); + Assert.ThrowsAny(() => value.AsDate()); + } + + [Fact] + public void Type_Value_Test() + { + var typeValue = new BslTypeValue(BasicTypes.String); + Assert.True(typeValue.SystemType == BasicTypes.Type); + Assert.Equal("Строка", typeValue.ToString()); + + Assert.ThrowsAny(() => typeValue.AsNumber()); + Assert.ThrowsAny(() => typeValue.AsBoolean()); + Assert.ThrowsAny(() => typeValue.AsObject()); + Assert.ThrowsAny(() => typeValue.AsDate()); + + } + + [Fact] + public void Number_To_Number_Comparison() + { + var num1 = ValueFactory.Create(1); + var num2 = ValueFactory.Create(2); + var num3 = ValueFactory.Create(1); + + Assert.True(num1.CompareTo(num2) < 0); + Assert.True(num1.CompareTo(num3) == 0); + Assert.True(num2.CompareTo(num1) > 0); + Assert.True(num3.CompareTo(num2) < 0); + + } + + [Fact] + public void Invalid_Comparison_Num_And_String() + { + var num1 = ValueFactory.Create(1); + var num2 = ValueFactory.Create("2"); + + Assert.ThrowsAny(() => num1.CompareTo(num2)); + Assert.ThrowsAny(() => num2.CompareTo(num1)); + } + + [Fact] + public void Invalid_Comparison_Undefined() + { + var v1 = ValueFactory.Create(); + var v2 = ValueFactory.Create(true); + + Assert.ThrowsAny(() => v1.CompareTo(v2)); + } + + [Fact] + public void String_To_String_Comparison() + { + var str1 = ValueFactory.Create("АБВ"); + var str2 = ValueFactory.Create("ВГД"); + + str1.CompareTo(str2).Should().BeLessThan(0); + str2.CompareTo(str1).Should().BeGreaterThan(0); + str1.CompareTo(ValueFactory.Create("абв")).Should().BeGreaterThan(0); + } + + [Fact] + public void Boolean_Equality() + { + var bool1 = ValueFactory.Create(true); + var bool0 = ValueFactory.Create(false); + var num1 = ValueFactory.Create(1); + var num0 = ValueFactory.Create(0); + var num2 = ValueFactory.Create(2); + + Assert.True(bool0.Equals(bool0)); + Assert.True(bool1.Equals(bool1)); + Assert.False(bool0.Equals(bool1)); + Assert.False(bool1.Equals(bool0)); + + Assert.True(bool0.Equals(num0)); + Assert.True(bool1.Equals(num1)); + Assert.False(bool0.Equals(num1)); + Assert.False(bool1.Equals(num0)); + + Assert.False(bool0.Equals(num2)); + Assert.False(bool1.Equals(num2)); + + Assert.True(num0.Equals(bool0)); + Assert.True(num1.Equals(bool1)); + Assert.False(num0.Equals(bool1)); + Assert.False(num1.Equals(bool0)); + + Assert.False(num2.Equals(bool1)); + Assert.False(num2.Equals(bool0)); + } + + [Fact] + public void Boolean_Comparison() + { + var v1 = ValueFactory.Create(true); + var v2 = ValueFactory.Create(false); + var num1 = ValueFactory.Create(1); + var num0 = ValueFactory.Create(0); + + Assert.True(v1.CompareTo(v2) > 0); + Assert.True(v2.CompareTo(v1) < 0); + Assert.True(v1.CompareTo(num1) == 0); + Assert.True(v2.CompareTo(num0) == 0); + Assert.True(v1.CompareTo(num0) > 0); + Assert.True(v2.CompareTo(num1) < 0); + Assert.True(num1.CompareTo(v1) == 0); + Assert.True(num1.CompareTo(v2) > 0); + } + + [Theory] + [InlineData("Null", DataType.Null, typeof(BslNullValue))] + [InlineData("Истина", DataType.Boolean, typeof(BslBooleanValue))] + [InlineData("Ложь", DataType.Boolean, typeof(BslBooleanValue))] + [InlineData("True", DataType.Boolean, typeof(BslBooleanValue))] + [InlineData("False", DataType.Boolean, typeof(BslBooleanValue))] + [InlineData("20140105", DataType.Date, typeof(BslDateValue))] + [InlineData("20140105010101", DataType.Date, typeof(BslDateValue))] + [InlineData("Неопределено", DataType.Undefined, typeof(BslUndefinedValue))] + [InlineData("Undefined", DataType.Undefined, typeof(BslUndefinedValue))] + public void ValueFactory_Parse(string literal, DataType type, Type implementation) + { + var value = ValueFactory.Parse(literal, type); + Assert.True(value.SystemType.ImplementingClass == implementation); + } + + [Fact] + public void TypeEqualityOnCreationTest() + { + var autoGuid = new GuidWrapper(); + var manualGuid = new GuidWrapper("9F3457C0-7D2A-4DCD-B9F9-3D9228986A6A"); + + var typeManager = new DefaultTypeManager(); + var discoverer = new ContextDiscoverer(typeManager, Mock.Of(), default); + + discoverer.DiscoverClasses(typeof(GuidWrapper).Assembly, x => x == typeof(GuidWrapper)); + + Assert.True(typeManager.IsKnownType(typeof(GuidWrapper))); + var typeFromManager = typeManager.GetTypeByFrameworkType(typeof(GuidWrapper)); + + Assert.Equal(autoGuid.SystemType, typeFromManager); + Assert.Equal(manualGuid.SystemType, typeFromManager); + Assert.Equal(manualGuid.SystemType, autoGuid.SystemType); + + } + + public static IEnumerable FilledValues => new [] + { + new object[] { ValueFactory.Create(1) }, + new object[] { ValueFactory.Create("hello") }, + new object[] { ValueFactory.Create(DateTime.Now) }, + new object[] { ValueFactory.Create(true) }, + new object[] { ValueFactory.Create(false) }, + new object[] { new GuidWrapper() } + }; + + public static IEnumerable EmptyValues => new [] + { + new object[] { ValueFactory.Create(0) }, + new object[] { ValueFactory.Create()}, + new object[] { ValueFactory.Create(DateTime.MinValue)}, + new object[] { ValueFactory.CreateNullValue()}, + new object[] { ValueFactory.Create("")}, + new object[] { ValueFactory.Create(" ")}, + new object[] { new GuidWrapper(Guid.Empty.ToString())} + }; + + [Theory] + [MemberData(nameof(FilledValues))] + public void ValueFilledTest(IValue value) + { + var globCtx = new StandardGlobalContext(); + Assert.True(globCtx.ValueIsFilled(ForbiddenBslProcess.Instance, (BslValue)value)); + } + + [Theory] + [MemberData(nameof(EmptyValues))] + public void ValueEmptyTest(IValue value) + { + var globCtx = new StandardGlobalContext(); + Assert.False(globCtx.ValueIsFilled(ForbiddenBslProcess.Instance, (BslValue)value)); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.DebugProtocol.Test/DebugVariablesTest.cs b/src/Tests/OneScript.DebugProtocol.Test/DebugVariablesTest.cs new file mode 100644 index 000000000..869a7a3fe --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/DebugVariablesTest.cs @@ -0,0 +1,237 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using FluentAssertions; +using OneScript.DebugServices; +using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Collections.ValueTree; +using OneScript.Types; +using ScriptEngine.Machine; +using ScriptEngine.Types; +using Xunit; + +namespace OneScript.DebugProtocol.Test +{ + // TODO стандартный визуализатор ничего не знает про особенности коллекций + // После завершения ветки breaking-refactory визуализатор, возможно переедет в другую сборку + // пока оставляю так, что IVariableVisualizer живет в DebugServices + public class DebugVariablesTest + { + + public DebugVariablesTest() + { + Visualizer = new DefaultVariableVisualizer(); + Manager = new DefaultTypeManager(); + } + + private ITypeManager Manager { get; } + + private IValue GetInstance(Type valueType, IValue[] args) + { + Manager.RegisterClass(valueType); + + var registeredType = Manager.GetTypeByFrameworkType(valueType); + var factory = (TypeFactory)Manager.GetFactoryFor(registeredType); + return factory.Activate(new TypeActivationContext + { + TypeManager = Manager, + TypeName = registeredType.Name + },args); + } + + private T GetInstance(params IValue[] args) where T : IValue + { + return (T) GetInstance(typeof(T), args); + } + + public IVariableVisualizer Visualizer { get; set; } + + [Fact] + public void SimpleValuePresentation() + { + var str = ValueFactory.Create("string value"); + + var debuggerVar = Visualizer.GetVariable(Contexts.Variable.Create(str,"myString")); + + debuggerVar.Name.Should().Be("myString"); + + debuggerVar.TypeName.Should().Be("Строка"); + debuggerVar.Presentation.Should().Be("string value"); + debuggerVar.IsStructured.Should().BeFalse(); + + var number = ValueFactory.Create(27.2m); + debuggerVar = Visualizer.GetVariable(Contexts.Variable.Create(number,"myInt")); + debuggerVar.Name.Should().Be("myInt"); + debuggerVar.TypeName.Should().Be("Число"); + debuggerVar.Presentation.Should().Be("27.2"); + debuggerVar.IsStructured.Should().BeFalse(); + } + + [Fact] + public void ObjectPresentation() + { + var obj = GetInstance(ValueFactory.Create("somefile.txt")); + + var debuggerVar = Visualizer.GetVariable(Contexts.Variable.Create(obj,"myFile")); + debuggerVar.Name.Should().Be("myFile"); + debuggerVar.TypeName.Should().Be("Файл"); + debuggerVar.Presentation.Should().Be("Файл"); + debuggerVar.IsStructured.Should().BeTrue(); + + var children = Visualizer.GetChildVariables(obj) + .Select(x => Visualizer.GetVariable(x)) + .ToArray(); + + children.Should().NotBeEmpty(); + children.All(x => x.IsStructured == false).Should().BeTrue(); + + var childrenMap = children.ToDictionary(x => x.Name); + childrenMap["Имя"].IsStructured.Should().BeFalse(); + childrenMap["Имя"].Presentation.Should().Be("somefile.txt"); + childrenMap["Расширение"].IsStructured.Should().BeFalse(); + childrenMap["Расширение"].Presentation.Should().Be(".txt"); + } + + [Fact] + public void ArrayPresentation() + { + var obj = GetInstance(); + obj.Add(ValueFactory.Create(1)); + obj.Add(ValueFactory.Create(2)); + + var debuggerVar = Visualizer.GetVariable(Contexts.Variable.Create(obj, "myArray")); + Assert.Equal("Массив", debuggerVar.Presentation); + Assert.True(debuggerVar.IsStructured); + + var items = Visualizer.GetChildVariables(obj).ToArray(); + items.Should().HaveCount(2); + Assert.Equal("0", items[0].Name); + Assert.Equal("1", items[1].Name); + } + + [Fact] + public void StructurePresentation() + { + Manager.RegisterClass(typeof(KeyAndValueImpl)); + + var obj = GetInstance(); + obj.Insert("first", ValueFactory.Create(1)); + obj.Insert("second", ValueFactory.Create(2)); + + var debuggerVar = Visualizer.GetVariable(Contexts.Variable.Create(obj, "myVar")); + Assert.Equal("Структура", debuggerVar.Presentation); + Assert.True(debuggerVar.IsStructured); + + var items = Visualizer + .GetChildVariables(obj) + .Select(x => Visualizer.GetVariable(x)) + .ToArray(); + + items.Should().HaveCount(2); + Assert.Equal("first", items[0].Name); + Assert.Equal("Число", items[0].TypeName); + Assert.Equal("1", items[0].Presentation); + Assert.False(items[0].IsStructured); + + Assert.Equal("second", items[1].Name); + Assert.Equal("Число", items[1].TypeName); + Assert.Equal("2", items[1].Presentation); + Assert.False(items[1].IsStructured); + } + + [Fact] + public void MapPresentation() + { + Manager.RegisterClass(typeof(KeyAndValueImpl)); + + var obj = GetInstance(); + obj.Insert(ValueFactory.Create("first"), ValueFactory.Create(1)); + obj.Insert(ValueFactory.Create("second"), ValueFactory.Create(2)); + + var debuggerVar = Visualizer.GetVariable(Contexts.Variable.Create(obj, "myVar")); + Assert.Equal("Соответствие", debuggerVar.Presentation); + Assert.True(debuggerVar.IsStructured); + + var items = Visualizer.GetChildVariables(obj) + .Select(x => Visualizer.GetVariable(x)) + .ToArray(); + + items.Should().HaveCount(2); + + Assert.Equal("0", items[0].Name); + Assert.Equal("КлючИЗначение", items[0].TypeName); + Assert.Equal("КлючИЗначение", items[0].Presentation); + Assert.True(items[0].IsStructured); + + Assert.Equal("1", items[1].Name); + Assert.Equal("КлючИЗначение", items[1].TypeName); + Assert.Equal("КлючИЗначение", items[1].Presentation); + Assert.True(items[1].IsStructured); + + var keyValue = Visualizer.GetChildVariables(obj.First()) + .Select(x => Visualizer.GetVariable(x)) + .ToArray(); + + Assert.Equal("Ключ", keyValue[0].Name); + Assert.Equal("first", keyValue[0].Presentation); + Assert.Equal("Значение", keyValue[1].Name); + Assert.Equal("1", keyValue[1].Presentation); + } + + [Fact] + public void ValueTreePresentation() + { + Manager.RegisterClass(typeof(ValueTreeRowCollection)); + Manager.RegisterClass(typeof(ValueTreeRow)); + Manager.RegisterClass(typeof(ValueTreeColumn)); + Manager.RegisterClass(typeof(ValueTreeColumnCollection)); + + var obj = GetInstance(); + obj.Columns.Add("first"); + obj.Columns.Add("second"); + + var row = obj.Rows.Add(); + row.Set(0, ValueFactory.Create("val1")); + row.Set(1, ValueFactory.Create("val2")); + + var variables = Visualizer.GetChildVariables(obj) + .Select(x => Visualizer.GetVariable(x)) + .ToDictionary(x => x.Name); + + variables.Should().HaveCount(2); + + Assert.True(variables["Строки"].IsStructured); + Assert.True(variables["Колонки"].IsStructured); + + var rows = Visualizer.GetChildVariables(obj.Rows) + .Select(x => Visualizer.GetVariable(x)) + .ToArray(); + + rows.Should().HaveCount(2); + Assert.Equal("Родитель", rows[0].Name); + Assert.False(rows[0].IsStructured); + Assert.Equal("0", rows[1].Name); + Assert.Equal("СтрокаДереваЗначений", rows[1].TypeName); + Assert.True(rows[1].IsStructured); + + var rowData = Visualizer.GetChildVariables(row) + .Select(x => Visualizer.GetVariable(x)) + .ToArray(); + + rowData.Should().HaveCount(4); + Assert.Equal("Родитель", rowData[0].Name); + Assert.Equal("Строки", rowData[1].Name); + Assert.Equal("first", rowData[2].Name); + Assert.Equal("val1", rowData[2].Presentation); + Assert.Equal("second", rowData[3].Name); + Assert.Equal("val2", rowData[3].Presentation); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.DebugProtocol.Test/DebuggerTests.cs b/src/Tests/OneScript.DebugProtocol.Test/DebuggerTests.cs new file mode 100644 index 000000000..c6e3bb33d --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/DebuggerTests.cs @@ -0,0 +1,151 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Threading; +using FluentAssertions; +using OneScript.DebugProtocol.TcpServer; +using OneScript.DebugProtocol.Test.Tools; +using OneScript.DebugServices; +using Xunit; + +namespace OneScript.DebugProtocol.Test +{ + public class DebuggerTests + { + [Fact(DisplayName = "В режиме Launch сессия до соединения неактивна, после - активна.")] + public void TestActivatingOnIncomingClient() + { + var transport = new TcpDebugServer(0); + var debugger = new DefaultDebugger(transport); + debugger.AttachMode = false; + debugger.Start(); + + debugger.GetSession().IsActive.Should().BeFalse(); + + var client = new TestDebuggerClient(); + client.Connect(transport.ActualPort()); + + WaitEvent(() => debugger.GetSession().IsActive, 2000).Should().BeTrue(); + } + + [Theory(DisplayName = "В режиме Attach метод WaitReadyToRun не блокируется.")] + [InlineData(true)] + [InlineData(false)] + public void TestNonBlockingSessionWaitToRun(bool connectBeforeWait) + { + var transport = new TcpDebugServer(0); + var debugger = new DefaultDebugger(transport); + debugger.AttachMode = true; + debugger.Start(); + + debugger.GetSession().IsActive.Should().BeFalse(); + + var client = new TestDebuggerClient(); + if (connectBeforeWait) + { + client.Connect(transport.ActualPort()); + Thread.Sleep(100); + } + + var exitEvent = new ManualResetEventSlim(false); + + ThreadPool.QueueUserWorkItem(_ => + { + debugger.GetSession().WaitReadyToRun(); // не должно зависнуть + exitEvent.Set(); + }); + + exitEvent.Wait(2000).Should().BeTrue(); + } + + [Theory(DisplayName = "В режиме Launch метод WaitReadyToRun блокируется.")] + [InlineData(true)] + [InlineData(false)] + public void TestBlockingSessionWaitToRun(bool connectBeforeWait) + { + var transport = new TcpDebugServer(0); + var debugger = new DefaultDebugger(transport); + debugger.AttachMode = false; + debugger.Start(); + + debugger.GetSession().IsActive.Should().BeFalse(); + + var client = new TestDebuggerClient(); + if (connectBeforeWait) + { + client.Connect(transport.ActualPort()); + Thread.Sleep(100); + } + + var exitEvent = new ManualResetEventSlim(false); + + ThreadPool.QueueUserWorkItem(_ => + { + debugger.GetSession().WaitReadyToRun(); // должно зависнуть до получения команды Execute + exitEvent.Set(); + }); + + if (!connectBeforeWait) + { + client.Connect(transport.ActualPort()); + Thread.Sleep(100); + } + + client.Send(RpcCall.Create(nameof(IDebuggerService), nameof(IDebuggerService.Execute), 0)); + + exitEvent.Wait(2000).Should().BeTrue(); + } + + [Fact] + public void CanCreateAnotherSessionAfterDisconnect() + { + var transport = new TcpDebugServer(0); + var debugger = new DefaultDebugger(transport); + debugger.AttachMode = true; + debugger.Start(); + + debugger.GetSession().IsActive.Should().BeFalse(); + + var client = new TestDebuggerClient(); + client.Connect(transport.ActualPort()); + + var session = debugger.GetSession(); + + WaitEvent(() => session.IsActive, 2000).Should().BeTrue(); + + client.Send(RpcCall.Create(nameof(IDebuggerService), nameof(IDebuggerService.Disconnect), false)); + + WaitEvent(() => !session.IsActive, 2000).Should().BeTrue(); + + var client2 = new TestDebuggerClient(); + client2.Connect(transport.ActualPort()); + var session2 = debugger.GetSession(); + + WaitEvent(() => session2.IsActive, 2000).Should().BeTrue(); + + session2.Should().NotBe(session); + } + + private bool WaitEvent(Func predicate, int timeout) + { + var wait = new SpinWait(); + int start = Environment.TickCount; + while (!predicate()) + { + wait.SpinOnce(); + var elapsed = Environment.TickCount - start; + if (elapsed > timeout) + { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.DebugProtocol.Test/JsonChannelTest.cs b/src/Tests/OneScript.DebugProtocol.Test/JsonChannelTest.cs new file mode 100644 index 000000000..e68fa074e --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/JsonChannelTest.cs @@ -0,0 +1,84 @@ +/*---------------------------------------------------------- +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.IO; +using FluentAssertions; +using OneScript.DebugProtocol.TcpServer; +using OneScript.Exceptions; +using Xunit; + +namespace OneScript.DebugProtocol.Test +{ + public class JsonChannelTest + { + + [Fact] + public void TestObjectWritingReadingCall() + { + var dataStream = new MemoryStream(); + var rpcCall = RpcCall.Create("Test", "Hello World", 1, 2, new Breakpoint() + { + Line = 2, Condition = "true" + }); + + var channel = new JsonDtoChannel(dataStream); + channel.Write(rpcCall); + dataStream.Position = 0; + var actual = channel.Read(); + actual.Should().BeEquivalentTo(rpcCall); + } + + [Fact] + public void TestObjectWritingReadingResponseSuccess() + { + var dataStream = new MemoryStream(); + var rpcCall = RpcCall.Create("Test", "Hello World", 1, 2, new Breakpoint() + { + Line = 2, Condition = "true" + }); + + var rpcResponse = RpcCallResult.Respond(rpcCall, new Variable + { + Name = "Test", + Presentation = "1" + }); + + var channel = new JsonDtoChannel(dataStream); + channel.Write(rpcResponse); + dataStream.Position = 0; + var actual = channel.Read(); + actual.Should().BeEquivalentTo(rpcResponse); + } + + [Fact] + public void TestObjectWritingReadingResponseException() + { + var dataStream = new MemoryStream(); + var rpcCall = RpcCall.Create("Test", "Hello World", 1, 2, new Breakpoint() + { + Line = 2, Condition = "true" + }); + + var rpcResponse = RpcCallResult.Exception(rpcCall, new RuntimeException("Test", "Test")); + + var channel = new JsonDtoChannel(dataStream); + channel.Write(rpcResponse); + dataStream.Position = 0; + var actual = channel.Read(); + actual.Should().BeEquivalentTo(rpcResponse); + } + + [Fact] + public void TestReconcileMarker() + { + var encoded = FormatReconcileUtils.EncodeFormatMarker(2, 4); + var (transport, version) = FormatReconcileUtils.DecodeFormatMarker(encoded); + + Assert.Equal(2, transport); + Assert.Equal(4, version); + } + } +} diff --git a/src/NUnitTests/MethodDispatcherTest.cs b/src/Tests/OneScript.DebugProtocol.Test/MethodDispatcherTest.cs similarity index 81% rename from src/NUnitTests/MethodDispatcherTest.cs rename to src/Tests/OneScript.DebugProtocol.Test/MethodDispatcherTest.cs index cf58b82e8..864a234ce 100644 --- a/src/NUnitTests/MethodDispatcherTest.cs +++ b/src/Tests/OneScript.DebugProtocol.Test/MethodDispatcherTest.cs @@ -5,16 +5,15 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using FluentAssertions; using Moq; -using NUnit.Framework; -using OneScript.DebugProtocol; +using Xunit; -namespace NUnitTests +namespace OneScript.DebugProtocol.Test { - [TestFixture] public class MethodDispatcherTest { - [Test] + [Fact] public void TestVoidCallOfInterface() { var instance = new Mock(); @@ -26,17 +25,17 @@ public void TestVoidCallOfInterface() instance.VerifyNoOtherCalls(); } - [Test] + [Fact] public void TestFunctionCallOfInterface() { var instance = new Mock(); instance.Setup(i => i.GetThreads()).Returns(new []{1,2,3}); var dispatcher = new MethodsDispatcher(); - var result = dispatcher.Dispatch(instance.Object, "GetThreads", new object[0]); + var result = (int[])dispatcher.Dispatch(instance.Object, "GetThreads", new object[0]); instance.Verify(i => i.GetThreads(), Times.Once); instance.VerifyNoOtherCalls(); - Assert.That(result, Has.Length.EqualTo(3)); + result.Should().HaveCount(3); } } } \ No newline at end of file diff --git a/src/Tests/OneScript.DebugProtocol.Test/OneScript.DebugProtocol.Test.csproj b/src/Tests/OneScript.DebugProtocol.Test/OneScript.DebugProtocol.Test.csproj new file mode 100644 index 000000000..120023d49 --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/OneScript.DebugProtocol.Test.csproj @@ -0,0 +1,37 @@ + + + + $(TargetFrameworkVersion) + + false + + Debug;Release;LinuxDebug + + AnyCPU + true + + + + true + false + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Tests/OneScript.DebugProtocol.Test/ReconcileForOlderVersionTests.cs b/src/Tests/OneScript.DebugProtocol.Test/ReconcileForOlderVersionTests.cs new file mode 100644 index 000000000..af3a8d95b --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/ReconcileForOlderVersionTests.cs @@ -0,0 +1,78 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using FluentAssertions; +using OneScript.DebugProtocol; +using OneScript.DebugProtocol.TcpServer; +using Xunit; + +namespace OneScript.DebugProtocol.Test +{ + public class ReconcileForOlderVersionTests + { + [Fact] + public void CanReadReconcileViaBinaryFormatter() + { + using (var stream = new MemoryStream(FormatReconcileUtils.GetReconcileMagic())) + { + var reader = new BinaryFormatter(); + + var value = reader.Deserialize(stream); + value.Should().BeEquivalentTo(new RpcCall()); + } + } + + [Fact(Skip = "Manual run only")] + public void DumpBinaryStream() + { + const string filePath = ""; + using (var stream = new MemoryStream(FormatReconcileUtils.GetReconcileMagic())) + { + var serializer = new BinaryFormatter(); + var sampleCall = RpcCall.Create(nameof(IDebuggerService), "$NonExistent$"); + + using (var dest = new MemoryStream()) + { + serializer.Serialize(dest, sampleCall); + using (var file = new FileStream(filePath, FileMode.Create)) + { + dest.Position = 0; + dest.CopyTo(file); + } + } + } + } + + [Fact] + public void ExchangeReconcileVersions() + { + using (var stream = new MemoryStream(FormatReconcileUtils.GetReconcileMagic())) + { + using(var response = new MemoryStream()) + { + if (FormatReconcileUtils.CheckReconcileRequest(stream)) + { + FormatReconcileUtils.WriteReconcileResponse(response, 1, 1); + } + + var data = response.ToArray(); + Assert.True(FormatReconcileUtils.CheckReconcilePrefix(data)); + Assert.Equal(FormatReconcileUtils.FORMAT_RECONCILE_RESPONSE_PREFIX.Length + sizeof(int), data.Length); + + var frmt = BitConverter.ToInt32(data, FormatReconcileUtils.FORMAT_RECONCILE_RESPONSE_PREFIX.Length); + var (transport, version) = FormatReconcileUtils.DecodeFormatMarker(frmt); + Assert.Equal(1, transport); + Assert.Equal(1, version); + } + } + } + } +} + diff --git a/src/Tests/OneScript.DebugProtocol.Test/TcpServerTest.cs b/src/Tests/OneScript.DebugProtocol.Test/TcpServerTest.cs new file mode 100644 index 000000000..c2a210e51 --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/TcpServerTest.cs @@ -0,0 +1,68 @@ +/*---------------------------------------------------------- +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.Net; +using System.Net.Sockets; +using System.Threading; +using FluentAssertions; +using OneScript.DebugServices; +using Xunit; + +namespace OneScript.DebugProtocol.Test +{ + public class TcpServerTest + { + [Fact] + public void ServerCanStartAndStop() + { + var logMessages = new List(); + var syncEvent = new AutoResetEvent(false); + var server = new TcpDebugServer(0); + server.OnLogEvent += (level, message) => + { + if ((int)level >= (int)TcpDebugServer.LogLevel.Info) + { + logMessages.Add(message); + syncEvent.Set(); + } + }; + + server.Listen(); + syncEvent.WaitOne(500).Should().BeTrue(); + server.Stop(); + syncEvent.WaitOne(500).Should().BeTrue(); + + logMessages.Should() + .HaveCount(2) + .And.Contain("Starting listener thread") + .And.Contain("Listener thread stopped"); + } + + [Fact] + public void ServerCanAcceptClients() + { + var server = new TcpDebugServer(0); + var syncEvent = new AutoResetEvent(false); + server.OnClientConnected += (sender, client) => + { + client.Should().NotBeNull(); + client.Connected.Should().BeTrue(); + syncEvent.Set(); + }; + + server.Listen(); + var client = new TcpClient(); + client.Connect(new IPEndPoint(IPAddress.Loopback, server.ActualPort())); + syncEvent.WaitOne(500).Should().BeTrue(); + + client.Connected.Should().BeTrue(); + client.Dispose(); + server.Stop(); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.DebugProtocol.Test/TestJsonSerializationForDto.cs b/src/Tests/OneScript.DebugProtocol.Test/TestJsonSerializationForDto.cs new file mode 100644 index 000000000..53c3a87f5 --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/TestJsonSerializationForDto.cs @@ -0,0 +1,87 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AutoFixture; +using AutoFixture.Kernel; +using FluentAssertions; +using Newtonsoft.Json; +using OneScript.DebugProtocol.TcpServer; +using Xunit; + +namespace OneScript.DebugProtocol.Test +{ + public class TestJsonSerializationForDto + { + [Theory] + [MemberData(nameof(DebuggerServiceCalls))] + public void CanSerializeAllMethodCallsForServer(RpcCall call, RpcCallResult? result) + { + // Не должен падать + var jsonCall = JsonConvert.SerializeObject(call); + string jsonResult = null; + if (result != null) + { + jsonResult = JsonConvert.SerializeObject(result); + } + + // должен суметь прочитать + var callFromJson = JsonConvert.DeserializeObject(jsonCall); + callFromJson.Should().BeEquivalentTo(call); + if (jsonResult != null) + { + var resultFromJson = JsonConvert.DeserializeObject(jsonResult); + resultFromJson.Should().BeEquivalentTo(result); + } + } + + [Fact] + public void CanReadWriteCallWithNullParams() + { + var call = RpcCall.Create("Test", "Test", null); + var jsonCall = JsonConvert.SerializeObject(call); + var callFromJson = JsonConvert.DeserializeObject(jsonCall); + + callFromJson.Should().BeEquivalentTo(call); + } + + [Fact] + public void CanReadWriteResultWithNullValue() + { + var call = RpcCall.Create("Test", "Test", null); + var result = RpcCallResult.Respond(call, null); + var jsonResult = JsonConvert.SerializeObject(result); + var resultFromJson = JsonConvert.DeserializeObject(jsonResult); + + resultFromJson.Should().BeEquivalentTo(result); + } + + public static IEnumerable DebuggerServiceCalls() + { + var allMethods = typeof(IDebuggerService).GetMethods() + .Where(mi => mi.GetCustomAttribute() == null); + + var fixture = new Fixture(); + foreach (var methodInfo in allMethods) + { + var parameters = methodInfo.GetParameters() + .Select(p => new SpecimenContext(fixture).Resolve(p.ParameterType)) + .ToArray(); + + var call = RpcCall.Create(nameof(IDebuggerService), methodInfo.Name, parameters); + var callResult = methodInfo.ReturnType == typeof(void)? + null: + RpcCallResult.Respond(call, new SpecimenContext(fixture).Resolve(methodInfo.ReturnType)); + + yield return new object[] { call, callResult }; + } + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.DebugProtocol.Test/Tools/TestDebuggerClient.cs b/src/Tests/OneScript.DebugProtocol.Test/Tools/TestDebuggerClient.cs new file mode 100644 index 000000000..9fddb999e --- /dev/null +++ b/src/Tests/OneScript.DebugProtocol.Test/Tools/TestDebuggerClient.cs @@ -0,0 +1,48 @@ +/*---------------------------------------------------------- +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.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; +using Newtonsoft.Json; +using OneScript.DebugProtocol.TcpServer; + +namespace OneScript.DebugProtocol.Test.Tools +{ + public class TestDebuggerClient + { + private readonly TcpClient _client = new TcpClient(); + + public void Connect(int port) + { + _client.Connect(new IPEndPoint(IPAddress.Loopback, port)); + var handshake = FormatReconcileUtils.GetReconcileMagic(); + _client.GetStream().Write(handshake); + _client.GetStream().Flush(); + + var buffer = new byte[FormatReconcileUtils.FORMAT_RECONCILE_RESPONSE_PREFIX.Length + sizeof(int)]; + StreamUtils.ReadStream(_client.GetStream(), buffer, buffer.Length); + } + + public void Send(RpcCall message) + { + var content = JsonConvert.SerializeObject(message); + var contentBytes = Encoding.UTF8.GetBytes(content); + + using var bufferedStream = new MemoryStream(contentBytes.Length + sizeof(int)); + using var writer = new BinaryWriter(bufferedStream, Encoding.UTF8); + + writer.Write(contentBytes.Length); + writer.Write(contentBytes, 0, contentBytes.Length); + + bufferedStream.Position = 0; + bufferedStream.CopyTo(_client.GetStream()); + _client.GetStream().Flush(); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Dynamic.Tests/CompileHelper.cs b/src/Tests/OneScript.Dynamic.Tests/CompileHelper.cs new file mode 100644 index 000000000..d6affacbe --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/CompileHelper.cs @@ -0,0 +1,106 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Commons; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using OneScript.Native.Compiler; +using OneScript.Sources; +using ScriptEngine.Hosting; + +namespace OneScript.Dynamic.Tests +{ + internal class CompileHelper + { + private readonly IServiceContainer _services; + private readonly IErrorSink _errors = new ListErrorSink(); + private SourceCode _codeIndexer; + private BslSyntaxNode _module; + + public CompileHelper(IServiceContainer services) + { + _services = services; + } + + public CompileHelper() + { + _services = new TinyIocImplementation(); + } + + public IEnumerable Errors => _errors.Errors; + + public BslSyntaxNode ParseBatch(string code) + { + var parser = GetBslParser(code); + + _module = parser.ParseCodeBatch(true); + ThrowOnErrors(); + + return _module; + } + + public BslSyntaxNode ParseModule(string code) + { + var parser = GetBslParser(code); + + _module = parser.ParseStatefulModule(); + ThrowOnErrors(); + + return _module; + } + + public void ThrowOnErrors() + { + if (_errors.HasErrors) + { + var prefix = Locale.NStr("ru = 'Ошибка комиляции модуля'; en = 'Module compilation error'"); + var text = string.Join('\n', + (new[] { prefix }).Concat(_errors.Errors.Select(x => x.ToString(CodeError.ErrorDetails.Simple)))); + throw new Exception(text); + } + } + + private DefaultBslParser GetBslParser(string code) + { + var lexer = new DefaultLexer(); + lexer.Iterator = SourceCodeBuilder.Create() + .FromString(code) + .WithName("") + .Build() + .CreateIterator(); + _codeIndexer = lexer.Iterator.Source; + + var providers = _services.ResolveEnumerable(); + var parser = new DefaultBslParser(lexer, _errors, new PreprocessorHandlers(providers)); + return parser; + } + + public DynamicModule Compile(SymbolTable scopes, IBslProcess process) + { + if (scopes.ScopeCount == 0) + scopes.PushScope(new SymbolScope(), ScopeBindingDescriptor.Static(null)); + var compiler = new ModuleCompiler(_errors, _services, new DependencyResolverMock()); + return compiler.Compile(_codeIndexer, _module, scopes, process); + } + + private class DependencyResolverMock : ICompileTimeDependencyResolver + { + public PackageInfo Resolve(SourceCode module, string libraryName, IBslProcess process) + { + return null; + } + } + } +} diff --git a/src/Tests/OneScript.Dynamic.Tests/CompilerTestBase.cs b/src/Tests/OneScript.Dynamic.Tests/CompilerTestBase.cs new file mode 100644 index 000000000..6901a3fd2 --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/CompilerTestBase.cs @@ -0,0 +1,36 @@ +/*---------------------------------------------------------- +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 Moq; +using OneScript.Compilation.Binding; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Native.Compiler; + +namespace OneScript.Dynamic.Tests; + +public class CompilerTestBase +{ + protected DynamicModule CreateModule(string code) + { + var helper = new CompileHelper(); + helper.ParseModule(code); + var result = helper.Compile(new SymbolTable(), Mock.Of()); + helper.ThrowOnErrors(); + return result; + } + + protected DynamicModule CreateModule(string code, List errors) + { + var helper = new CompileHelper(); + helper.ParseModule(code); + var result = helper.Compile(new SymbolTable(), Mock.Of()); + errors.AddRange(helper.Errors); + return result; + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Dynamic.Tests/Compiler_Api_Tests.cs b/src/Tests/OneScript.Dynamic.Tests/Compiler_Api_Tests.cs new file mode 100644 index 000000000..c44e603d2 --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/Compiler_Api_Tests.cs @@ -0,0 +1,196 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using FluentAssertions; +using OneScript.Language; +using OneScript.Language.SyntaxAnalysis; +using Xunit; + +namespace OneScript.Dynamic.Tests +{ + public class Compiler_Api_Tests : CompilerTestBase + { + [Fact] + public void CanCompile_Empty_Module() + { + var module = CreateModule(""); + Assert.Empty(module.Fields); + Assert.Empty(module.Methods); + module.ModuleBody.Should().BeNull(); + } + + [Fact] + public void CanCompile_ModuleBody() + { + var module = CreateModule("А = 1"); + module.Methods.Should().HaveCount(1); + } + + [Fact] + public void CanCompile_Module_With_SeveralMethods() + { + var module = CreateModule( + @"Процедура А() + Б = 1; + КонецПроцедуры + + Процедура Б() + Б = 1; + КонецПроцедуры + + В = 4; + "); + + module.Methods.Should().HaveCount(3); + module.ModuleBody.Should().NotBeNull(); + } + + [Fact] + public void CanCompile_Module_With_AllSections() + { + var module = CreateModule( + @"Перем Ы; + + Процедура А() + Б = 1; + КонецПроцедуры + + Процедура Б() + Б = 1; + КонецПроцедуры + + В = 4; + "); + + module.Methods.Should().HaveCount(3); + module.ModuleBody.Should().NotBeNull(); + module.Fields.Should().HaveCount(1); + } + + [Fact] + public void Detects_DuplicateVars_InModule() + { + List errors = new List(); + CreateModule( + @"Перем Ы; + Перем О; + Перем Ы; + ", errors); + + errors.Should().HaveCount(1); + errors[0].ErrorId.Should().Be(nameof(LocalizedErrors.DuplicateVarDefinition)); + } + + [Fact] + public void Detects_DuplicateMethods_InModule() + { + List errors = new List(); + CreateModule( + @"Процедура А() + Б = 1; + КонецПроцедуры + + Процедура А() + Б = 1; + КонецПроцедуры + ", errors); + + errors.Should().HaveCount(1); + errors[0].ErrorId.Should().Be(nameof(LocalizedErrors.DuplicateMethodDefinition)); + } + + [Fact] + public void Test_Variant_Conversions() + { + var code = + @"Перем Стр; + Перем Индекс; + + Стр = ""1""; + Если Стр = ""2"" Тогда + КонецЕсли; + + Индекс = 1; + Символ = Сред(""АБВ"", Индекс, 1);"; + + var errors = new List(); + CreateModule(code, errors); + + errors.Should().BeEmpty(); + } + + [Fact] + public void Test_Min_Max_Functions() + { + var code = "Рез = Мин(0, 1); Рез = Макс(0, 1, 2, 3);"; + var errors = new List(); + CreateModule(code, errors); + + errors.Should().BeEmpty(); + } + + [Fact] + public void CanCompile_Func() + { + var code = @" + Функция Тест() + Возврат 123; + КонецФункции + + а = Тест();"; + + var errors = new List(); + CreateModule(code, errors); + + errors.Should().BeEmpty(); + } + + [Fact] + public void CanCompile_Proc_With_Return() + { + var code = @" + Процедура Тест() + Возврат; + КонецПроцедуры + + Тест();"; + + var errors = new List(); + CreateModule(code, errors); + + errors.Should().BeEmpty(); + } + + [Fact] + public void Detects_Proc_As_Func_Invoke() + { + var code = @" + Процедура Тест() + + КонецПроцедуры + + a = Тест();"; + + var errors = new List(); + CreateModule(code, errors); + + errors.Should().HaveCount(1); + errors[0].ErrorId.Should().Be(nameof(LocalizedErrors.UseProcAsFunction)); + } + + [Fact] + public void Test_Conversion_To_Boolean_For_Decimals_In_BooleanExpressions() + { + var code = @" + Номер = 0; + Операнд = 2=2 И Номер+1;"; + + CreateModule(code); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Dynamic.Tests/DefaultDlrBehaviorsTest.cs b/src/Tests/OneScript.Dynamic.Tests/DefaultDlrBehaviorsTest.cs new file mode 100644 index 000000000..812e000c7 --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/DefaultDlrBehaviorsTest.cs @@ -0,0 +1,58 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Values; +using Xunit; + +namespace OneScript.Dynamic.Tests +{ + public class DefaultDlrBehaviorsTest + { + [Fact] + public void Test_BinaryOperation_Binding_LeftSide() + { + dynamic bslNumber = BslNumericValue.Create(5); + var result = bslNumber + 5; + + Assert.Equal(10, result); + } + + [Fact] + public void Test_BinaryOperation_Binding_RightSide() + { + dynamic bslNumber = BslNumericValue.Create(5); + var result = 5 + bslNumber; + + Assert.Equal(10, result); + } + + [Fact] + public void Test_BinaryOperation_EqualityTest() + { + dynamic bslNumber = BslNumericValue.Create(5); + Assert.True(bslNumber == 5); + } + + [Fact] + public void Test_BinaryOperation_EqualityTest_OfBoth() + { + dynamic bslNumber1 = BslNumericValue.Create(5); + dynamic bslNumber2 = BslNumericValue.Create(5); + + Assert.True(bslNumber1 == bslNumber2); + Assert.Equal(bslNumber1, bslNumber2); + } + + [Fact] + public void Test_BinaryOperation_ComparisonTest() + { + dynamic bslNumber1 = BslNumericValue.Create(5); + dynamic bslNumber2 = BslNumericValue.Create(2); + Assert.True(bslNumber1 > bslNumber2); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Dynamic.Tests/DynamicOperationsTest.cs b/src/Tests/OneScript.Dynamic.Tests/DynamicOperationsTest.cs new file mode 100644 index 000000000..9fa15c9cd --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/DynamicOperationsTest.cs @@ -0,0 +1,47 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System.Collections.Generic; +using FluentAssertions; +using OneScript.Exceptions; +using OneScript.Native.Runtime; +using OneScript.Values; +using Xunit; + +namespace OneScript.Dynamic.Tests; + +public class DynamicOperationsTest +{ + [Theory] + [MemberData(nameof(ArgsForEqualityOperators))] + public void DynamicEquality(BslValue left, BslValue right, bool expected) + { + var result = DynamicOperations.Equality(left, right); + result.Should().Be(expected); + } + + [Fact] + public void DynamicAdditionForNulls() + { + Assert.Throws(() => DynamicOperations.Add(null, null)); + } + + [Fact] + public void DynamicSubtractionForNulls() + { + Assert.Throws(() => DynamicOperations.Subtract(null, null)); + } + + public static IEnumerable ArgsForEqualityOperators() + { + yield return new object[] { BslBooleanValue.True, BslBooleanValue.True, true }; + yield return new object[] { BslBooleanValue.True, null, false }; + yield return new object[] { null, BslBooleanValue.True, false }; + yield return new object[] { BslBooleanValue.True, BslUndefinedValue.Instance, false }; + // TODO: расширить по мере возможности + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Dynamic.Tests/NativeExecutableTestProcess.cs b/src/Tests/OneScript.Dynamic.Tests/NativeExecutableTestProcess.cs new file mode 100644 index 000000000..f5c18cb9c --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/NativeExecutableTestProcess.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Contexts; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Native.Runtime; +using OneScript.Values; +using ScriptEngine.Machine; + +namespace OneScript.Dynamic.Tests; + +public class NativeExecutableTestProcess : IBslProcess +{ + private readonly IExecutorProvider _executorProvider = new NativeExecutorProvider(); + + public BslValue Run(BslObjectValue target, IExecutableModule module, BslScriptMethodInfo method, IValue[] arguments) + { + return _executorProvider.GetInvokeDelegate()(this, target, module, method, arguments); + } + + public IServiceContainer Services { get; set; } + + public int VirtualThreadId => 0; +} \ No newline at end of file diff --git a/src/Tests/OneScript.Dynamic.Tests/NativeSdoTests.cs b/src/Tests/OneScript.Dynamic.Tests/NativeSdoTests.cs new file mode 100644 index 000000000..531d7fd3b --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/NativeSdoTests.cs @@ -0,0 +1,488 @@ +/*---------------------------------------------------------- +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 FluentAssertions; +using Moq; +using OneScript.Compilation; +using OneScript.Compilation.Binding; +using OneScript.DependencyInjection; +using OneScript.Execution; +using OneScript.Language; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Native.Compiler; +using OneScript.Native.Extensions; +using OneScript.Sources; +using OneScript.StandardLibrary; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Collections.ValueTable; +using OneScript.StandardLibrary.Regex; +using OneScript.StandardLibrary.TypeDescriptions; +using OneScript.Types; +using ScriptEngine; +using ScriptEngine.Compiler; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Contexts; +using ScriptEngine.Types; +using Xunit; + +namespace OneScript.Dynamic.Tests +{ + public class NativeSdoTests + { + private IServiceDefinitions testServices; + + public NativeSdoTests() + { + testServices = new TinyIocImplementation(); + testServices.Register(sp => sp); + testServices.RegisterSingleton(); + testServices.RegisterSingleton(); + testServices.RegisterSingleton(); + testServices.RegisterEnumerable(); + testServices.Register(); + testServices.UseImports(); + } + + // ReSharper disable once ClassNeverInstantiated.Local + private class EmptyDirectiveHandler : IDirectiveHandler + { + public void OnModuleEnter() + { + } + + public void OnModuleLeave() + { + } + + public bool HandleDirective(ref Lexem lastExtractedLexem, ILexer lexer) + { + return false; + } + } + + [Fact] + public void Test_Can_Instantiate_Sdo_With_NativeModule() + { + var module = CreateModule( + @"Перем Ы; + + Процедура А() + Б = 1; + КонецПроцедуры + + Процедура Б() + Б = 1; + КонецПроцедуры + + В = 4; + "); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass", default)); + + sdo.InitOwnData(); + sdo.Initialize(GetProcess(testServices.CreateContainer())); + } + + private IBslProcess GetProcess(IServiceContainer serviceContainer) + { + return new NativeExecutableTestProcess + { + Services = serviceContainer + }; + } + + [Fact] + public void Test_Local_Method_Can_Be_Called_and_Field_Set() + { + var symbols = new SymbolTable(); + + var serviceContainer = testServices.CreateContainer(); + var module = CreateModule( + @"Перем Ы Экспорт; + + Процедура А() Экспорт + Ы = 1; + КонецПроцедуры + + А(); + ", serviceContainer, symbols); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(serviceContainer)); + var n = sdo.GetPropertyNumber("Ы"); + var val = sdo.GetPropValue(n); + val.SystemType.Should().Be(BasicTypes.Number); + val.AsNumber().Should().Be(1); + } + + [Fact] + public void Test_Can_Pass_Arguments_To_Local_Method() + { + var symbols = new SymbolTable(); + + var serviceContainer = testServices.CreateContainer(); + var discoverer = serviceContainer.Resolve(); + discoverer.DiscoverClasses(typeof(StructureImpl).Assembly); + var module = CreateModule( + @"Перем Результат Экспорт; + + Процедура Тест(А, Б, В, Г) + Результат = Новый Структура(""А,Б,В,Г"", А,Б,В,Г); + КонецПроцедуры + + Тест(1, 2, 3, 4); + ", serviceContainer, symbols); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(serviceContainer)); + var n = sdo.GetPropertyNumber("Результат"); + var val = sdo.GetPropValue(n) as StructureImpl; + val.Should().NotBeNull(); + + val.GetPropValue("А").AsNumber().Should().Be(1); + val.GetPropValue("Б").AsNumber().Should().Be(2); + val.GetPropValue("В").AsNumber().Should().Be(3); + val.GetPropValue("Г").AsNumber().Should().Be(4); + + } + + [Fact] + public void Test_Can_Read_Module_Variables() + { + var symbols = new SymbolTable(); + var serviceContainer = testServices.CreateContainer(); + var module = CreateModule( + @"Перем М Экспорт; + + Процедура А() Экспорт + М = М + 20; + КонецПроцедуры + + М = 1; + А(); + ", serviceContainer, symbols); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(serviceContainer)); + var n = sdo.GetPropertyNumber("М"); + var val = sdo.GetPropValue(n); + val.SystemType.Should().Be(BasicTypes.Number); + val.AsNumber().Should().Be(21); + } + + [Fact] + public void CanSelectNativeCompiler() + { + testServices.UseNativeRuntime(); + + var code = + @" + #native + A = 1; + N = 4;"; + + var services = testServices.CreateContainer(); + var compiler = services.Resolve(); + + var source = SourceCodeBuilder.Create().FromString(code).Build(); + var module = compiler.Compile(source, Mock.Of()); + + module.Should().BeOfType(); + } + + [Fact] + public void Test_Can_Call_ForwardedDeclarationCall() + { + var symbols = new SymbolTable(); + var serviceContainer = testServices.CreateContainer(); + var module = CreateModule( + @"Процедура Процедура1() + Процедура2(); + КонецПроцедуры + + Процедура Процедура2() + КонецПроцедуры + + Процедура1()", serviceContainer, symbols); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(serviceContainer)); + } + + [Fact] + public void Can_Assign_To_BslDictionary() + { + var code = string.Join('\n', + "Соответствие = Новый Соответствие;", + "Соответствие[\"1\"] = \"2\";" + ); + + var services = testServices.CreateContainer(); + services.Resolve().RegisterClass(typeof(MapImpl)); + CreateModule(code, services, new SymbolTable()); + } + + [Fact] + public void Can_Read_From_BslDictionary() + { + var code = string.Join('\n', + "Соответствие = Новый Соответствие;", + "Соответствие.Вставить(\"1\",\"2\");", + "Рез = Соответствие[\"1\"];" + ); + + var services = testServices.CreateContainer(); + services.Resolve().RegisterClass(typeof(MapImpl)); + CreateModule(code, services, new SymbolTable()); + } + + [Fact] + public void NRE_At_Runtime_On_Method_Var_Issue1206() + { + var code = + @"Процедура Проц() + Перем Лок; + Лок = 0; + Если Лок > 1 Тогда + КонецЕсли; + КонецПроцедуры + + Проц();"; + + var module = CreateModule(code); + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(testServices.CreateContainer())); + } + + [Fact] + public void Integer_To_ClassField_assignment_In_Loop() + { + var code = + @"Перем Индекс; + Индекс = Неопределено; + Для Индекс = 0 По 1 Цикл + КонецЦикла;"; + + var module = CreateModule(code); + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(testServices.CreateContainer())); + } + + [Fact] + public void Integer_To_Variant_assignment_In_Loop() + { + var code = + @"Процедура Тест() + Перем Индекс; + Индекс = Неопределено; + Для Индекс = 0 По 1 Цикл + КонецЦикла; + КонецПроцедуры"; + + var module = CreateModule(code); + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(testServices.CreateContainer())); + } + + [Fact] + public void Call_Proc_On_Variants_Chain() + { + var code = + @"Процедура Проц() + Перем Таблица; + Таблица = Новый ТаблицаЗначений; + Таблица.Колонки.Добавить(""Имя"", Новый ОписаниеТипов(""Строка"")); + КонецПроцедуры"; + + var services = testServices.CreateContainer(); + var typeManager = services.Resolve(); + typeManager.RegisterClass(typeof(ValueTable)); + typeManager.RegisterClass(typeof(ValueTableColumnCollection)); + typeManager.RegisterClass(typeof(ValueTableColumn)); + typeManager.RegisterClass(typeof(TypeDescription)); + CreateModule(code, services, new SymbolTable()); + } + + [Fact] + public void Pass_Nullable_as_Optional_Args() + { + var code = "Массив = СтрРазделить(\"А,Б,В\", \",\", Ложь)"; + + var services = testServices.CreateContainer(); + var stringOperations = new StringOperations(); + var symbols = new SymbolTable(); + symbols.PushObject(stringOperations); + + CreateModule(code, services, symbols); + } + + [Fact] + public void Conditional_Expression() + { + var code = + @"Функция Знак(Число) + Возврат ?(Число<0,-1, 1); + КонецФункции"; + + var services = testServices.CreateContainer(); + var stringOperations = new StringOperations(); + var symbols = new SymbolTable(); + symbols.PushObject(stringOperations); + + CreateModule(code, services, symbols); + } + + [Fact] + public void Conditional_Expression_With_Different_Types() + { + var code = + @"Функция Ноль(Число) + Возврат ?(Число=0, ""ноль"", 1); + КонецФункции + Функция Один(Число) + Возврат ?(Число=1, ""один"", Число); + КонецФункции + "; + + var services = testServices.CreateContainer(); + var stringOperations = new StringOperations(); + var symbols = new SymbolTable(); + symbols.PushObject(stringOperations); + + CreateModule(code, services, symbols); + } + + [Fact] + public void Conditional_Expression_With_Arbitrary_Value() + { + var symbols = new SymbolTable(); + var serviceContainer = testServices.CreateContainer(); + var module = CreateModule( + @"Перем Рез0 Экспорт; + Перем Рез1 Экспорт; + Функция Тест(Условие) + Возврат ?(Условие, 1, 0); + КонецФункции + Рез0 = Тест(false); + Рез1 = Тест(2); + ", serviceContainer, symbols); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(serviceContainer)); + + var n = sdo.GetPropertyNumber("Рез0"); + var val0 = sdo.GetPropValue(n); + val0.SystemType.Should().Be(BasicTypes.Number); + val0.AsNumber().Should().Be(0); + n = sdo.GetPropertyNumber("Рез1"); + var val1 = sdo.GetPropValue(n); + val1.SystemType.Should().Be(BasicTypes.Number); + val1.AsNumber().Should().Be(1); + } + + [Fact] + public void Can_Instantiate_Native_Class_With_Args() + { + var code = @"#native + + Перем РегулярноеВыражение; + + Процедура ПриСозданииОбъекта(ТекстРегулярки) + РегулярноеВыражение = Новый РегулярноеВыражение(ТекстРегулярки); + КонецПроцедуры + + Функция НайтиСовпадения(Текст) Экспорт + + КоллекцияСовпадений = РегулярноеВыражение.НайтиСовпадения(Текст); + + Возврат КоллекцияСовпадений; + + КонецФункции"; + + var symbols = new SymbolTable(); + + testServices.UseNativeRuntime(); + var serviceContainer = testServices.CreateContainer(); + var discoverer = serviceContainer.Resolve(); + discoverer.DiscoverClasses(typeof(RegExpImpl).Assembly); + var module = CreateModule(code, serviceContainer, symbols); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass"), + new IValue[] { new RegExpImpl(".") }); + sdo.InitOwnData(); + sdo.Initialize(GetProcess(testServices.CreateContainer())); + } + + [Fact(Skip = "Выходные параметры не поддерживаются нативной средой")] + public void NativeSdoToStringOverride() + { + var code = @"//#native + + Процедура ОбработкаПолученияПредставления(Представление, СтандартнаяОбработка) + СтандартнаяОбработка = Ложь; + Представление = ""Привет"" + КонецПроцедуры"; + + var symbols = new SymbolTable(); + + testServices.UseNativeRuntime(); + var serviceContainer = testServices.CreateContainer(); + var discoverer = serviceContainer.Resolve(); + discoverer.DiscoverClasses(typeof(RegExpImpl).Assembly); + var module = CreateModule(code, serviceContainer, symbols); + + var sdo = new UserScriptContextInstance(module, + new TypeDescriptor(typeof(UserScriptContextInstance), "TestClass")); + sdo.InitOwnData(); + var process = GetProcess(testServices.CreateContainer()); + sdo.Initialize(process); + sdo.ToString(process).Should().Be("Привет"); + } + + [Fact(Skip = "Выходные параметры не поддерживаются нативной средой")] + public void NativeSdoConcatenationWithToStringOverride() + { + Assert.True(false); + } + + private DynamicModule CreateModule(string code) => CreateModule(code, testServices.CreateContainer(), new SymbolTable()); + + private DynamicModule CreateModule(string code, IServiceContainer services, SymbolTable symbols) + { + var symbolProvider = services.Resolve(); + var moduleScope = new SymbolScope(); + symbolProvider.Get().FillSymbols(moduleScope); + symbols.PushScope(moduleScope, ScopeBindingDescriptor.Static(null)); + + var helper = new CompileHelper(services); + helper.ParseModule(code); + var result = helper.Compile(symbols, Mock.Of()); + helper.ThrowOnErrors(); + return result; + } + } +} diff --git a/src/Tests/OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj b/src/Tests/OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj new file mode 100644 index 000000000..46f92b10e --- /dev/null +++ b/src/Tests/OneScript.Dynamic.Tests/OneScript.Dynamic.Tests.csproj @@ -0,0 +1,42 @@ + + + + $(TargetFrameworkVersion) + + false + + Debug;Release;LinuxDebug + 10.0 + AnyCPU + + + + true + false + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + diff --git a/src/Tests/OneScript.Language.Tests/AstNodeAppendingHandler.cs b/src/Tests/OneScript.Language.Tests/AstNodeAppendingHandler.cs new file mode 100644 index 000000000..733b17f4d --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/AstNodeAppendingHandler.cs @@ -0,0 +1,51 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.Tests +{ + public class AstNodeAppendingHandler : ModuleAnnotationDirectiveHandler + { + private readonly ILexer _allLineContentLexer; + + public AstNodeAppendingHandler(IErrorSink errorSink) : base(errorSink) + { + var builder = new LexerBuilder(); + builder.Detect((cs, i) => !char.IsWhiteSpace(cs)) + .HandleWith(new FullLineLexerState()); + + _allLineContentLexer = builder.Build(); + } + + protected override bool DirectiveSupported(string directive) + { + return true; + } + + protected override void ParseAnnotationInternal( + ref Lexem lastExtractedLexem, + ILexer lexer, + ParserContext parserContext) + { + var node = new PreprocessorDirectiveNode(lastExtractedLexem); + _allLineContentLexer.Iterator = lexer.Iterator; + + lastExtractedLexem = _allLineContentLexer.NextLexemOnSameLine(); + if (lastExtractedLexem.Type != LexemType.EndOfText) + { + var child = new TerminalNode(NodeKind.Unknown, lastExtractedLexem); + node.AddChild(child); + } + + lastExtractedLexem = lexer.NextLexem(); + parserContext.AddChild(node); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/CompilerExtensions.cs b/src/Tests/OneScript.Language.Tests/CompilerExtensions.cs new file mode 100644 index 000000000..5722ff934 --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/CompilerExtensions.cs @@ -0,0 +1,14 @@ +/*---------------------------------------------------------- +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.Language.Tests +{ + public static class CompilerExtensions + { + + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/ErrorRecoveryTests.cs b/src/Tests/OneScript.Language.Tests/ErrorRecoveryTests.cs new file mode 100644 index 000000000..a35135d89 --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/ErrorRecoveryTests.cs @@ -0,0 +1,93 @@ +/*---------------------------------------------------------- +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 FluentAssertions; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using Xunit; +using Xunit.Sdk; + +namespace OneScript.Language.Tests +{ + public class ErrorRecoveryTests + { + [Fact] + public void Successfully_Goes_To_Next_Line() + { + var code = "line1 %^ & # (#\n" + + "line2 some other stuff\n" + + "(*& line3"; + + var testLexer = BuildLexer(); + + testLexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + var strategy = new NextLineRecoveryStrategy(); + Lexem lex; + + lex = testLexer.NextLexem(); + lex.Content.Should().Be("line1"); + lex = strategy.Recover(testLexer); + lex.Content.Should().Be("line2"); + + try + { + strategy.Recover(testLexer); + } + catch (SyntaxErrorException) + { + return; + } + + throw new XunitException("should throw unknown symbol"); + } + + [Fact] + public void NextStatement_Strategy_Rewinds_Through_Errors() + { + var code = "ths is a line with error\n" + + "#$e d0-f %^ Если\n" + + "!@-2 Пока\n" + + "!@-2 Перем\n" + + "!@-2 Для\n" + + "!@-2 Попытка\n"; + + var testLexer = BuildLexer(); + + testLexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + var strategy = new NextStatementRecoveryStrategy(); + Lexem lex; + + testLexer.Iterator.MoveToContent(); + + lex = strategy.Recover(testLexer); + lex.Token.Should().Be(Token.If); + + testLexer.Iterator.MoveNext(); + lex = strategy.Recover(testLexer); + lex.Token.Should().Be(Token.While); + + testLexer.Iterator.MoveNext(); + lex = strategy.Recover(testLexer); + lex.Token.Should().Be(Token.VarDef); + + testLexer.Iterator.MoveNext(); + lex = strategy.Recover(testLexer); + lex.Token.Should().Be(Token.For); + + testLexer.Iterator.MoveNext(); + lex = strategy.Recover(testLexer); + lex.Token.Should().Be(Token.Try); + } + + private ILexer BuildLexer() + { + var lb = new LexerBuilder(); + lb.DetectWords(); + return lb.Build(); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/FullLineLexerState.cs b/src/Tests/OneScript.Language.Tests/FullLineLexerState.cs new file mode 100644 index 000000000..3e083bb3f --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/FullLineLexerState.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; + +namespace OneScript.Language.Tests +{ + public class FullLineLexerState : LexerState + { + public override Lexem ReadNextLexem(SourceCodeIterator iterator) + { + if (!iterator.MoveToContent()) + return Lexem.EndOfText(); + + var location = new CodeRange(iterator.CurrentLine, iterator.CurrentColumn); + var content = iterator.ReadToLineEnd(); + return new Lexem + { + Content = content, + Location = location, + Type = LexemType.NotALexem + }; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language.Tests/LexerBasicsTests.cs b/src/Tests/OneScript.Language.Tests/LexerBasicsTests.cs similarity index 98% rename from src/OneScript.Language.Tests/LexerBasicsTests.cs rename to src/Tests/OneScript.Language.Tests/LexerBasicsTests.cs index 9b58508a1..34b6ec80a 100644 --- a/src/OneScript.Language.Tests/LexerBasicsTests.cs +++ b/src/Tests/OneScript.Language.Tests/LexerBasicsTests.cs @@ -34,7 +34,7 @@ public void EndOfText_Lexem_Is_Correct() public void IteratorReturnsCorrectLine() { var code = "\r\nF = 1;\r\nD = 2;\r\nX = 3;\r\n"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); Assert.Equal(1, iterator.CurrentLine); Assert.Equal(-1, iterator.CurrentColumn); @@ -127,6 +127,7 @@ public void IsBuiltIn_Function() Assert.True(LanguageDef.IsBuiltInFunction(Token.Hour)); Assert.True(LanguageDef.IsBuiltInFunction(Token.Minute)); Assert.True(LanguageDef.IsBuiltInFunction(Token.Second)); + Assert.True(LanguageDef.IsBuiltInFunction(Token.BegOfWeek)); Assert.True(LanguageDef.IsBuiltInFunction(Token.BegOfYear)); Assert.True(LanguageDef.IsBuiltInFunction(Token.BegOfMonth)); Assert.True(LanguageDef.IsBuiltInFunction(Token.BegOfDay)); @@ -139,6 +140,7 @@ public void IsBuiltIn_Function() Assert.True(LanguageDef.IsBuiltInFunction(Token.EndOfHour)); Assert.True(LanguageDef.IsBuiltInFunction(Token.EndOfMinute)); Assert.True(LanguageDef.IsBuiltInFunction(Token.EndOfQuarter)); + Assert.True(LanguageDef.IsBuiltInFunction(Token.EndOfWeek)); Assert.True(LanguageDef.IsBuiltInFunction(Token.WeekOfYear)); Assert.True(LanguageDef.IsBuiltInFunction(Token.DayOfYear)); Assert.True(LanguageDef.IsBuiltInFunction(Token.DayOfWeek)); @@ -272,6 +274,7 @@ public void All_Tokens() Assert.Equal(Token.Hour, LanguageDef.GetToken("час")); Assert.Equal(Token.Minute, LanguageDef.GetToken("минута")); Assert.Equal(Token.Second, LanguageDef.GetToken("секунда")); + Assert.Equal(Token.BegOfWeek, LanguageDef.GetToken("началонедели")); Assert.Equal(Token.BegOfYear, LanguageDef.GetToken("началогода")); Assert.Equal(Token.BegOfMonth, LanguageDef.GetToken("началомесяца")); Assert.Equal(Token.BegOfDay, LanguageDef.GetToken("началодня")); @@ -284,6 +287,7 @@ public void All_Tokens() Assert.Equal(Token.EndOfHour, LanguageDef.GetToken("конецчаса")); Assert.Equal(Token.EndOfMinute, LanguageDef.GetToken("конецминуты")); Assert.Equal(Token.EndOfQuarter, LanguageDef.GetToken("конецквартала")); + Assert.Equal(Token.EndOfWeek, LanguageDef.GetToken("конецнедели")); Assert.Equal(Token.WeekOfYear, LanguageDef.GetToken("неделягода")); Assert.Equal(Token.DayOfYear, LanguageDef.GetToken("деньгода")); Assert.Equal(Token.DayOfWeek, LanguageDef.GetToken("деньнедели")); diff --git a/src/Tests/OneScript.Language.Tests/LexerBuilderTest.cs b/src/Tests/OneScript.Language.Tests/LexerBuilderTest.cs new file mode 100644 index 000000000..eec86804e --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/LexerBuilderTest.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.LexicalAnalysis; +using OneScript.Localization; +using Xunit; +using Xunit.Sdk; + +namespace OneScript.Language.Tests +{ + public class LexerBuilderTest + { + [Fact] + public void Can_Build_SelectorClause() + { + var builder = new LexerBuilder(); + + builder.Detect((cs,i) => char.IsLetter(cs) || cs == SpecialChars.Underscore) + .HandleWith(new WordLexerState()); + + builder.Detect((cs, i) => char.IsDigit(cs)) + .HandleWith(new NumberLexerState()); + + var lexer = builder.Build(); + + lexer.Iterator = MakeCodeIterator("1 Hello 2 ("); + + var lexem1 = lexer.NextLexem(); + var lexem2 = lexer.NextLexem(); + var lexem3 = lexer.NextLexem(); + + try + { + lexer.NextLexem(); + throw new XunitException("Must throw error"); + } + catch (SyntaxErrorException e) + { + var localeString = BilingualString.Localize("Неизвестный символ", "Unexpected character"); + Assert.Contains(localeString, e.Message); + } + + Assert.Equal(LexemType.NumberLiteral, lexem1.Type); + Assert.Equal(LexemType.Identifier, lexem2.Type); + Assert.Equal(LexemType.NumberLiteral, lexem3.Type); + } + + private SourceCodeIterator MakeCodeIterator(string code) + { + return SourceCodeHelper.FromString(code).CreateIterator(); + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language.Tests/LexerTests.cs b/src/Tests/OneScript.Language.Tests/LexerTests.cs similarity index 77% rename from src/OneScript.Language.Tests/LexerTests.cs rename to src/Tests/OneScript.Language.Tests/LexerTests.cs index 01b122191..2f13ab36c 100644 --- a/src/OneScript.Language.Tests/LexerTests.cs +++ b/src/Tests/OneScript.Language.Tests/LexerTests.cs @@ -15,31 +15,17 @@ public class LexerTests [Fact] public void Empty_Lexer_Position_Is_Negative() { - var lexer = new Lexer(); - Assert.True(lexer.CurrentColumn == CodePositionInfo.OUT_OF_TEXT); - Assert.True(lexer.CurrentLine == CodePositionInfo.OUT_OF_TEXT); + var lexer = new DefaultLexer(); + Assert.True(lexer.Iterator.CurrentColumn == ErrorPositionInfo.OUT_OF_TEXT); + Assert.True(lexer.Iterator.CurrentLine == ErrorPositionInfo.OUT_OF_TEXT); } - [Fact] - public void Code_Set_Sets_Position() - { - var lexer = new Lexer(); - lexer.Code = "А = 1;"; - Assert.True(lexer.Code == "А = 1;"); - Assert.True(lexer.CurrentColumn == CodePositionInfo.OUT_OF_TEXT); - Assert.True(lexer.CurrentLine == 1); - lexer.NextLexem(); - Assert.True(lexer.CurrentColumn >= 0); - lexer.Code = "А = 1;"; - Assert.True(lexer.CurrentColumn == CodePositionInfo.OUT_OF_TEXT); - } - [Fact] public void SourceCode_Iterator_Basics() { string code = "Б = 1;"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); Assert.True(iterator.CurrentLine == 1); Assert.True(iterator.CurrentSymbol == '\0'); @@ -93,7 +79,7 @@ public void Whitespace_Skipping() { string code = "Б \t\t=\n 1; "; string check = "Б=1;"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); for (int i = 0; i < check.Length; i++) { @@ -116,7 +102,7 @@ public void Retrieve_Line_Of_Code() В = 7-11; Г = 8"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); while(iterator.CurrentLine<4) { if (!iterator.MoveNext()) @@ -138,7 +124,7 @@ public void CurrentColumn_Calculation() В = 7-11; Г = 8"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); while (iterator.CurrentLine < 3) { if (!iterator.MoveNext()) @@ -155,7 +141,7 @@ public void CurrentColumn_Calculation() public void Identifier_LexerState_Works_Fine() { string code = " \ndddddd-"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); var state = new WordLexerState(); iterator.MoveToContent(); var lexem = state.ReadNextLexem(iterator); @@ -167,7 +153,7 @@ public void Identifier_LexerState_Works_Fine() public void Word_Lexer_State_BuiltIn_Tokens_As_Usual_Words() { string code = "Лев СтрДлина Прав"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); var state = new WordLexerState(); iterator.MoveToContent(); var lexem = state.ReadNextLexem(iterator); @@ -197,7 +183,7 @@ public void StringLiteral_LexerState_WorksFine() StringLexerState state = new StringLexerState(); code = " \"-just string \""; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); lex = state.ReadNextLexem(iterator); Assert.True(lex.Type == LexemType.StringLiteral); @@ -205,14 +191,14 @@ public void StringLiteral_LexerState_WorksFine() code = @" ""-just |string """; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); lex = state.ReadNextLexem(iterator); Assert.True(lex.Type == LexemType.StringLiteral); Assert.Equal("-just\nstring ", lex.Content); code = @" ""-just "" ""string"" ""123"""; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); lex = state.ReadNextLexem(iterator); Assert.True(lex.Type == LexemType.StringLiteral); @@ -222,7 +208,7 @@ public void StringLiteral_LexerState_WorksFine() |second line // comment |third line"""; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); lex = state.ReadNextLexem(iterator); Assert.True(lex.Type == LexemType.StringLiteral); @@ -238,7 +224,7 @@ public void Word_Literals_Processed_Correctly() WordLexerState state = new WordLexerState(); code = " Истина Ложь Неопределено Null True False Undefined"; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); lex = state.ReadNextLexem(iterator); Assert.Equal(LexemType.BooleanLiteral, lex.Type); @@ -282,7 +268,7 @@ public void Preprocessor_Lexem_ProcessedCorrectly() string code = @"#Если #КонецЕсли"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); var wordParser = new PreprocessorDirectiveLexerState(); Lexem lex; @@ -308,7 +294,7 @@ public void Unclosed_String_Literal() StringLexerState state = new StringLexerState(); code = " \"-just string "; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); Assert.Throws(() => state.ReadNextLexem(iterator)); } @@ -322,7 +308,7 @@ public void Incorrect_NewLine_InString() code = @" ""-just d|string """; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); Assert.Throws(() => state.ReadNextLexem(iterator)); } @@ -331,7 +317,7 @@ public void Incorrect_NewLine_InString() public void NumberLiteral_State_Works_Fine() { string code = " 123.45 "; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); var state = new NumberLexerState(); var lex = state.ReadNextLexem(iterator); @@ -343,14 +329,14 @@ public void NumberLiteral_State_Works_Fine() public void Wrong_Number_Literal() { string code = " 123.45.45 "; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); var state = new NumberLexerState(); Assert.Throws(() => state.ReadNextLexem(iterator)); code = " 12jk"; - iterator = new SourceCodeIterator(code); + iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); Assert.Throws(() => state.ReadNextLexem(iterator)); @@ -360,7 +346,7 @@ public void Wrong_Number_Literal() public void Date_LexerState_Works_With_8_Numbers() { string code = " '12341212' "; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); var state = new DateLexerState(); var lex = state.ReadNextLexem(iterator); @@ -372,7 +358,7 @@ public void Date_LexerState_Works_With_8_Numbers() public void Date_LexerState_Works_With_14_Numbers() { string code = " '12341212020202' "; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); iterator.MoveToContent(); var state = new DateLexerState(); var lex = state.ReadNextLexem(iterator); @@ -384,7 +370,7 @@ public void Date_LexerState_Works_With_14_Numbers() public void Operators_Lexer_State() { string code = " + - * / < > <= >= <> % ,.()[]"; - var iterator = new SourceCodeIterator(code); + var iterator = SourceCodeHelper.FromString(code).CreateIterator(); var state = new OperatorLexerState(); Lexem lex; @@ -469,6 +455,77 @@ public void Operators_Lexer_State() Assert.Equal("]", lex.Content); } + [Fact] + public void NonWhiteSpaceLexer_Reads_UnquotedStrings() + { + var code = "AA.BB()--Data Second"; + + var lb = new LexerBuilder(); + lb.Detect((cs,i) => !char.IsWhiteSpace(cs)) + .HandleWith(new NonWhitespaceLexerState()); + + var lexer = lb.Build(); + + lexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + + var lex = lexer.NextLexem(); + + Assert.Equal(LexemType.NotALexem, lex.Type); + Assert.Equal(Token.NotAToken, lex.Token); + Assert.Equal("AA.BB()--Data", lex.Content); + + lex = lexer.NextLexem(); + Assert.Equal("Second", lex.Content); + + lex = lexer.NextLexem(); + Assert.Equal(Lexem.EndOfText(), lex); + } + + [Fact] + public void NonWhiteSpaceLexer_Discards_Comments() + { + var code = "AA.BB()-// comment"; + + var lb = new LexerBuilder(); + lb.Detect((cs,i) => !char.IsWhiteSpace(cs)) + .HandleWith(new NonWhitespaceLexerState()); + + var lexer = lb.Build(); + + lexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + + var lex = lexer.NextLexem(); + + Assert.Equal(LexemType.NotALexem, lex.Type); + Assert.Equal(Token.NotAToken, lex.Token); + Assert.Equal("AA.BB()-", lex.Content); + + lex = lexer.NextLexem(); + Assert.Equal(Lexem.EndOfText(), lex); + } + + [Fact] + public void NonWhiteSpaceLexer_Reads_QuotedStrings() + { + var code = @"""Quoted space //and comment"" next"; + + var lb = new LexerBuilder(); + lb.Detect((cs,i) => !char.IsWhiteSpace(cs)) + .HandleWith(new NonWhitespaceLexerState()); + + var lexer = lb.Build(); + + lexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + var lex = lexer.NextLexem(); + Assert.Equal("\"Quoted space //and comment\"", lex.Content); + + lex = lexer.NextLexem(); + Assert.Equal("next", lex.Content); + + lex = lexer.NextLexem(); + Assert.Equal(Lexem.EndOfText(), lex); + } + [Fact] public void Code_Walkthrough() { @@ -476,8 +533,8 @@ public void Code_Walkthrough() А = Б+11.2 <> '20100207' - ""ffff"""; - var lexer = new Lexer(); - lexer.Code = code; + var lexer = new DefaultLexer(); + lexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); Lexem lex; lex = lexer.NextLexem(); @@ -515,8 +572,8 @@ public void Syntax_Error_Handling() string code = @" А$Б"; - var lexer = new Lexer(); - lexer.Code = code; + var lexer = new DefaultLexer(); + lexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); lexer.UnexpectedCharacterFound += (s, e) => { e.Iterator.MoveNext(); @@ -532,7 +589,7 @@ public void Syntax_Error_Handling() [Fact] public void New_Exception_Shows_Negative_Line_And_Column() { - var e = new ScriptException(); + var e = new ScriptException("fake"); Assert.True(e.LineNumber == -1); Assert.True(e.ColumnNumber == -1); } @@ -541,8 +598,7 @@ public void New_Exception_Shows_Negative_Line_And_Column() public void Comments_Are_Retrieved_Correctly() { string code = "а //comment\r\n// another comment"; - var lexer = new FullSourceLexer(); - lexer.Code = code; + var lexer = GetLexerForCode(code); Lexem lex; lexer.NextLexem(); @@ -563,8 +619,8 @@ public void Comments_Are_Retrieved_Correctly() public void Lexer_Ignores_Comments() { string code = "a //comment\r\n// another comment\r\nvalue"; - var lexer = new Lexer(); - lexer.Code = code; + var lexer = new DefaultLexer(); + lexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); Lexem lex; lex = lexer.NextLexem(); @@ -576,5 +632,30 @@ public void Lexer_Ignores_Comments() Assert.Equal(LexemType.Identifier, lex.Type); Assert.Equal("value", lex.Content); } + + [Fact] + public void Lexer_Extracts_Labels() + { + string code = "~ImALabel:\n" + + " ~LabelRef;"; + var lexer = GetLexerForCode(code); + + var lex = lexer.NextLexem(); + + Assert.Equal(LexemType.Label, lex.Type); + Assert.Equal("ImALabel", lex.Content); + + lex = lexer.NextLexem(); + Assert.Equal(LexemType.LabelRef, lex.Type); + Assert.Equal("LabelRef", lex.Content); + } + + private ILexer GetLexerForCode(string code) + { + return new FullSourceLexer + { + Iterator = SourceCodeHelper.FromString(code).CreateIterator() + }; + } } } diff --git a/src/Tests/OneScript.Language.Tests/OneScript.Language.Tests.csproj b/src/Tests/OneScript.Language.Tests/OneScript.Language.Tests.csproj new file mode 100644 index 000000000..a6d51dfaa --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/OneScript.Language.Tests.csproj @@ -0,0 +1,31 @@ + + + + $(TargetFrameworkVersion) + + false + + Debug;Release;LinuxDebug + + AnyCPU + + + + true + false + + + + + + + + + + + + + + + + diff --git a/src/Tests/OneScript.Language.Tests/ParserTests.cs b/src/Tests/OneScript.Language.Tests/ParserTests.cs new file mode 100644 index 000000000..92d498423 --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/ParserTests.cs @@ -0,0 +1,1678 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; +using Xunit; +using FluentAssertions; +using Moq; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.Tests +{ + public class ParserTests + { + [Fact] + public void CheckBuild_Of_VariablesSection() + { + var code = @" + Перем П1; + Перем П2 Экспорт; + &Аннотация + Перем П3; + Перем П4 Экспорт, П5 Экспорт;"; + + var treeValidator = ParseModuleAndGetValidator(code); + + treeValidator.Is(NodeKind.VariablesSection); + + var child = treeValidator.NextChild(); + child.Is(NodeKind.VariableDefinition) + .WithNode(NodeKind.Identifier) + .Equal("П1"); + + child = treeValidator.NextChild(); + child.Is(NodeKind.VariableDefinition) + .WithNode(NodeKind.Identifier) + .Equal("П2"); + child.HasNode(NodeKind.ExportFlag); + + child = treeValidator.NextChild(); + child.Is(NodeKind.VariableDefinition) + .WithNode(NodeKind.Annotation) + .Equal("Аннотация"); + + child.HasNode(NodeKind.Identifier) + .Equal("П3"); + + child = treeValidator.NextChild(); + child.Is(NodeKind.VariableDefinition).WithNode(NodeKind.Identifier).Equal("П4"); + child.HasNode(NodeKind.ExportFlag); + + child = treeValidator.NextChild(); + child.Is(NodeKind.VariableDefinition).WithNode(NodeKind.Identifier).Equal("П5"); + child.HasNode(NodeKind.ExportFlag); + } + + [Fact] + public void CheckBuild_Of_Methods_Section() + { + var code = "Процедура А() КонецПроцедуры Функция Б() КонецФункции"; + var node = ParseModuleAndGetValidator(code); + + node.Is(NodeKind.MethodsSection); + node.CurrentNode.ChildrenList.Should().HaveCount(2, "two methods in code"); + + var methodNode = node.NextChild(); + methodNode.Is(NodeKind.Method) + .NextChildIs(NodeKind.MethodSignature) + .DownOneLevel() + .NextChildIs(NodeKind.Procedure) + .NextChildIs(NodeKind.Identifier).ChildItself() + .Equal("А"); + + methodNode = node.NextChild(); + methodNode.Is(NodeKind.Method) + .NextChildIs(NodeKind.MethodSignature) + .DownOneLevel() + .NextChildIs(NodeKind.Function) + .HasNode(NodeKind.Identifier) + .Equal("Б"); + } + + [Fact] + public void Check_Annotation_Parameters() + { + var code = @" + &БезПараметров + &СИменемПараметра(Имя) + &НесколькоПараметров(Имя, Имя2) + &Литерал(""Привет"") + &ИмяИЗначение(А = ""Привет"", М = 1) + Перем УзелВладелец;"; + + var variable = ParseModuleAndGetValidator(code).NextChild(); + + var anno = variable.NextChild(); + anno.Is(NodeKind.Annotation) + .NoMoreChildren(); + anno.Equal("БезПараметров"); + + anno = variable.NextChild() + .Is(NodeKind.Annotation); + anno.Equal("СИменемПараметра"); + anno.DownOneLevel().Is(NodeKind.AnnotationParameter) + .NextChildIs(NodeKind.AnnotationParameterName) + .NoMoreChildren(); + anno.NoMoreChildren(); + + anno = variable.NextChild().Is(NodeKind.Annotation); + anno.Equal("НесколькоПараметров"); + anno.HasChildNodes(2); + anno.NextChild().Is(NodeKind.AnnotationParameter) + .NextChildIs(NodeKind.AnnotationParameterName) + .NoMoreChildren(); + + anno.NextChild().Is(NodeKind.AnnotationParameter) + .NextChildIs(NodeKind.AnnotationParameterName) + .NoMoreChildren(); + + anno = variable.NextChild(); + anno.Equal("Литерал"); + var param = anno.NextChild().Is(NodeKind.AnnotationParameter); + anno.NoMoreChildren(); + param.NextChildIs(NodeKind.AnnotationParameterValue).Equals("Привет"); + param.NoMoreChildren(); + + anno = variable.NextChild(); + anno.Equal("ИмяИЗначение"); + anno.NextChild().Is(NodeKind.AnnotationParameter) + .NextChildIs(NodeKind.AnnotationParameterName) + .NextChildIs(NodeKind.AnnotationParameterValue) + .NoMoreChildren(); + anno.NextChild().Is(NodeKind.AnnotationParameter) + .NextChildIs(NodeKind.AnnotationParameterName) + .NextChildIs(NodeKind.AnnotationParameterValue) + .NoMoreChildren(); + + } + + [Fact] + public void CheckBuild_Of_AnnotationAsValue() + { + var code = @" + &Аннотация(Параметр = &ТожеАннотация(&СТожеПараметромАннотацией, П2 = &СТожеПараметромАннотацией)) + Процедура Процедура1() Экспорт + КонецПроцедуры"; + + var treeValidator = ParseModuleAndGetValidator(code); + + treeValidator.Is(NodeKind.MethodsSection); + + var methodNode = treeValidator.NextChild(); + methodNode.Is(NodeKind.Method); + var annotationNode = methodNode.NextChild(); + annotationNode.Is(NodeKind.Annotation); + var annotationParameter = annotationNode.NextChild(); + annotationParameter.Is(NodeKind.AnnotationParameter); + + annotationParameter + .NextChildIs(NodeKind.AnnotationParameterName) + .NextChildIs(NodeKind.Annotation); + } + + [Fact] + public void Check_AnnotationNotAllowed_InList() + { + var code = @" + &Аннотация + Перем Пер1, &Анн Пер2;"; + + CatchParsingError(code, err => err.First().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + [Fact] + public void Check_AnnotationNotAllowed_InMethod() + { + var code = @" + Процедура Процедура1() + &Аннотация + Возврат + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + [Fact] + public void Check_AnnotationNotAllowed_BeforeMethodsEnd() + { + var code = @" + Процедура Процедура1() + Ч = 0; + &Аннотация + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + [Fact] + public void Check_AnnotationNotAllowed_InModuleBody() + { + var code = @" + Процедура Процедура1() + КонецПроцедуры + &Аннотация + Ч = 0"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("AnnotationNotAllowed")); + } + + + [Fact] + public void Check_Method_Parameters() + { + var code = @" + Процедура П(А, Знач А, Б = 1, Знач Д = -10) Экспорт КонецПроцедуры"; + + var proc = ParseModuleAndGetValidator(code).NextChild(); + + var signature = proc.NextChild().Is(NodeKind.MethodSignature); + signature + .NextChildIs(NodeKind.Procedure) + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.MethodParameters) + .NextChildIs(NodeKind.ExportFlag) + .NoMoreChildren(); + + var paramList = signature.HasNode(NodeKind.MethodParameters); + paramList.NextChild().Is(NodeKind.MethodParameter) + .NextChildIs(NodeKind.Identifier).ChildItself() + .Equal("А"); + + paramList.NextChild().Is(NodeKind.MethodParameter) + .NextChildIs(NodeKind.ByValModifier) + .NextChildIs(NodeKind.Identifier) + .NoMoreChildren(); + + paramList.NextChild().Is(NodeKind.MethodParameter) + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.ParameterDefaultValue) + .ChildItself().Equal("1"); + + paramList.NextChild().Is(NodeKind.MethodParameter) + .NextChildIs(NodeKind.ByValModifier) + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.ParameterDefaultValue) + .ChildItself().Equal("-10"); + } + + [Fact] + public void Check_Method_Variables() + { + var code = @" + Процедура П() + Перем А; + Перем Б; + Код = Истина; + КонецПроцедуры"; + + var proc = ParseModuleAndGetValidator(code) + .NextChild() + .CastTo(); + + proc.VariableDefinitions().Should().HaveCount(2); + + } + + [Fact] + public void Check_Statement_GlobalFunctionCall() + { + var batch = ParseBatchAndGetValidator("Proc();"); + batch.Is(NodeKind.CodeBatch); + var node = batch.NextChild(); + node.Is(NodeKind.GlobalCall) + .HasNode(NodeKind.Identifier) + .Equal("Proc"); + + } + + [Fact] + public void Check_Statement_ObjectMethod_Call() + { + var code = @"Target.Call(); + Target().Call(); + Target[0].Call(); + Target.SubCall().Call()"; + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.MethodCall); + + node = batch.NextChild(); + node.Is(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.GlobalCall) + .NextChildIs(NodeKind.MethodCall); + + node = batch.NextChild(); + node.Is(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.IndexAccess) + .NextChildIs(NodeKind.MethodCall); + + node = batch.NextChild(); + node.Is(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.MethodCall); + } + + [Fact] + public void Check_Argument_Passing() + { + var code = @"Proc(); + Proc(А+1, Б+2); + Proc('00010101'); + Proc(,); + Proc(1,); + Proc(,1)"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + var node = batch.NextChild(); + node.Is(NodeKind.GlobalCall) + .NextChild().Is(NodeKind.Identifier) + .Equal("Proc"); + node.NextChild().Is(NodeKind.CallArgumentList) + .NoMoreChildren(); + + node = batch.NextChild(); + node.NextChild(); + var list = node.NextChild().Is(NodeKind.CallArgumentList); + list.NextChildIs(NodeKind.CallArgument) + .NextChildIs(NodeKind.CallArgument) + .NoMoreChildren(); + + list.CurrentNode.ChildrenList[0].ChildrenList[0].Kind.Should().Be(NodeKind.BinaryOperation); + list.CurrentNode.ChildrenList[1].ChildrenList[0].Kind.Should().Be(NodeKind.BinaryOperation); + + node = batch.NextChild(); + node.NextChild(); + list = node.NextChild(); + list.HasChildNodes(1); + + node = batch.NextChild(); + node.NextChild(); + list = node.NextChild(); + list.HasChildNodes(2); + list.NextChild().Is(NodeKind.CallArgument).NoMoreChildren(); + list.NextChild().Is(NodeKind.CallArgument).NoMoreChildren(); + + node = batch.NextChild(); + node.NextChild(); + list = node.NextChild(); + list.HasChildNodes(2); + list.NextChild().Is(NodeKind.CallArgument).NextChildIs(NodeKind.Constant); + list.NextChild().Is(NodeKind.CallArgument).NoMoreChildren(); + + node = batch.NextChild(); + node.NextChild(); + list = node.NextChild(); + list.HasChildNodes(2); + list.NextChild().Is(NodeKind.CallArgument).NoMoreChildren(); + list.NextChild().Is(NodeKind.CallArgument).NextChildIs(NodeKind.Constant); + + } + + [Fact] + public void Check_Assignment_OnVariable() + { + var code = @"Target = 1"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Assignment) + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Constant); + } + + [Fact] + public void Check_Assignment_OnProperty() + { + var code = @"Target.Prop = 1"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Assignment) + .NextChildIs(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.Constant); + } + + [Fact] + public void Check_Assignment_OnIndex() + { + var code = @"Target[0] = 1"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Assignment) + .NextChildIs(NodeKind.IndexAccess) + .NextChildIs(NodeKind.Constant); + } + + [Fact] + public void Check_Assignment_OnComplex_Chain() + { + var code = @"Target[0].SomeProp.Method(Object.Prop[3*(8-2)].Data).Prop = ?(Data = True, Object[0], Object.Method()[12]);"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Assignment) + .NextChildIs(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.TernaryOperator); + } + + [Fact] + public void Throws_When_Single_Identifier() + { + var code = @"Variable;"; + + var parser = PrepareParser(code); + parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("Expression syntax error"); + parser.Errors.First().ErrorId.Should().Be("ExpressionSyntax"); + } + + [Fact] + public void Throws_When_Single_Indexed() + { + var code = @"Target[0];"; + + var parser = PrepareParser(code); + parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("Expression syntax error"); + parser.Errors.First().ErrorId.Should().Be("ExpressionSyntax"); + } + + [Fact] + public void Throws_When_Single_Property() + { + var code = @"Object.Prop;"; + + var parser = PrepareParser(code); + parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("Expression syntax error"); + parser.Errors.First().ErrorId.Should().Be("ExpressionSyntax"); + } + + [Fact] + public void Throws_When_Assignment_To_Function() + { + var code = @"Function F() + Var V; + Return V + EndFunction + F() = 0;"; + + var parser = PrepareParser(code); + parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("Expression syntax error"); + parser.Errors.First().ErrorId.Should().Be("ExpressionSyntax"); + } + + [Fact] + public void Check_Binary_And_Unary_Expressions() + { + var code = @"А = -2 + 2"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var assignment = batch + .NextChild().Is(NodeKind.Assignment); + assignment.NextChild(); + var node = assignment.NextChild(); + node.Is(NodeKind.BinaryOperation) + .Equal(Token.Plus.ToString()); + node.NextChild().Is(NodeKind.Constant); + node.NextChild().Is(NodeKind.Constant); + } + + [Fact] + public void Check_Binary_Expressions_Chain() + { + var code = @"А = 2 + 2 + 3 - 1"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var assignment = batch + .NextChild().Is(NodeKind.Assignment); + assignment.NextChild(); + var node = assignment.NextChild(); + node.Is(NodeKind.BinaryOperation) + .Equal(Token.Minus.ToString()); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.Constant); + } + + [Fact] + public void AdditionPriority_For_Strings() + { + var code = @"А = ""Стр1"" + Сч + ""Стр2"""; + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var assignment = batch + .NextChild().Is(NodeKind.Assignment); + assignment.NextChild(); + var node = assignment.NextChild(); + node.Is(NodeKind.BinaryOperation).Equal(Token.Plus.ToString()); + + var firstAddition = node.NextChild(); + firstAddition.Is(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.Constant) + .NextChildIs(NodeKind.Identifier); + + node.NextChild().Is(NodeKind.Constant) + .Equal("Стр2"); + } + + + [Fact] + public void Check_Binary_Expressions_Priority() + { + var code = @"А = 2 + 2 * 3"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var assignment = batch + .NextChild().Is(NodeKind.Assignment); + assignment.NextChild(); + var node = assignment.NextChild(); + node.Is(NodeKind.BinaryOperation) + .Equal(Token.Plus.ToString()); + node.NextChild(); + node.NextChild().Is(NodeKind.BinaryOperation) + .Equal(Token.Multiply.ToString()); + } + + [Fact] + public void Check_Binary_Division() + { + var code = @"А = Variable/Variable;"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var assignment = batch + .NextChild().Is(NodeKind.Assignment); + assignment.NextChild(); + var node = assignment.NextChild(); + node.Is(NodeKind.BinaryOperation) + .Equal(Token.Division.ToString()); + } + + [Fact] + public void Check_Binary_Modulo() + { + var code = @"А = Variable%Variable;"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var assignment = batch + .NextChild().Is(NodeKind.Assignment); + assignment.NextChild(); + var node = assignment.NextChild(); + node.Is(NodeKind.BinaryOperation) + .Equal(Token.Modulo.ToString()); + } + + [Fact] + public void Check_Logical_Expressions() + { + var code = @"Переменная >= 2 ИЛИ Не Переменная < 1"; + + var expr = ParseExpressionAndGetValidator(code); + expr.Is(NodeKind.BinaryOperation) + .Equal(Token.Or.ToString()); + + expr.NextChild().Is(NodeKind.BinaryOperation) + .Equal(Token.MoreOrEqual.ToString()); + + expr.NextChild().Is(NodeKind.UnaryOperation) + .NextChild().Is(NodeKind.BinaryOperation) + .Equal(Token.LessThan.ToString()); + } + + [Fact] + public void Check_EqualExpression_Is_Comparison_But_Not_Assignment() + { + var code = @"Переменная = 2"; + + var expr = ParseExpressionAndGetValidator(code); + expr.Is(NodeKind.BinaryOperation) + .Equal(Token.Equal.ToString()); + } + + [Fact] + public void Check_Logical_Priority_Direct() + { + var code = @"Переменная1 ИЛИ Переменная2 И Переменная3"; + + var expr = ParseExpressionAndGetValidator(code); + expr.Is(NodeKind.BinaryOperation) + .Equal(Token.Or.ToString()); + } + + [Fact] + public void Check_Logical_Priority_Parenthesis() + { + var code = @"(Переменная1 ИЛИ Переменная2) И Переменная3"; + + var expr = ParseExpressionAndGetValidator(code); + expr.Is(NodeKind.BinaryOperation) + .Equal(Token.And.ToString()); + } + + [Fact] + public void Check_Keywords_As_PropertyNames() + { + var code = @"ТипЗначенияJson.Null"; + + var expr = ParseExpressionAndGetValidator(code); + expr.Is(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Identifier); + } + + [Fact] + public void Can_Dereference_After_Construction() + { + var code = @"ТекПуть = Новый Файл(ТекущийСценарий().Источник).Путь;"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Assignment); + node.NextChild(); + var rightSide = node.NextChild(); + rightSide.Is(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.NewObject) + .NextChildIs(NodeKind.Identifier); + + } + + [Fact] + public void Can_Create_DynamicNew_With_No_Args() + { + var code = @"Объект = Новый(""Рефлектор"");"; + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Assignment); + node.NextChild(); + var rightSide = node.NextChild(); + rightSide.Is(NodeKind.NewObject) + .NextChildIs(NodeKind.CallArgument) + .NextChildIs(NodeKind.CallArgumentList); + } + + [Fact] + public void Can_Create_DynamicNew_With_One_Arg() + { + var code = @"Объект = Новый(""Рефлектор"", Новый Массив);"; + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Assignment); + node.NextChild(); + var rightSide = node.NextChild(); + rightSide.Is(NodeKind.NewObject) + .NextChildIs(NodeKind.CallArgument) + .NextChildIs(NodeKind.CallArgumentList); + + var argsList = rightSide.CurrentNode.Children[1]; + argsList.Children.Should().HaveCount(1); + } + + [Fact] + public void Explicit_Parameter_Skipping() + { + var code = @"Метод(,)"; + + var call = ParseExpressionAndGetValidator(code); + call.Is(NodeKind.GlobalCall); + call.NextChild(); + call.NextChild() + .CurrentNode.Children.Should().HaveCount(2); + } + + [Fact] + public void Throws_When_Last_ParameterDefinition_IsMissing() + { + var code = @"Процедура ПодключитьсяКХранилищу(Знач СтрокаСоединения, + Знач ПользовательХранилища, + Знач ПарольХранилища = """", + Знач ИгнорироватьНаличиеПодключеннойБД = Ложь, + Знач ЗаменитьКонфигурациюБД = Истина, + ) Экспорт + КонецПроцедуры"; + + var parser = PrepareParser(code); + parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("last parameter is missing"); + parser.Errors.First().ErrorId.Should().Be("IdentifierExpected"); + } + + [Fact] + public void Check_If_With_No_Alternatives() + { + var code = + @"Если А = 1 Тогда + Б = 0; + КонецЕсли;"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Condition); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_If_With_Else_Alternative() + { + var code = + @"Если А = 1 Тогда + ; + Иначе + ; + КонецЕсли;"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Condition); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_If_With_ElseIf_Alternatives() + { + var code = + @"Если А = 1 Тогда + ; + ИначеЕсли Б = 2 Тогда + ; + ИначеЕсли Б = 2 Тогда + ; + КонецЕсли;"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Condition); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.Condition) + .NextChildIs(NodeKind.Condition) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_If_With_ElseIf_And_Else_Alternatives() + { + var code = + @"Если А = 1 Тогда + ; + ИначеЕсли Б = 2 Тогда + ; + ИначеЕсли Б = 2 Тогда + ; + Иначе + ; + КонецЕсли;"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Condition); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.Condition) + .NextChildIs(NodeKind.Condition) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + + var altNodes = ((ConditionNode) node.CurrentNode.RealNode).GetAlternatives(); + altNodes.Should().HaveCount(3); + } + + [Fact] + public void Check_While_Statement() + { + var code = + @"Пока А = 1 Цикл + ; + КонецЦикла"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.WhileLoop); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_Foreach_Statement() + { + var code = + @"Для Каждого Итератор Из Коллекция Цикл + ; + КонецЦикла"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.ForEachLoop); + node.NextChildIs(NodeKind.ForEachVariable) + .NextChildIs(NodeKind.ForEachCollection) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void CheckHandler_Expression_And_EventName() + { + var code = "ДобавитьОбработчик ЧтоТо[Индекс].ИмяСобытия, ЧтоТо().ИмяОбработчика"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.AddHandler); + node.NextChildIs(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.DereferenceOperation) + .NoMoreChildren(); + } + + [Fact] + public void CheckHandler_Expression_And_EventName_As_Indexer() + { + var code = "ДобавитьОбработчик ЧтоТо[Индекс][\"ИмяСобытия\"+1], ЧтоТо().ИмяОбработчика"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.AddHandler); + node.NextChildIs(NodeKind.IndexAccess) + .NextChildIs(NodeKind.DereferenceOperation) + .NoMoreChildren(); + } + + [Fact] + public void CheckHandler_Expression_And_HandlerName_As_Indexer() + { + var code = "ДобавитьОбработчик ЧтоТо[Индекс].ИмяСобытия, ЧтоТо()[ИмяОбработчика]"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.AddHandler); + node.NextChildIs(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.IndexAccess) + .NoMoreChildren(); + } + + [Fact] + public void CheckHandler_Expression_And_ProcedureName() + { + var code = "ДобавитьОбработчик ЧтоТо[Индекс].ИмяСобытия, ИмяОбработчика"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.AddHandler); + node.NextChildIs(NodeKind.DereferenceOperation) + .NextChildIs(NodeKind.Identifier) + .NoMoreChildren(); + } + + [Theory] + [InlineData("-1 / 1 * 2")] + [InlineData("1 / 1 * 2")] + public void Test_Positive_And_Negative_Literals_And_Priority(string code) + { + var expression = ParseExpressionAndGetValidator(code); + expression.Is(NodeKind.BinaryOperation).Equal(nameof(Token.Multiply)); + + var division = expression.NextChild(); + division.CurrentNode.Value.Should().Be(nameof(Token.Division)); + division + .NextChildIs(NodeKind.Constant) // оптимизация унарного минуса в константу + .NextChildIs(NodeKind.Constant) + .NoMoreChildren(); + + var constant = expression.NextChild(); + constant.CurrentNode.RealNode.As().Lexem.Type.Should().Be(LexemType.NumberLiteral); + + expression.NoMoreChildren(); + } + + [Theory] + [InlineData("-Variable / 1 * 2", true)] + [InlineData("Variable / 1 * 2", false)] + public void Test_Positive_And_Negative_Expressions_And_Priority(string code, bool findUnary) + { + var expression = ParseExpressionAndGetValidator(code); + expression.Is(NodeKind.BinaryOperation); + + expression.CurrentNode.Value.Should().Be(nameof(Token.Multiply)); + + var division = expression.NextChild(); + division.CurrentNode.Value.Should().Be(nameof(Token.Division)); + var expectedFirstArg = findUnary ? NodeKind.UnaryOperation : NodeKind.Identifier; + division + .NextChildIs(expectedFirstArg) + .NextChildIs(NodeKind.Constant) + .NoMoreChildren(); + + expression.NextChildIs(NodeKind.Constant); + expression.NoMoreChildren(); + } + + [Fact] + public void Test_Non_Numeric_For_Unary_Minus() + { + // унарные операции приводят к числу в рантайме, а не при компиляции + var expression = ParseExpressionAndGetValidator("-\"hello\""); + expression.Is(NodeKind.UnaryOperation); + expression.NextChildIs(NodeKind.Constant); + } + + [Fact] + public void Check_AstDirectives_Creation() + { + var code = @"#Использовать АБВ + #Использовать ""аааа"" + #Область Продукты + #КонецОбласти + #ВерсияЯзыка 2.0.8 + #ЧтоТоЕще АбраКадабра(>= 18, ??)"; + + var firstChild = ParseModuleAndGetValidator(code); + var batch = new SyntaxTreeValidator(new TestAstNode(firstChild.CurrentNode.RealNode.Parent)); + + var node = batch.NextChild(); + node.Is(NodeKind.Preprocessor) + .Equal("Использовать"); + node.NextChild().Is(NodeKind.Unknown) + .Equal("АБВ"); + + node = batch.NextChild(); + node.Is(NodeKind.Preprocessor) + .Equal("Использовать"); + node.NextChild().Is(NodeKind.Unknown) + .Equal("\"аааа\""); + + node = batch.NextChild(); + node.Is(NodeKind.Preprocessor) + .Equal("Область"); + node.NextChild().Is(NodeKind.Unknown) + .Equal("Продукты"); + + node = batch.NextChild(); + node.Is(NodeKind.Preprocessor) + .Equal("КонецОбласти"); + node.NoMoreChildren(); + + node = batch.NextChild(); + node.Is(NodeKind.Preprocessor) + .Equal("ВерсияЯзыка"); + node.NextChild().Is(NodeKind.Unknown) + .Equal("2.0.8"); + + node = batch.NextChild(); + node.Is(NodeKind.Preprocessor) + .Equal("ЧтоТоЕще"); + node.NextChild().Is(NodeKind.Unknown) + .Equal("АбраКадабра(>= 18, ??)"); + + batch.NoMoreChildren(); + } + + [Fact] + public void CanHaveCommentAfterUseStatement() + { + var code = @" + #Использовать Библиотека // Подключаем библиотеку + #Использовать ""Библиотека с кавычками"" // Подключаем библиотеку с кавычками + "; + + var defaultLex = new DefaultLexer(); + defaultLex.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + + var lexer = new PreprocessingLexer(defaultLex); + + lexer.Handlers = new PreprocessorHandlers( + new[] {new ImportDirectivesHandler(new ThrowingErrorSink())}); + + var parser = new DefaultBslParser( + lexer, + new ListErrorSink(), + lexer.Handlers); + + var moduleNode = parser.ParseStatefulModule(); + moduleNode.Should().NotBeNull(); + parser.Errors.Should().BeEmpty("the valid code is passed"); + var firstChild = new SyntaxTreeValidator(new TestAstNode(moduleNode.Children.First())); + + var batch = new SyntaxTreeValidator(new TestAstNode(firstChild.CurrentNode.RealNode.Parent)); + + var node = batch.NextChild(); + node.Is(NodeKind.Import) + .Equal("Использовать"); + node.NextChild().Is(NodeKind.AnnotationParameter) + .Equal("Библиотека"); + + node = batch.NextChild(); + node.Is(NodeKind.Import) + .Equal("Использовать"); + node.NextChild().Is(NodeKind.AnnotationParameter) + .Equal("\"Библиотека с кавычками\""); + } + + [Fact] + public void Check_No_Semicolon_In_If() + { + var code = + @"Если А = 1 Тогда + Б = 2 + ИначеЕсли Б = 2 Тогда + А = 2 + Иначе + А = 0 + КонецЕсли"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.Condition); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.Condition) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_No_Semicolon_Before_EndDo() + { + var code = + @"Пока А = 1 Цикл + Б = 2 + КонецЦикла"; + + var batch = ParseBatchAndGetValidator(code); + batch.Is(NodeKind.CodeBatch); + + var node = batch.NextChild(); + node.Is(NodeKind.WhileLoop); + node.NextChildIs(NodeKind.BinaryOperation) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_No_Semicolon_Before_EndProcedure() + { + var code = + @"Процедура А() + Возврат + КонецПроцедуры"; + + var node = ParseModuleAndGetValidator(code); + + node.Is(NodeKind.MethodsSection); + + var methodNode = node.NextChild(); + methodNode.Is(NodeKind.Method) + .NextChildIs(NodeKind.MethodSignature) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_No_Semicolon_Before_EndFunction() + { + var code = + @"Функция А() + Возврат 0 + КонецФункции"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var methodNode = node.NextChild(); + methodNode.Is(NodeKind.Method) + .NextChildIs(NodeKind.MethodSignature) + .NextChildIs(NodeKind.CodeBatch) + .NextChildIs(NodeKind.BlockEnd) + .NoMoreChildren(); + } + + [Fact] + public void Check_No_Semicolon_In_Try() + { + var code = + @"Процедура Проц() + Попытка + Возврат + Исключение + ВызватьИсключение + КонецПопытки + КонецПроцедуры"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var methodNode = node.NextChild().Is(NodeKind.Method) + .NextChildIs(NodeKind.MethodSignature); + + var tryNode = methodNode.NextChild().Is(NodeKind.CodeBatch) + .NextChild().Is(NodeKind.TryExcept); + tryNode.NextChild().Is(NodeKind.CodeBatch) + .NextChild().Is(NodeKind.ReturnStatement); + tryNode.NextChild().Is(NodeKind.CodeBatch) + .NextChild().Is(NodeKind.RaiseException); + tryNode.NextChild().Is(NodeKind.BlockEnd); + + methodNode.NextChildIs(NodeKind.BlockEnd); + node.NoMoreChildren(); + } + + [Fact] + public void Check_EndProcedure_Does_Not_End_Function() + { + var code = + @"Функция А() + Возврат 0 + КонецПроцедуры"; + + CatchParsingError(code); + } + + [Fact] + public void Check_EndFunction_Does_Not_End_Procedure() + { + var code = + @"Процедура А() + Возврат 0 + КонецФункции"; + + CatchParsingError(code); + } + + [Fact] + public void Check_ElseIf_Does_Not_End_Procedure() + { + var code = + @"Процедура Проц() + Возврат + ИначеЕсли"; + + CatchParsingError(code); + } + + + [Fact] + public void Check_EndTry_Does_Not_End_Procedure() + { + var code = + @"Процедура Проц() + Возврат + КонецПопытки"; + + CatchParsingError(code); + } + + [Fact] + public void Check_Elseif_Does_Not_Delimit_AddHandler() + { + var code = + @"Если Истина Тогда + ДобавитьОбработчик ЭтотОбъект.Событие ИначеЕсли ЭтотОбъект.Обработчик + КонецЕсли"; + + CatchParsingError(code); + } + + [Fact] + public void Check_Semicolon_Before_Procedures() + { + var code = + @"Перем Глоб + Процедура Проц1() + Возврат + КонецПроцедуры"; + + CatchParsingError(code); + } + + [Fact] + public void Check_Semicolon_After_Locals() + { + var code = + @"Процедура Проц1() + Перем Локал + Если Истина Тогда + Локал = 1 + КонецЕсли + КонецПроцедуры"; + + CatchParsingError(code); + } + + [Fact] + public void Check_No_Semicolon_Between_Procedures() + { + var code = + @"Процедура Проц1() + Возврат + КонецПроцедуры; + Процедура Проц2() + Возврат + КонецПроцедуры"; + + CatchParsingError(code); + } + + [Fact] + public void Check_Semicolon_Between_Statements() + { + var code = + @"Если Истина Тогда + Ф = 1 + КонецЕсли + Если Ложь Тогда + Ф = 2 + КонецЕсли"; + + CatchParsingError(code); + } + + [Fact] + public void Check_Question_Operator_Delimiters() + { + var code = @"Ф = ?(Истина? 1 ; 2);"; + + CatchParsingError(code); + } + + [Fact] + public void Check_Method_Definition_Delimiters() + { + var code = @"Процедура Проц1(арг1 арг2) + КонецПроцедуры"; + + CatchParsingError(code); + } + + [Fact] + public void Check_Method_Call_Delimiters() + { + var code = @"Процедура Проц1(арг1, арг2) + КонецПроцедуры + Проц1(""1"" 2)"; + + CatchParsingError(code); + } + + + + [Fact] + public void TestLocalExportVar() + { + var code = + @"Процедура Проц1() + Перем Переменная Экспорт; + КонецПроцедуры"; + + CatchParsingError(code, err => err.First().ErrorId.Should().Be("ExportedLocalVar")); + } + + [Fact] + public void TestAsyncProcedure() + { + var code = + @"Асинх Процедура Проц1() + Перем Переменная; + КонецПроцедуры"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var methodNode = node.NextChild().Is(NodeKind.Method); + + methodNode.CurrentNode.RealNode.As().IsAsync.Should().BeTrue(); + } + + [Fact] + public void TestAsyncFunction() + { + var code = + @"Асинх Функция Ф1() + Перем Переменная; + КонецФункции"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var methodNode = node.NextChild().Is(NodeKind.Method); + + methodNode.CurrentNode.RealNode.As().IsAsync.Should().BeTrue(); + } + + [Fact] + public void TestAwaitPriority() + { + var code = + @"Асинх Процедура Проц1() + А = Ждать Вызов().Поле; + КонецПроцедуры"; + + var node = ParseModuleAndGetValidator(code); + node.Is(NodeKind.MethodsSection); + + var method = node.NextChild().Is(NodeKind.Method); + var expression = method.DownTo(NodeKind.Assignment) + .NextChildIs(NodeKind.Identifier) + .NextChild(); + + expression.CurrentNode.RealNode.As().Operation.Should().Be(Token.Await); + expression + .NextChildIs(NodeKind.DereferenceOperation) + .NoMoreChildren(); + } + + [Fact] + public void TestAwaitMustBeInAsyncOnly() + { + var code = + @"Процедура Проц1() + Ждать Операция(); + КонецПроцедуры"; + + CatchParsingError(code, errors => + { + errors.Single().ErrorId.Should().Be("ExpressionSyntax"); + }); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_Variable() + { + var code = + @"Процедура Проц1() + А = Ждать; + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.Assignment); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Identifier); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_VariableFor() + { + var code = + @"Процедура Проц1() + А = Ждать; + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.Assignment); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Identifier); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_VariableForEach() + { + var code = + @"Процедура Проц1() + А = Ждать; + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.Assignment); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Identifier); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_Method() + { + var code = + @"Процедура Проц1() + А = Ждать(); + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.Assignment); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.GlobalCall); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_PropertyName() + { + var code = + @"Процедура Проц1() + А = П.Ждать; + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.DereferenceOperation); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.Identifier); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_MemberMethodName() + { + var code = + @"Процедура Проц1() + А = П.Ждать(); + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code) + .DownTo(NodeKind.DereferenceOperation); + + validator + .NextChildIs(NodeKind.Identifier) + .NextChildIs(NodeKind.MethodCall); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_MethodName() + { + var code = + @"Процедура Ждать() + А = 1; + КонецПроцедуры"; + + var validator = ParseModuleAndGetValidator(code); + validator.DownOneLevel().Is(NodeKind.Method); + } + + [Fact] + public void AwaitIsNotKeywordInNonAsyncContext_ModuleVariable() + { + var code = @"Перем Ждать;"; + + var validator = ParseModuleAndGetValidator(code); + validator + .DownTo(NodeKind.VariableDefinition) + .CurrentNode.RealNode.As() + .Name.Should().Be("Ждать"); + } + + [Fact] + public void Await_In_NonAsync_Expression_Fails() + { + var code = + @"Процедура Проц1() + Если Ждать ВтороеСлово Тогда + КонецЕсли; + КонецПроцедуры"; + + CatchParsingError(code, err => + { + err.Single().ErrorId.Should().Be("AwaitMustBeInAsyncMethod"); + }); + } + + [Fact] + public void AwaitRequiresExpression() + { + var code = + @"Асинх Процедура Проц1() + А = Ждать; + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("ExpressionSyntax")); + } + + [Fact] + public void DoubleAwaitIsForbidden() + { + var code = + @"Асинх Процедура Проц1() + А = Ждать Ждать Б; + КонецПроцедуры"; + + CatchParsingError(code, err => err.Single().ErrorId.Should().Be("ExpressionSyntax")); + } + + [Fact] + public void Labels_Can_Appear_In_CodeBlocks() + { + var code = + @"А = 1; + ~Метка: + Б = 2; + "; + + var validator = ParseBatchAndGetValidator(code); + + validator.NextChildIs(NodeKind.Assignment); + validator.NextChildIs(NodeKind.Label); + validator.NextChildIs(NodeKind.Assignment); + } + + [Fact] + public void Goto_Can_Appear_In_CodeBlocks() + { + var code = + @"А = 1; + Перейти ~Метка; + Б = 2; + "; + + var validator = ParseBatchAndGetValidator(code); + + validator.NextChildIs(NodeKind.Assignment); + validator.NextChildIs(NodeKind.Goto); + validator.NextChildIs(NodeKind.Assignment); + } + + [Fact] + public void Async_And_Await_Are_Ordinary_Identifiers_In_NonAsync_Scope() + { + var code = @" + Ждать = ""МояПеременная""; + Асинх = ""МояПеременная2""; + "; + + var validator = ParseBatchAndGetValidator(code); + + validator.Is(NodeKind.CodeBatch); + + var awaitNode = validator.NextChild().Is(NodeKind.Assignment) + .NextChildIs(NodeKind.Identifier) + .ChildItself().Value.Should().Be("Ждать"); + + var asyncNode = validator.NextChild().Is(NodeKind.Assignment) + .NextChildIs(NodeKind.Identifier) + .ChildItself().Value.Should().Be("Асинх"); + } + + private static void CatchParsingError(string code) + { + var parser = PrepareParser(code); + _ = parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("error has not been detected"); + } + + private static void CatchParsingError(string code, Action> validator) + { + var parser = PrepareParser(code); + _ = parser.ParseStatefulModule(); + + parser.Errors.Should().NotBeEmpty("error has not been detected"); + validator(parser.Errors); + } + + + private static SyntaxTreeValidator ParseModuleAndGetValidator(string code) + { + return MakeValidator(code, p => p.ParseStatefulModule()); + } + + private static SyntaxTreeValidator ParseBatchAndGetValidator(string code) + { + var body = MakeValidator(code, p => p.ParseCodeBatch()); + return body.NextChild(); + } + + private static SyntaxTreeValidator ParseExpressionAndGetValidator(string code) + { + var topLevel = MakeValidator(code, p => p.ParseExpression()); + return topLevel.NextChild(); + } + + private static SyntaxTreeValidator MakeValidator(string code, Func action) + { + var parser = PrepareParser(code); + var node = action(parser); + + node.Should().NotBeNull(); + parser.Errors.Should().BeEmpty("the valid code is passed"); + var treeValidator = new SyntaxTreeValidator(new TestAstNode(node.Children.First())); + return treeValidator; + } + + private static DefaultBslParser PrepareParser(string code) + { + var defaultLex = new DefaultLexer(); + defaultLex.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + + var lexer = new PreprocessingLexer(defaultLex); + + lexer.Handlers = new PreprocessorHandlers( + new[] {new AstNodeAppendingHandler(Mock.Of())}); + + var parser = new DefaultBslParser( + lexer, + new ListErrorSink(), + lexer.Handlers); + + return parser; + } + } +} \ No newline at end of file diff --git a/src/OneScript.Language.Tests/PreprocessorTests.cs b/src/Tests/OneScript.Language.Tests/PreprocessorTests.cs similarity index 75% rename from src/OneScript.Language.Tests/PreprocessorTests.cs rename to src/Tests/OneScript.Language.Tests/PreprocessorTests.cs index d10f10ff7..bf7612a1e 100644 --- a/src/OneScript.Language.Tests/PreprocessorTests.cs +++ b/src/Tests/OneScript.Language.Tests/PreprocessorTests.cs @@ -6,7 +6,9 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System.Text; +using Moq; using OneScript.Language.LexicalAnalysis; +using OneScript.Language.SyntaxAnalysis; using Xunit; namespace OneScript.Language.Tests @@ -16,7 +18,7 @@ public class PreprocessorTests [Fact] public void Definitions_Are_Defined() { - var pp = new PreprocessingLexer();; + var pp = new ConditionalDirectiveHandler(Mock.Of()); pp.Define("Сервер"); pp.Define("ВебКлиент"); Assert.True(pp.IsDefined("сервер")); @@ -390,7 +392,7 @@ public void PreprocessingLexer_Extra_Endif() [Fact] public void PreprocessingLexer_SimpleRegion() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Область reg1 @@ -406,7 +408,7 @@ public void PreprocessingLexer_SimpleRegion() [Fact] public void PreprocessingLexer_MultipleNestedRegions() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Region reg1 @@ -432,7 +434,7 @@ public void PreprocessingLexer_MultipleNestedRegions() [Fact] public void PreprocessingLexer_NoEndRegion() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Область reg1 @@ -447,7 +449,7 @@ public void PreprocessingLexer_NoEndRegion() [Fact] public void PreprocessingLexer_ExtraEndRegion() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Область reg1 @@ -462,21 +464,24 @@ public void PreprocessingLexer_ExtraEndRegion() [Fact] public void PreprocessingLexer_BadRegionName() { - var pp = new PreprocessingLexer(); + var pp = new RegionDirectiveHandler(new ThrowingErrorSink()); + var lexer = new DefaultLexer(); var code = @" #Область -reg #КонецОбласти F"; - pp.Code = code; - Assert.Throws(() => pp.NextLexem()); + lexer.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); + var lexem = lexer.NextLexem(); + + Assert.Throws(() => pp.HandleDirective(ref lexem, lexer)); } [Fact] public void PreprocessingLexer_NoRegionName() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Область @@ -490,7 +495,7 @@ public void PreprocessingLexer_NoRegionName() [Fact] public void PreprocessingLexer_NoRegionNameWithComment() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Область // no name @@ -504,7 +509,7 @@ public void PreprocessingLexer_NoRegionNameWithComment() [Fact] public void PreprocessingLexer_SymbolsAfterName() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Область reg 00 @@ -518,7 +523,7 @@ public void PreprocessingLexer_SymbolsAfterName() [Fact] public void PreprocessingLexer_SymbolsAfterEndRegion() { - var pp = new PreprocessingLexer(); + var pp = new RegionsLexer(); var code = @" #Область reg @@ -575,6 +580,54 @@ public void PreprocessingLexer_DirectiveNotOnSingleLine() Assert.Throws(() => { while (pp.NextLexem().Token != Token.EndOfText) ; }); } + [Fact] + public void ConditionalsCompiler_DirectiveNotOnSingleLine() + { + var code = @" + #Если Нет + Тогда + F; + #КонецОбласти + "; + + var sinkMock = new Mock(); + var handler = new ConditionalDirectiveHandler(sinkMock.Object); + + var lexer = GetLexerForCode(code); + var lexem = lexer.NextLexem(); + + handler.HandleDirective(ref lexem, lexer); + + sinkMock.Verify(sink => sink.AddError(It.IsAny()), Times.Exactly(2)); + } + + [Fact] + public void PreprocessingLexer_LF_LineEnding() + { + var pp = new PreprocessingLexer(); + + { + string code = @" + #Если ЛожноеУсловие Тогда + Неважно Что тут + #Иначе + Л + #КонецЕсли".Replace("\r\n", "\n"); + + Assert.Equal("Л", GetPreprocessedContent(pp, code)); + } + + { + string code = @" + #Если ЛожноеУсловие Тогда + Неважно что тут + #КонецЕсли + Л".Replace("\r\n", "\n"); + + Assert.Equal("Л", GetPreprocessedContent(pp, code)); + } + } + [Fact] public void PreprocessingLexer_ExcludedLines() { @@ -596,9 +649,36 @@ public void PreprocessingLexer_ExcludedLines() Assert.Equal(Token.EndOfText, lex.Token); } - private string GetPreprocessedContent(PreprocessingLexer pp, string code) + [Fact] + public void ConditionalsCompiler_ExcludedLines() { - pp.Code = code; + var code = @" + #Если Да Тогда + F; + #Иначе + !! + #КонецЕсли + "; + + var handler = new ConditionalDirectiveHandler(Mock.Of()); + handler.Define("Да"); + + var lexer = GetLexerForCode(code); + var lexem = lexer.NextLexem(); + + while (lexem.Token != Token.EndOfText) + { + handler.HandleDirective(ref lexem, lexer); + while (lexem.Type != LexemType.PreprocessorDirective && lexem.Type != LexemType.EndOfText) + lexem = lexer.NextLexem(); + } + + Assert.Equal(Token.EndOfText, lexem.Token); + } + + private string GetPreprocessedContent(ILexer pp, string code) + { + pp.Iterator = SourceCodeHelper.FromString(code).CreateIterator(); Lexem lex = Lexem.Empty(); StringBuilder builder = new StringBuilder(); @@ -613,5 +693,77 @@ private string GetPreprocessedContent(PreprocessingLexer pp, string code) } return builder.ToString().Trim(); } + + private ILexer GetLexerForCode(string code) + { + return new FullSourceLexer + { + Iterator = SourceCodeHelper.FromString(code).CreateIterator() + }; + } + + private abstract class DirectiveHandlingLexer : ILexer + { + protected ILexer _lexer = new DefaultLexer(); + private IDirectiveHandler _handler; + + protected abstract IDirectiveHandler GetHandler(); + + public Lexem NextLexem() + { + var lex = _lexer.NextLexem(); + + if(lex.Type == LexemType.PreprocessorDirective) + { + while (_handler.HandleDirective(ref lex, _lexer) && lex.Type == LexemType.PreprocessorDirective) + { + ; + } + } + + if(lex.Type == LexemType.EndOfText) + _handler.OnModuleLeave(); + + return lex; + } + + public SourceCodeIterator Iterator + { + get => _lexer.Iterator; + set + { + _lexer.Iterator = value; + _handler = GetHandler(); + _handler.OnModuleEnter(); + } + } + + public string Code + { + set => Iterator = SourceCodeHelper.FromString(value).CreateIterator(); + } + } + + private class PreprocessingLexer : DirectiveHandlingLexer + { + private ConditionalDirectiveHandler _handler = new ConditionalDirectiveHandler(new ThrowingErrorSink()); + + protected override IDirectiveHandler GetHandler() => _handler; + + public void Define(string item) + { + _handler.Define(item); + } + + public void Undef(string item) + { + _handler.Undef(item); + } + } + + private class RegionsLexer : DirectiveHandlingLexer + { + protected override IDirectiveHandler GetHandler() => new RegionDirectiveHandler(new ThrowingErrorSink()); + } } } \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/SourceCodeHelper.cs b/src/Tests/OneScript.Language.Tests/SourceCodeHelper.cs new file mode 100644 index 000000000..36f10b99a --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/SourceCodeHelper.cs @@ -0,0 +1,35 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.Language.Sources; +using OneScript.Sources; + +namespace OneScript.Language.Tests +{ + public static class SourceCodeHelper + { + public static SourceCode FromString(string code) + { + return SourceCodeBuilder.Create() + .FromSource(new StringSource(code)) + .Build(); + } + + private class StringSource : ICodeSource + { + private readonly string _code; + + public StringSource(string code) + { + _code = code; + } + + public string Location => _code; + public string GetSourceCode() => _code; + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/SyntaxTreeValidator.cs b/src/Tests/OneScript.Language.Tests/SyntaxTreeValidator.cs new file mode 100644 index 000000000..b8954de95 --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/SyntaxTreeValidator.cs @@ -0,0 +1,83 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.Language.SyntaxAnalysis; +using Xunit; + +namespace OneScript.Language.Tests +{ + public class SyntaxTreeValidator + { + private int _currentChildIndex = -1; + + public SyntaxTreeValidator(TestAstNode node) + { + CurrentNode = node; + } + + public TestAstNode CurrentNode { get; set; } + + public SyntaxTreeValidator NextChild() + { + MoveToNextChild(); + + return new SyntaxTreeValidator(CurrentNode.ChildrenList[_currentChildIndex]); + } + + private void MoveToNextChild() + { + _currentChildIndex++; + EnsureHasCurrentChild(); + } + + public SyntaxTreeValidator NextChildIs(NodeKind nodeType) + { + MoveToNextChild(); + + var child = CurrentNode.ChildrenList[_currentChildIndex]; + child.Is(nodeType); + return this; + } + + public SyntaxTreeValidator DownOneLevel() + { + EnsureHasCurrentChild(); + return new SyntaxTreeValidator(CurrentNode.ChildrenList[_currentChildIndex]); + } + + private void EnsureHasCurrentChild() + { + if(_currentChildIndex ==-1) + MoveToNextChild(); + + if (_currentChildIndex >= CurrentNode.ChildrenList.Count) + { + throw new Exception("No more children"); + } + } + + public TestAstNode ChildItself() + { + EnsureHasCurrentChild(); + return CurrentNode.ChildrenList[_currentChildIndex]; + } + + public void NoMoreChildren() + { + if (_currentChildIndex == -1) + { + Assert.Empty(CurrentNode.ChildrenList); + } + else + { + _currentChildIndex++; + Assert.True(_currentChildIndex >= CurrentNode.ChildrenList.Count, "should not have more children"); + } + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/TestAstNode.cs b/src/Tests/OneScript.Language.Tests/TestAstNode.cs new file mode 100644 index 000000000..888c99cf0 --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/TestAstNode.cs @@ -0,0 +1,61 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using OneScript.Language.SyntaxAnalysis.AstNodes; + +namespace OneScript.Language.Tests +{ + public class TestAstNode : BslSyntaxNode + { + private Lazy> _childrenLazy; + + public BslSyntaxNode RealNode { get; } + + public TestAstNode(BslSyntaxNode node) + { + RealNode = node; + Kind = node.Kind; + if (node is NonTerminalNode nonTerm) + { + _childrenLazy = new Lazy>(nonTerm.Children.Select(x => new TestAstNode(x)).ToArray()); + Value = nonTerm switch + { + AnnotationNode anno => anno.Name, + BinaryOperationNode binary => binary.Operation.ToString(), + UnaryOperationNode unary => unary.Operation.ToString(), + PreprocessorDirectiveNode preproc => preproc.DirectiveName, + AnnotationParameterNode annParam => annParam.Value.Content, + _ => nonTerm.ToString() + }; + } + else if(node is TerminalNode term) + { + _childrenLazy = new Lazy>(new TestAstNode[0]); + Value = term.Lexem.Content; + } + else + { + _childrenLazy = new Lazy>(new TestAstNode[0]); + Value = node.ToString(); + } + } + + public string Value { get; set; } + + public IReadOnlyList ChildrenList => _childrenLazy.Value; + + public override IReadOnlyList Children => ChildrenList; + + public override string ToString() + { + return $"{Kind} ({Location.LineNumber},{Location.ColumnNumber})"; + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/TreeValidatorExtensions.cs b/src/Tests/OneScript.Language.Tests/TreeValidatorExtensions.cs new file mode 100644 index 000000000..1b246c6cb --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/TreeValidatorExtensions.cs @@ -0,0 +1,124 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Linq; +using FluentAssertions; +using OneScript.Language.SyntaxAnalysis; +using OneScript.Language.SyntaxAnalysis.AstNodes; +using Xunit; + +namespace OneScript.Language.Tests +{ + public static class TreeValidatorExtensions + { + public static SyntaxTreeValidator Is(this SyntaxTreeValidator validator, string type) + { + Assert.Equal(type, validator.CurrentNode.Kind.ToString()); + return validator; + } + + public static SyntaxTreeValidator Is(this SyntaxTreeValidator validator, NodeKind type) + { + validator.CurrentNode.Kind.Should().Be(type); + return validator; + } + + public static SyntaxTreeValidator DownTo(this SyntaxTreeValidator validator, NodeKind type) + { + var node = DownTo(validator.CurrentNode, type); + if (node == null) + { + throw new Exception("No such child: " + type); + } + + return new SyntaxTreeValidator(node); + } + + private static TestAstNode DownTo(this TestAstNode node, NodeKind type) + { + if (node.Kind == type) + return node; + + if (node.Children.Count == 0) + return null; + + foreach (var childNode in node.ChildrenList) + { + if (childNode.Kind == type) + return childNode; + + if (childNode.ChildrenList.Count == 0) + continue; + + var result = DownTo(childNode, type); + if (result != null) + return result; + } + + return null; + } + + public static void Is(this TestAstNode node, NodeKind type) + { + node.Kind.Should().Be(type, node.Kind + " is unexpected"); + } + + public static SyntaxTreeValidator HasChildNodes(this SyntaxTreeValidator validator, int count) + { + Assert.Equal(count, validator.CurrentNode.ChildrenList.Count); + return validator; + } + + public static SyntaxTreeValidator HasChildNodes(this SyntaxTreeValidator validator) + { + Assert.NotEmpty(validator.CurrentNode.ChildrenList); + return validator; + } + + public static TestAstNode FirstChild(this SyntaxTreeValidator validator) + { + if (validator.CurrentNode.ChildrenList.Count < 0) + { + throw new Exception("No more children"); + } + + return validator.CurrentNode.ChildrenList[0]; + } + + public static TestAstNode WithNode(this SyntaxTreeValidator validator, NodeKind type) + { + var child = validator.FirstChild(); + Assert.Equal(type, child.Kind); + + return child; + } + + public static SyntaxTreeValidator HasNode(this SyntaxTreeValidator validator, NodeKind type) + { + var child = validator.CurrentNode.ChildrenList.FirstOrDefault(x => x.Kind == type); + Assert.NotNull(child); + + return new SyntaxTreeValidator(child); + } + + public static void Equal(this TestAstNode node, string value) + { + Assert.Equal(value, node.Value); + } + + public static void Equal(this SyntaxTreeValidator node, string value) + { + Assert.Equal(value, node.CurrentNode.Value); + } + + public static T CastTo(this SyntaxTreeValidator node) where T : BslSyntaxNode + { + return (T) node.CurrentNode.RealNode; + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.Language.Tests/TrieTests.cs b/src/Tests/OneScript.Language.Tests/TrieTests.cs new file mode 100644 index 000000000..0c476e7d0 --- /dev/null +++ b/src/Tests/OneScript.Language.Tests/TrieTests.cs @@ -0,0 +1,62 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using Xunit; + +namespace OneScript.Language.Tests +{ + public class TrieTests + { + [Fact] + public void IdentifiersTrieAdd() + { + var t = new IdentifiersTrie(); + t.Add("Иван", 0); + t.Add("Иволга", 1); + + Assert.Equal(1, t.Get("Иволга")); + Assert.Equal(1, t.Get("ивОлга")); + Assert.Equal(0, t.Get("Иван")); + Assert.Equal(0, t.Get("иван")); + Assert.Equal(0, t.Get("ивАн")); + } + + [Fact] + public void Tokens() + { + var t = new IdentifiersTrie(); + t.Add("иначе", 1); + t.Add("иначеесли", 2); + + Assert.Equal(2, t.Get("ИначеЕсли")); + Assert.Equal(1, t.Get("Иначе")); + } + + [Fact] + public void IdentifiersTrie_Inclusive_Test_ContainsKey() + { + var t = new IdentifiersTrie(); + + t.Add("ЕслиИначе", true); + Assert.False(t.ContainsKey("Если")); + Assert.True(t.ContainsKey("ЕслиИначе")); + } + + [Fact] + public void IdentifiersTrie_Inclusive_Test_TryGetValue() + { + var t = new IdentifiersTrie(); + + t.Add("МетодОдин", 1); + t.Add("МетодОдинИДва", 2); + + var exist = t.TryGetValue("Метод", out _); + + Assert.False(exist); + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.StandardLibrary.Tests/FileBackingStreamTest.cs b/src/Tests/OneScript.StandardLibrary.Tests/FileBackingStreamTest.cs new file mode 100644 index 000000000..53079bd19 --- /dev/null +++ b/src/Tests/OneScript.StandardLibrary.Tests/FileBackingStreamTest.cs @@ -0,0 +1,220 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using Xunit; +using FluentAssertions; +using OneScript.StandardLibrary.Binary; + +namespace OneScript.StandardLibrary.Tests +{ + public class FileBackingStreamTest + { + [Fact] + public void CanDoBasicReadWithMemory() + { + using var stream = new FileBackingStream(10); + stream.Write(new byte[] { 1, 2, 3, 4, 5 }); + + stream.Position = 0; + stream.Position.Should().Be(0); + stream.HasBackingFile.Should().BeFalse(); + stream.Length.Should().Be(5); + + var dest = new byte[5]; + var size = stream.Read(dest, 0, 5); + size.Should().Be(5); + dest.Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void CanDoBasicReadWithFile() + { + using var stream = new FileBackingStream(10); + stream.SwitchToFile(); + stream.Write(new byte[] { 1, 2, 3, 4, 5 }); + + stream.Position = 0; + stream.Position.Should().Be(0); + stream.HasBackingFile.Should().BeTrue(); + stream.Length.Should().Be(5); + + var dest = new byte[5]; + var size = stream.Read(dest, 0, 5); + size.Should().Be(5); + dest.Should().Equal(1, 2, 3, 4, 5); + } + + [Fact] + public void GrowsOnWriteToEnd() + { + using var stream = new FileBackingStream(5); + stream.Write(new byte[] { 1, 2, 3, 4, 5 }); + stream.HasBackingFile.Should().BeFalse(); + stream.Position.Should().Be(5); + + stream.Write(new byte[] { 1, 2, 3, 4, 5 }); + stream.HasBackingFile.Should().BeTrue(); + stream.Position.Should().Be(10); + stream.Length.Should().Be(10); + + EnsureContent(stream, new byte[] { 1, 2, 3, 4, 5, 1, 2, 3, 4, 5 }); + } + + [Fact] + public void GrowsOnWriteToMiddle() + { + using var stream = new FileBackingStream(5); + stream.Write(new byte[] { 1, 2, 3, 4, 5 }); + stream.Position = 2; + stream.HasBackingFile.Should().BeFalse(); + + stream.Write(new byte[] { 6, 6, 6, 6, 6 }); + stream.HasBackingFile.Should().BeTrue(); + stream.Position.Should().Be(7); + stream.Length.Should().Be(7); + + EnsureContent(stream, new byte[] { 1, 2, 6, 6, 6, 6, 6 }); + } + + [Fact] + public void GrowsOnWriteToStart() + { + using var stream = new FileBackingStream(5); + stream.Write(new byte[] { 1, 2, 3, 4, 5 }); + stream.Position = 0; + stream.HasBackingFile.Should().BeFalse(); + + stream.Write(new byte[] { 7, 6, 5, 4, 3, 2, 1 }); + stream.HasBackingFile.Should().BeTrue(); + stream.Position.Should().Be(7); + stream.Length.Should().Be(7); + + EnsureContent(stream, new byte[] { 7, 6, 5, 4, 3, 2, 1 }); + } + + [Fact] + public void DoesNotGrowOnWriteInsideLimit() + { + using var stream = new FileBackingStream(5); + stream.Write(new byte[] { 1, 2, 3, 4, 5 }); + stream.Position = 2; + stream.HasBackingFile.Should().BeFalse(); + + stream.Write(new byte[] { 0, 0, 0 }); + stream.HasBackingFile.Should().BeFalse(); + stream.Position.Should().Be(5); + stream.Length.Should().Be(5); + + EnsureContent(stream, new byte[] { 1, 2, 0, 0, 0 }); + } + + [Fact] + public void CanCropIntoMemoryAgain() + { + using var stream = new FileBackingStream(5); + stream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + stream.HasBackingFile.Should().BeTrue(); + stream.Length.Should().Be(10); + stream.SetLength(5); + stream.HasBackingFile.Should().BeFalse(); + + EnsureContent(stream, new byte[] { 1, 2, 3, 4, 5 }); + } + + [Fact] + public void SettingLengthBeyondLimitSwitchesToFile() + { + using var stream = new FileBackingStream(5); + stream.HasBackingFile.Should().BeFalse(); + stream.SetLength(15); + stream.HasBackingFile.Should().BeTrue(); + } + + [Fact] + public void ThrowsWhenCantSwitchIntoMemory() + { + using var stream = new FileBackingStream(5); + stream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + stream.HasBackingFile.Should().BeTrue(); + + Assert.Throws(() => stream.SwitchToMemory()); + } + + [Fact] + public void SetPositionBeyondLength() + { + using var stream = new FileBackingStream(5); + stream.Position = 10; + + stream.Length.Should().Be(0); + stream.HasBackingFile.Should().BeFalse(); + + stream.Write(new byte[] { 1, 2, 3 }); + stream.Position.Should().Be(13); + stream.Length.Should().Be(13); + stream.HasBackingFile.Should().BeTrue(); + } + + [Fact] + public void TempFileGetsDeletedOnClose() + { + using var stream = new FileBackingStream(5); + stream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + stream.HasBackingFile.Should().BeTrue(); + var tempFile = stream.FileName; + + stream.SetLength(4); + stream.HasBackingFile.Should().BeFalse(); + File.Exists(tempFile).Should().BeFalse(); + } + + private void EnsureContent(Stream stream, IEnumerable expected) + { + stream.Position = 0; + var actual = new byte[stream.Length]; + stream.Read(actual).Should().Be((int)stream.Length); + + actual.Should().Equal(expected); + } + + [Fact(Skip = "Platform test")] + public void SetPositionBeyondMemoryStream() + { + using var ms = new MemoryStream(); + ms.Position = 10; + + ms.Length.Should().Be(0); + ms.Write(new byte[] { 1, 2, 3 }); + ms.Position.Should().Be(13); + ms.Length.Should().Be(13); + } + + [Fact(Skip = "Platform test")] + public void SetPositionBeyondFileStream() + { + var path = Path.GetTempFileName(); + var fs = new FileStream(path, FileMode.Create); + try + { + fs.Position = 10; + + fs.Length.Should().Be(0); + fs.Write(new byte[] { 1, 2, 3 }); + fs.Position.Should().Be(13); + fs.Length.Should().Be(13); + } + finally + { + fs.Close(); + File.Delete(path); + } + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.StandardLibrary.Tests/ObsoleteEnumTest.cs b/src/Tests/OneScript.StandardLibrary.Tests/ObsoleteEnumTest.cs new file mode 100644 index 000000000..08768b7dd --- /dev/null +++ b/src/Tests/OneScript.StandardLibrary.Tests/ObsoleteEnumTest.cs @@ -0,0 +1,41 @@ +/*---------------------------------------------------------- +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 Moq; +using OneScript.Contexts; +using OneScript.Contexts.Enums; +using ScriptEngine; + +namespace OneScript.StandardLibrary.Tests +{ + public class ObsoleteEnumTest + { + private List _messages; + + public ObsoleteEnumTest() + { + var mock = new Mock(); + mock.Setup(x => x.Write(It.IsAny())) + .Callback(str => _messages.Add(str)); + + _messages = new List(); + LogWriter = mock.Object; + SystemLogger.SetWriter(LogWriter); + } + + private ISystemLogWriter LogWriter { get; set; } + + [DeprecatedName("NonActual")] + [EnumerationType("Актуальное", "Actual")] + public enum TestEnum + { + One, + Two + } + } +} \ No newline at end of file diff --git a/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj b/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj new file mode 100644 index 000000000..4d93b540e --- /dev/null +++ b/src/Tests/OneScript.StandardLibrary.Tests/OneScript.StandardLibrary.Tests.csproj @@ -0,0 +1,29 @@ + + + + $(TargetFrameworkVersion) + enable + + false + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/Tests/OneScript.StandardLibrary.Tests/TimeZoneConverterTests.cs b/src/Tests/OneScript.StandardLibrary.Tests/TimeZoneConverterTests.cs new file mode 100644 index 000000000..332adae69 --- /dev/null +++ b/src/Tests/OneScript.StandardLibrary.Tests/TimeZoneConverterTests.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using FluentAssertions; +using OneScript.StandardLibrary.Timezones; +using Xunit; + +namespace OneScript.StandardLibrary.Tests +{ + public class TimeZoneConverterTests + { + [Fact] + public void Kiev_Summer_Dst_ToUtc_And_Back() + { + var dtLocal = new DateTime(2025, 6, 24, 10, 0, 0); + var utc = TimeZoneConverter.ToUniversalTime(dtLocal, "Europe/Kiev"); + utc.Should().Be(new DateTime(2025, 6, 24, 7, 0, 0, DateTimeKind.Utc)); + + var backLocal = TimeZoneConverter.ToLocalTime(utc, "Europe/Kiev"); + backLocal.Should().Be(dtLocal); + } + + [Fact] + public void Kiev_EET_StandardTimeOffset_Is_BaseUtcOffset_2h() + { + var dtLocal = new DateTime(2025, 6, 24, 10, 0, 0); + var offsetSeconds = TimeZoneConverter.StandardTimeOffset("EET", dtLocal); + offsetSeconds.Should().Be(7200); + } + + [Fact] + public void Kiev_EET_TimeZonePresentation_Is_GmtPlus02() + { + var presentation = TimeZoneConverter.TimeZonePresentation("EET"); + presentation.Should().Be("GMT+02:00"); + } + } +} \ No newline at end of file diff --git a/src/UpgradeLog.htm b/src/UpgradeLog.htm deleted file mode 100644 index 25209322f..000000000 Binary files a/src/UpgradeLog.htm and /dev/null differ diff --git a/src/VSCode.DebugAdapter/.vscode/launch.json b/src/VSCode.DebugAdapter/.vscode/launch.json new file mode 100644 index 000000000..3a032722f --- /dev/null +++ b/src/VSCode.DebugAdapter/.vscode/launch.json @@ -0,0 +1,10 @@ +{ + "version": "0.1.0", + "configurations": [{ + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}"] + }] + } \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/App.config b/src/VSCode.DebugAdapter/App.config index 74ade9db5..5ce7dba1d 100644 --- a/src/VSCode.DebugAdapter/App.config +++ b/src/VSCode.DebugAdapter/App.config @@ -3,4 +3,19 @@ + + + + + + + + + + + diff --git a/src/VSCode.DebugAdapter/AttachOptions.cs b/src/VSCode.DebugAdapter/AttachOptions.cs new file mode 100644 index 000000000..cd9c0c57f --- /dev/null +++ b/src/VSCode.DebugAdapter/AttachOptions.cs @@ -0,0 +1,15 @@ +// /*---------------------------------------------------------- +// 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 VSCode.DebugAdapter +{ + internal class AttachOptions + { + public int DebugPort { get; set; } = 2801; + public WorkspaceMapper PathsMapping { get; set; } + } +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/CommonLaunchOptions.cs b/src/VSCode.DebugAdapter/CommonLaunchOptions.cs index 77031b5d0..f16d26efe 100644 --- a/src/VSCode.DebugAdapter/CommonLaunchOptions.cs +++ b/src/VSCode.DebugAdapter/CommonLaunchOptions.cs @@ -20,5 +20,7 @@ public abstract class CommonLaunchOptions public string Protocol { get; set; } public Dictionary Env { get; set; } + + public string OutputEncoding { get; set; } } } \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/ConsoleLaunchOptions.cs b/src/VSCode.DebugAdapter/ConsoleLaunchOptions.cs index ceca6bf1c..7345f3fac 100644 --- a/src/VSCode.DebugAdapter/ConsoleLaunchOptions.cs +++ b/src/VSCode.DebugAdapter/ConsoleLaunchOptions.cs @@ -14,5 +14,7 @@ public class ConsoleLaunchOptions : CommonLaunchOptions public string[] Args { get; set; } public string Cwd { get; set; } + + public bool? WaitOnStart { get; set; } } } \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/ConsoleProcess.cs b/src/VSCode.DebugAdapter/ConsoleProcess.cs index 5587155e1..99444187e 100644 --- a/src/VSCode.DebugAdapter/ConsoleProcess.cs +++ b/src/VSCode.DebugAdapter/ConsoleProcess.cs @@ -5,10 +5,13 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using Newtonsoft.Json.Linq; +using EvilBeaver.DAP.Dto.Requests; +using EvilBeaver.DAP.Dto.Serialization; +using Serilog; namespace VSCode.DebugAdapter { @@ -29,21 +32,20 @@ public ConsoleProcess(PathHandlingStrategy pathHandling) : base(pathHandling) public string RuntimeArguments { get; set; } public IDictionary Environment { get; set; } = new Dictionary(); + + public override void Init(LaunchRequestArguments args) + { + var options = args.DeserializeAdditionalProperties(); + InitInternal(options); + } - protected override void InitInternal(JObject args) + private void InitInternal(ConsoleLaunchOptions options) { - var options = args.ToObject(); if (options.Program == null) { throw new InvalidDebugeeOptionsException(1001, "Property 'program' is missing or empty."); } - if (!File.Exists(options.Program)) - { - var path = Path.Combine(Directory.GetCurrentDirectory(), options.Program); - throw new InvalidDebugeeOptionsException(1002, $"Script '{path}' does not exist."); - } - // validate argument 'cwd' var workingDirectory = options.Cwd; if (workingDirectory != null) @@ -63,8 +65,18 @@ protected override void InitInternal(JObject args) { workingDirectory = Path.GetDirectoryName(options.Program); } - + + // Кодировка DAP + SetEncoding(options.OutputEncoding); + WorkingDirectory = workingDirectory; + Log.Information("Working directory for debuggee is {WorkingDirectory}", WorkingDirectory); + + var programPath = Path.Combine(workingDirectory ?? Directory.GetCurrentDirectory(), options.Program); + if (!File.Exists(programPath)) + { + throw new InvalidDebugeeOptionsException(1002, $"Script '{programPath}' does not exist."); + } // validate argument 'runtimeExecutable' var runtimeExecutable = options.RuntimeExecutable; @@ -91,9 +103,9 @@ protected override void InitInternal(JObject args) RuntimeArguments = Utilities.ConcatArguments(options.RuntimeArgs); StartupScript = options.Program; ScriptArguments = Utilities.ConcatArguments(options.Args); - DebugProtocol = options.Protocol; DebugPort = options.DebugPort; Environment = options.Env; + WaitOnStart = options.WaitOnStart ?? true; } protected override Process CreateProcess() @@ -103,9 +115,10 @@ protected override Process CreateProcess() { dbgArgs.Add($"-port={DebugPort}"); } - if (!string.IsNullOrEmpty(DebugProtocol)) + + if (!WaitOnStart) { - dbgArgs.Add($"-protocol={DebugProtocol}"); + dbgArgs.Add("-noWait"); } var debugArguments = string.Join(" ", dbgArgs); @@ -113,7 +126,7 @@ protected override Process CreateProcess() var psi = process.StartInfo; psi.FileName = RuntimeExecutable; psi.UseShellExecute = false; - psi.Arguments = $"-debug {debugArguments} {RuntimeArguments} \"{StartupScript}\" {ScriptArguments}"; + psi.Arguments = $"{RuntimeArguments} -debug {debugArguments} \"{StartupScript}\" {ScriptArguments}"; psi.WorkingDirectory = WorkingDirectory; psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; diff --git a/src/OneScript.DebugProtocol/IVariablePresentationHint.cs b/src/VSCode.DebugAdapter/Dap/IVariablePresentationHint.cs similarity index 98% rename from src/OneScript.DebugProtocol/IVariablePresentationHint.cs rename to src/VSCode.DebugAdapter/Dap/IVariablePresentationHint.cs index f54f4f1f6..e69dc6a63 100644 --- a/src/OneScript.DebugProtocol/IVariablePresentationHint.cs +++ b/src/VSCode.DebugAdapter/Dap/IVariablePresentationHint.cs @@ -5,7 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -namespace OneScript.DebugProtocol +namespace VSCode.DebugAdapter.Dap { public interface IVariablePresentationHint { diff --git a/src/VSCode.DebugAdapter/DebugSession.cs b/src/VSCode.DebugAdapter/DebugSession.cs deleted file mode 100644 index c78b7a0c5..000000000 --- a/src/VSCode.DebugAdapter/DebugSession.cs +++ /dev/null @@ -1,482 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.IO; -using VSCode.DebugAdapter; - -namespace VSCodeDebug -{ - // ---- Types ------------------------------------------------------------------------- - - public class Message { - public int id { get; } - public string format { get; } - public dynamic variables { get; } - public dynamic showUser { get; } - public dynamic sendTelemetry { get; } - - public Message(int id, string format, dynamic variables = null, bool user = true, bool telemetry = false) { - this.id = id; - this.format = format; - this.variables = variables; - this.showUser = user; - this.sendTelemetry = telemetry; - } - } - - public class StackFrame - { - public int id { get; } - public Source source { get; } - public int line { get; } - public int column { get; } - public string name { get; } - - public StackFrame(int id, string name, Source source, int line, int column) { - this.id = id; - this.name = name; - this.source = source; - this.line = line; - this.column = column; - } - } - - public class Scope - { - public string name { get; } - public int variablesReference { get; } - public bool expensive { get; } - - public Scope(string name, int variablesReference, bool expensive = false) { - this.name = name; - this.variablesReference = variablesReference; - this.expensive = expensive; - } - } - - public class Variable - { - public string name { get; } - public string value { get; } - public string type { get; } - public int variablesReference { get; } - - public Variable(string name, string value, string type, int variablesReference = 0) { - this.name = name; - this.value = value; - this.type = type; - this.variablesReference = variablesReference; - } - } - - public class Thread - { - public int id { get; } - public string name { get; } - - public Thread(int id, string name) { - this.id = id; - if (name == null || name.Length == 0) { - this.name = string.Format("Thread #{0}", id); - } - else { - this.name = name; - } - } - } - - public class Source - { - public string name { get; } - public string path { get; } - public int sourceReference { get; } - - public string origin { get; set; } - - public string presentationHint { get; set; } - - public Source(string name, string path, int sourceReference = 0) { - this.name = name; - this.path = path; - this.sourceReference = sourceReference; - } - - public Source(string path, int sourceReference = 0) { - this.name = Path.GetFileName(path); - this.path = path; - this.sourceReference = sourceReference; - } - } - - public class Breakpoint - { - public bool verified { get; } - public int line { get; } - - public Breakpoint(bool verified, int line) { - this.verified = verified; - this.line = line; - } - } - - // ---- Events ------------------------------------------------------------------------- - - public class InitializedEvent : Event - { - public InitializedEvent() - : base("initialized") { } - } - - public class StoppedEvent : Event - { - public StoppedEvent(int tid, string reasn, string txt = null) - : base("stopped", new { - threadId = tid, - reason = reasn, - text = txt - }) { } - } - - public class ExitedEvent : Event - { - public ExitedEvent(int exCode) - : base("exited", new { exitCode = exCode } ) { } - } - - public class TerminatedEvent : Event - { - public TerminatedEvent() - : base("terminated") { } - } - - public class ThreadEvent : Event - { - public ThreadEvent(string reasn, int tid) - : base("thread", new { - reason = reasn, - threadId = tid - }) { } - } - - public class OutputEvent : Event - { - public OutputEvent(string cat, string outpt) - : base("output", new { - category = cat, - output = outpt - }) { } - } - - // ---- Response ------------------------------------------------------------------------- - - public class Capabilities : ResponseBody { - - public bool supportsConfigurationDoneRequest; - public bool supportsFunctionBreakpoints; - public bool supportsConditionalBreakpoints; - public bool supportsEvaluateForHovers; - public dynamic[] exceptionBreakpointFilters; - } - - public class ErrorResponseBody : ResponseBody { - - public Message error { get; } - - public ErrorResponseBody(Message error) { - this.error = error; - } - } - - public class StackTraceResponseBody : ResponseBody - { - public StackFrame[] stackFrames { get; } - - public StackTraceResponseBody(IEnumerable frames = null) { - if (frames == null) - stackFrames = new StackFrame[0]; - else - stackFrames = frames.ToArray(); - } - } - - public class ScopesResponseBody : ResponseBody - { - public Scope[] scopes { get; } - - public ScopesResponseBody(IList scps = null) { - if (scps == null) - scopes = new Scope[0]; - else - scopes = scps.ToArray(); - } - } - - public class VariablesResponseBody : ResponseBody - { - public Variable[] variables { get; } - - public VariablesResponseBody(IList vars = null) { - if (vars == null) - variables = new Variable[0]; - else - variables = vars.ToArray(); - } - } - - public class ThreadsResponseBody : ResponseBody - { - public Thread[] threads { get; } - - public ThreadsResponseBody(List vars = null) { - if (vars == null) - threads = new Thread[0]; - else - threads = vars.ToArray(); - } - } - - public class EvaluateResponseBody : ResponseBody - { - public string result { get; } - - public string type { get; set; } - - public int variablesReference { get; } - - - public EvaluateResponseBody(string value, int reff = 0) { - result = value; - variablesReference = reff; - } - } - - public class SetBreakpointsResponseBody : ResponseBody - { - public Breakpoint[] breakpoints { get; } - - public SetBreakpointsResponseBody(List bpts = null) { - if (bpts == null) - breakpoints = new Breakpoint[0]; - else - breakpoints = bpts.ToArray(); - } - } - - // ---- The Session -------------------------------------------------------- - - public abstract class DebugSession : ProtocolServer - { - private bool _clientLinesStartAt1 = true; - private bool _clientPathsAreUri = true; - - public PathHandlingStrategy PathStrategy { get; } - - public DebugSession(bool debuggerLinesStartAt1, bool debuggerPathsAreURI = false) - { - PathStrategy = new PathHandlingStrategy - { - DebuggerLinesStartAt1 = debuggerLinesStartAt1, - DebuggerPathsAreUri = debuggerPathsAreURI - }; - } - - public void SendResponse(Response response, dynamic body = null) - { - if (body != null) { - response.SetBody(body); - } - SendMessage(response); - } - - public void SendErrorResponse(Response response, int id, string format, dynamic arguments = null, bool user = true, bool telemetry = false) - { - var msg = new Message(id, format, arguments, user, telemetry); - var message = Utilities.ExpandVariables(msg.format, msg.variables); - response.SetErrorBody(message, new ErrorResponseBody(msg)); - SendMessage(response); - } - - protected override void DispatchRequest(string command, dynamic args, Response response) - { - if (args == null) { - args = new { }; - } - - try { - switch (command) { - - case "initialize": - if (args.linesStartAt1 != null) { - _clientLinesStartAt1 = (bool)args.linesStartAt1; - } - var pathFormat = (string)args.pathFormat; - if (pathFormat != null) { - switch (pathFormat) { - case "uri": - _clientPathsAreUri = true; - break; - case "path": - _clientPathsAreUri = false; - break; - default: - SendErrorResponse(response, 1015, "initialize: bad value '{_format}' for pathFormat", new { _format = pathFormat }); - return; - } - } - - SetPathStrategy(); - Initialize(response, args); - break; - - case "launch": - Launch(response, args); - break; - - case "attach": - Attach(response, args); - break; - - case "disconnect": - Disconnect(response, args); - break; - - case "next": - Next(response, args); - break; - - case "continue": - Continue(response, args); - break; - - case "stepIn": - StepIn(response, args); - break; - - case "stepOut": - StepOut(response, args); - break; - - case "pause": - Pause(response, args); - break; - - case "stackTrace": - StackTrace(response, args); - break; - - case "scopes": - Scopes(response, args); - break; - - case "variables": - Variables(response, args); - break; - - case "source": - Source(response, args); - break; - - case "threads": - Threads(response, args); - break; - - case "setBreakpoints": - SetBreakpoints(response, args); - break; - - case "setFunctionBreakpoints": - SetFunctionBreakpoints(response, args); - break; - - case "setExceptionBreakpoints": - SetExceptionBreakpoints(response, args); - break; - - case "evaluate": - Evaluate(response, args); - break; - case "configurationDone": - ConfigurationDone(response, args); - break; - - default: - SendErrorResponse(response, 1014, "unrecognized request: {_request}", new { _request = command }); - break; - } - } - catch (Exception e) { - OnRequestError(e); - SendErrorResponse(response, 1104, "error while processing request '{_request}' (exception: {_exception})", new { _request = command, _exception = e.Message }); - } - - if (command == "disconnect") { - Stop(); - } - } - - private void SetPathStrategy() - { - PathStrategy.ClientLinesStartAt1 = _clientLinesStartAt1; - PathStrategy.ClientPathsAreUri = _clientPathsAreUri; - } - - protected string ConvertClientPathToDebugger(string path) - { - return PathStrategy.ConvertClientPathToDebugger(path); - } - - public abstract void ConfigurationDone(Response response, dynamic args); - - public abstract void Initialize(Response response, dynamic args); - - public abstract void Launch(Response response, dynamic arguments); - - public abstract void Attach(Response response, dynamic arguments); - - public abstract void Disconnect(Response response, dynamic arguments); - - public virtual void SetFunctionBreakpoints(Response response, dynamic arguments) - { - } - - public virtual void SetExceptionBreakpoints(Response response, dynamic arguments) - { - } - - protected virtual void OnRequestError(Exception e) - { - SessionLog.WriteLine(e.ToString()); - } - - public abstract void SetBreakpoints(Response response, dynamic arguments); - - public abstract void Continue(Response response, dynamic arguments); - - public abstract void Next(Response response, dynamic arguments); - - public abstract void StepIn(Response response, dynamic arguments); - - public abstract void StepOut(Response response, dynamic arguments); - - public abstract void Pause(Response response, dynamic arguments); - - public abstract void StackTrace(Response response, dynamic arguments); - - public abstract void Scopes(Response response, dynamic arguments); - - public abstract void Variables(Response response, dynamic arguments); - - public virtual void Source(Response response, dynamic arguments) - { - SendErrorResponse(response, 1020, "Source not supported"); - } - - public abstract void Threads(Response response, dynamic arguments); - - public abstract void Evaluate(Response response, dynamic arguments); - } -} diff --git a/src/VSCode.DebugAdapter/DebugeeProcess.cs b/src/VSCode.DebugAdapter/DebugeeProcess.cs index c320fddfa..b651b5bca 100644 --- a/src/VSCode.DebugAdapter/DebugeeProcess.cs +++ b/src/VSCode.DebugAdapter/DebugeeProcess.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- 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 @@ -8,11 +8,13 @@ This Source Code Form is subject to the terms of the using System.Diagnostics; using OneScript.DebugProtocol; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.ServiceModel; +using System.Runtime.InteropServices; using System.Text; +using EvilBeaver.DAP.Dto.Requests; using Newtonsoft.Json.Linq; +using Serilog; +using VSCode.DebugAdapter.Transport; using StackFrame = OneScript.DebugProtocol.StackFrame; namespace VSCode.DebugAdapter @@ -24,77 +26,176 @@ internal abstract class DebugeeProcess private bool _terminated; private bool _stdoutEOF; private bool _stderrEOF; + private bool _attachMode; - private IDebuggerService _debugger; + private Encoding _dapEncoding; + + private OneScriptDebuggerClient _debugger; private readonly PathHandlingStrategy _strategy; + private int _activeProtocolVersion; + + private ILogger Log { get; } = Serilog.Log.ForContext(); + public DebugeeProcess(PathHandlingStrategy pathHandling) { _strategy = pathHandling; } - - public string DebugProtocol { get; protected set; } - - public bool HasExited => _process?.HasExited ?? true; - public int ExitCode => _process.ExitCode; - private IDebuggerService DebugChannel { get; set; } + public bool HasExited + { + get + { + if (_process != null) + return _process.HasExited; + + if (_attachMode && _debugger != null) + return false; + + return true; + } + } + + public int ExitCode => _process?.ExitCode ?? 0; + public int DebugPort { get; set; } + public int ProtocolVersion + { + get => _activeProtocolVersion; + set + { + ValidateProtocolVersion(value); + _activeProtocolVersion = value; + } + } + + public bool WaitOnStart { get; set; } + + public WorkspaceMapper PathsMapper { get; set; } + public void Start() { _process = CreateProcess(); var psi = _process.StartInfo; - + psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; + + if (_dapEncoding != null) + { + psi.StandardErrorEncoding = _dapEncoding; + psi.StandardOutputEncoding = _dapEncoding; + } + _process.EnableRaisingEvents = true; _process.OutputDataReceived += Process_OutputDataReceived; _process.ErrorDataReceived += Process_ErrorDataReceived; _process.Exited += Process_Exited; - + _attachMode = false; _process.Start(); System.Threading.Thread.Sleep(1500); _process.BeginOutputReadLine(); _process.BeginErrorReadLine(); } - public void Init(JObject args) + public void InitAttached() { - InitInternal(args); + var pid = _debugger.GetProcessId(); + + try + { + _process = Process.GetProcessById(pid); + _process.EnableRaisingEvents = true; + _process.Exited += Process_Exited; + } + catch + { + _process = null; + } + + _attachMode = true; + } + + public abstract void Init(LaunchRequestArguments args); - protected abstract Process CreateProcess(); + public void InitPathsMapper(AttachRequestArguments args) + { + if (!args.AdditionalData?.ContainsKey("pathsMapping") ?? false) + { + PathsMapper = null; + return; + } + + try + { + var mappingToken = args.AdditionalData?["pathsMapping"]; + if (mappingToken == null || mappingToken.Type == JTokenType.Null) + { + PathsMapper = null; + return; + } + + PathsMapper = mappingToken.ToObject(); + } + catch (Exception ex) + { + Log.Warning(ex, "Failed to initialize paths mapper; path mapping will be disabled"); + PathsMapper = null; + } + } - protected abstract void InitInternal(JObject args); + protected abstract Process CreateProcess(); protected string ConvertClientPathToDebugger(string clientPath) { return _strategy.ConvertClientPathToDebugger(clientPath); } - + protected void LoadEnvironment(ProcessStartInfo psi, IDictionary variables) { if (variables == null || variables.Count <= 0) return; - + foreach (var pair in variables) { psi.EnvironmentVariables[pair.Key] = pair.Value; } } - public void SetConnection(IDebuggerService service) + protected void SetEncoding(string encodingFromOptions) + { + if (string.IsNullOrWhiteSpace(encodingFromOptions)) + { + _dapEncoding = DefaultEncoding(); + } + else + { + _dapEncoding = Utilities.GetEncodingFromOptions(encodingFromOptions); + } + + Log.Information("Encoding for debuggee output is {Encoding}", _dapEncoding); + } + + private Encoding DefaultEncoding() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? null : Encoding.UTF8; + } + + public void SetClient(OneScriptDebuggerClient service) { _debugger = service; + ProtocolVersion = service.ProtocolVersion; } - + public event EventHandler OutputReceived; public event EventHandler ProcessExited; - + private void Process_Exited(object sender, EventArgs e) { + _debugger?.Stop(); Terminate(); ProcessExited?.Invoke(this, new EventArgs()); } @@ -108,6 +209,14 @@ private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) RaiseOutputReceivedEvent("stdout", e.Data); } + private void ValidateProtocolVersion(int value) + { + if (!ProtocolVersions.IsValid(value)) + { + throw new ArgumentOutOfRangeException($"Protocol version {value} is unknown."); + } + } + private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) { if (e.Data == null) @@ -132,25 +241,86 @@ private void Terminate() { System.Threading.Thread.Sleep(100); } - + _terminated = true; _process = null; _debugger = null; } } + public void HandleDisconnect(bool terminate) + { + if (_debugger == null) + { + Log.Debug("Debugger is not connected. Nothing to disconnect"); + return; + } + _debugger.Disconnect(terminate); + + var mustKill = terminate && !_attachMode; + + if (mustKill && _process != null && !_process.HasExited) + { + Log.Debug("Stopping child process..."); + if (_process.WaitForExit(2000)) + { + Log.Debug("Process stopped"); + } + else + { + _process.Kill(); + Log.Debug("Process killed"); + } + } + + Log.Debug("Debuggee disconnected"); + } + public void Kill() { - if (_process != null && !_process.HasExited) + if (_process == null) + return; + + _process.Kill(); + _process.WaitForExit(1500); + } + + public void SetExceptionsBreakpoints((string Id, string Condition)[] filters) + { + switch (ProtocolVersion) { - _process.Kill(); + case ProtocolVersions.UnknownVersion: + case ProtocolVersions.Version1: + // Version 1 doesn't support exception breakpoints + Log.Warning("Exception breakpoints not supported in protocol version {Version}", ProtocolVersion); + break; + case ProtocolVersions.Version2: + _debugger.SetMachineExceptionBreakpoints(filters); + break; + default: // Version 3 and higher + _debugger.SetExceptionBreakpoints(filters.Select(t => new ExceptionBreakpointFilter + { + Id = t.Id, + Condition = t.Condition + }).ToArray()); + break; } } public Breakpoint[] SetBreakpoints(IEnumerable breakpoints) { - var confirmedBreaks = _debugger.SetMachineBreakpoints(breakpoints.ToArray()); - + var breakpointsArray = breakpoints.ToArray(); + + if (PathsMapper != null) + { + for (int i = 0; i < breakpointsArray.Length; i++) + { + breakpointsArray[i].Source = PathsMapper.LocalToRemote(breakpointsArray[i].Source); + } + } + + var confirmedBreaks = _debugger.SetMachineBreakpoints(breakpointsArray); + return confirmedBreaks; } @@ -158,21 +328,29 @@ public void BeginExecution(int threadId) { _debugger.Execute(threadId); } - + public StackFrame[] GetStackTrace(int threadId, int firstFrameIdx, int limit) { var allFrames = _debugger.GetStackFrames(threadId); - + var pathsMapperInit = PathsMapper != null; + if (limit == 0) limit = allFrames.Length; - if(allFrames.Length < firstFrameIdx) + if (allFrames.Length < firstFrameIdx) return new StackFrame[0]; var result = new List(); for (int i = firstFrameIdx; i < limit && i < allFrames.Length; i++) { + allFrames[i].ThreadId = threadId; + + if (pathsMapperInit) + { + allFrames[i].Source = PathsMapper.RemoteToLocal(allFrames[i].Source); + } + result.Add(allFrames[i]); } @@ -180,9 +358,9 @@ public StackFrame[] GetStackTrace(int threadId, int firstFrameIdx, int limit) } - public void FillVariables(IVariableLocator locator) + public Variable[] FetchVariables(IVariablesProvider provider) { - locator.Hydrate(_debugger); + return provider.FetchVariables(_debugger); } public Variable Evaluate(StackFrame frame, string expression) @@ -191,7 +369,7 @@ public Variable Evaluate(StackFrame frame, string expression) { return _debugger.Evaluate(frame.ThreadId, frame.Index, expression); } - catch (FaultException e) + catch (RpcOperationException e) { throw new Exception(e.Message); } @@ -216,13 +394,5 @@ public int[] GetThreads() { return _debugger.GetThreads(); } - - public void InitAttached() - { - var pid = _debugger.GetProcessId(); - _process = Process.GetProcessById(pid); - _process.EnableRaisingEvents = true; - _process.Exited += Process_Exited; - } } } diff --git a/src/VSCode.DebugAdapter/Handles.cs b/src/VSCode.DebugAdapter/Handles.cs index 77fd71e55..f3cecbd7c 100644 --- a/src/VSCode.DebugAdapter/Handles.cs +++ b/src/VSCode.DebugAdapter/Handles.cs @@ -4,10 +4,8 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; + using System.Collections.Generic; -using System.Linq; -using System.Text; namespace VSCode.DebugAdapter { diff --git a/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs new file mode 100644 index 000000000..e98313f71 --- /dev/null +++ b/src/VSCode.DebugAdapter/OneScriptDebugAdapter.cs @@ -0,0 +1,513 @@ +// /*---------------------------------------------------------- +// This Source Code Form is subject to the terms of the +// Mozilla Public License, v.2.0. If a copy of the MPL +// was not distributed with this file, You can obtain one +// at http://mozilla.org/MPL/2.0/. +// ----------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using EvilBeaver.DAP.Dto.Events; +using EvilBeaver.DAP.Dto.Requests; +using EvilBeaver.DAP.Dto.Serialization; +using EvilBeaver.DAP.Dto.Types; +using EvilBeaver.DAP.Server; +using Microsoft.Extensions.Logging; +using VSCode.DebugAdapter.Transport; + +namespace VSCode.DebugAdapter +{ + internal class OneScriptDebugAdapter : DebugAdapterBase + { + private DebugeeProcess _debuggee; + private readonly ThreadStateContainer _threadState = new ThreadStateContainer(); + + private ILogger Log { get; } + + public OneScriptDebugAdapter(ILogger logger) + { + Log = logger; + } + + protected override async Task OnInitializeAsync(InitializeRequest request, CancellationToken ct) + { + var pathStrategy = new PathHandlingStrategy + { + ClientLinesStartAt1 = Client.LinesStartAt1, + ClientPathsAreUri = Client.PathFormat == "uri", + DebuggerLinesStartAt1 = true, + DebuggerPathsAreUri = false + }; + + _debuggee = DebugeeFactory.CreateProcess(Client.AdapterId, pathStrategy); + Log.LogDebug("Debuggee created"); + + await EventsChannel.SendEventAsync(new InitializedEvent(), ct); + + return new InitializeResponse() + { + Body = new Capabilities + { + SupportsConditionalBreakpoints = true, + SupportsFunctionBreakpoints = false, + SupportsConfigurationDoneRequest = true, + SupportsExceptionFilterOptions = true, + ExceptionBreakpointFilters = new [] + { + new ExceptionBreakpointsFilter + { + Filter = "uncaught", + Label = "Необработанные исключения", + Description = "Остановка при возникновении необработанного исключения", + SupportsCondition = true, + ConditionDescription = "Искомая подстрока текста исключения" + }, + new ExceptionBreakpointsFilter + { + Filter = "all", + Label = "Все исключения", + Description = "Остановка при возникновении любого исключения", + SupportsCondition = true, + ConditionDescription = "Искомая подстрока текста исключения" + } + }, + SupportsEvaluateForHovers = true, + SupportTerminateDebuggee = true + } + }; + } + + public override Task ConfigurationDoneAsync(ConfigurationDoneRequest request, CancellationToken ct) + { + if (_debuggee == null) + { + Log.LogDebug("Config Done. Process is not started"); + return Task.FromResult(new ConfigurationDoneResponse()); + } + + Log.LogDebug("Config Done. Process is started, sending Execute"); + _debuggee.BeginExecution(-1); + + return Task.FromResult(new ConfigurationDoneResponse()); + } + + public override Task DisconnectAsync(DisconnectRequest request, CancellationToken ct) + { + Log.LogDebug("Disconnect requested, terminate={Terminate}", request.Arguments?.TerminateDebuggee); + bool terminateDebuggee = request.Arguments?.TerminateDebuggee == true; + + _debuggee?.HandleDisconnect(terminateDebuggee); + + return Task.FromResult(new DisconnectResponse()); + } + + public override Task ContinueAsync(ContinueRequest request, CancellationToken ct) + { + _debuggee.BeginExecution(-1); + return Task.FromResult(new ContinueResponse()); + } + + public override Task NextAsync(NextRequest request, CancellationToken ct) + { + lock (_debuggee) + { + if (!_debuggee.HasExited) + _debuggee.Next(request.Arguments.ThreadId); + } + return Task.FromResult(new NextResponse()); + } + + public override Task StepInAsync(StepInRequest request, CancellationToken ct) + { + lock (_debuggee) + { + if (!_debuggee.HasExited) + _debuggee.StepIn(request.Arguments.ThreadId); + } + return Task.FromResult(new StepInResponse()); + } + + public override Task StepOutAsync(StepOutRequest request, CancellationToken ct) + { + lock (_debuggee) + { + if (!_debuggee.HasExited) + _debuggee.StepOut(request.Arguments.ThreadId); + } + return Task.FromResult(new StepOutResponse()); + } + + public override Task ThreadsAsync(ThreadsRequest request, CancellationToken ct) + { + var processThreads = _debuggee.GetThreads(); + var threads = new EvilBeaver.DAP.Dto.Types.Thread[processThreads.Length]; + for (int i = 0; i < processThreads.Length; i++) + { + threads[i] = new EvilBeaver.DAP.Dto.Types.Thread + { + Id = processThreads[i], + Name = $"Thread {processThreads[i]}" + }; + } + + return Task.FromResult(new ThreadsResponse + { + Body = new ThreadsResponseBody { Threads = threads } + }); + } + + public override Task StackTraceAsync(StackTraceRequest request, CancellationToken ct) + { + var args = request.Arguments; + var firstFrameIdx = args.StartFrame ?? 0; + var limit = args.Levels ?? 0; + var threadId = args.ThreadId; + + var processFrames = _debuggee.GetStackTrace(threadId, firstFrameIdx, limit); + var frames = new EvilBeaver.DAP.Dto.Types.StackFrame[processFrames.Length]; + for (int i = 0; i < processFrames.Length; i++) + { + frames[i] = new EvilBeaver.DAP.Dto.Types.StackFrame + { + Id = _threadState.RegisterFrame(processFrames[i]), + Name = processFrames[i].MethodName, + Source = processFrames[i].GetSource(), + Line = processFrames[i].LineNumber, + Column = 0 + }; + } + + return Task.FromResult(new StackTraceResponse + { + Body = new StackTraceResponseBody + { + StackFrames = frames, + TotalFrames = frames.Length + } + }); + } + + public override Task ScopesAsync(ScopesRequest request, CancellationToken ct) + { + int frameId = request.Arguments.FrameId; + var frame = _threadState.GetFrameById(frameId); + if (frame == null) + { + throw new ErrorResponseException("No active stackframe"); + } + + var scopes = new List(); + + var localProvider = new LocalScopeProvider(frame.ThreadId, frame.Index); + var localHandle = _threadState.RegisterVariablesProvider(localProvider); + scopes.Add(new Scope + { + Name = "Локальные переменные", + VariablesReference = localHandle + }); + + if (_debuggee.ProtocolVersion >= ProtocolVersions.Version4) + { + var moduleProvider = new ModuleScopeProvider(frame.ThreadId, frame.Index); + var moduleHandle = _threadState.RegisterVariablesProvider(moduleProvider); + scopes.Add(new Scope + { + Name = "Переменные модуля", + VariablesReference = moduleHandle + }); + } + + return Task.FromResult(new ScopesResponse + { + Body = new ScopesResponseBody { Scopes = scopes.ToArray() } + }); + } + + public override Task VariablesAsync(VariablesRequest request, CancellationToken ct) + { + int varsHandle = request.Arguments.VariablesReference; + var provider = _threadState.GetVariablesProviderById(varsHandle); + if (provider == null) + { + throw new ErrorResponseException("Invalid variables reference"); + } + + var variables = _debuggee.FetchVariables(provider); + var responseArray = new Variable[variables.Length]; + + for (int i = 0; i < responseArray.Length; i++) + { + var variable = variables[i]; + int childHandle = 0; + + if (variable.IsStructured) + { + var childProvider = provider.CreateChildProvider(i); + childHandle = _threadState.RegisterVariablesProvider(childProvider); + } + + responseArray[i] = new Variable + { + Name = variable.Name, + Value = variable.Presentation, + Type = variable.TypeName, + VariablesReference = childHandle + }; + } + + return Task.FromResult(new VariablesResponse + { + Body = new VariablesResponseBody { Variables = responseArray } + }); + } + + public override Task EvaluateAsync(EvaluateRequest request, CancellationToken ct) + { + var args = request.Arguments; + int frameId = args.FrameId ?? 0; + var frame = _threadState.GetFrameById(frameId); + if (frame == null) + { + throw new ErrorResponseException("No active stackframe"); + } + + var expression = args.Expression; + var context = args.Context; + + int id = 0; + OneScript.DebugProtocol.Variable evalResult; + try + { + evalResult = _debuggee.Evaluate(frame, expression); + if (evalResult.IsStructured) + { + var provider = new EvaluatedExpressionProvider(expression, frame.ThreadId, frame.Index); + id = _threadState.RegisterVariablesProvider(provider); + } + } + catch (Exception e) + { + evalResult = new OneScript.DebugProtocol.Variable() { Presentation = e.Message, Name = "$evalFault" }; + } + + if (evalResult.Name.Equals("$evalFault") && "hover".Equals(context)) + { + evalResult.Presentation = $"err: {expression}"; + } + + return Task.FromResult(new EvaluateResponse + { + Body = new EvaluateResponseBody + { + Result = evalResult.Presentation, + Type = evalResult.TypeName, + VariablesReference = id + } + }); + } + + public override Task SetExceptionBreakpointsAsync(SetExceptionBreakpointsRequest request, CancellationToken ct) + { + var args = request.Arguments; + var filters = new List<(string Id, string Condition)>(); + var acceptedFilters = new List(); + + if (args.Filters != null) + { + foreach (var filter in args.Filters) + { + filters.Add((filter, "")); + acceptedFilters.Add(new Breakpoint { Verified = true }); + } + } + + if (args.FilterOptions != null) + { + foreach (var filterOption in args.FilterOptions) + { + filters.Add((filterOption.FilterId, filterOption.Condition ?? "")); + acceptedFilters.Add(new Breakpoint { Verified = true }); + } + } + + _debuggee.SetExceptionsBreakpoints(filters.ToArray()); + + return Task.FromResult(new SetExceptionBreakpointsResponse + { + Body = new SetExceptionBreakpointsResponseBody + { + Breakpoints = acceptedFilters.ToArray() + } + }); + } + + public override Task AttachAsync(AttachRequest request, CancellationToken ct) + { + var options = request.Arguments.DeserializeAdditionalProperties(); + + _debuggee.DebugPort = options.DebugPort; + _debuggee.PathsMapper = options.PathsMapping; + + SubscribeForDebuggeeProcessEvents(); + + DebugClientFactory debugClientFactory; + try + { + debugClientFactory = ConnectDebugServer(); + } + catch (Exception e) + { + Log.LogError(e, "Can't connect debuggee"); + throw new ErrorResponseException("Can't connect: " + e.ToString()); + } + + _debuggee.SetClient(debugClientFactory.CreateDebugClient()); + try + { + _debuggee.InitAttached(); + } + catch (Exception e) + { + Log.LogError(e, "Attach failed"); + throw new ErrorResponseException("Attach failed: " + e.ToString()); + } + + return Task.FromResult(new AttachResponse()); + } + + public override Task SetBreakpointsAsync(SetBreakpointsRequest request, CancellationToken ct) + { + if (request.Arguments.SourceModified == true) + { + throw new ErrorResponseException("Нельзя установить точку останова на модифицированный файл."); + } + + Debug.Assert(request.Arguments.Source.Path != null, "request.Arguments.Source.Path != null"); + var path = ToNativePath(request.Arguments.Source.Path); + + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + { + // vscode иногда передает путь, где диск - маленькая буква + path = Utilities.NormalizeDriveLetter(path); + } + + var useConditions = _debuggee.ProtocolVersion >= ProtocolVersions.Version2; + + Debug.Assert(request.Arguments.Breakpoints != null, "request.Arguments.Breakpoints != null"); + var breaks = request.Arguments.Breakpoints + .Select(srcBreakpoint => new OneScript.DebugProtocol.Breakpoint + { + Line = srcBreakpoint.Line, + Source = path, + Condition = useConditions ? srcBreakpoint.Condition ?? string.Empty : string.Empty + }).ToList(); + + var confirmedBreaks = _debuggee.SetBreakpoints(breaks); + var confirmedDapBreaks = new List(confirmedBreaks.Length); + confirmedDapBreaks.AddRange(confirmedBreaks + .Select(t => new Breakpoint + { + Line = t.Line, + Verified = true + }) + ); + + return Task.FromResult(new SetBreakpointsResponse + { + Body = new SetBreakpointsResponseBody + { + Breakpoints = confirmedDapBreaks.ToArray(), + } + }); + } + + public override async Task LaunchAsync(LaunchRequest request, CancellationToken ct) + { + try + { + Log.LogDebug("Initializing process settings"); + + _debuggee.Init(request.Arguments); + } + catch (InvalidDebugeeOptionsException e) + { + Log.LogError(e, "Wrong options received {ErrorCode}: {Message}", e.ErrorCode, e.Message); + throw new ErrorResponseException(e.Message); + } + + SubscribeForDebuggeeProcessEvents(); + + try + { + Log.LogDebug("Starting debuggee"); + _debuggee.Start(); + Log.LogInformation("Debuggee started"); + } + catch (Exception e) + { + Log.LogError(e, "Can't launch debuggee"); + throw new ErrorResponseException($"Can't launch debuggee ({e.Message})."); + } + + DebugClientFactory debugClientFactory; + try + { + debugClientFactory = ConnectDebugServer(); + } + catch (Exception e) + { + _debuggee.Kill(); + await EventsChannel.SendEventAsync(new TerminatedEvent(), ct); + Log.LogError(e, "Can't connect to debug server"); + throw new ErrorResponseException("Can't connect: " + e.ToString()); + } + + _debuggee.SetClient(debugClientFactory.CreateDebugClient()); + + return new LaunchResponse(); + } + + private void SubscribeForDebuggeeProcessEvents() + { + _debuggee.OutputReceived += (s, e) => + { + Log.LogDebug("Output received {Output}", e.Content); + + if (string.IsNullOrEmpty(e.Content)) + return; + + var data = e.Content; + if (data[data.Length - 1] != '\n') + { + data += '\n'; + } + + EventsChannel.SendEventAsync(new OutputEvent + { + Body = new OutputEventBody + { + Category = e.Category, + Output = data + } + }); + }; + + _debuggee.ProcessExited += (s, e) => + { + Log.LogInformation("Debuggee has exited"); + EventsChannel.SendEventAsync(new TerminatedEvent()); + }; + } + + private DebugClientFactory ConnectDebugServer() + { + var tcpConnection = ConnectionFactory.Connect(_debuggee.DebugPort); + var listener = new OscriptDebugEventsListener(EventsChannel, _threadState); + return new DebugClientFactory(tcpConnection, listener); + } + } +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/OneScriptDebuggerClient.cs b/src/VSCode.DebugAdapter/OneScriptDebuggerClient.cs new file mode 100644 index 000000000..486ac3629 --- /dev/null +++ b/src/VSCode.DebugAdapter/OneScriptDebuggerClient.cs @@ -0,0 +1,206 @@ +/*---------------------------------------------------------- +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.Runtime.CompilerServices; +using OneScript.DebugProtocol; +using OneScript.DebugProtocol.Abstractions; +using OneScript.DebugProtocol.TcpServer; +using Serilog; +using VSCode.DebugAdapter.Transport; + +namespace VSCode.DebugAdapter +{ + public class OneScriptDebuggerClient : IDebuggerService + { + private readonly IDebugEventListener _eventBackChannel; + private readonly IMessageChannel _commandsChannel; + private RpcProcessor _processor; + + private readonly ILogger Log = Serilog.Log.ForContext(); + + public OneScriptDebuggerClient( + IMessageChannel commandsChannel, + IDebugEventListener eventBackChannel, + int protocolVersion) + { + _commandsChannel = commandsChannel; + _eventBackChannel = eventBackChannel; + ProtocolVersion = protocolVersion; + } + + public int ProtocolVersion { get; } + + public void Start() + { + RunEventsListener(_commandsChannel); + } + + public void Stop() + { + _processor.Stop(); + } + + private void RunEventsListener(IMessageChannel channelToListen) + { + var server = new DefaultMessageServer(channelToListen); + server.ServerThreadName = "dbg-client-event-listener"; + + _processor = new RpcProcessor(server); + _processor.AddChannel( + nameof(IDebugEventListener), + typeof(IDebugEventListener), + _eventBackChannel); + + _processor.AddChannel( + nameof(IDebuggerService), + typeof(IDebuggerService), + this); + + _processor.Start(); + Log.Debug("Debuggee event listener started"); + } + + private void WriteCommand(T data, [CallerMemberName] string command = "") + { + Log.Verbose("Sending {Command} to debuggee, param {@Parameter}", command, data); + var dto = RpcCall.Create(nameof(IDebuggerService), command, data); + _commandsChannel.Write(dto); + Log.Verbose("Successfully written: {Command}", command); + + } + + private void WriteCommand(object[] data, [CallerMemberName] string command = "") + { + Log.Verbose("Sending {Command} to debuggee, params {@Parameters}", command, data); + var dto = RpcCall.Create(nameof(IDebuggerService), command, data); + _commandsChannel.Write(dto); + Log.Verbose("Successfully written: {Command}", command); + } + + private T GetResponse() + { + var rpcResult = _processor.GetResult(); + Log.Verbose("Response received {Result} = {@Value}", rpcResult.Id, rpcResult.ReturnValue); + if (rpcResult.ReturnValue is RpcExceptionDto excDto) + { + Log.Verbose("RPC Exception received: {Description}", excDto.Description); + throw new RpcOperationException(excDto); + } + + return (T) rpcResult.ReturnValue; + } + + public void Execute(int threadId) + { + WriteCommand(threadId); + } + + public void SetMachineExceptionBreakpoints((string Id, string Condition)[] filters) + { + WriteCommand(filters); + } + + public void SetExceptionBreakpoints(ExceptionBreakpointFilter[] filters) + { + WriteCommand(filters); + } + + public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet) + { + WriteCommand(breaksToSet); + return GetResponse(); + } + + public StackFrame[] GetStackFrames(int threadId) + { + WriteCommand(threadId); + return GetResponse(); + } + + public Variable[] GetVariables(int threadId, int frameIndex, int[] path) + { + WriteCommand(new object[] + { + threadId, + frameIndex, + path + }); + + return GetResponse(); + } + + public Variable[] GetModuleVariables(int threadId, int frameIndex, int[] path) + { + WriteCommand(new object[] + { + threadId, + frameIndex, + path + }); + + return GetResponse(); + } + + public Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path) + { + WriteCommand(new object[] + { + expression, + threadId, + frameIndex, + path + }); + + return GetResponse(); + } + + public Variable Evaluate(int threadId, int contextFrame, string expression) + { + WriteCommand(new object[] + { + threadId, + contextFrame, + expression + }); + + return GetResponse(); + } + + public void Next(int threadId) + { + WriteCommand(threadId); + } + + public void StepIn(int threadId) + { + WriteCommand(threadId); + } + + public void StepOut(int threadId) + { + WriteCommand(threadId); + } + + public void Disconnect(bool terminate) + { + WriteCommand(terminate); + Stop(); + } + + public int[] GetThreads() + { + WriteCommand(null); + return GetResponse(); + } + + public int GetProcessId() + { + WriteCommand(null); + return GetResponse(); + } + } +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/OscriptDebugEventsListener.cs b/src/VSCode.DebugAdapter/OscriptDebugEventsListener.cs new file mode 100644 index 000000000..d583596db --- /dev/null +++ b/src/VSCode.DebugAdapter/OscriptDebugEventsListener.cs @@ -0,0 +1,97 @@ +/*---------------------------------------------------------- +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.Runtime.CompilerServices; +using EvilBeaver.DAP.Dto.Events; +using EvilBeaver.DAP.Server; +using OneScript.DebugProtocol; +using Serilog; + +namespace VSCode.DebugAdapter +{ + public class OscriptDebugEventsListener : IDebugEventListener + { + private readonly IClientChannel _channel; + private readonly ThreadStateContainer _threadState; + private readonly ILogger Log = Serilog.Log.ForContext(); + + public OscriptDebugEventsListener(IClientChannel channel, ThreadStateContainer threadState) + { + _channel = channel; + _threadState = threadState; + } + + public void ThreadStopped(int threadId, ThreadStopReason reason) + { + LogEventOccured(); + _threadState.Reset(); + _channel.SendEventAsync(new StoppedEvent + { + Body = new StoppedEventBody + { + ThreadId = threadId, + Reason = reason.ToString(), + AllThreadsStopped = true + } + }); + } + + public void ThreadStoppedEx(int threadId, ThreadStopReason reason, string errorMessage) + { + LogEventOccured(); + _threadState.Reset(); + + if (!string.IsNullOrEmpty(errorMessage)) + SendOutput("stderr", errorMessage); + + _channel.SendEventAsync(new StoppedEvent + { + Body = new StoppedEventBody + { + ThreadId = threadId, + Reason = reason.ToString(), + AllThreadsStopped = true + } + }); + } + + public void ProcessExited(int exitCode) + { + LogEventOccured(); + _channel.SendEventAsync(new ExitedEvent + { + Body = new ExitedEventBody + { + ExitCode = exitCode + } + }); + } + + private void SendOutput(string category, string data) + { + if (!string.IsNullOrEmpty(data)) + { + if (data[data.Length - 1] != '\n') + { + data += '\n'; + } + _channel.SendEventAsync(new OutputEvent + { + Body = new OutputEventBody + { + Category = category, + Output = data + } + }); + } + } + + private void LogEventOccured([CallerMemberName] string eventName = "") + { + Log.Debug("Event occured {Event}", eventName); + } + } +} diff --git a/src/VSCode.DebugAdapter/OscriptDebugSession.cs b/src/VSCode.DebugAdapter/OscriptDebugSession.cs deleted file mode 100644 index ad4840d10..000000000 --- a/src/VSCode.DebugAdapter/OscriptDebugSession.cs +++ /dev/null @@ -1,459 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Linq.Expressions; -using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json.Linq; -using OneScript.DebugProtocol; -using VSCodeDebug; - - -namespace VSCode.DebugAdapter -{ - internal class OscriptDebugSession : DebugSession, IDebugEventListener - { - private DebugeeProcess _process; - private bool _startupPerformed = false; - private readonly Handles _framesHandles; - private readonly Handles _variableHandles; - - public OscriptDebugSession() : base(true, false) - { - _framesHandles = new Handles(); - _variableHandles = new Handles(); - } - - private string AdapterID { get; set; } - - public override void Initialize(Response response, dynamic args) - { - SessionLog.WriteLine("Initialize:" + args); - AdapterID = (string) args.adapterID; - - _process = DebugeeFactory.CreateProcess(AdapterID, PathStrategy); - - SendResponse(response, new Capabilities() - { - supportsConditionalBreakpoints = false, - supportsFunctionBreakpoints = false, - supportsConfigurationDoneRequest = true, - exceptionBreakpointFilters = new dynamic[0], - supportsEvaluateForHovers = true - }); - - SendEvent(new InitializedEvent()); - } - - public override void Launch(Response response, dynamic args) - { - SessionLog.WriteLine("Launch command accepted"); - - try - { - _process.Init(args); - } - catch (InvalidDebugeeOptionsException e) - { - SendErrorResponse(response, e.ErrorCode, e.Message); - return; - } - - _process.OutputReceived += (s, e) => - { - SessionLog.WriteLine("output received: " + e.Content); - SendOutput(e.Category, e.Content); - }; - - _process.ProcessExited += (s, e) => - { - SessionLog.WriteLine("_process exited"); - SendEvent(new TerminatedEvent()); - }; - - try - { - _process.Start(); - SessionLog.WriteLine("Debuggee started"); - } - catch (Exception e) - { - SessionLog.WriteLine(e.ToString()); - SendErrorResponse(response, 3012, "Can't launch debugee ({reason}).", new { reason = e.Message }); - return; - } - - try - { - IDebuggerService service; - if (_process.DebugProtocol == "wcf") - { - var wcfConnector = new WcfDebuggerConnection(_process.DebugPort, this); - wcfConnector.Connect(); - service = wcfConnector; - } - else - { - var tcpConnector = new TcpDebugConnector(_process.DebugPort, this); - tcpConnector.Connect(); - service = tcpConnector; - } - - _process.SetConnection(service); - } - catch (Exception e) - { - _process.Kill(); - _process = null; - SessionLog.WriteLine(e.ToString()); - SendErrorResponse(response, 4550, "Can't connect: " + e.ToString()); - return; - } - - SendResponse(response); - - } - - public override void Attach(Response response, dynamic arguments) - { - SessionLog.WriteLine("Attach command received"); - _process.DebugPort = getInt(arguments, "debugPort", 2801); - _process.ProcessExited += (s, e) => - { - SessionLog.WriteLine("_process exited"); - SendEvent(new TerminatedEvent()); - }; - - try - { - IDebuggerService service; - var tcpConnector = new TcpDebugConnector(_process.DebugPort, this); - tcpConnector.Connect(); - SessionLog.WriteLine($"Connected to host on port {_process.DebugPort}"); - service = tcpConnector; - - _process.SetConnection(service); - _process.InitAttached(); - } - catch (Exception e) - { - SessionLog.WriteLine(e.ToString()); - SendErrorResponse(response, 4550, "Can't connect: " + e.ToString()); - return; - } - - SendResponse(response); - } - - public override void Disconnect(Response response, dynamic arguments) - { - _process.Kill(); - SendResponse(response); - } - - public override void SetBreakpoints(Response response, dynamic arguments) - { - SessionLog.WriteLine($"Set breakpoints command accepted {arguments}"); - - if ((bool)arguments.sourceModified) - { - if (_startupPerformed) - { - SendErrorResponse(response, 1102, "Нельзя установить точку останова на модифицированный файл."); - return; - } - SendResponse(response, new SetBreakpointsResponseBody()); - return; - } - - var path = (string) arguments.source.path; - path = ConvertClientPathToDebugger(path); - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - { - // vscode иногда передает путь, где диск - маленькая буква - path = NormalizeDriveLetter(path); - } - - var breaks = new List(); - - foreach (var srcBreakpoint in arguments.breakpoints) - { - var bpt = new OneScript.DebugProtocol.Breakpoint(); - bpt.Line = (int) srcBreakpoint.line; - bpt.Source = path; - breaks.Add(bpt); - } - - if(breaks.Count == 0) // в целях сохранения интерфейса WCF придется сделать костыль на перех. период - { - var bpt = new OneScript.DebugProtocol.Breakpoint(); - bpt.Line = 0; - bpt.Source = path; - breaks.Add(bpt); - } - - var confirmedBreaks = _process.SetBreakpoints(breaks); - var confirmedBreaksVSCode = new List(confirmedBreaks.Length); - for (int i = 0; i < confirmedBreaks.Length; i++) - { - confirmedBreaksVSCode.Add(new VSCodeDebug.Breakpoint(true, confirmedBreaks[i].Line)); - } - - SendResponse(response, new SetBreakpointsResponseBody(confirmedBreaksVSCode)); - - } - - private string NormalizeDriveLetter(string path) - { - if (Path.IsPathRooted(path)) - return path[0].ToString().ToUpperInvariant() + path.Substring(1); - else - return path; - - } - - public void ThreadStopped(int threadId, ThreadStopReason reason) - { - SessionLog.WriteLine("thread stopped"); - _framesHandles.Reset(); - _variableHandles.Reset(); - SendEvent(new StoppedEvent(threadId, reason.ToString())); - } - - public void ProcessExited(int exitCode) - { - SessionLog.WriteLine("Exited event recieved"); - SendEvent(new ExitedEvent(exitCode)); - } - - public override void ConfigurationDone(Response response, dynamic args) - { - if (_process == null) - { - SessionLog.WriteLine("Config Done. Process is not started"); - SendResponse(response); - return; - } - SessionLog.WriteLine("Config Done. Process is started"); - _process.BeginExecution(-1); - _startupPerformed = true; - SendResponse(response); - } - - public override void Continue(Response response, dynamic arguments) - { - SendResponse(response); - _process.BeginExecution(-1); - } - - public override void Next(Response response, dynamic arguments) - { - SendResponse(response); - lock (_process) - { - if (!_process.HasExited) - { - _process.Next((int)arguments.threadId); - } - } - - } - - public override void StepIn(Response response, dynamic arguments) - { - SendResponse(response); - lock (_process) - { - if (!_process.HasExited) - { - _process.StepIn((int)arguments.threadId); - } - } - } - - public override void StepOut(Response response, dynamic arguments) - { - SendResponse(response); - lock (_process) - { - if (!_process.HasExited) - { - _process.StepOut((int)arguments.threadId); - } - } - } - - public override void Pause(Response response, dynamic arguments) - { - throw new NotImplementedException(); - } - - public override void StackTrace(Response response, dynamic arguments) - { - SessionLog.WriteLine("Stacktrace request accepted"); - SessionLog.WriteLine(arguments.ToString()); - var firstFrameIdx = (int?)arguments.startFrame ?? 0; - var limit = (int?) arguments.levels ?? 0; - var threadId = (int) arguments.threadId; - var processFrames = _process.GetStackTrace(threadId, firstFrameIdx, limit); - var frames = new VSCodeDebug.StackFrame[processFrames.Length]; - for (int i = 0; i < processFrames.Length; i++) - { - frames[i] = new VSCodeDebug.StackFrame( - _framesHandles.Create(processFrames[i]), - processFrames[i].MethodName, - processFrames[i].GetSource(), - processFrames[i].LineNumber, 0); - } - - SendResponse(response, new StackTraceResponseBody(frames)); - } - - public override void Scopes(Response response, dynamic arguments) - { - int frameId = getInt(arguments, "frameId"); - var frame = _framesHandles.Get(frameId, null); - if (frame == null) - { - SendErrorResponse(response, 10001, "No active stackframe"); - return; - } - - var frameVariablesHandle = _variableHandles.Create(frame); - var localScope = new Scope("Локальные переменные", frameVariablesHandle); - SendResponse(response, new ScopesResponseBody(new Scope[] {localScope})); - SessionLog.WriteLine("Scopes done"); - } - - public override void Variables(Response response, dynamic arguments) - { - int varsHandle = getInt(arguments, "variablesReference"); - SessionLog.WriteLine($"variables request {varsHandle}"); - var variables = _variableHandles.Get(varsHandle, null); - if (variables == null) - { - SendErrorResponse(response, 10001, "No active stackframe"); - return; - } - - _process.FillVariables(variables); - - var responseArray = new VSCodeDebug.Variable[variables.Count]; - - for (int i = 0; i < responseArray.Length; i++) - { - var variable = variables[i]; - - if (variable.IsStructured && variable.ChildrenHandleID == 0) - { - variable.ChildrenHandleID = _variableHandles.Create(variables.CreateChildLocator(i)); - } - - responseArray[i] = new VSCodeDebug.Variable( - variable.Name, - variable.Presentation, - variable.TypeName, - variable.ChildrenHandleID); - } - - SendResponse(response, new VariablesResponseBody(responseArray)); - } - - public override void Threads(Response response, dynamic arguments) - { - var threads = new List(); - SessionLog.WriteLine("Threads request accepted"); - var processThreads = _process.GetThreads(); - for (int i = 0; i < processThreads.Length; i++) - { - threads.Add(new VSCodeDebug.Thread(processThreads[i], $"Thread {processThreads[i]}")); - } - - SendResponse(response, new ThreadsResponseBody(threads)); - SessionLog.WriteLine("Threads processed"); - } - - public override void Evaluate(Response response, dynamic arguments) - { - // expression, frameId, context - int frameId = getInt(arguments, "frameId"); - var frame = _framesHandles.Get(frameId, null); - if (frame == null) - { - SendErrorResponse(response, 10001, "No active stackframe"); - return; - } - - var expression = (string) arguments.expression; - var context = (string) arguments.context; - - int id = -1; - OneScript.DebugProtocol.Variable evalResult; - try - { - evalResult = _process.Evaluate(frame, expression); - - if (evalResult.IsStructured) - { - var loc = new EvaluatedVariableLocator(expression, frameId); - id = _variableHandles.Create(loc); - } - } - catch (Exception e) - { - evalResult = new OneScript.DebugProtocol.Variable() { Presentation = e.Message, Name = "$evalFault" }; - } - - if (evalResult.Name.Equals("$evalFault") && context.Equals("hover")) - { - evalResult.Presentation = $"err: {expression}"; - } - - var protResult = new EvaluateResponseBody(evalResult.Presentation, id) {type = evalResult.TypeName}; - SendResponse(response, protResult); - } - - - private void SendOutput(string category, string data) - { - if (!String.IsNullOrEmpty(data)) - { - if (data[data.Length - 1] != '\n') - { - data += '\n'; - } - SendEvent(new OutputEvent(category, data)); - } - } - - private static int getInt(dynamic container, string propertyName, int dflt = 0) - { - try - { - return (int)container[propertyName]; - } - catch (Exception) - { - // ignore and return default value - } - return dflt; - } - - - private void RequestDummy(string message, Response response, dynamic arguments) - { - SessionLog.WriteLine(message); - SendResponse(response, arguments); - } - - } -} diff --git a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/TcpDebugConnector.cs b/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/TcpDebugConnector.cs deleted file mode 100644 index 7883b1401..000000000 --- a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/TcpDebugConnector.cs +++ /dev/null @@ -1,171 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.IO; -using System.Net.Sockets; -using System.Runtime.CompilerServices; -using System.Threading; -using OneScript.DebugProtocol; -using OneScript.DebugProtocol.Abstractions; -using OneScript.DebugProtocol.TcpServer; - -namespace VSCode.DebugAdapter -{ - public class TcpDebugConnector : IDebuggerService - { - private readonly int _port; - private readonly IDebugEventListener _eventBackChannel; - private BinaryChannel _commandsChannel; - private RpcProcessor _processor; - - public TcpDebugConnector(int port, IDebugEventListener eventBackChannel) - { - _port = port; - _eventBackChannel = eventBackChannel; - } - - public void Connect() - { - var debuggerUri = Binder.GetDebuggerUri(_port); - - SessionLog.WriteLine("Creating commands tcp channel"); - _commandsChannel = new BinaryChannel(new TcpClient(debuggerUri.Host, debuggerUri.Port)); - - SessionLog.WriteLine("connected"); - - RunEventsListener(_commandsChannel); - } - - private void RunEventsListener(ICommunicationChannel channelToListen) - { - var server = new DefaultMessageServer(channelToListen); - - _processor = new RpcProcessor(server); - _processor.AddChannel( - nameof(IDebugEventListener), - typeof(IDebugEventListener), - _eventBackChannel); - - _processor.AddChannel( - nameof(IDebuggerService), - typeof(IDebuggerService), - this); - - _processor.Start(); - } - - private void WriteCommand(T data, [CallerMemberName] string command = "") - { - SessionLog.WriteLine($"Sending {command} to debuggee"); - var dto = RpcCall.Create(nameof(IDebuggerService), command, data); - _commandsChannel.Write(dto); - - } - - private void WriteCommand(object[] data, [CallerMemberName] string command = "") - { - SessionLog.WriteLine($"Sending {command} to debuggee"); - var dto = RpcCall.Create(nameof(IDebuggerService), command, data); - _commandsChannel.Write(dto); - } - - private T GetResponse() - { - var rpcResult = _processor.GetResult(); - SessionLog.WriteLine("Response received " + rpcResult.Id + " t = " + rpcResult.ReturnValue); - if (rpcResult.ReturnValue is RpcExceptionDto excDto) - { - SessionLog.WriteLine($"Exception received: {excDto.Description}"); - throw new RpcOperationException(excDto); - } - - return (T) rpcResult.ReturnValue; - } - - public void Execute(int threadId) - { - WriteCommand(threadId); - } - - public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet) - { - WriteCommand(breaksToSet); - return GetResponse(); - } - - public StackFrame[] GetStackFrames(int threadId) - { - WriteCommand(threadId); - return GetResponse(); - } - - public Variable[] GetVariables(int threadId, int frameIndex, int[] path) - { - WriteCommand(new object[] - { - threadId, - frameIndex, - path - }); - - return GetResponse(); - } - - public Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path) - { - WriteCommand(new object[] - { - expression, - threadId, - frameIndex, - path - }); - - return GetResponse(); - } - - public Variable Evaluate(int threadId, int contextFrame, string expression) - { - WriteCommand(new object[] - { - threadId, - contextFrame, - expression - }); - - return GetResponse(); - } - - public void Next(int threadId) - { - WriteCommand(threadId); - } - - public void StepIn(int threadId) - { - WriteCommand(threadId); - } - - public void StepOut(int threadId) - { - WriteCommand(threadId); - } - - public int[] GetThreads() - { - WriteCommand(null); - return GetResponse(); - } - - public int GetProcessId() - { - WriteCommand(null); - return GetResponse(); - } - } -} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/OscriptProtocols/Wcf/ServiceProxy.cs b/src/VSCode.DebugAdapter/OscriptProtocols/Wcf/ServiceProxy.cs deleted file mode 100644 index 35d0fc0fd..000000000 --- a/src/VSCode.DebugAdapter/OscriptProtocols/Wcf/ServiceProxy.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel; -using System.Text; - -namespace VSCode.DebugAdapter -{ - internal class ServiceProxy where T : class - { - private T _instance; - private readonly Func _factory; - - private readonly object _lock = new object(); - - public ServiceProxy(Func creationMethod) - { - _factory = creationMethod; - } - - public T Instance - { - get - { - lock (_lock) - { - var ico = (ICommunicationObject) _instance; - - if (ico != null && (ico.State == CommunicationState.Faulted || ico.State == CommunicationState.Closed)) - { - ico.Abort(); - _instance = null; - } - - if (_instance == null) - { - _instance = _factory(); - } - } - - return _instance; - } - } - } -} diff --git a/src/VSCode.DebugAdapter/OscriptProtocols/Wcf/WcfDebuggerConnection.cs b/src/VSCode.DebugAdapter/OscriptProtocols/Wcf/WcfDebuggerConnection.cs deleted file mode 100644 index 3f45e3aa2..000000000 --- a/src/VSCode.DebugAdapter/OscriptProtocols/Wcf/WcfDebuggerConnection.cs +++ /dev/null @@ -1,92 +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.ServiceModel; -using OneScript.DebugProtocol; - -namespace VSCode.DebugAdapter -{ - public class WcfDebuggerConnection : IDebuggerService - { - private readonly int _port; - private readonly IDebugEventListener _eventBackChannel; - private ServiceProxy _serviceProxy; - - public WcfDebuggerConnection(int port, IDebugEventListener eventBackChannel) - { - _port = port; - _eventBackChannel = eventBackChannel; - } - - public void Connect() - { - var binding = (NetTcpBinding) Binder.GetBinding(); - binding.MaxBufferPoolSize = DebuggerSettings.MAX_BUFFER_SIZE; - binding.MaxBufferSize = DebuggerSettings.MAX_BUFFER_SIZE; - binding.MaxReceivedMessageSize = DebuggerSettings.MAX_BUFFER_SIZE; - - var channelFactory = new DuplexChannelFactory(_eventBackChannel, binding, - new EndpointAddress(Binder.GetDebuggerUri(_port))); - _serviceProxy = new ServiceProxy(channelFactory.CreateChannel); - } - - public void Execute(int threadId) - { - _serviceProxy.Instance.Execute(threadId); - } - - public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet) - { - return _serviceProxy.Instance.SetMachineBreakpoints(breaksToSet); - } - - public StackFrame[] GetStackFrames(int threadId) - { - return _serviceProxy.Instance.GetStackFrames(threadId); - } - - public Variable[] GetVariables(int threadId, int frameIndex, int[] path) - { - return _serviceProxy.Instance.GetVariables(threadId, frameIndex, path); - } - - public Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path) - { - return _serviceProxy.Instance.GetEvaluatedVariables(expression, threadId, frameIndex, path); - } - - public Variable Evaluate(int threadId, int contextFrame, string expression) - { - return _serviceProxy.Instance.Evaluate(threadId, contextFrame, expression); - } - - public void Next(int threadId) - { - _serviceProxy.Instance.Next(threadId); - } - - public void StepIn(int threadId) - { - _serviceProxy.Instance.StepIn(threadId); - } - - public void StepOut(int threadId) - { - _serviceProxy.Instance.StepOut(threadId); - } - - public int[] GetThreads() - { - return _serviceProxy.Instance.GetThreads(); - } - - public int GetProcessId() - { - return _serviceProxy.Instance.GetProcessId(); - } - } -} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/Program.cs b/src/VSCode.DebugAdapter/Program.cs index 5d6af6bea..9cae5a4e7 100644 --- a/src/VSCode.DebugAdapter/Program.cs +++ b/src/VSCode.DebugAdapter/Program.cs @@ -1,17 +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/. ----------------------------------------------------------*/ using System; -using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Linq; -using System.Net; using System.Net.Sockets; -using System.Text; -using System.Threading.Tasks; +using EvilBeaver.DAP.Server; +using EvilBeaver.DAP.Server.Transport; +using Serilog; +using Serilog.Extensions.Logging; namespace VSCode.DebugAdapter { @@ -19,81 +20,56 @@ class Program { static void Main(string[] args) { - bool showTrace = false; - - foreach (var argument in args) + if (args.Contains("--debug")) { - switch (argument) + var listener = TcpListener.Create(4711); + listener.Start(); + + using (var client = listener.AcceptTcpClient()) { - case "-trace": - showTrace = true; - break; + using (var stream = client.GetStream()) + { + StartSession(stream, stream); + } } + } - - AppDomain currentDomain = AppDomain.CurrentDomain; - currentDomain.UnhandledException += (s, e) => - { - SessionLog.WriteLine(e.ExceptionObject.ToString()); - }; - - StartSession(showTrace, Console.OpenStandardInput(), Console.OpenStandardOutput()); + else + StartSession(Console.OpenStandardInput(), Console.OpenStandardOutput()); } - private static void StartSession(bool showTrace, Stream input, Stream output) + private static void StartSession(Stream input, Stream output) { - var session = new OscriptDebugSession(); - session.TRACE = showTrace; - session.TRACE_RESPONSE = showTrace; - SessionLog.Open(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "/debug.log"); + var enabled = ConfigurationManager.AppSettings["onescript:log-enable"].ToLower() == "true"; + var file = ConfigurationManager.AppSettings["serilog:write-to:File.path"]; + if (enabled && !string.IsNullOrEmpty(file)) + { + Log.Logger = new LoggerConfiguration() + .ReadFrom.AppSettings() + .Enrich.FromLogContext() + .WriteTo.File(file, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Message:lj} ({SourceContext}){NewLine}{Exception}") + .CreateLogger(); + } + + var factory = new SerilogLoggerFactory(Log.Logger); + var adapter = new OneScriptDebugAdapter(factory.CreateLogger("OneScriptDebugAdapter")); + var dapServer = new DapServer(new BufferedTransport(input, output), adapter, factory); + try { - session.Start(input, output); + Log.Logger.Information("Starting debug adapter"); + dapServer.RunAsync(System.Threading.CancellationToken.None).GetAwaiter().GetResult(); } catch (Exception e) { - SessionLog.WriteLine(e.ToString()); + Log.Fatal(e, "Exception on session start"); } finally { - SessionLog.Close(); + Log.CloseAndFlush(); } + + Log.Logger.Information("Session completed"); } - - -#if DEBUG - private static void RunServer(int port) - { - TcpListener serverSocket = new TcpListener(IPAddress.Parse("127.0.0.1"), port); - serverSocket.Start(); - - new System.Threading.Thread(() => { - while (true) - { - var clientSocket = serverSocket.AcceptSocket(); - if (clientSocket != null) - { - Console.Error.WriteLine(">> accepted connection from client"); - - new System.Threading.Thread(() => { - using (var networkStream = new NetworkStream(clientSocket)) - { - try - { - StartSession(true, networkStream, networkStream); - } - catch (Exception e) - { - Console.Error.WriteLine("Exception: " + e); - } - } - clientSocket.Close(); - Console.Error.WriteLine(">> client connection closed"); - }).Start(); - } - } - }).Start(); - } -#endif } } diff --git a/src/VSCode.DebugAdapter/Properties/launchSettings.json b/src/VSCode.DebugAdapter/Properties/launchSettings.json new file mode 100644 index 000000000..472a0d8df --- /dev/null +++ b/src/VSCode.DebugAdapter/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "VSCode.DebugAdapter": { + "commandName": "Project", + "commandLineArgs": "--debug" + } + } +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/Protocol.cs b/src/VSCode.DebugAdapter/Protocol.cs deleted file mode 100644 index 20d372e79..000000000 --- a/src/VSCode.DebugAdapter/Protocol.cs +++ /dev/null @@ -1,280 +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/. -----------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -using System; -using System.Text; -using System.IO; -using System.Threading.Tasks; -using System.Text.RegularExpressions; -using Newtonsoft.Json; - -namespace VSCodeDebug -{ - public class ProtocolMessage - { - public int seq; - public string type { get; } - - public ProtocolMessage(string typ) { - type = typ; - } - - public ProtocolMessage(string typ, int sq) { - type = typ; - seq = sq; - } - } - - public class Request : ProtocolMessage - { - public string command; - public dynamic arguments; - - public Request(int id, string cmd, dynamic arg) : base("request", id) { - command = cmd; - arguments = arg; - } - } - - /* - * subclasses of ResponseBody are serialized as the body of a response. - * Don't change their instance variables since that will break the debug protocol. - */ - public class ResponseBody { - // empty - } - - public class Response : ProtocolMessage - { - public bool success { get; private set; } - public string message { get; private set; } - public int request_seq { get; } - public string command { get; } - public ResponseBody body { get; private set; } - - public Response(Request req) : base("response") { - success = true; - request_seq = req.seq; - command = req.command; - } - - public void SetBody(ResponseBody bdy) { - success = true; - body = bdy; - } - - public void SetErrorBody(string msg, ResponseBody bdy = null) { - success = false; - message = msg; - body = bdy; - } - } - - public class Event : ProtocolMessage - { - [JsonProperty(PropertyName = "event")] - public string eventType { get; } - public dynamic body { get; } - - public Event(string type, dynamic bdy = null) : base("event") { - eventType = type; - body = bdy; - } - } - - /* - * The ProtocolServer can be used to implement a server that uses the VSCode debug protocol. - */ - public abstract class ProtocolServer - { - public bool TRACE; - public bool TRACE_RESPONSE; - - protected const int BUFFER_SIZE = 4096; - protected const string TWO_CRLF = "\r\n\r\n"; - protected static readonly Regex CONTENT_LENGTH_MATCHER = new Regex(@"Content-Length: (\d+)"); - - protected static readonly Encoding Encoding = System.Text.Encoding.UTF8; - - private int _sequenceNumber; - - private Stream _outputStream; - - private ByteBuffer _rawData; - private int _bodyLength; - - private bool _stopRequested; - - - public ProtocolServer() { - _sequenceNumber = 1; - _bodyLength = -1; - _rawData = new ByteBuffer(); - } - - public void Start(Stream inputStream, Stream outputStream) - { - _outputStream = outputStream; - - byte[] buffer = new byte[BUFFER_SIZE]; - - _stopRequested = false; - while (!_stopRequested) { - var read = inputStream.Read(buffer, 0, buffer.Length); - - if (read == 0) { - // end of stream - break; - } - - if (read > 0) { - _rawData.Append(buffer, read); - ProcessData(); - } - } - } - - public void Stop() - { - _stopRequested = true; - } - - public void SendEvent(Event e) - { - SendMessage(e); - } - - protected abstract void DispatchRequest(string command, dynamic args, Response response); - - // ---- private ------------------------------------------------------------------------ - - private void ProcessData() - { - while (true) { - if (_bodyLength >= 0) { - if (_rawData.Length >= _bodyLength) { - var buf = _rawData.RemoveFirst(_bodyLength); - - _bodyLength = -1; - - Dispatch(Encoding.GetString(buf)); - - continue; // there may be more complete messages to process - } - } - else { - string s = _rawData.GetString(Encoding); - var idx = s.IndexOf(TWO_CRLF); - if (idx != -1) { - Match m = CONTENT_LENGTH_MATCHER.Match(s); - if (m.Success && m.Groups.Count == 2) { - _bodyLength = Convert.ToInt32(m.Groups[1].ToString()); - - _rawData.RemoveFirst(idx + TWO_CRLF.Length); - - continue; // try to handle a complete message - } - } - } - break; - } - } - - private void Dispatch(string req) - { - var request = JsonConvert.DeserializeObject(req); - if (request != null && request.type == "request") { - if (TRACE) - Console.Error.WriteLine(string.Format("C {0}: {1}", request.command, JsonConvert.SerializeObject(request.arguments))); - - var response = new Response(request); - - DispatchRequest(request.command, request.arguments, response); - - SendMessage(response); - } - } - - protected void SendMessage(ProtocolMessage message) - { - message.seq = _sequenceNumber++; - - if (TRACE_RESPONSE && message.type == "response") { - Console.Error.WriteLine(string.Format(" R: {0}", JsonConvert.SerializeObject(message))); - } - if (TRACE && message.type == "event") { - Event e = (Event)message; - Console.Error.WriteLine(string.Format("E {0}: {1}", e.eventType, JsonConvert.SerializeObject(e.body))); - } - - var data = ConvertToBytes(message); - try { - _outputStream.Write(data, 0, data.Length); - _outputStream.Flush(); - } - catch (Exception) { - // ignore - } - } - - private static byte[] ConvertToBytes(ProtocolMessage request) - { - var asJson = JsonConvert.SerializeObject(request); - byte[] jsonBytes = Encoding.GetBytes(asJson); - - string header = string.Format("Content-Length: {0}{1}", jsonBytes.Length, TWO_CRLF); - byte[] headerBytes = Encoding.GetBytes(header); - - byte[] data = new byte[headerBytes.Length + jsonBytes.Length]; - System.Buffer.BlockCopy(headerBytes, 0, data, 0, headerBytes.Length); - System.Buffer.BlockCopy(jsonBytes, 0, data, headerBytes.Length, jsonBytes.Length); - - return data; - } - } - - //-------------------------------------------------------------------------------------- - - class ByteBuffer - { - private byte[] _buffer; - - public ByteBuffer() { - _buffer = new byte[0]; - } - - public int Length { - get { return _buffer.Length; } - } - - public string GetString(Encoding enc) - { - return enc.GetString(_buffer); - } - - public void Append(byte[] b, int length) - { - byte[] newBuffer = new byte[_buffer.Length + length]; - System.Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _buffer.Length); - System.Buffer.BlockCopy(b, 0, newBuffer, _buffer.Length, length); - _buffer = newBuffer; - } - - public byte[] RemoveFirst(int n) - { - byte[] b = new byte[n]; - System.Buffer.BlockCopy(_buffer, 0, b, 0, n); - byte[] newBuffer = new byte[_buffer.Length - n]; - System.Buffer.BlockCopy(_buffer, n, newBuffer, 0, _buffer.Length - n); - _buffer = newBuffer; - return b; - } - } -} diff --git a/src/VSCode.DebugAdapter/ProtocolExtensions.cs b/src/VSCode.DebugAdapter/ProtocolExtensions.cs index a7e508b1c..c013b2bcd 100644 --- a/src/VSCode.DebugAdapter/ProtocolExtensions.cs +++ b/src/VSCode.DebugAdapter/ProtocolExtensions.cs @@ -5,30 +5,38 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using VSCodeDebug; +using System.IO; +using EvilBeaver.DAP.Dto.Types; using StackFrame = OneScript.DebugProtocol.StackFrame; namespace VSCode.DebugAdapter { public static class ProtocolExtensions { + private static readonly char[] specialChars = new char[] { '<', '>' }; + public static bool IsStringModule(this StackFrame frame) { - return frame.Source == ""; + return frame.Source.IndexOfAny(specialChars) != -1; } public static Source GetSource(this StackFrame frame) { if (frame.IsStringModule()) { - return new Source(frame.Source, null) + return new Source { - origin = frame.Source, - presentationHint = "deemphasize" + Name = frame.Source, + Origin = frame.Source, + PresentationHint = "deemphasize" }; } - return new Source(frame.Source); + return new Source + { + Name = Path.GetFileName(frame.Source), + Path = frame.Source + }; } } -} \ No newline at end of file +} diff --git a/src/VSCode.DebugAdapter/README.md b/src/VSCode.DebugAdapter/README.md index 4e2c3743c..fc4030e3a 100644 --- a/src/VSCode.DebugAdapter/README.md +++ b/src/VSCode.DebugAdapter/README.md @@ -14,4 +14,99 @@ ### Подробное описание каждого параметра выводится при наведении мышки на опцию в файле launch.json. -При возникновении вопросов напишите обращение на https://github.com/EvilBeaver/OneScript/issues. Мы обязательно вам поможем. \ No newline at end of file +При возникновении вопросов напишите обращение на https://github.com/EvilBeaver/OneScript/issues. Мы обязательно вам поможем. + +## Маппинг путей для удаленной отладки + +При отладке приложений 1Script, работающих в Docker-контейнерах, WSL или на удаленных машинах, возникает проблема несовпадения путей к исходным файлам. Локальная IDE устанавливает точки останова (breakpoints) по локальным путям (например, `D:\projects\myapp\main.os`), но удаленный runtime видит другие пути (например, `/app/main.os` в контейнере). Без маппинга точки останова не будут срабатывать. + +Для решения этой проблемы используется параметр **pathsMapping**, который автоматически преобразует пути между локальной и удаленной файловой системой: + +* При отправке точек останова в отладчик локальные пути преобразуются в удаленные +* При получении информации о стеке вызовов удаленные пути преобразуются обратно в локальные + +Параметр **pathsMapping** доступен только в режиме "attach" и содержит два обязательных поля: +* **localPath** - путь к каталогу проекта на локальной машине +* **remotePath** - путь к каталогу проекта на удаленной машине (или в контейнере) + +## Примеры конфигураций запуска + +### Запуск 1Script, файл my-program.os с передачей аргументов командной строки и установкой переменных окружения + +```json +{ + "name": "Отладка 1Script", + "type": "oscript", + "request": "launch", + "cwd": "${workspaceRoot}/src", + "program": "my-program.os", + "args": ["arg1", "arg2"], + "env": { + "OSCRIPT_CONFIG": "lib.system=D:/myOsLibraries", + "JAVA_HOME": "D:/MyJava/JDK_29_Full" + }, + "debugPort": 5051 +} +``` + +### Запуск сервера 1Script.Web, установленного по пути e:/osweb на порту 5051 + +```json +{ + "name": "Отладка 1Script.Web", + "type": "oscript.web", + "request": "launch", + "appDir": "${workspaceRoot}/src", + "runtimeExecutable": "e:/osweb/OneScript.WebHost.exe", + "debugPort": 5051 +} +``` + +### Подключение к работающему процессу 1Script.Web на порту 5051 + +```json +{ + "name": "Отладка 1Script.Web (attach)", + "type": "oscript.web", + "request": "attach", + "debugPort": 5051 +} +``` + +### Подключение к процессу 1Script в Docker-контейнере с маппингом путей + +При отладке приложения, запущенного в Docker-контейнере, необходимо сопоставить локальные пути с путями внутри контейнера. Например, если локальный проект находится в `D:\myproject`, а внутри контейнера примонтирован в `/app`: + +```json +{ + "name": "Attach к Docker-контейнеру", + "type": "oscript", + "request": "attach", + "debugPort": 2801, + "pathsMapping": { + "localPath": "D:/myproject", + "remotePath": "/app" + } +} +``` + +**Примечание:** В localPath используйте прямые слэши (/) даже в Windows для корректной работы маппинга. + +### Удаленная отладка с маппингом путей (Linux/WSL) + +Для отладки приложения на удаленной Linux-машине или в WSL: + +```json +{ + "name": "Удаленная отладка на Linux", + "type": "oscript", + "request": "attach", + "debugPort": 2801, + "pathsMapping": { + "localPath": "C:/Users/developer/projects/myapp", + "remotePath": "/home/developer/myapp" + } +} +``` + +Отладчик автоматически преобразует пути при установке точек останова и отображении стека вызовов, что позволяет корректно работать с исходным кодом независимо от различий в файловых системах. \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/ServerProcess.cs b/src/VSCode.DebugAdapter/ServerProcess.cs index ad9c8c9be..644bf20aa 100644 --- a/src/VSCode.DebugAdapter/ServerProcess.cs +++ b/src/VSCode.DebugAdapter/ServerProcess.cs @@ -8,7 +8,8 @@ This Source Code Form is subject to the terms of the using System.Collections.Generic; using System.Diagnostics; using System.IO; -using Newtonsoft.Json.Linq; +using EvilBeaver.DAP.Dto.Requests; +using EvilBeaver.DAP.Dto.Serialization; namespace VSCode.DebugAdapter { @@ -34,7 +35,7 @@ protected override Process CreateProcess() dbgArgs.Add($"--debug.port={DebugPort}"); } dbgArgs.Add("--debug.protocol=tcp"); - dbgArgs.Add("--debug.wait=1"); + dbgArgs.Add($"--debug.wait={(WaitOnStart ? "1" : "0")}"); var debugArguments = string.Join(" ", dbgArgs); var process = new Process(); @@ -51,10 +52,13 @@ protected override Process CreateProcess() return process; } - protected override void InitInternal(JObject args) + public override void Init(LaunchRequestArguments args) + { + InitInternal(args.DeserializeAdditionalProperties()); + } + + private void InitInternal(WebLaunchOptions options) { - var options = args.ToObject(); - // validate argument 'cwd' var workingDirectory = options.AppDir; if (workingDirectory != null) @@ -76,6 +80,9 @@ protected override void InitInternal(JObject args) } options.AppDir = workingDirectory; + + // Кодировка DAP + SetEncoding(options.OutputEncoding); // validate argument 'runtimeExecutable' var runtimeExecutable = options.RuntimeExecutable; @@ -103,7 +110,7 @@ protected override void InitInternal(JObject args) WorkingDirectory = options.AppDir; DebugPort = options.DebugPort; Environment = options.Env; - DebugProtocol = "tcp"; + WaitOnStart = options.WaitOnStart; } } } \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/SessionLog.cs b/src/VSCode.DebugAdapter/SessionLog.cs deleted file mode 100644 index a43663d5a..000000000 --- a/src/VSCode.DebugAdapter/SessionLog.cs +++ /dev/null @@ -1,62 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VSCode.DebugAdapter -{ - static class SessionLog - { - private static StreamWriter _log; - private static object lockObj = new object(); - - private static string _path; - [Conditional("DEBUG")] - public static void Open(string path) - { - _path = path; - _log = new StreamWriter(path); - _log.AutoFlush = true; - _log.WriteLine("started: " + DateTime.Now); - } - - public static void WriteLine(string text) - { -#if DEBUG - lock (lockObj) - { - if (_log == null) - { - _log = new StreamWriter(_path, true); - _log.AutoFlush = true; - _log.WriteLine("started: " + DateTime.Now); - } - _log.WriteLine(text); - } -#endif - } - - [Conditional("DEBUG")] - public static void Close() - { - lock (lockObj) - { - if(_log == null) - return; - - _log.WriteLine("closed: " + DateTime.Now); - _log.Dispose(); - _log = null; - } - } - } -} diff --git a/src/VSCode.DebugAdapter/ThreadStateContainer.cs b/src/VSCode.DebugAdapter/ThreadStateContainer.cs new file mode 100644 index 000000000..63ae3b80d --- /dev/null +++ b/src/VSCode.DebugAdapter/ThreadStateContainer.cs @@ -0,0 +1,30 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using OneScript.DebugProtocol; + +namespace VSCode.DebugAdapter +{ + public class ThreadStateContainer + { + private readonly Handles _framesHandles = new Handles(); + private readonly Handles _variablesHandles = new Handles(); + + public void Reset() + { + _framesHandles.Reset(); + _variablesHandles.Reset(); + } + + public int RegisterFrame(StackFrame processFrame) => _framesHandles.Create(processFrame); + + public StackFrame GetFrameById(int id) => _framesHandles.Get(id, null); + + public int RegisterVariablesProvider(IVariablesProvider provider) => _variablesHandles.Create(provider); + + public IVariablesProvider GetVariablesProviderById(int id) => _variablesHandles.Get(id, null); + } +} diff --git a/src/VSCode.DebugAdapter/Transport/ConnectionFactory.cs b/src/VSCode.DebugAdapter/Transport/ConnectionFactory.cs new file mode 100644 index 000000000..df14d95de --- /dev/null +++ b/src/VSCode.DebugAdapter/Transport/ConnectionFactory.cs @@ -0,0 +1,65 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Net.Sockets; +using System.Threading; +using Serilog; +using Serilog.Core; + +namespace VSCode.DebugAdapter.Transport +{ + public static class ConnectionFactory + { + private static ILogger Log { get; set; } = Logger.None; + + public static TcpClient Connect(int port) + { + Log = Serilog.Log.ForContext(typeof(ConnectionFactory)); + + var debuggerUri = GetDebuggerUri(port); + + var client = new TcpClient(); + TryConnect(client, debuggerUri); + + return client; + } + + private static Uri GetDebuggerUri(int port) + { + var builder = new UriBuilder(); + builder.Scheme = "net.tcp"; + builder.Port = port; + builder.Host = "localhost"; + + return builder.Uri; + } + + private static void TryConnect(TcpClient client, Uri debuggerUri) + { + const int limit = 3; + // TODO: параметризовать ожидания и попытки + for (int i = 0; i < limit; ++i) + { + try + { + client.Connect(debuggerUri.Host, debuggerUri.Port); + break; + } + catch (SocketException) + { + if (i == limit - 1) + throw; + + Log.Warning("Error. Retry connect {Attempt}", i); + Thread.Sleep(1500); + } + } + + Log.Debug("Connected to {Host}:{Port}", debuggerUri.Host, debuggerUri.Port); + } + } +} diff --git a/src/VSCode.DebugAdapter/Transport/DebugClientFactory.cs b/src/VSCode.DebugAdapter/Transport/DebugClientFactory.cs new file mode 100644 index 000000000..ca4ea1082 --- /dev/null +++ b/src/VSCode.DebugAdapter/Transport/DebugClientFactory.cs @@ -0,0 +1,191 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.IO; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using OneScript.DebugProtocol; +using OneScript.DebugProtocol.Abstractions; +using OneScript.DebugProtocol.TcpServer; +using Serilog; + +namespace VSCode.DebugAdapter.Transport +{ + public class DebugClientFactory + { + private readonly TcpClient _tcpClient; + private readonly IDebugEventListener _eventsListener; + + private readonly ILogger Log = Serilog.Log.ForContext(); + + public DebugClientFactory(TcpClient tcpClient, IDebugEventListener eventsListener) + { + _tcpClient = tcpClient; + _eventsListener = eventsListener; + } + + private TransportProtocols _transport = TransportProtocols.Invalid; + private int _protocolVersion = ProtocolVersions.SafestVersion; + + public OneScriptDebuggerClient CreateDebugClient() + { + ReconcileDataFormat(_tcpClient); + + IMessageChannel commandsChannel; + + switch (_transport) + { + case TransportProtocols.Json: + commandsChannel = new JsonDtoChannel(new TcpDebuggerClient(_tcpClient)); + break; + case TransportProtocols.Binary: + commandsChannel = new BinaryChannel(_tcpClient); + break; + default: + throw new InvalidOperationException($"Should not get here. [Transport protocol selection] {_transport}"); + }; + + var client = new OneScriptDebuggerClient(commandsChannel, _eventsListener, _protocolVersion); + client.Start(); + + return client; + } + + private void ReconcileDataFormat(TcpClient client) + { + Log.Verbose("Sending reconcile message"); + var stream = client.GetStream(); + var magic = FormatReconcileUtils.GetReconcileMagic(); + stream.Write(magic, 0, magic.Length); + + var pollResult = client.Client + .Poll(FormatReconcileUtils.FORMAT_RECONCILE_TIMEOUT.Milliseconds * 1000, SelectMode.SelectRead); + + if (pollResult) + { + Log.Verbose("Reconcile data available. Waiting for full data"); + var attempts = 0; + const int WAIT_ATTEMPTS = 3; + const int ATTEMPT_INTERVAL = 250; + + var requiredDataLength = FormatReconcileUtils.FORMAT_RECONCILE_RESPONSE_PREFIX.Length + sizeof(int); + while (client.Client.Available < requiredDataLength && attempts < WAIT_ATTEMPTS) + { + attempts++; + Log.Verbose("Attempt # {Attempt}", attempts); + Thread.Sleep(ATTEMPT_INTERVAL); + } + + if (client.Client.Available >= requiredDataLength) + { + Log.Verbose("We have data available. Reading reconcile response"); + var dataBuffer = new byte[requiredDataLength]; + using (var binaryReader = new BinaryReader(stream, Encoding.ASCII, true)) + { + StreamUtils.ReadStream(binaryReader.BaseStream, dataBuffer,FormatReconcileUtils.FORMAT_RECONCILE_RESPONSE_PREFIX.Length); + + if (!FormatReconcileUtils.CheckReconcilePrefix(dataBuffer)) + { + Log.Verbose("Received data is not reconcile message"); + SelectSafestFormat(); + EmptyIncomingBuffer(client); + return; + } + + + var formatMarker = binaryReader.ReadInt32(); + var (transport, version) = FormatReconcileUtils.DecodeFormatMarker(formatMarker); + Log.Verbose("Received format marker {FormatVersion}. Transport {Transport}, Format {Format}", + formatMarker, + transport, + version); + + if (transport != (int)TransportProtocols.Json) + { + throw new ApplicationException($"Transport protocol is out of range {transport}"); + } + + _transport = TransportProtocols.Json; + _protocolVersion = ProtocolVersions.Adjust(version); + Log.Debug("Active protocol version {ProtocolVersion}", _protocolVersion); + } + } + else + { + Log.Verbose("We waited for full reconcile data, but it hadn't arrived"); + SelectSafestFormat(); + EmptyIncomingBuffer(client); + } + } + else + { + Log.Verbose("No reconciliation response"); + SelectSafestFormat(); + } + } + + private void EmptyIncomingBuffer(TcpClient client) + { + Log.Verbose("Reading out all incoming buffer"); + const int waitForDataInterval = 300; + const int gotNothingAttempts = 3; + + int gotNothingCount = 0; + + var buf = new byte[1024]; + do + { + var hasBytes = client.Available; + if (hasBytes > 0) + { + gotNothingCount = 0; + var bytesRead = client.GetStream().Read(buf, 0, Math.Min(hasBytes, buf.Length)); + if (bytesRead == 0) + return; + } + else + { + gotNothingCount++; + } + + Log.Verbose("We have {Bytes} incoming bytes ({Attempt}). Waiting for more", hasBytes, gotNothingCount); + Thread.Sleep(waitForDataInterval); + + } while (gotNothingCount < gotNothingAttempts); + Log.Verbose("Reading out completed"); + } + + private void SelectSafestFormat() + { + _protocolVersion = ProtocolVersions.SafestVersion; + _transport = TransportProtocols.Binary; + } + + private class TcpDebuggerClient : IDebuggerClient + { + private TcpClient Client { get; } + + public TcpDebuggerClient(TcpClient client) + { + Client = client; + } + + public void Dispose() + { + Client.Dispose(); + } + + public Stream GetDataStream() + { + return Client.GetStream(); + } + + public bool Connected => Client.Connected; + } + } +} diff --git a/src/VSCode.DebugAdapter/Transport/ProtocolVersions.cs b/src/VSCode.DebugAdapter/Transport/ProtocolVersions.cs new file mode 100644 index 000000000..9fa239e37 --- /dev/null +++ b/src/VSCode.DebugAdapter/Transport/ProtocolVersions.cs @@ -0,0 +1,60 @@ +/*---------------------------------------------------------- +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 VSCode.DebugAdapter.Transport +{ + public static class ProtocolVersions + { + public static bool IsValid(int valueToCheck) + { + return valueToCheck >= 0 && valueToCheck <= LatestKnownVersion; + } + + public static int Adjust(int valueToAdjust) + { + if (valueToAdjust <= 0 || valueToAdjust > LatestKnownVersion) + return SafestVersion; + + return valueToAdjust; + } + + /// + /// Неизвестная версия, пытаемся определить сами + /// + public const int UnknownVersion = 0; + + /// + /// До появления условных брейкпоинтов + /// + public const int Version1 = 1; + + /// + /// После появления условных брейкпоинтов + /// + public const int Version2 = 2; + + /// + /// Выделенный тип для параметра в SetExceptionBreakpoints + /// + public const int Version3 = 3; + + /// + /// Переменные модуля + /// + public const int Version4 = 4; + + /// + /// Значение, безопасное для всех версий движка + /// + public const int SafestVersion = Version1; + + /// + /// Контрольное значение + /// + public const int LatestKnownVersion = Version4; + } +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/RpcOperationException.cs b/src/VSCode.DebugAdapter/Transport/RpcOperationException.cs similarity index 93% rename from src/VSCode.DebugAdapter/OscriptProtocols/Tcp/RpcOperationException.cs rename to src/VSCode.DebugAdapter/Transport/RpcOperationException.cs index a38e4b5b6..e994cf92e 100644 --- a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/RpcOperationException.cs +++ b/src/VSCode.DebugAdapter/Transport/RpcOperationException.cs @@ -8,7 +8,7 @@ This Source Code Form is subject to the terms of the using System; using OneScript.DebugProtocol.TcpServer; -namespace VSCode.DebugAdapter +namespace VSCode.DebugAdapter.Transport { public class RpcOperationException : ApplicationException { diff --git a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/RpcProcessor.cs b/src/VSCode.DebugAdapter/Transport/RpcProcessor.cs similarity index 76% rename from src/VSCode.DebugAdapter/OscriptProtocols/Tcp/RpcProcessor.cs rename to src/VSCode.DebugAdapter/Transport/RpcProcessor.cs index a1e20d51f..c9d6768bf 100644 --- a/src/VSCode.DebugAdapter/OscriptProtocols/Tcp/RpcProcessor.cs +++ b/src/VSCode.DebugAdapter/Transport/RpcProcessor.cs @@ -11,8 +11,9 @@ This Source Code Form is subject to the terms of the using OneScript.DebugProtocol; using OneScript.DebugProtocol.Abstractions; using OneScript.DebugProtocol.TcpServer; +using Serilog; -namespace VSCode.DebugAdapter +namespace VSCode.DebugAdapter.Transport { public class RpcProcessor { @@ -21,6 +22,8 @@ public class RpcProcessor private readonly Queue _responses = new Queue(); private readonly AutoResetEvent _responseAvailable = new AutoResetEvent(false); + private readonly ILogger Log = Serilog.Log.ForContext(); + private struct ChannelRecord { public IMethodsDispatcher Dispatcher; @@ -35,25 +38,33 @@ public RpcProcessor(ICommunicationServer server) public void Start() { _server.DataReceived += ServerOnDataReceived; + _server.OnError += ServerOnServerExited; _server.Start(); } - + + private void ServerOnServerExited(object sender, CommunicationEventArgs e) + { + Log.Fatal(e.Exception, "Server error"); + Stop(); + } + public void Stop() { _server.DataReceived -= ServerOnDataReceived; + _server.OnError -= ServerOnServerExited; _server.Stop(); } private void ServerOnDataReceived(object sender, CommunicationEventArgs e) { - SessionLog.WriteLine("Data received " + e.Data); if (e.Exception == null) { + Log.Verbose("Data received {@Data}", (TcpProtocolDtoBase)e.Data); DispatchMessage((TcpProtocolDtoBase)e.Data, e.Channel); } else { - SessionLog.WriteLine("RPC Exception " + e.Exception + " critical: " + e.Exception.StopChannel); + Log.Error(e.Exception, "RPC Exception received. Critical: {Critical}", e.Exception.StopChannel); if (e.Exception.StopChannel) { Stop(); @@ -61,27 +72,29 @@ private void ServerOnDataReceived(object sender, CommunicationEventArgs e) } } - private void DispatchMessage(TcpProtocolDtoBase data, ICommunicationChannel responseChannel) + private void DispatchMessage(TcpProtocolDtoBase data, IMessageChannel responseChannel) { if (!_dispatchers.TryGetValue(data.ServiceName, out var serviceRecord)) { - SessionLog.WriteLine("No dispatcher for " + data.ServiceName); + Log.Warning("No dispatcher for {ServiceName}", data.ServiceName); return; } if (data is RpcCall rpcCall) { - SessionLog.WriteLine("Processing call to " + rpcCall.Id); + Log.Verbose("Processing call to {Id}", rpcCall.Id); var result = serviceRecord.Dispatcher.Dispatch(serviceRecord.ServiceInstance, rpcCall.Id, rpcCall.Parameters); + Log.Verbose("Completed call to {Id}", rpcCall.Id); if (result != null) { var callResult = RpcCallResult.Respond(rpcCall, result); + Log.Verbose("Sending response {Result}", callResult.ReturnValue); responseChannel.Write(callResult); } } else if(data is RpcCallResult result) { - SessionLog.WriteLine("Saving response to " + result.Id); + Log.Verbose("Enque response to {Id}. Value {Value}", result.Id, result.ReturnValue); lock (_responses) { _responses.Enqueue(result); diff --git a/src/VSCode.DebugAdapter/Transport/TransportException.cs b/src/VSCode.DebugAdapter/Transport/TransportException.cs new file mode 100644 index 000000000..29d685f5d --- /dev/null +++ b/src/VSCode.DebugAdapter/Transport/TransportException.cs @@ -0,0 +1,17 @@ +/*---------------------------------------------------------- +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 VSCode.DebugAdapter.Transport +{ + public class TransportException : ApplicationException + { + public TransportException(string message) : base(message) + { + } + } +} diff --git a/src/VSCode.DebugAdapter/Transport/TransportProtocols.cs b/src/VSCode.DebugAdapter/Transport/TransportProtocols.cs new file mode 100644 index 000000000..875a01d5a --- /dev/null +++ b/src/VSCode.DebugAdapter/Transport/TransportProtocols.cs @@ -0,0 +1,16 @@ +/*---------------------------------------------------------- +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 VSCode.DebugAdapter.Transport +{ + public enum TransportProtocols + { + Auto = 0, + Binary = 1, + Json = 2, + Invalid, + } +} diff --git a/src/VSCode.DebugAdapter/Utilities.cs b/src/VSCode.DebugAdapter/Utilities.cs index 6cdc168f9..e628ceb27 100644 --- a/src/VSCode.DebugAdapter/Utilities.cs +++ b/src/VSCode.DebugAdapter/Utilities.cs @@ -6,11 +6,10 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; -using System.Linq; +using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; namespace VSCode.DebugAdapter { @@ -58,5 +57,24 @@ public static string ExpandVariables(string format, dynamic variables, bool unde return match.Groups[0].Value; }); } + + public static Encoding GetEncodingFromOptions(string optionsValue) + { + if (string.IsNullOrWhiteSpace(optionsValue)) + { + throw new ArgumentException("Encoding must be specified"); + } + + return Encoding.GetEncoding(optionsValue); + } + + public static string NormalizeDriveLetter(string path) + { + if (Path.IsPathRooted(path)) + return path[0].ToString().ToUpperInvariant() + path.Substring(1); + else + return path; + + } } } diff --git a/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj b/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj index 2e536476f..d342b399d 100644 --- a/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj +++ b/src/VSCode.DebugAdapter/VSCode.DebugAdapter.csproj @@ -3,28 +3,38 @@ - net452 + net48 Exe VSCode.DebugAdapter.Program - Debug;Release + Debug;Release;LinuxDebug VSC debug adapter for 1Script AnyCPU 7.3 - + 1.7.0.0 + 1.7.0.0 + true + + + + true + false + + + + - - + Always - + Always - + Always @@ -34,4 +44,9 @@ + + + + + \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/Variables/ChildVariablesProvider.cs b/src/VSCode.DebugAdapter/Variables/ChildVariablesProvider.cs new file mode 100644 index 000000000..166aebfa7 --- /dev/null +++ b/src/VSCode.DebugAdapter/Variables/ChildVariablesProvider.cs @@ -0,0 +1,49 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.DebugProtocol; + +namespace VSCode.DebugAdapter +{ + /// + /// Универсальный провайдер вложенных переменных + /// + public class ChildVariablesProvider : IVariablesProvider + { + private readonly int _threadId; + private readonly int _frameIndex; + private readonly int[] _path; + private readonly Func _fetchFunc; + + public ChildVariablesProvider( + int threadId, + int frameIndex, + int[] path, + Func fetchFunc) + { + _threadId = threadId; + _frameIndex = frameIndex; + _path = path; + _fetchFunc = fetchFunc; + } + + public Variable[] FetchVariables(IDebuggerService service) + { + return _fetchFunc(service, _threadId, _frameIndex, _path); + } + + public IVariablesProvider CreateChildProvider(int variableIndex) + { + var newPath = new int[_path.Length + 1]; + Array.Copy(_path, newPath, _path.Length); + newPath[_path.Length] = variableIndex; + return new ChildVariablesProvider(_threadId, _frameIndex, newPath, _fetchFunc); + } + } +} + diff --git a/src/VSCode.DebugAdapter/Variables/EvaluatedExpressionProvider.cs b/src/VSCode.DebugAdapter/Variables/EvaluatedExpressionProvider.cs new file mode 100644 index 000000000..d9706d3ce --- /dev/null +++ b/src/VSCode.DebugAdapter/Variables/EvaluatedExpressionProvider.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.DebugProtocol; + +namespace VSCode.DebugAdapter +{ + /// + /// Провайдер переменных для вычисленного выражения + /// + public class EvaluatedExpressionProvider : IVariablesProvider + { + private readonly string _expression; + private readonly int _threadId; + private readonly int _frameIndex; + private readonly int[] _path; + + public EvaluatedExpressionProvider(string expression, int threadId, int frameIndex) + { + _expression = expression; + _threadId = threadId; + _frameIndex = frameIndex; + _path = Array.Empty(); + } + + private EvaluatedExpressionProvider(string expression, int threadId, int frameIndex, int[] path) + { + _expression = expression; + _threadId = threadId; + _frameIndex = frameIndex; + _path = path; + } + + public Variable[] FetchVariables(IDebuggerService service) + { + return service.GetEvaluatedVariables(_expression, _threadId, _frameIndex, _path); + } + + public IVariablesProvider CreateChildProvider(int variableIndex) + { + var newPath = new int[_path.Length + 1]; + Array.Copy(_path, newPath, _path.Length); + newPath[_path.Length] = variableIndex; + return new ChildVariablesProvider( + _threadId, + _frameIndex, + newPath, + (service, tid, fid, path) => service.GetEvaluatedVariables(_expression, tid, fid, path)); + } + } +} + diff --git a/src/VSCode.DebugAdapter/Variables/IVariablesProvider.cs b/src/VSCode.DebugAdapter/Variables/IVariablesProvider.cs new file mode 100644 index 000000000..c2b59ee74 --- /dev/null +++ b/src/VSCode.DebugAdapter/Variables/IVariablesProvider.cs @@ -0,0 +1,29 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.DebugProtocol; + +namespace VSCode.DebugAdapter +{ + /// + /// Провайдер переменных для конкретного scope или вложенного объекта. + /// Не хранит данные - только знает как их получить из рантайма. + /// + public interface IVariablesProvider + { + /// + /// Получить переменные из рантайма через debug service + /// + Variable[] FetchVariables(IDebuggerService service); + + /// + /// Создать провайдер для дочерних элементов переменной по индексу + /// + IVariablesProvider CreateChildProvider(int variableIndex); + } +} + diff --git a/src/VSCode.DebugAdapter/Variables/LocalScopeProvider.cs b/src/VSCode.DebugAdapter/Variables/LocalScopeProvider.cs new file mode 100644 index 000000000..99f8fa173 --- /dev/null +++ b/src/VSCode.DebugAdapter/Variables/LocalScopeProvider.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.DebugProtocol; + +namespace VSCode.DebugAdapter +{ + /// + /// Провайдер локальных переменных фрейма стека + /// + public class LocalScopeProvider : IVariablesProvider + { + private readonly int _threadId; + private readonly int _frameIndex; + + public LocalScopeProvider(int threadId, int frameIndex) + { + _threadId = threadId; + _frameIndex = frameIndex; + } + + public Variable[] FetchVariables(IDebuggerService service) + { + // path пустой - получаем переменные верхнего уровня фрейма + return service.GetVariables(_threadId, _frameIndex, Array.Empty()); + } + + public IVariablesProvider CreateChildProvider(int variableIndex) + { + return new ChildVariablesProvider( + _threadId, + _frameIndex, + new[] { variableIndex }, + (service, tid, fid, path) => service.GetVariables(tid, fid, path)); + } + } +} + diff --git a/src/VSCode.DebugAdapter/Variables/ModuleScopeProvider.cs b/src/VSCode.DebugAdapter/Variables/ModuleScopeProvider.cs new file mode 100644 index 000000000..e72655e70 --- /dev/null +++ b/src/VSCode.DebugAdapter/Variables/ModuleScopeProvider.cs @@ -0,0 +1,43 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using System; +using OneScript.DebugProtocol; + +namespace VSCode.DebugAdapter +{ + /// + /// Провайдер переменных модуля + /// + public class ModuleScopeProvider : IVariablesProvider + { + private readonly int _threadId; + private readonly int _frameIndex; + + public ModuleScopeProvider(int threadId, int frameIndex) + { + _threadId = threadId; + _frameIndex = frameIndex; + } + + public Variable[] FetchVariables(IDebuggerService service) + { + // path пустой - получаем переменные модуля верхнего уровня фрейма + return service.GetModuleVariables(_threadId, _frameIndex, Array.Empty()); + } + + public IVariablesProvider CreateChildProvider(int variableIndex) + { + return new ChildVariablesProvider( + _threadId, + _frameIndex, + new[] { variableIndex }, + (service, tid, fid, path) => service.GetModuleVariables(tid, fid, path)); + } + } +} + diff --git a/src/VSCode.DebugAdapter/WebLaunchOptions.cs b/src/VSCode.DebugAdapter/WebLaunchOptions.cs index ff6314671..f53df4c5f 100644 --- a/src/VSCode.DebugAdapter/WebLaunchOptions.cs +++ b/src/VSCode.DebugAdapter/WebLaunchOptions.cs @@ -5,12 +5,12 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System.Reflection; - namespace VSCode.DebugAdapter { public class WebLaunchOptions : CommonLaunchOptions { public string AppDir { get; set; } + + public bool WaitOnStart { get; set; } } } \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/WorkspaceMapper.cs b/src/VSCode.DebugAdapter/WorkspaceMapper.cs new file mode 100644 index 000000000..d16590507 --- /dev/null +++ b/src/VSCode.DebugAdapter/WorkspaceMapper.cs @@ -0,0 +1,87 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using System.Runtime.InteropServices; +using Newtonsoft.Json; + +namespace VSCode.DebugAdapter +{ + public class WorkspaceMapper + { + private Workspace _localWorkspace; + private Workspace _remoteWorkspace; + + [JsonProperty("localPath")] + private string LocalPathForDeserialization + { + set { _localWorkspace = new Workspace(value ?? ""); } + } + + [JsonProperty("remotePath")] + private string RemotePathForDeserialization + { + set { _remoteWorkspace = new Workspace(value ?? ""); } + } + + public WorkspaceMapper(){} + + public WorkspaceMapper(string localPath, string remotePath) { + + this._localWorkspace = new Workspace(localPath); + this._remoteWorkspace = new Workspace(remotePath); + } + + public string LocalToRemote(string path) + { + return ConvertPath(path, _localWorkspace, _remoteWorkspace); + } + + public string RemoteToLocal(string path) + { + return ConvertPath(path, _remoteWorkspace, _localWorkspace); + } + + private string ConvertPath(string path, Workspace fromPrefix, Workspace toPrefix) + { + if (string.IsNullOrWhiteSpace(path) || + string.IsNullOrWhiteSpace(fromPrefix.Original) || + string.IsNullOrWhiteSpace(toPrefix.Original)) + return path; + + var normalizedPath = path.Replace('\\', '/'); + var normalizedFrom = fromPrefix.Normalized; + + var comparison = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? StringComparison.OrdinalIgnoreCase + : StringComparison.Ordinal; + + if (normalizedPath.StartsWith(toPrefix.Normalized, comparison) || + !normalizedPath.StartsWith(normalizedFrom, comparison)) + return path; + + var relativePath = normalizedPath.Substring(normalizedFrom.Length).TrimStart('/'); + var result = toPrefix.Normalized + "/" + relativePath; + + return result; + } + + } + + internal class Workspace + { + public string Original; + public string Normalized; + + internal Workspace(string path) + { + this.Original = path; + this.Normalized = path.Replace('\\', '/').TrimEnd('/'); + } + + } + +} \ No newline at end of file diff --git a/src/VSCode.DebugAdapter/images/logo-dbg.png b/src/VSCode.DebugAdapter/images/logo-dbg.png new file mode 100644 index 000000000..883249e8b Binary files /dev/null and b/src/VSCode.DebugAdapter/images/logo-dbg.png differ diff --git a/src/VSCode.DebugAdapter/images/mono-debug-icon.png b/src/VSCode.DebugAdapter/images/mono-debug-icon.png deleted file mode 100644 index 7ee274e94..000000000 Binary files a/src/VSCode.DebugAdapter/images/mono-debug-icon.png and /dev/null differ diff --git a/src/VSCode.DebugAdapter/package.json b/src/VSCode.DebugAdapter/package.json index 68a01a5b5..a0938e592 100644 --- a/src/VSCode.DebugAdapter/package.json +++ b/src/VSCode.DebugAdapter/package.json @@ -1,10 +1,10 @@ { "name": "oscript-debug", - "displayName": "OneScript Debug", - "version": "0.6.0", + "displayName": "OneScript Debug (BSL)", + "version": "1.1.0", "publisher": "EvilBeaver", - "description": "Visual Studio Code debugger extension for OneScript", - "icon": "images/mono-debug-icon.png", + "description": "Visual Studio Code debugger extension for OneScript (BSL)", + "icon": "images/logo-dbg.png", "categories": [ "Debuggers" ], @@ -14,10 +14,9 @@ "license": "MPL-2.0", "private": true, "engines": { - "vscode": "^1.0.0" - }, - "dependencies": { + "vscode": "^1.9.0" }, + "dependencies": {}, "repository": { "type": "git", "url": "https://github.com/EvilBeaver/OneScript.git" @@ -50,16 +49,17 @@ "program": "${file}", "args": [], "cwd": "${workspaceRoot}", - "env" : {}, + "env": {}, "runtimeExecutable": null, - "runtimeArgs" : [], - "debugPort": 2801, - "protocol":"tcp" + "runtimeArgs": [], + "debugPort": 2801 } ], "configurationAttributes": { "launch": { - "required": [ "program" ], + "required": [ + "program" + ], "properties": { "program": { "type": "string", @@ -68,7 +68,9 @@ "args": { "type": "array", "description": "Аргументы, передаваемые в сценарий", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "default": [] }, "cwd": { @@ -80,19 +82,26 @@ "type": "object", "description": "Переменные окружения для отлаживаемого процесса", "patternProperties": { - "[a-zA-Z_][0-9a-zA-Z_]+": { "type": "string" } + "[a-zA-Z_][0-9a-zA-Z_]+": { + "type": "string" + } }, "default": null }, "runtimeExecutable": { - "type": [ "string", "null" ], + "type": [ + "string", + "null" + ], "description": "Путь к исполняемому файлу oscript.exe / OneScript.WebHost.exe", "default": "oscript" }, "runtimeArgs": { "type": "array", "description": "Аргументы движка 1Script (encoding, codestat, etc...)", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "default": [] }, "debugPort": { @@ -100,20 +109,48 @@ "description": "Порт для запуска отладчика. Одновременные сеансы отладки должны быть разделены по портам", "default": 2801 }, - "protocol": { + "outputEncoding": { "type": "string", - "description": "Вариант протокола взаимодействия с отлаживаемым процессом", - "enum" : ["wcf", "tcp"] + "description": "Кодировка вывода отлаживаемого приложения. Отладчик будет интерпретировать вывод приложения в указанной кодировке", + "default": "" + }, + "waitOnStart": { + "type": "boolean", + "description": "Ожидать подключения отладчика при старте приложения (не выполнять bsl до его подключения)", + "default": true } } }, "attach": { "properties": { - "debugPort": { + "debugPort": { "type": "number", "description": "Присоединиться к работающему процессу", "default": 2801 + }, + "outputEncoding": { + "type": "string", + "description": "Кодировка вывода отлаживаемого приложения. Отладчик будет интерпретировать вывод приложения в указанной кодировке", + "default": "" + }, + "pathsMapping": { + "type": "object", + "description": "Сопоставление путей между локальной и удаленной машиной (для удаленной отладки)", + "properties": { + "localPath": { + "type": "string", + "description": "Путь к каталогу проекта на локальной машине.", + "default": "" + }, + "remotePath": { + "type": "string", + "description": "Путь к каталогу проекта на удаленной машине.", + "default": "" + } + }, + "required": ["localPath", "remotePath"] } + } } } @@ -136,14 +173,17 @@ "appDir": "${workspaceRoot}", "env": {}, "runtimeExecutable": null, - "runtimeArgs" : [], + "runtimeArgs": [], "debugPort": 2801, - "protocol":"tcp" + "waitOnStart": false } ], "configurationAttributes": { "launch": { - "required": [ "appDir", "runtimeExecutable" ], + "required": [ + "appDir", + "runtimeExecutable" + ], "properties": { "appDir": { "type": "string", @@ -154,19 +194,26 @@ "type": "object", "description": "Переменные окружения для отлаживаемого процесса", "patternProperties": { - "[a-zA-Z_][0-9a-zA-Z_]+": { "type": "string" } + "[a-zA-Z_][0-9a-zA-Z_]+": { + "type": "string" + } }, "default": null }, "runtimeExecutable": { - "type": [ "string", "null" ], + "type": [ + "string", + "null" + ], "description": "Путь к исполняемому файлу oscript.exe / OneScript.WebHost.exe", "default": "oscript" }, "runtimeArgs": { "type": "array", "description": "Аргументы, передаваемые в строке запуска", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "default": [] }, "debugPort": { @@ -174,10 +221,20 @@ "description": "Порт для запуска отладчика. Одновременные сеансы отладки должны быть разделены по портам", "default": 2801 }, - "protocol": { + "waitOnStart": { + "type": "boolean", + "description": "Ожидать подключения отладчика при старте приложения (не выполнять bsl до его подключения)", + "default": false + }, + "outputEncoding": { "type": "string", - "description": "Вариант протокола взаимодействия с отлаживаемым процессом", - "enum" : ["wcf", "tcp"] + "description": "Кодировка вывода отлаживаемого приложения. Отладчик будет интерпретировать вывод приложения в указанной кодировке", + "default": "" + }, + "protocolVersion": { + "type": "number", + "description": "Версия протокола отладки, поддерживаемая движком 1Скрипт", + "default": 0 } } }, @@ -187,6 +244,16 @@ "type": "number", "description": "Присоединиться к работающему процессу", "default": 2801 + }, + "outputEncoding": { + "type": "string", + "description": "Кодировка вывода отлаживаемого приложения. Отладчик будет интерпретировать вывод приложения в указанной кодировке", + "default": "" + }, + "protocolVersion": { + "type": "number", + "description": "Версия протокола отладки, поддерживаемая движком 1Скрипт", + "default": 0 } } } diff --git a/src/oscommon.targets b/src/oscommon.targets index e2ff410cc..019db5bd6 100644 --- a/src/oscommon.targets +++ b/src/oscommon.targets @@ -2,29 +2,36 @@ - 1.2.0 $(BUILD_NUMBER) 0 - Release + Release x86 - 7.3 + 8.0 + net8.0 + $(NoWarn);CS1591 + False + disable EvilBeaver <ovsiankin.aa@gmail.com> MPL-2.0 - http://oscript.io + https://oscript.io false - OneScript OpenSource Community - $(ReleaseNumber)$(Suffix) + OneScript OpenSource Community - $(ReleaseNumber).$(BuildNumber) - $(Version) + 2.0.0 + $(VersionPrefix).$(BuildNumber) + $(VersionPrefix)-$(VersionSuffix) 1C (BSL) language runtime - Copyright (c) 2017 EvilBeaver + Copyright (c) 2021 EvilBeaver + + + + bin\Release\$(MSBuildProjectName).xml \ No newline at end of file diff --git a/src/oscript/AppBehavior.cs b/src/oscript/AppBehavior.cs index b67b25555..423f36891 100644 --- a/src/oscript/AppBehavior.cs +++ b/src/oscript/AppBehavior.cs @@ -5,11 +5,6 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - namespace oscript { internal abstract class AppBehavior diff --git a/src/oscript/BehaviorSelector.cs b/src/oscript/BehaviorSelector.cs index d0293ea18..264cfb64f 100644 --- a/src/oscript/BehaviorSelector.cs +++ b/src/oscript/BehaviorSelector.cs @@ -6,10 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; using System.Collections.Generic; -using System.Globalization; -using System.Linq; using System.Text; -using System.Threading; namespace oscript { @@ -44,14 +41,12 @@ private static AppBehavior SelectParametrized(CmdLineHelper helper) initializers.Add("-measure", MeasureBehavior.Create); initializers.Add("-compile", ShowCompiledBehavior.Create); initializers.Add("-check", CheckSyntaxBehavior.Create); - initializers.Add("-make", MakeAppBehavior.Create); initializers.Add("-cgi", h => new CgiBehavior()); initializers.Add("-version", h => new ShowVersionBehavior()); initializers.Add("-v", h => new ShowVersionBehavior()); initializers.Add("-encoding", ProcessEncodingKey); initializers.Add("-codestat", EnableCodeStatistics); initializers.Add("-debug", DebugBehavior.Create); - initializers.Add("-serialize", SerializeModuleBehavior.Create); var param = helper.Parse(helper.Current()); if(initializers.TryGetValue(param.Name.ToLowerInvariant(), out var action)) @@ -69,9 +64,12 @@ private static AppBehavior EnableCodeStatistics(CmdLineHelper helper) return null; var outputStatFile = param.Value; - ScriptFileHelper.EnableCodeStatistics(outputStatFile); - return Select(helper.Tail()); + var behavior = Select(helper.Tail()); + if (behavior is ExecuteScriptBehavior executor) + executor.CodeStatFile = outputStatFile; + + return behavior; } private static AppBehavior ProcessEncodingKey(CmdLineHelper helper) diff --git a/src/oscript/CgiBehavior.cs b/src/oscript/CgiBehavior.cs index 69691f93d..911782454 100644 --- a/src/oscript/CgiBehavior.cs +++ b/src/oscript/CgiBehavior.cs @@ -8,19 +8,18 @@ This Source Code Form is subject to the terms of the using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Reflection; using System.Text; - +using OneScript.Contexts; +using OneScript.Exceptions; +using OneScript.Execution; +using OneScript.StandardLibrary; using oscript.Web; using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; +using ScriptEngine.Hosting; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; -using MethodInfo = ScriptEngine.Machine.MethodInfo; - namespace oscript { internal class CgiBehavior : AppBehavior, IHostApplication, IRuntimeContextInstance, IAttachableContext @@ -59,18 +58,20 @@ public override int Execute() private int RunCGIMode(string scriptFile) { - var engine = new HostedScriptEngine - { - CustomConfig = ScriptFileHelper.CustomConfigPath(scriptFile) - }; - engine.AttachAssembly(Assembly.GetExecutingAssembly()); + var builder = ConsoleHostBuilder + .Create(scriptFile); + builder.SetupEnvironment(e => + { + e.AddAssembly(GetType().Assembly); + }); + + var engine = ConsoleHostBuilder.Build(builder); + var request = new WebRequestContext(); - engine.InjectGlobalProperty("ВебЗапрос", request, true); - engine.InjectGlobalProperty("WebRequest", request, true); - engine.InjectObject(this, false); + engine.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true); + engine.InjectObject(this); - ScriptFileHelper.OnBeforeScriptRead(engine); var source = engine.Loader.FromFile(scriptFile); Process process; @@ -90,29 +91,26 @@ private int RunCGIMode(string scriptFile) if (!_isContentEchoed) Echo(""); + request.Dispose(); + return exitCode; } - public void OnAttach(MachineInstance machine, out IVariable[] variables, out MethodInfo[] methods) + IVariable IAttachableContext.GetVariable(int index) { - variables = new IVariable[0]; - methods = (MethodInfo[]) GetMethods(); + throw new IndexOutOfRangeException("No such variable at index: " + index); } - public IEnumerable GetProperties() + BslMethodInfo IAttachableContext.GetMethod(int index) { - return new VariableInfo[0]; + return GetMethodInfo(index); } - public IEnumerable GetMethods() - { - var array = new MethodInfo[_methods.Count]; - for (var i = 0; i < _methods.Count; i++) - array[i] = _methods.GetMethodInfo(i); - - return array; - } + int IAttachableContext.VariablesCount => 0; + + int IAttachableContext.MethodsCount => _methods.Count; + #region CGIHost [ContextMethod("ВывестиЗаголовок", "Header")] @@ -192,7 +190,7 @@ public void ShowExceptionInfo(Exception exc) Echo(exc.ToString()); } - public bool InputString(out string result, int maxLen) + public bool InputString(out string result, string prompt, int maxLen, bool multiline) { result = null; return false; @@ -221,9 +219,9 @@ public void SetIndexedValue(IValue index, IValue val) throw new NotImplementedException(); } - public int FindProperty(string name) + public int GetPropertyNumber(string name) { - throw RuntimeException.PropNotFoundException(name); + throw PropertyAccessException.PropNotFoundException(name); } public bool IsPropReadable(int propNum) @@ -246,24 +244,29 @@ public void SetPropValue(int propNum, IValue newVal) throw new InvalidOperationException("global props are not writable"); } - public int FindMethod(string name) + public int GetMethodNumber(string name) { return _methods.FindMethod(name); } - - public MethodInfo GetMethodInfo(int methodNumber) + + public BslMethodInfo GetMethodInfo(int methodNumber) { - return _methods.GetMethodInfo(methodNumber); + return _methods.GetRuntimeMethod(methodNumber); } - public void CallAsProcedure(int methodNumber, IValue[] arguments) + public BslPropertyInfo GetPropertyInfo(int propertyNumber) + { + throw new NotImplementedException(); + } + + public void CallAsProcedure(int methodNumber, IValue[] arguments, IBslProcess process) { - _methods.GetMethod(methodNumber)(this, arguments); + _methods.GetCallableDelegate(methodNumber)(this, arguments, process); } - public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue) + public void CallAsFunction(int methodNumber, IValue[] arguments, out IValue retValue, IBslProcess process) { - retValue = _methods.GetMethod(methodNumber)(this, arguments); + retValue = _methods.GetCallableDelegate(methodNumber)(this, arguments, process); } public int GetPropCount() diff --git a/src/oscript/CheckSyntaxBehavior.cs b/src/oscript/CheckSyntaxBehavior.cs index 18498e04c..aa285ec6e 100644 --- a/src/oscript/CheckSyntaxBehavior.cs +++ b/src/oscript/CheckSyntaxBehavior.cs @@ -5,13 +5,7 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using OneScript.Language; -using ScriptEngine; -using ScriptEngine.HostedScript; using ScriptEngine.Machine; namespace oscript @@ -33,34 +27,31 @@ public CheckSyntaxBehavior(string path, string envFile, bool isCgi = false) public override int Execute() { - var hostedScript = new HostedScriptEngine - { - CustomConfig = ScriptFileHelper.CustomConfigPath(_path) - }; + var builder = ConsoleHostBuilder.Create(_path); + var hostedScript = ConsoleHostBuilder.Build(builder); hostedScript.Initialize(); if (_isCgi) { var request = ValueFactory.Create(); - hostedScript.InjectGlobalProperty("ВебЗапрос", request, true); - hostedScript.InjectGlobalProperty("WebRequest", request, true); + hostedScript.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true); } - - ScriptFileHelper.OnBeforeScriptRead(hostedScript); + var source = hostedScript.Loader.FromFile(_path); hostedScript.SetGlobalEnvironment(new DoNothingHost(), source); try { + var compilerProcess = hostedScript.Engine.NewProcess(); if (_envFile != null) { var envCompiler = hostedScript.GetCompilerService(); var envSrc = hostedScript.Loader.FromFile(_envFile); - envCompiler.Compile(envSrc); + envCompiler.Compile(envSrc, compilerProcess); } var compiler = hostedScript.GetCompilerService(); - compiler.Compile(source); + compiler.Compile(source, compilerProcess); } catch (ScriptException e) { diff --git a/src/oscript/CmdLineHelper.cs b/src/oscript/CmdLineHelper.cs index a84afa332..12a9ce78b 100644 --- a/src/oscript/CmdLineHelper.cs +++ b/src/oscript/CmdLineHelper.cs @@ -4,11 +4,8 @@ This Source Code Form is subject to the terms of the was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ -using System; -using System.Collections.Generic; + using System.Linq; -using System.Text; -using Newtonsoft.Json; namespace oscript { diff --git a/src/oscript/ConsoleHostBuilder.cs b/src/oscript/ConsoleHostBuilder.cs new file mode 100644 index 000000000..675179567 --- /dev/null +++ b/src/oscript/ConsoleHostBuilder.cs @@ -0,0 +1,57 @@ +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +using OneScript.StandardLibrary; +using ScriptEngine.HostedScript; +using ScriptEngine.Hosting; +using ScriptEngine.HostedScript.Extensions; +using OneScript.Web.Server; + +namespace oscript +{ + internal static class ConsoleHostBuilder + { + public static IEngineBuilder Create(string codePath) + { + var builder = DefaultEngineBuilder.Create() + .SetupConfiguration(p => + { + p.UseSystemConfigFile() + .UseEntrypointConfigFile(codePath) + .UseEnvironmentVariableConfig("OSCRIPT_CONFIG"); + }); + + BuildUpWithIoC(builder); + + builder.SetupEnvironment(env => + { + env.AddStandardLibrary() + .AddWebServer() + .UseTemplateFactory(new DefaultTemplatesFactory()); + }); + + return builder; + } + + public static HostedScriptEngine Build(IEngineBuilder builder) + { + var engine = builder.Build(); + var mainEngine = new HostedScriptEngine(engine); + + return mainEngine; + } + + private static void BuildUpWithIoC(IEngineBuilder builder) + { + builder.SetDefaultOptions() + .UseImports() + .UseFileSystemLibraries() + .UseNativeRuntime() + .UseEventHandlers(); + } + } +} \ No newline at end of file diff --git a/src/oscript/ConsoleHostImpl.cs b/src/oscript/ConsoleHostImpl.cs index 9d9fe9a83..8ff35e2b1 100644 --- a/src/oscript/ConsoleHostImpl.cs +++ b/src/oscript/ConsoleHostImpl.cs @@ -7,7 +7,7 @@ This Source Code Form is subject to the terms of the using System; using OneScript.Language; -using ScriptEngine.HostedScript.Library; +using OneScript.StandardLibrary; namespace oscript { @@ -64,14 +64,47 @@ public static void ShowExceptionInfo(Exception exc) Echo(exc.ToString()); } - public static bool InputString(out string result, int maxLen) + public static bool InputString(out string result, string prompt, int maxLen, bool multiline) { - if (maxLen == 0) - result = Console.ReadLine(); - else - result = Console.ReadLine()?.Substring(0, maxLen); + if( !String.IsNullOrEmpty(prompt) ) + Console.Write(prompt); + + result = multiline ? ReadMultilineString() : Console.ReadLine(); + + if (result == null) + return false; - return result?.Length > 0; + if (maxLen > 0 && maxLen < result.Length) + result = result.Substring(0, maxLen); + + return true; } + + private static string ReadMultilineString() + { + string read; + System.Text.StringBuilder text = null; + + while (true) + { + read = Console.ReadLine(); + + if (read == null) + break; + + if (text == null) + { + text = new System.Text.StringBuilder(read); + } + else + { + text.Append("\n"); + text.Append(read); + } + } + + return text?.ToString(); + } + } } \ No newline at end of file diff --git a/src/oscript/DebugBehavior.cs b/src/oscript/DebugBehavior.cs index e8d6bb7db..740c7cd46 100644 --- a/src/oscript/DebugBehavior.cs +++ b/src/oscript/DebugBehavior.cs @@ -5,54 +5,38 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using System; -using System.Threading; using OneScript.DebugServices; -using oscript.DebugServer; -using ScriptEngine; -using ScriptEngine.Machine; namespace oscript { - internal class DebugBehavior : AppBehavior + internal class DebugBehavior : ExecuteScriptBehavior { - private readonly string[] _args; - private readonly string _path; - private readonly int _port; - - public DebugBehavior(int port, string path, string[] args) + public DebugBehavior(string path, string[] args) : base(path, args) { - _args = args; - _path = path; - _port = port; } - public DebugProtocolType ProtocolType { get; set; } + public int Port { get; set; } = 2801; + public bool AttachMode { get; set; } = true; + public override int Execute() { - var executor = new ExecuteScriptBehavior(_path, _args); - SystemLogger.SetWriter(executor); - switch (ProtocolType) + var tcpDebugServer = new TcpDebugServer(Port); + + DebugController = new DefaultDebugger(tcpDebugServer) { - case DebugProtocolType.Wcf: - executor.DebugController = new WcfDebugController(_port); - break; - case DebugProtocolType.Tcp: - default: - var tcpDebugServer = new BinaryTcpDebugServer(_port); - executor.DebugController = tcpDebugServer.CreateDebugController(); - break; - } - - return executor.Execute(); + AttachMode = AttachMode + }; + + return base.Execute(); } public static AppBehavior Create(CmdLineHelper helper) { int port = 2801; string path = null; - DebugProtocolType protocolType = DebugProtocolType.Tcp; - + bool noWait = false; + while (true) { var arg = helper.Next(); @@ -74,14 +58,14 @@ public static AppBehavior Create(CmdLineHelper helper) return null; } } + else if (parsedArg.Name == "-noWait") + { + noWait = true; + } else if (parsedArg.Name == "-protocol") { - var proto = parsedArg.Value; - if (string.IsNullOrEmpty(proto) || !Enum.TryParse(proto, true, out protocolType)) - { - Output.WriteLine("Unknown protocol. Using default"); - protocolType = DebugProtocolType.Tcp; - } + // Обратная совместимость, не используется в реальности + continue; } else { @@ -90,9 +74,9 @@ public static AppBehavior Create(CmdLineHelper helper) } } - return path == null ? null : new DebugBehavior(port, path, helper.Tail()) + return path == null ? null : new DebugBehavior(path, helper.Tail()) { - ProtocolType = protocolType + Port = port, AttachMode = noWait }; } } diff --git a/src/oscript/DebugServer/DebugControllerBase.cs b/src/oscript/DebugServer/DebugControllerBase.cs deleted file mode 100644 index 2f1185e80..000000000 --- a/src/oscript/DebugServer/DebugControllerBase.cs +++ /dev/null @@ -1,131 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using OneScript.DebugProtocol; -using OneScript.DebugServices; -using ScriptEngine.Machine; - -namespace oscript.DebugServer -{ - [Obsolete] - internal class MachineWaitToken - { - public MachineInstance Machine; - - public ManualResetEventSlim ThreadEvent; - } - - - [Obsolete] - internal abstract class DebugControllerBase : IDebugController - { - private readonly Dictionary _machinesOnThreads = new Dictionary(); - - public DebugControllerBase() - { - BreakpointManager = new DefaultBreakpointManager(); - } - - public virtual void Init() - { - - } - - public virtual void Wait() - { - var token = GetTokenForThread(Thread.CurrentThread.ManagedThreadId); - token.ThreadEvent = new ManualResetEventSlim(); - token.ThreadEvent.Wait(); - } - - public virtual void NotifyProcessExit(int exitCode) - { - var t = _machinesOnThreads[Thread.CurrentThread.ManagedThreadId]; - t.Machine.MachineStopped -= Machine_MachineStopped; - _machinesOnThreads.Remove(Thread.CurrentThread.ManagedThreadId); - } - - public void DetachFromThread() - { - if (_machinesOnThreads.TryGetValue(Thread.CurrentThread.ManagedThreadId, out var t)) - { - t.Machine.MachineStopped -= Machine_MachineStopped; - _machinesOnThreads.Remove(Thread.CurrentThread.ManagedThreadId); - } - } - - public IBreakpointManager BreakpointManager { get; set; } - - public virtual void AttachToThread() - { - var machine = MachineInstance.Current; - _machinesOnThreads[Thread.CurrentThread.ManagedThreadId] = new MachineWaitToken() - { - Machine = machine - }; - - machine.MachineStopped += Machine_MachineStopped; - } - - private void Machine_MachineStopped(object sender, MachineStoppedEventArgs e) - { - OnMachineStopped((MachineInstance)sender, e.Reason); - } - - protected abstract void OnMachineStopped(MachineInstance machine, MachineStopReason reason); - - public void Dispose() - { - Dispose(true); - } - - public MachineWaitToken GetTokenForThread(int threadId) - { - return _machinesOnThreads[threadId]; - } - - public MachineWaitToken[] GetAllTokens() - { - return _machinesOnThreads.Values.ToArray(); - } - - public int[] GetAllThreadIds() - { - return _machinesOnThreads.Keys.ToArray(); - } - - protected virtual void Dispose(bool disposing) - { - var tokens = GetAllTokens(); - foreach (var machineWaitToken in tokens) - { - machineWaitToken.Machine.MachineStopped -= Machine_MachineStopped; - } - - _machinesOnThreads.Clear(); - } - - protected ThreadStopReason ConvertStopReason(MachineStopReason reason) - { - switch(reason) - { - case MachineStopReason.Breakpoint: - return ThreadStopReason.Breakpoint; - case MachineStopReason.Step: - return ThreadStopReason.Step; - case MachineStopReason.Exception: - return ThreadStopReason.Exception; - default: - throw new NotImplementedException(); - } - } - } -} diff --git a/src/oscript/DebugServer/DebugProtocolType.cs b/src/oscript/DebugServer/DebugProtocolType.cs deleted file mode 100644 index 6bc9af08b..000000000 --- a/src/oscript/DebugServer/DebugProtocolType.cs +++ /dev/null @@ -1,15 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -namespace oscript.DebugServer -{ - public enum DebugProtocolType - { - Wcf, - Tcp - } -} \ No newline at end of file diff --git a/src/oscript/DebugServer/DebugServiceImpl.cs b/src/oscript/DebugServer/DebugServiceImpl.cs deleted file mode 100644 index ce0d9a09c..000000000 --- a/src/oscript/DebugServer/DebugServiceImpl.cs +++ /dev/null @@ -1,382 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using OneScript.DebugProtocol; -using OneScript.Language; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.Machine; -using ScriptEngine.Machine.Contexts; -using StackFrame = OneScript.DebugProtocol.StackFrame; -using Variable = OneScript.DebugProtocol.Variable; -using MachineVariable = ScriptEngine.Machine.Variable; - -namespace oscript.DebugServer -{ - [Obsolete] - internal class DebugServiceImpl : IDebuggerService - { - private DebugControllerBase Controller { get; } - - public DebugServiceImpl(DebugControllerBase controller) - { - Controller = controller; - } - - public void Execute(int threadId) - { - if (threadId > 0) - { - var token = Controller.GetTokenForThread(threadId); - token.Machine.PrepareDebugContinuation(); - token.ThreadEvent.Set(); - } - else - { - var tokens = Controller.GetAllTokens(); - foreach (var token in tokens) - { - token.Machine.PrepareDebugContinuation(); - token.ThreadEvent.Set(); - } - } - } - - public virtual Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet) - { - var confirmedBreakpoints = new List(); - - var grouped = breaksToSet.GroupBy(g => g.Source); - - foreach (var item in grouped) - { - var lines = item - .Where(x => x.Line != 0) - .Select(x => x.Line) - .ToArray(); - - foreach (var machine in Controller.GetAllTokens().Select(x=>x.Machine)) - { - Controller.BreakpointManager.SetLineStops(item.Key, lines); - } - - foreach (var line in lines) - { - confirmedBreakpoints.Add(new Breakpoint() - { - Line = line, - Source = item.Key - }); - } - - } - - return confirmedBreakpoints.ToArray(); - } - - public virtual StackFrame[] GetStackFrames(int threadId) - { - var machine = Controller.GetTokenForThread(threadId).Machine; - var frames = machine.GetExecutionFrames(); - var result = new StackFrame[frames.Count]; - int index = 0; - foreach (var frameInfo in frames) - { - var frame = new StackFrame(); - frame.LineNumber = frameInfo.LineNumber; - frame.Index = index++; - frame.MethodName = frameInfo.MethodName; - frame.Source = frameInfo.Source; - result[frame.Index] = frame; - - } - return result; - } - - private MachineInstance GetMachine(int threadId) - { - return Controller.GetTokenForThread(threadId).Machine; - } - - - public virtual Variable[] GetVariables(int threadId, int frameIndex, int[] path) - { - var machine = Controller.GetTokenForThread(threadId).Machine; - var locals = machine.GetFrameLocals(frameIndex); - - foreach (var step in path) - { - var variable = locals[step]; - locals = GetChildVariables(variable); - } - - return GetDebugVariables(locals); - } - - public virtual Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path) - { - IValue value; - - try - { - value = GetMachine(threadId).Evaluate(expression, true); - } - catch (Exception e) - { - value = ValueFactory.Create(e.Message); - } - - var locals = GetChildVariables(MachineVariable.Create(value, "$eval")); - - foreach (var step in path) - { - var variable = locals[step]; - locals = GetChildVariables(variable); - } - - return GetDebugVariables(locals); - } - - public virtual Variable Evaluate(int threadId, int contextFrame, string expression) - { - try - { - var value = GetMachine(threadId).Evaluate(expression, true); - - var variable = CreateDebuggerVariable("$evalResult", - value.AsString(), value.SystemType.Name); - - variable.IsStructured = IsStructured(MachineVariable.Create(value, "$eval")); - - return variable; - } - catch (ScriptException e) - { - return CreateDebuggerVariable("$evalFault", e.ErrorDescription, - "Ошибка"); - } - } - - public virtual void Next(int threadId) - { - var t = Controller.GetTokenForThread(threadId); - t.Machine.StepOver(); - t.ThreadEvent.Set(); - } - - public virtual void StepIn(int threadId) - { - var t = Controller.GetTokenForThread(threadId); - t.Machine.StepIn(); - t.ThreadEvent.Set(); - } - - public virtual void StepOut(int threadId) - { - var t = Controller.GetTokenForThread(threadId); - t.Machine.StepOut(); - t.ThreadEvent.Set(); - } - - public virtual int[] GetThreads() - { - return Controller.GetAllThreadIds(); - } - - public int GetProcessId() - { - return System.Diagnostics.Process.GetCurrentProcess().Id; - } - - private Variable[] GetDebugVariables(IList machineVariables) - { - var result = new Variable[machineVariables.Count]; - - for (int i = 0; i < machineVariables.Count; i++) - { - string presentation; - string typeName; - - var currentVar = machineVariables[i]; - - //На случай проблем, подобных: - //https://github.com/EvilBeaver/OneScript/issues/918 - - try - { - presentation = currentVar.AsString(); - } - catch (Exception e) - { - presentation = e.Message; - } - - try - { - typeName = currentVar.SystemType.Name; - } - catch (Exception e) - { - typeName = e.Message; - } - - result[i] = CreateDebuggerVariable(currentVar.Name, presentation, typeName); - result[i].IsStructured = IsStructured(currentVar); - } - - return result; - } - - public Variable CreateDebuggerVariable(string name, string presentation, string typeName) - { - if (presentation.Length > DebuggerSettings.MAX_PRESENTATION_LENGTH) - presentation = presentation.Substring(0, DebuggerSettings.MAX_PRESENTATION_LENGTH) + "..."; - - return new Variable() - { - Name = name, - Presentation = presentation, - TypeName = typeName - }; - } - - private List GetChildVariables(IVariable src) - { - var variables = new List(); - - if (HasProperties(src)) - { - FillProperties(src, variables); - } - else if(src.AsObject() is IEnumerable collection) - { - FillKeyValueProperties(collection, variables); - } - - if (HasIndexes(src)) - { - FillIndexedProperties(src, variables); - } - - return variables; - } - - private void FillKeyValueProperties(IEnumerable collection, List variables) - { - var propsCount = collection.Count(); - - int i = 0; - - foreach (var kv in collection) - { - IVariable value; - - value = MachineVariable.Create(kv, i.ToString()); - - variables.Add(value); - - i++; - } - } - - private void FillIndexedProperties(IVariable src, List variables) - { - var obj = src.AsObject(); - - if (obj is ICollectionContext cntx) - { - var itemsCount = cntx.Count(); - for (int i = 0; i < itemsCount; i++) - { - IValue value; - - try - { - value = obj.GetIndexedValue(ValueFactory.Create(i)); - } - catch (Exception) - { - continue; - } - - variables.Add(MachineVariable.Create(value, i.ToString())); - } - } - } - - private void FillProperties(IVariable src, List variables) - { - var obj = src.AsObject(); - var propsCount = obj.GetPropCount(); - for (int i = 0; i < propsCount; i++) - { - var propNum = i; - var propName = obj.GetPropName(propNum); - - IVariable value; - - try - { - value = MachineVariable.Create(obj.GetPropValue(propNum), propName); - } - catch (Exception e) - { - value = MachineVariable.Create(ValueFactory.Create(e.Message), propName); - } - - variables.Add(value); - } - } - - private bool IsStructured(IVariable variable) - { - var result = HasProperties(variable) || HasIndexes(variable); - if(!result) - { - if (VariableHasType(variable, DataType.Object)) - { - var obj = variable.AsObject(); - result = obj is IEnumerable; - } - } - return result; - } - - private bool HasIndexes(IValue variable) - { - if (VariableHasType(variable, DataType.Object)) - { - var obj = variable.AsObject(); - if (!(obj is IEnumerable) - && obj is IRuntimeContextInstance cntx && cntx.IsIndexed) - { - if (obj is ICollectionContext collection) - { - return collection.Count() > 0; - } - } - } - - return false; - } - - private static bool HasProperties(IValue variable) - { - if (!VariableHasType(variable, DataType.Object)) - return false; - var obj = variable.AsObject(); - return obj.GetPropCount() > 0; - } - - private static bool VariableHasType(IValue variable, DataType type) - { - return variable.GetRawValue() != null && variable.DataType == type; - } - } -} \ No newline at end of file diff --git a/src/oscript/DebugServer/WCFDebugService.cs b/src/oscript/DebugServer/WCFDebugService.cs deleted file mode 100644 index 387b83451..000000000 --- a/src/oscript/DebugServer/WCFDebugService.cs +++ /dev/null @@ -1,97 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.ServiceModel; -using OneScript.DebugProtocol; -using StackFrame = OneScript.DebugProtocol.StackFrame; -using Variable = OneScript.DebugProtocol.Variable; -using MachineVariable = ScriptEngine.Machine.Variable; - -namespace oscript.DebugServer -{ - [Obsolete] - [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)] - internal class WcfDebugService : IDebuggerService - { - private readonly DebugServiceImpl _debugServiceImpl; - private WcfDebugController Controller { get; } - - public WcfDebugService(WcfDebugController controller) - { - Controller = controller; - _debugServiceImpl = new DebugServiceImpl(controller); - } - - #region WCF Communication methods - - public void Execute(int threadId) - { - RegisterEventListener(); - _debugServiceImpl.Execute(threadId); - } - - private void RegisterEventListener() - { - var eventChannel = OperationContext.Current. - GetCallbackChannel(); - - Controller.SetCallback(eventChannel); - } - - public Breakpoint[] SetMachineBreakpoints(Breakpoint[] breaksToSet) - { - return _debugServiceImpl.SetMachineBreakpoints(breaksToSet); - } - - public StackFrame[] GetStackFrames(int threadId) - { - return _debugServiceImpl.GetStackFrames(threadId); - } - - public Variable[] GetVariables(int threadId, int frameId, int[] path) - { - return _debugServiceImpl.GetVariables(threadId, frameId, path); - } - - public Variable[] GetEvaluatedVariables(string expression, int threadId, int frameIndex, int[] path) - { - return _debugServiceImpl.GetEvaluatedVariables(expression, threadId, frameIndex, path); - } - - public Variable Evaluate(int threadId, int contextFrame, string expression) - { - return _debugServiceImpl.Evaluate(threadId, contextFrame, expression); - } - - public void Next(int threadId) - { - _debugServiceImpl.Next(threadId); - } - - public void StepIn(int threadId) - { - _debugServiceImpl.StepIn(threadId); - } - - public void StepOut(int threadId) - { - _debugServiceImpl.StepOut(threadId); - } - - public int[] GetThreads() - { - return _debugServiceImpl.GetThreads(); - } - - public int GetProcessId() - { - return System.Diagnostics.Process.GetCurrentProcess().Id; - } - - #endregion - } -} diff --git a/src/oscript/DebugServer/WcfDebugController.cs b/src/oscript/DebugServer/WcfDebugController.cs deleted file mode 100644 index ca082357e..000000000 --- a/src/oscript/DebugServer/WcfDebugController.cs +++ /dev/null @@ -1,81 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceModel; -using System.Text; -using System.Threading; -using OneScript.DebugProtocol; -using ScriptEngine; -using ScriptEngine.Machine; - -namespace oscript.DebugServer -{ - [Obsolete] - internal class WcfDebugController : DebugControllerBase - { - private readonly int _port; - private ServiceHost _serviceHost; - private IDebugEventListener _eventChannel; - - public WcfDebugController(int listenerPort) - { - _port = listenerPort; - } - - public override void Init() - { - var serviceInstance = new WcfDebugService(this); - var host = new ServiceHost(serviceInstance); - var binding = (NetTcpBinding)Binder.GetBinding(); - binding.MaxBufferPoolSize = DebuggerSettings.MAX_BUFFER_SIZE; - binding.MaxBufferSize = DebuggerSettings.MAX_BUFFER_SIZE; - binding.MaxReceivedMessageSize = DebuggerSettings.MAX_BUFFER_SIZE; - host.AddServiceEndpoint(typeof(IDebuggerService), binding, Binder.GetDebuggerUri(_port)); - _serviceHost = host; - host.Open(); - - } - - public override void NotifyProcessExit(int exitCode) - { - base.NotifyProcessExit(exitCode); - if (!CallbackChannelIsReady()) - return; // нет подписчика - - _eventChannel.ProcessExited(exitCode); - _serviceHost?.Close(); - } - - protected override void OnMachineStopped(MachineInstance machine, MachineStopReason reason) - { - if (!CallbackChannelIsReady()) - return; // нет подписчика - - var handle = GetTokenForThread(Thread.CurrentThread.ManagedThreadId); - handle.ThreadEvent.Reset(); - _eventChannel.ThreadStopped(1, ConvertStopReason(reason)); - handle.ThreadEvent.Wait(); - } - - private bool CallbackChannelIsReady() - { - if (_eventChannel != null) - { - var ico = (ICommunicationObject)_eventChannel; - return ico.State == CommunicationState.Opened; - } - return false; - } - - public void SetCallback(IDebugEventListener eventChannel) - { - _eventChannel = eventChannel; - } - } -} diff --git a/src/oscript/DoNothingHost.cs b/src/oscript/DoNothingHost.cs index be0e15bf9..26f2aebc4 100644 --- a/src/oscript/DoNothingHost.cs +++ b/src/oscript/DoNothingHost.cs @@ -6,12 +6,8 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - +using OneScript.StandardLibrary; using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; namespace oscript { @@ -25,7 +21,7 @@ public void ShowExceptionInfo(Exception exc) { } - public bool InputString(out string result, int maxLen) + public bool InputString(out string result, string prompt, int maxLen, bool multiline) { result = ""; return true; diff --git a/src/oscript/ExecuteScriptBehavior.cs b/src/oscript/ExecuteScriptBehavior.cs index c221f280f..49cbc29c4 100644 --- a/src/oscript/ExecuteScriptBehavior.cs +++ b/src/oscript/ExecuteScriptBehavior.cs @@ -1,99 +1,110 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using OneScript.DebugProtocol; - -using ScriptEngine; -using ScriptEngine.Compiler; -using ScriptEngine.HostedScript; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.Machine; - -namespace oscript -{ - class ExecuteScriptBehavior : AppBehavior, IHostApplication, ISystemLogWriter - { - string[] _scriptArgs; - string _path; - - public ExecuteScriptBehavior(string path, string[] args) - { - _scriptArgs = args; - _path = path; - } - - public IDebugController DebugController { get; set; } - - public override int Execute() - { - if (!System.IO.File.Exists(_path)) - { - Echo($"Script file is not found '{_path}'"); - return 2; - } - - SystemLogger.SetWriter(this); - - var hostedScript = new HostedScriptEngine(); - hostedScript.DebugController = DebugController; - hostedScript.CustomConfig = ScriptFileHelper.CustomConfigPath(_path); - ScriptFileHelper.OnBeforeScriptRead(hostedScript); - var source = hostedScript.Loader.FromFile(_path); - - Process process; - try - { - process = hostedScript.CreateProcess(this, source); - } - catch(Exception e) - { - this.ShowExceptionInfo(e); - return 1; - } - - var result = process.Start(); - hostedScript.Dispose(); - - ScriptFileHelper.OnAfterScriptExecute(hostedScript); - - return result; - } - - #region IHostApplication Members - - public void Echo(string text, MessageStatusEnum status = MessageStatusEnum.Ordinary) - { - ConsoleHostImpl.Echo(text, status); - } - - public void ShowExceptionInfo(Exception exc) - { - ConsoleHostImpl.ShowExceptionInfo(exc); - } - - public bool InputString(out string result, int maxLen) - { - return ConsoleHostImpl.InputString(out result, maxLen); - } - - public string[] GetCommandLineArguments() - { - return _scriptArgs; - } - - #endregion - - public void Write(string text) - { - Console.Error.WriteLine(text); - } - } -} +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ +using System; +using OneScript.StandardLibrary; +using ScriptEngine; +using ScriptEngine.HostedScript; +using ScriptEngine.Hosting; +using ScriptEngine.Machine; +using ScriptEngine.Machine.Debugger; + +namespace oscript +{ + class ExecuteScriptBehavior : AppBehavior, IHostApplication, ISystemLogWriter + { + protected string[] _scriptArgs; + protected string _path; + + public ExecuteScriptBehavior(string path, string[] args) + { + _scriptArgs = args; + _path = path; + } + + public IDebugger DebugController { get; set; } = new DisabledDebugger(); + + public string CodeStatFile { get; set; } + + public bool CodeStatisticsEnabled => CodeStatFile != null; + + public override int Execute() + { + if (!System.IO.File.Exists(_path)) + { + Echo($"Script file is not found '{_path}'"); + return 2; + } + + SystemLogger.SetWriter(this); + + var builder = ConsoleHostBuilder.Create(_path); + builder.WithDebugger(DebugController); + CodeStatProcessor codeStatProcessor = null; + if (CodeStatisticsEnabled) + { + codeStatProcessor = new CodeStatProcessor(); + builder.Services.RegisterSingleton(codeStatProcessor); + } + + var hostedScript = ConsoleHostBuilder.Build(builder); + + var source = hostedScript.Loader.FromFile(_path); + Process process; + try + { + process = hostedScript.CreateProcess(this, source); + } + catch (Exception e) + { + ShowExceptionInfo(e); + return 1; + } + + var result = process.Start(); + hostedScript.Dispose(); + + if (codeStatProcessor != null) + { + codeStatProcessor.EndCodeStat(); + var codeStat = codeStatProcessor.GetStatData(); + var statsWriter = new CodeStatWriter(CodeStatFile, CodeStatWriterType.JSON); + statsWriter.Write(codeStat); + } + + return result; + } + + #region IHostApplication Members + + public void Echo(string text, MessageStatusEnum status = MessageStatusEnum.Ordinary) + { + ConsoleHostImpl.Echo(text, status); + } + + public void ShowExceptionInfo(Exception exc) + { + ConsoleHostImpl.ShowExceptionInfo(exc); + } + + public bool InputString(out string result, string prompt, int maxLen, bool multiline) + { + return ConsoleHostImpl.InputString(out result, prompt, maxLen, multiline); + } + + public string[] GetCommandLineArguments() + { + return _scriptArgs; + } + + #endregion + + public void Write(string text) + { + Console.Error.WriteLine(text); + } + } +} diff --git a/src/oscript/MakeAppBehavior.cs b/src/oscript/MakeAppBehavior.cs deleted file mode 100644 index c8723507a..000000000 --- a/src/oscript/MakeAppBehavior.cs +++ /dev/null @@ -1,180 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization.Formatters.Binary; -using System.Text; - -using ScriptEngine; -using ScriptEngine.Compiler; -using ScriptEngine.HostedScript; -using ScriptEngine.Machine; - -namespace oscript -{ - internal class MakeAppBehavior : AppBehavior - { - private readonly string _codePath; - - private readonly string _exePath; - - public MakeAppBehavior(string codePath, string exePath) - { - _codePath = codePath; - _exePath = exePath; - } - - public bool CreateDumpOnly { get; set; } - - public override int Execute() - { - Output.WriteLine("Make started..."); - - if (CreateDumpOnly) - { - using (var ms = new MemoryStream()) - { - CreateDump(ms); - } - } - else - { - CreateExe(); - } - - Output.WriteLine("Make completed"); - return 0; - } - - private void CreateDump(Stream output) - { - var offset = (int)output.Length; - - var engine = new HostedScriptEngine - { - CustomConfig = ScriptFileHelper.CustomConfigPath(_codePath) - }; - engine.Initialize(); - ScriptFileHelper.OnBeforeScriptRead(engine); - var source = engine.Loader.FromFile(_codePath); - var compiler = engine.GetCompilerService(); - engine.SetGlobalEnvironment(new DoNothingHost(), source); - var entry = compiler.Compile(source); - - var embeddedContext = engine.GetExternalLibraries() - .SelectMany(x => x.Modules.Concat(x.Classes)); - - var templates = GlobalsManager.GetGlobalContext(); - - var dump = new ApplicationDump(); - dump.Scripts = new UserAddedScript[] - { - new UserAddedScript() - { - Image = entry, - Symbol = "$entry", - Type = UserAddedScriptType.Module - } - }.Concat(embeddedContext) - .ToArray(); - dump.Resources = templates.GetTemplates() - .Select(SerializeTemplate) - .ToArray(); - - // не пишем абсолютных путей в дамп - foreach (var script in dump.Scripts) - { - script.Image.ModuleInfo.Origin = "oscript://" + script.ModuleName(); - script.Image.ModuleInfo.ModuleName = script.Image.ModuleInfo.Origin; - } - - using (var bw = new BinaryWriter(output)) - { - var serializer = new BinaryFormatter(); - serializer.Serialize(output, dump); - - var signature = new byte[] - { - 0x4f, - 0x53, - 0x4d, - 0x44 - }; - output.Write(signature, 0, signature.Length); - - bw.Write(offset); - - OutputToFile(output); - } - } - - private ApplicationResource SerializeTemplate(KeyValuePair keyValuePair) - { - byte[] data; - using (var stream = new MemoryStream()) - { - using (var bw = new BinaryWriter(stream)) - { - var buf = keyValuePair.Value.GetBinaryData().Buffer; - bw.Write(keyValuePair.Value.Kind.ToString()); - bw.Write(buf.Length); - bw.Write(buf); - data = stream.ToArray(); - } - } - - var appResource = new ApplicationResource - { - ResourceName = keyValuePair.Key, - Data = data - }; - - return appResource; - } - - private void CreateExe() - { - using (var exeStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("oscript.StandaloneRunner.exe")) - using (var output = new MemoryStream()) - { - exeStream?.CopyTo(output); - - CreateDump(output); - } - } - - - private void OutputToFile(Stream memoryStream) - { - using (var fileOutput = new FileStream(_exePath, FileMode.Create)) - { - memoryStream.Position = 0; - memoryStream.CopyTo(fileOutput); - } - } - - public static AppBehavior Create(CmdLineHelper helper) - { - var codepath = helper.Next(); - var output = helper.Next(); - var makeBin = helper.Next(); - if (output == null || codepath == null) - return null; - - var appMaker = new MakeAppBehavior(codepath, output); - if (makeBin != null && makeBin == "-bin") - appMaker.CreateDumpOnly = true; - - return appMaker; - - } - } -} \ No newline at end of file diff --git a/src/oscript/MeasureBehavior.cs b/src/oscript/MeasureBehavior.cs index c847996d4..325310290 100644 --- a/src/oscript/MeasureBehavior.cs +++ b/src/oscript/MeasureBehavior.cs @@ -6,10 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Text; namespace oscript { diff --git a/src/oscript/Output.cs b/src/oscript/Output.cs index b83ca4132..73f026b74 100644 --- a/src/oscript/Output.cs +++ b/src/oscript/Output.cs @@ -6,8 +6,6 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.Collections.Generic; -using System.Linq; using System.Text; namespace oscript diff --git a/src/oscript/Program.cs b/src/oscript/Program.cs index 3527e6933..15839bfde 100644 --- a/src/oscript/Program.cs +++ b/src/oscript/Program.cs @@ -6,8 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.Collections.Generic; -using System.Linq; +using System.Reflection; using System.Text; namespace oscript @@ -16,14 +15,17 @@ public static class Program { public static Encoding ConsoleOutputEncoding { - get { return Output.ConsoleOutputEncoding; } - set { Output.ConsoleOutputEncoding = value; } + get => Output.ConsoleOutputEncoding; + set => Output.ConsoleOutputEncoding = value; } public static int Main(string[] args) { int returnCode; +#if NETCOREAPP + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +#endif var behavior = BehaviorSelector.Select(args); try { @@ -40,5 +42,14 @@ public static int Main(string[] args) return returnCode; } + + public static string GetVersion() + { + var assembly = Assembly.GetExecutingAssembly(); + var informationVersion = assembly.GetCustomAttribute()? + .InformationalVersion ?? assembly.GetName().Version?.ToString() ?? ""; + + return informationVersion; + } } } \ No newline at end of file diff --git a/src/oscript/ScriptFileHelper.cs b/src/oscript/ScriptFileHelper.cs deleted file mode 100644 index 86a34d9e1..000000000 --- a/src/oscript/ScriptFileHelper.cs +++ /dev/null @@ -1,103 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.IO; -using System.Net.Configuration; -using System.Reflection; -using System.Text; - -using ScriptEngine.Environment; -using ScriptEngine.HostedScript; - -namespace oscript -{ - internal static class ScriptFileHelper - { - public static bool CodeStatisticsEnabled { get; private set; } - - public static string CodeStatisticsFileName { get; private set; } - - public static string CustomConfigPath(string scriptPath) - { - var dir = Path.GetDirectoryName(scriptPath); - var cfgPath = Path.Combine(dir, HostedScriptEngine.ConfigFileName); - return File.Exists(cfgPath) ? cfgPath : null; - } - - public static void EnableCodeStatistics(string fileName) - { - CodeStatisticsEnabled = fileName != null; - CodeStatisticsFileName = fileName; - } - - // http://www.cookcomputing.com/blog/archives/000556.html - public static bool SetAllowUnsafeHeaderParsing() - { - var aNetAssembly = Assembly.GetAssembly(typeof(SettingsSection)); - if (aNetAssembly == null) return false; - - var aSettingsType = aNetAssembly.GetType("System.Net.Configuration.SettingsSectionInternal"); - if (aSettingsType == null) return false; - - var anInstance = aSettingsType.InvokeMember("Section", BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.NonPublic, null, null, new object[] - { - }); - if (anInstance == null) return false; - - var aUseUnsafeHeaderParsing = aSettingsType.GetField("useUnsafeHeaderParsing", BindingFlags.NonPublic | BindingFlags.Instance); - if (aUseUnsafeHeaderParsing == null) return false; - - aUseUnsafeHeaderParsing.SetValue(anInstance, true); - return true; - } - - private static bool ConvertSettingValueToBool(string value, bool defaultValue = false) - { - if (value == null) return defaultValue; - - if (string.Compare(value, "true", StringComparison.InvariantCultureIgnoreCase) == 0) return true; - if (string.Compare(value, "1", StringComparison.InvariantCultureIgnoreCase) == 0) return true; - if (string.Compare(value, "yes", StringComparison.InvariantCultureIgnoreCase) == 0) return true; - - if (string.Compare(value, "false", StringComparison.InvariantCultureIgnoreCase) == 0) return false; - if (string.Compare(value, "0", StringComparison.InvariantCultureIgnoreCase) == 0) return false; - if (string.Compare(value, "no", StringComparison.InvariantCultureIgnoreCase) == 0) return false; - - return defaultValue; - } - - public static void OnBeforeScriptRead(HostedScriptEngine engine) - { - var cfg = engine.GetWorkingConfig(); - - var openerEncoding = cfg["encoding.script"]; - if (!string.IsNullOrWhiteSpace(openerEncoding)) - if (StringComparer.InvariantCultureIgnoreCase.Compare(openerEncoding, "default") == 0) - engine.Loader.ReaderEncoding = FileOpener.SystemSpecificEncoding(); - else - engine.Loader.ReaderEncoding = Encoding.GetEncoding(openerEncoding); - - var strictWebRequest = ConvertSettingValueToBool(cfg["http.strictWebRequest"]); - if (!strictWebRequest) - SetAllowUnsafeHeaderParsing(); - - if (CodeStatisticsEnabled) - engine.EnableCodeStatistics(); - } - - public static void OnAfterScriptExecute(HostedScriptEngine engine) - { - if (CodeStatisticsEnabled) - { - var codeStat = engine.GetCodeStatData(); - var statsWriter = new CodeStatWriter(CodeStatisticsFileName, CodeStatWriterType.JSON); - statsWriter.Write(codeStat); - } - } - } -} \ No newline at end of file diff --git a/src/oscript/SerializeModuleBehavior.cs b/src/oscript/SerializeModuleBehavior.cs deleted file mode 100644 index 11ba88c37..000000000 --- a/src/oscript/SerializeModuleBehavior.cs +++ /dev/null @@ -1,96 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Newtonsoft.Json; - -using ScriptEngine; -using ScriptEngine.HostedScript; - -namespace oscript -{ - internal class SerializeModuleBehavior : AppBehavior - { - private readonly string _path; - public SerializeModuleBehavior(string path) - { - _path = path; - } - - public override int Execute() - { - var engine = new HostedScriptEngine - { - CustomConfig = ScriptFileHelper.CustomConfigPath(_path) - }; - engine.Initialize(); - ScriptFileHelper.OnBeforeScriptRead(engine); - var source = engine.Loader.FromFile(_path); - var compiler = engine.GetCompilerService(); - engine.SetGlobalEnvironment(new DoNothingHost(), source); - var entry = compiler.Compile(source); - var embeddedContext = engine.GetExternalLibraries(); - - var serializer = new JsonSerializer(); - var sb = new StringBuilder(); - using (var textWriter = new StringWriter(sb)) - { - var writer = new JsonTextWriter(textWriter); - writer.WriteStartArray(); - - WriteImage(new UserAddedScript - { - Type = UserAddedScriptType.Module, - Symbol = "$entry", - Image = entry - }, writer, serializer); - - foreach (var item in embeddedContext) - { - item.Classes.ForEach(x => WriteImage(x, writer, serializer)); - item.Modules.ForEach(x => WriteImage(x, writer, serializer)); - } - - writer.WriteEndArray(); - } - - string result = sb.ToString(); - Output.WriteLine(result); - - return 0; - } - - private void WriteImage(UserAddedScript script, JsonTextWriter writer, JsonSerializer serializer) - { - writer.WriteStartObject(); - writer.WritePropertyName("name"); - writer.WriteValue(script.Symbol); - writer.WritePropertyName("type"); - writer.WriteValue(script.Type.ToString()); - writer.WritePropertyName("image"); - serializer.Serialize(writer, script.Image); - writer.WriteEndObject(); - } - - public static AppBehavior Create(CmdLineHelper helper) - { - var path = helper.Next(); - if (path != null) - { - return new SerializeModuleBehavior(path); - } - - return new ShowUsageBehavior(); - } - } -} diff --git a/src/oscript/ShowCompiledBehavior.cs b/src/oscript/ShowCompiledBehavior.cs index 57d60cd4a..5be120562 100644 --- a/src/oscript/ShowCompiledBehavior.cs +++ b/src/oscript/ShowCompiledBehavior.cs @@ -6,13 +6,8 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using OneScript.Language; -using ScriptEngine; using ScriptEngine.Compiler; -using ScriptEngine.HostedScript; namespace oscript { @@ -27,16 +22,14 @@ public ShowCompiledBehavior(string path) public override int Execute() { - var hostedScript = new HostedScriptEngine - { - CustomConfig = ScriptFileHelper.CustomConfigPath(_path) - }; + var builder = ConsoleHostBuilder.Create(_path); + var hostedScript = ConsoleHostBuilder.Build(builder); hostedScript.Initialize(); - ScriptFileHelper.OnBeforeScriptRead(hostedScript); + var source = hostedScript.Loader.FromFile(_path); var compiler = hostedScript.GetCompilerService(); hostedScript.SetGlobalEnvironment(new DoNothingHost(), source); - var writer = new ModuleWriter(compiler); + var writer = new ModuleDumpWriter(compiler, hostedScript.Engine.NewProcess()); try { writer.Write(Console.Out, source); diff --git a/src/oscript/ShowUsageBehavior.cs b/src/oscript/ShowUsageBehavior.cs index 175098c47..ee1c26a57 100644 --- a/src/oscript/ShowUsageBehavior.cs +++ b/src/oscript/ShowUsageBehavior.cs @@ -1,46 +1,52 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace oscript -{ - internal class ShowUsageBehavior : AppBehavior - { - public override int Execute() - { - Output.WriteLine($"1Script Execution Engine. Version {Assembly.GetExecutingAssembly().GetName().Version}"); - Output.WriteLine(); - Output.WriteLine("Usage:"); - Output.WriteLine(); - Output.WriteLine("I. Script execution: oscript.exe [script arguments..]"); - Output.WriteLine(); - Output.WriteLine("II. Special mode: oscript.exe [script arguments..]"); - Output.WriteLine("Mode can be one of these:"); - Output.WriteLine($" {"-measure",-12}measures execution time"); - Output.WriteLine($" {"-compile",-12}shows compiled module without execution"); - Output.WriteLine($" {"-check [-env=]",-12}provides syntax check"); - Output.WriteLine($" {"-check -cgi",-12}provides syntax check in CGI-mode"); - Output.WriteLine($" {"-version",-12}output version string"); - Output.WriteLine(); - Output.WriteLine(" -encoding= set output encoding"); - Output.WriteLine(" -codestat= write code statistics"); - Output.WriteLine(); - Output.WriteLine("III. Build standalone executable: oscript.exe -make "); - Output.WriteLine(" Builds a standalone executable module based on script specified"); - Output.WriteLine(); - Output.WriteLine("IV. Run as CGI application: oscript.exe -cgi [script arguments..]"); - Output.WriteLine(" Runs as CGI application under HTTP-server (Apache/Nginx/IIS/etc...)"); - - return 0; - } - } +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace oscript +{ + internal class ShowUsageBehavior : AppBehavior + { + public override int Execute() + { + Output.WriteLine($"1Script Execution Engine. Version {Program.GetVersion()}"); + Output.WriteLine(); + Output.WriteLine("Usage:"); + Output.WriteLine(" oscript.exe [options] [script_arguments...]"); + Output.WriteLine(" oscript.exe [mode_options] [script_arguments...]"); + Output.WriteLine(); + + const int modeWidth = -18; + const int subOptionWidth = -14; + + Output.WriteLine("Modes:"); + Output.WriteLine($" {"-measure",modeWidth} Measures script execution time."); + Output.WriteLine($" {"-compile",modeWidth} Shows compiled module without execution."); + Output.WriteLine($" {"-check",modeWidth} Provides syntax check."); + Output.WriteLine($" {"",modeWidth} Options:"); + Output.WriteLine($" {"",modeWidth} {"-cgi",subOptionWidth} Syntax check in CGI-mode."); + Output.WriteLine($" {"",modeWidth} {"-env=",subOptionWidth} Path to entrypoint file for context."); + + Output.WriteLine($" {"-debug",modeWidth} Runs script in debug mode."); + Output.WriteLine($" {"",modeWidth} Options:"); + Output.WriteLine($" {"",modeWidth} {"-port=",subOptionWidth} Debugger port (default is 2801)."); + Output.WriteLine($" {"",modeWidth} {"-noWait",subOptionWidth} Do not wait for debugger connection."); + + Output.WriteLine($" {"-version, -v",modeWidth} Output version string."); + Output.WriteLine(); + + Output.WriteLine("Options:"); + Output.WriteLine($" {"-encoding=",modeWidth} Set output encoding (e.g. utf-8)."); + Output.WriteLine($" {"-codestat=",modeWidth} Write code execution statistics to file."); + Output.WriteLine(); + + Output.WriteLine("CGI Mode:"); + Output.WriteLine(" oscript.exe -cgi [script_arguments...]"); + Output.WriteLine(" Runs as CGI application under HTTP-server."); + + return 0; + } + } } \ No newline at end of file diff --git a/src/oscript/ShowVersionBehavior.cs b/src/oscript/ShowVersionBehavior.cs index bb340764a..bdb13a095 100644 --- a/src/oscript/ShowVersionBehavior.cs +++ b/src/oscript/ShowVersionBehavior.cs @@ -1,21 +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/. -----------------------------------------------------------*/ - -using System; -using System.Reflection; - -namespace oscript -{ - internal class ShowVersionBehavior : AppBehavior - { - public override int Execute() - { - Output.WriteLine($"{Assembly.GetExecutingAssembly().GetName().Version}"); - return 0; - } - } +/*---------------------------------------------------------- +This Source Code Form is subject to the terms of the +Mozilla Public License, v.2.0. If a copy of the MPL +was not distributed with this file, You can obtain one +at http://mozilla.org/MPL/2.0/. +----------------------------------------------------------*/ + +namespace oscript +{ + internal class ShowVersionBehavior : AppBehavior + { + public override int Execute() + { + Output.WriteLine(Program.GetVersion()); + return 0; + } + } } \ No newline at end of file diff --git a/src/oscript/Web/Multipart/BinaryStreamStack.cs b/src/oscript/Web/Multipart/BinaryStreamStack.cs index eac9dbb10..bb07961cc 100644 --- a/src/oscript/Web/Multipart/BinaryStreamStack.cs +++ b/src/oscript/Web/Multipart/BinaryStreamStack.cs @@ -1,404 +1,404 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2013 Jake Woods -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software -// and associated documentation files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, publish, distribute, -// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies -// or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -// Jake Woods -// -// Provides character based and byte based stream-like read operations over multiple -// streams and provides methods to add data to the front of the buffer. -// -// -------------------------------------------------------------------------------------------------------------------- - -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace HttpMultipartParser -{ - /// - /// Provides character based and byte based stream-like read operations over multiple - /// streams and provides methods to add data to the front of the buffer. - /// - internal class BinaryStreamStack - { - #region Fields - - /// - /// Holds the streams to read from, the stream on the top of the - /// stack will be read first. - /// - private readonly Stack streams = new Stack(); - - #endregion - - #region Constructors and Destructors - - /// - /// Initializes a new instance of the class with the default - /// encoding of UTF8. - /// - public BinaryStreamStack() - : this(Encoding.UTF8) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// - /// The encoding to use for character based operations. - /// - public BinaryStreamStack(Encoding encoding) - { - CurrentEncoding = encoding; - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the current encoding. - /// - public Encoding CurrentEncoding { get; set; } - - #endregion - - #region Public Methods and Operators - - /// - /// Returns true if there is any data left to read. - /// - /// - /// True or false. - /// - public bool HasData() - { - return streams.Any(); - } - - /// - /// Returns the reader on the top of the stack but does not remove it. - /// - /// - /// The . - /// - public BinaryReader Peek() - { - return streams.Peek(); - } - - /// - /// Returns the reader on the top of the stack and removes it - /// - /// - /// The . - /// - public BinaryReader Pop() - { - return streams.Pop(); - } - - /// - /// Pushes data to the front of the stack. The most recently pushed data will - /// be read first. - /// - /// - /// The data to add to the stack. - /// - public void Push(byte[] data) - { - streams.Push(new BinaryReader(new MemoryStream(data), CurrentEncoding)); - } - - /// - /// Reads a single byte as an integer from the stack. Returns -1 if no - /// data is left to read. - /// - /// - /// The that was read. - /// - public int Read() - { - BinaryReader top = streams.Peek(); - - int value; - while ((value = top.Read()) == -1) - { - top.Dispose(); - streams.Pop(); - - if (!streams.Any()) - { - return -1; - } - - top = streams.Peek(); - } - - return value; - } - - /// - /// Reads the specified number of bytes from the stack, starting from a specified point in the byte array. - /// - /// - /// The buffer to read data into. - /// - /// - /// The index of buffer to start reading into. - /// - /// - /// The number of bytes to read into the buffer. - /// - /// - /// The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, - /// or it might be zero if the end of the stream is reached. - /// - public int Read(byte[] buffer, int index, int count) - { - if (!HasData()) - { - return 0; - } - - // Read through all the stream untill we exhaust them - // or untill count is satisfied - int amountRead = 0; - BinaryReader top = streams.Peek(); - while (amountRead < count && streams.Any()) - { - int read = top.Read(buffer, index + amountRead, count - amountRead); - if (read == 0) - { - if ((top = NextStream()) == null) - { - return amountRead; - } - } - else - { - amountRead += read; - } - } - - return amountRead; - } - - /// - /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. - /// - /// - /// The buffer to read data into. - /// - /// - /// The index of buffer to start reading into. - /// - /// - /// The number of characters to read into the buffer. - /// - /// - /// The number of characters read into buffer. This might be less than the number of bytes requested if that many bytes are not available, - /// or it might be zero if the end of the stream is reached. - /// - public int Read(char[] buffer, int index, int count) - { - if (!HasData()) - { - return 0; - } - - // Read through all the stream untill we exhaust them - // or untill count is satisfied - int amountRead = 0; - BinaryReader top = streams.Peek(); - while (amountRead < count && streams.Any()) - { - int read = top.Read(buffer, index + amountRead, count - amountRead); - if (read == 0) - { - if ((top = NextStream()) == null) - { - return amountRead; - } - } - else - { - amountRead += read; - } - } - - return amountRead; - } - - /// - /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. - /// - /// - /// A byte array containing all the data up to but not including the next newline in the stack. - /// - public byte[] ReadByteLine() - { - bool dummy; - return ReadByteLine(out dummy); - } - - /// - /// Reads a line from the stack delimited by the newline for this platform. The newline - /// characters will not be included in the stream - /// - /// - /// This will be set to true if we did not end on a newline but instead found the end of - /// our data. - /// - /// - /// The containing the line. - /// - public byte[] ReadByteLine(out bool hitStreamEnd) - { - hitStreamEnd = false; - if (!HasData()) - { - // No streams, no data! - return null; - } - - // This is horribly inefficient, consider profiling here if - // it becomes an issue. - BinaryReader top = streams.Peek(); - byte[] ignore = CurrentEncoding.GetBytes(new[] {'\r'}); - byte[] search = CurrentEncoding.GetBytes(new[] {'\n'}); - int searchPos = 0; - var builder = new MemoryStream(); - - while (true) - { - // First we need to read a byte from one of the streams - var bytes = new byte[search.Length]; - int amountRead = top.Read(bytes, 0, bytes.Length); - while (amountRead == 0) - { - streams.Pop(); - if (!streams.Any()) - { - hitStreamEnd = true; - return builder.ToArray(); - } - - top.Dispose(); - top = streams.Peek(); - amountRead = top.Read(bytes, 0, bytes.Length); - } - - // Now we've got some bytes, we need to check it against the search array. - foreach (byte b in bytes) - { - if (ignore.Contains(b)) - { - continue; - } - - if (b == search[searchPos]) - { - searchPos += 1; - } - else - { - // We only want to append the information if it's - // not part of the newline sequence - if (searchPos != 0) - { - byte[] append = search.Take(searchPos).ToArray(); - builder.Write(append, 0, append.Length); - } - - builder.Write(new[] {b}, 0, 1); - searchPos = 0; - } - - // Finally if we've found our string - if (searchPos == search.Length) - { - return builder.ToArray(); - } - } - } - } - - /// - /// Reads a line from the stack delimited by the newline for this platform. The newline - /// characters will not be included in the stream - /// - /// - /// The containing the line. - /// - public string ReadLine() - { - bool dummy; - return ReadLine(out dummy); - } - - /// - /// Reads a line from the stack delimited by the newline for this platform. The newline - /// characters will not be included in the stream - /// - /// - /// This will be set to true if we did not end on a newline but instead found the end of - /// our data. - /// - /// - /// The containing the line. - /// - public string ReadLine(out bool hitStreamEnd) - { - bool foundEnd; - byte[] result = ReadByteLine(out foundEnd); - hitStreamEnd = foundEnd; - - if (result == null) - { - return null; - } - - return CurrentEncoding.GetString(result); - } - - #endregion - - #region Methods - - /// - /// Removes the current reader from the stack and ensures it is correctly - /// destroyed and then returns the next available reader. If no reader - /// is available this method returns null. - /// - /// - /// The next reader. - /// - private BinaryReader NextStream() - { - BinaryReader top = streams.Pop(); - top.Dispose(); - - return streams.Any() ? streams.Peek() : null; - } - - #endregion - } +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2013 Jake Woods +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, publish, distribute, +// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies +// or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Jake Woods +// +// Provides character based and byte based stream-like read operations over multiple +// streams and provides methods to add data to the front of the buffer. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace HttpMultipartParser +{ + /// + /// Provides character based and byte based stream-like read operations over multiple + /// streams and provides methods to add data to the front of the buffer. + /// + internal class BinaryStreamStack + { + #region Fields + + /// + /// Holds the streams to read from, the stream on the top of the + /// stack will be read first. + /// + private readonly Stack streams = new Stack(); + + #endregion + + #region Constructors and Destructors + + /// + /// Initializes a new instance of the class with the default + /// encoding of UTF8. + /// + public BinaryStreamStack() + : this(Encoding.UTF8) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The encoding to use for character based operations. + /// + public BinaryStreamStack(Encoding encoding) + { + CurrentEncoding = encoding; + } + + #endregion + + #region Public Properties + + /// + /// Gets or sets the current encoding. + /// + public Encoding CurrentEncoding { get; set; } + + #endregion + + #region Public Methods and Operators + + /// + /// Returns true if there is any data left to read. + /// + /// + /// True or false. + /// + public bool HasData() + { + return streams.Count != 0; + } + + /// + /// Returns the reader on the top of the stack but does not remove it. + /// + /// + /// The . + /// + public BinaryReader Peek() + { + return streams.Peek(); + } + + /// + /// Returns the reader on the top of the stack and removes it + /// + /// + /// The . + /// + public BinaryReader Pop() + { + return streams.Pop(); + } + + /// + /// Pushes data to the front of the stack. The most recently pushed data will + /// be read first. + /// + /// + /// The data to add to the stack. + /// + public void Push(byte[] data) + { + streams.Push(new BinaryReader(new MemoryStream(data), CurrentEncoding)); + } + + /// + /// Reads a single byte as an integer from the stack. Returns -1 if no + /// data is left to read. + /// + /// + /// The that was read. + /// + public int Read() + { + BinaryReader top = streams.Peek(); + + int value; + while ((value = top.Read()) == -1) + { + top.Dispose(); + streams.Pop(); + + if (streams.Count == 0) + { + return -1; + } + + top = streams.Peek(); + } + + return value; + } + + /// + /// Reads the specified number of bytes from the stack, starting from a specified point in the byte array. + /// + /// + /// The buffer to read data into. + /// + /// + /// The index of buffer to start reading into. + /// + /// + /// The number of bytes to read into the buffer. + /// + /// + /// The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, + /// or it might be zero if the end of the stream is reached. + /// + public int Read(byte[] buffer, int index, int count) + { + if (!HasData()) + { + return 0; + } + + // Read through all the stream untill we exhaust them + // or untill count is satisfied + int amountRead = 0; + BinaryReader top = streams.Peek(); + while (amountRead < count && streams.Count != 0) + { + int read = top.Read(buffer, index + amountRead, count - amountRead); + if (read == 0) + { + if ((top = NextStream()) == null) + { + return amountRead; + } + } + else + { + amountRead += read; + } + } + + return amountRead; + } + + /// + /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. + /// + /// + /// The buffer to read data into. + /// + /// + /// The index of buffer to start reading into. + /// + /// + /// The number of characters to read into the buffer. + /// + /// + /// The number of characters read into buffer. This might be less than the number of bytes requested if that many bytes are not available, + /// or it might be zero if the end of the stream is reached. + /// + public int Read(char[] buffer, int index, int count) + { + if (!HasData()) + { + return 0; + } + + // Read through all the stream untill we exhaust them + // or untill count is satisfied + int amountRead = 0; + BinaryReader top = streams.Peek(); + while (amountRead < count && streams.Count != 0) + { + int read = top.Read(buffer, index + amountRead, count - amountRead); + if (read == 0) + { + if ((top = NextStream()) == null) + { + return amountRead; + } + } + else + { + amountRead += read; + } + } + + return amountRead; + } + + /// + /// Reads the specified number of characters from the stack, starting from a specified point in the byte array. + /// + /// + /// A byte array containing all the data up to but not including the next newline in the stack. + /// + public byte[] ReadByteLine() + { + bool dummy; + return ReadByteLine(out dummy); + } + + /// + /// Reads a line from the stack delimited by the newline for this platform. The newline + /// characters will not be included in the stream + /// + /// + /// This will be set to true if we did not end on a newline but instead found the end of + /// our data. + /// + /// + /// The containing the line. + /// + public byte[] ReadByteLine(out bool hitStreamEnd) + { + hitStreamEnd = false; + if (!HasData()) + { + // No streams, no data! + return null; + } + + // This is horribly inefficient, consider profiling here if + // it becomes an issue. + BinaryReader top = streams.Peek(); + byte[] ignore = CurrentEncoding.GetBytes(new[] {'\r'}); + byte[] search = CurrentEncoding.GetBytes(new[] {'\n'}); + int searchPos = 0; + var builder = new MemoryStream(); + + while (true) + { + // First we need to read a byte from one of the streams + var bytes = new byte[search.Length]; + int amountRead = top.Read(bytes, 0, bytes.Length); + while (amountRead == 0) + { + streams.Pop(); + if (streams.Count == 0) + { + hitStreamEnd = true; + return builder.ToArray(); + } + + top.Dispose(); + top = streams.Peek(); + amountRead = top.Read(bytes, 0, bytes.Length); + } + + // Now we've got some bytes, we need to check it against the search array. + foreach (byte b in bytes) + { + if (ignore.Contains(b)) + { + continue; + } + + if (b == search[searchPos]) + { + searchPos += 1; + } + else + { + // We only want to append the information if it's + // not part of the newline sequence + if (searchPos != 0) + { + byte[] append = search.Take(searchPos).ToArray(); + builder.Write(append, 0, append.Length); + } + + builder.Write(new[] {b}, 0, 1); + searchPos = 0; + } + + // Finally if we've found our string + if (searchPos == search.Length) + { + return builder.ToArray(); + } + } + } + } + + /// + /// Reads a line from the stack delimited by the newline for this platform. The newline + /// characters will not be included in the stream + /// + /// + /// The containing the line. + /// + public string ReadLine() + { + bool dummy; + return ReadLine(out dummy); + } + + /// + /// Reads a line from the stack delimited by the newline for this platform. The newline + /// characters will not be included in the stream + /// + /// + /// This will be set to true if we did not end on a newline but instead found the end of + /// our data. + /// + /// + /// The containing the line. + /// + public string ReadLine(out bool hitStreamEnd) + { + bool foundEnd; + byte[] result = ReadByteLine(out foundEnd); + hitStreamEnd = foundEnd; + + if (result == null) + { + return null; + } + + return CurrentEncoding.GetString(result); + } + + #endregion + + #region Methods + + /// + /// Removes the current reader from the stack and ensures it is correctly + /// destroyed and then returns the next available reader. If no reader + /// is available this method returns null. + /// + /// + /// The next reader. + /// + private BinaryReader NextStream() + { + BinaryReader top = streams.Pop(); + top.Dispose(); + + return streams.Count != 0 ? streams.Peek() : null; + } + + #endregion + } } \ No newline at end of file diff --git a/src/oscript/Web/Multipart/PostFileDescription.cs b/src/oscript/Web/Multipart/PostFileDescription.cs index c6e67c112..fca15eac3 100644 --- a/src/oscript/Web/Multipart/PostFileDescription.cs +++ b/src/oscript/Web/Multipart/PostFileDescription.cs @@ -6,8 +6,8 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using HttpMultipartParser; -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; using ScriptEngine.Machine.Contexts; namespace oscript.Web.Multipart diff --git a/src/oscript/Web/Multipart/PostRequestData.cs b/src/oscript/Web/Multipart/PostRequestData.cs index d46dc8a9c..05aee2264 100644 --- a/src/oscript/Web/Multipart/PostRequestData.cs +++ b/src/oscript/Web/Multipart/PostRequestData.cs @@ -5,14 +5,14 @@ This Source Code Form is subject to the terms of the at http://mozilla.org/MPL/2.0/. ----------------------------------------------------------*/ using HttpMultipartParser; -using ScriptEngine.HostedScript.Library; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; using System; using System.IO; using System.Text; - -using ScriptEngine.HostedScript.Library.Binary; +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Collections; namespace oscript.Web.Multipart { @@ -23,28 +23,25 @@ public class PostRequestData : AutoContext private FixedMapImpl _params; private FixedMapImpl _files; - public PostRequestData(byte []buffer, string boundary) + public PostRequestData(Stream input, string boundary) { - using (var stdin = new MemoryStream(buffer)) + var parser = new MultipartFormDataParser(input, boundary, Encoding.UTF8); + MapImpl mParams = new MapImpl(); + foreach (var param in parser.Parameters) { - var parser = new MultipartFormDataParser(stdin, boundary, Encoding.UTF8); - MapImpl m_params = new MapImpl(); - foreach (var param in parser.Parameters) - { - m_params.Insert(ValueFactory.Create(param.Name), ValueFactory.Create(param.Data)); - } - _params = new FixedMapImpl(m_params); + mParams.Insert(ValueFactory.Create(param.Name), ValueFactory.Create(param.Data)); + } + _params = new FixedMapImpl(mParams); - MapImpl m_files = new MapImpl(); - foreach (var file in parser.Files) - { - m_files.Insert( - ValueFactory.Create(file.Name), - ValueFactory.Create(new PostFileDescription(file)) - ); - } - _files = new FixedMapImpl(m_files); + MapImpl mFiles = new MapImpl(); + foreach (var file in parser.Files) + { + mFiles.Insert( + ValueFactory.Create(file.Name), + new PostFileDescription(file) + ); } + _files = new FixedMapImpl(mFiles); } public PostRequestData(string data) @@ -96,7 +93,7 @@ public FixedMapImpl Files [ScriptConstructor(Name = "Из двоичных данных")] public static PostRequestData Constructor(BinaryDataContext data, IValue boundary) { - return new PostRequestData(data.Buffer, boundary.ToString()); + return new PostRequestData(data.GetStream(), boundary.ToString()); } [ScriptConstructor(Name = "Из строки запроса")] diff --git a/src/oscript/Web/Multipart/RebufferableBinaryReader.cs b/src/oscript/Web/Multipart/RebufferableBinaryReader.cs index 677768833..a1449a1f3 100644 --- a/src/oscript/Web/Multipart/RebufferableBinaryReader.cs +++ b/src/oscript/Web/Multipart/RebufferableBinaryReader.cs @@ -25,7 +25,6 @@ //
// -------------------------------------------------------------------------------------------------------------------- -using System; using System.IO; using System.Text; diff --git a/src/oscript/Web/Multipart/SubsequenceFinder.cs b/src/oscript/Web/Multipart/SubsequenceFinder.cs index 872a4e451..d36b41121 100644 --- a/src/oscript/Web/Multipart/SubsequenceFinder.cs +++ b/src/oscript/Web/Multipart/SubsequenceFinder.cs @@ -25,7 +25,6 @@ // -------------------------------------------------------------------------------------------------------------------- using System.Collections.Generic; -using System.Linq; namespace HttpMultipartParser { diff --git a/src/oscript/Web/WebRequestContext.cs b/src/oscript/Web/WebRequestContext.cs index 36cef1222..30f46d52c 100644 --- a/src/oscript/Web/WebRequestContext.cs +++ b/src/oscript/Web/WebRequestContext.cs @@ -7,23 +7,25 @@ This Source Code Form is subject to the terms of the using System; using System.Collections; +using System.IO; using System.Text; - +using System.Threading.Tasks; +using OneScript.Contexts; +using OneScript.StandardLibrary.Binary; +using OneScript.StandardLibrary.Collections; +using OneScript.StandardLibrary.Text; using oscript.Web.Multipart; - -using ScriptEngine.HostedScript.Library; -using ScriptEngine.HostedScript.Library.Binary; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; namespace oscript.Web { [ContextClass("ВебЗапрос", "WebRequest")] - public class WebRequestContext : AutoContext + public sealed class WebRequestContext : AutoContext, IDisposable, IAsyncDisposable { private PostRequestData _post; - private byte[] _postRaw; + private FileBackingStream _postBody; public WebRequestContext() { @@ -62,23 +64,27 @@ private void ProcessPostData() var len = int.Parse(contentLen); if (len == 0) return; - - _postRaw = new byte[len]; - using (var stdin = Console.OpenStandardInput()) - { - stdin.Read(_postRaw, 0, len); - } - + var type = Environment.GetEnvironmentVariable("CONTENT_TYPE"); + + using var stdin = Console.OpenStandardInput(); + var dest = new FileBackingStream(FileBackingConstants.DEFAULT_MEMORY_LIMIT, len); + stdin.CopyTo(dest); + dest.Position = 0; + if (type != null && type.StartsWith("multipart/")) { var boundary = type.Substring(type.IndexOf('=') + 1); - _post = new PostRequestData(_postRaw, boundary); + _post = new PostRequestData(dest, boundary); } else { - _post = new PostRequestData(Encoding.UTF8.GetString(_postRaw)); + using var reader = new StreamReader(dest, Encoding.UTF8, leaveOpen: true); + _post = new PostRequestData(reader.ReadToEnd()); } + + dest.Position = 0; + _postBody = dest; } private void FillEnvironmentVars() @@ -100,7 +106,8 @@ private void FillGetMap(string get) [ContextMethod("ПолучитьТелоКакДвоичныеДанные", "GetBodyAsBinaryData")] public BinaryDataContext GetBodyAsBinaryData() { - return new BinaryDataContext(_postRaw); + _postBody.Position = 0; + return new BinaryDataContext(_postBody); } [ContextMethod("ПолучитьТелоКакСтроку", "GetBodyAsString")] @@ -110,7 +117,19 @@ public string GetBodyAsString(IValue encoding = null) ? new UTF8Encoding(false) : TextEncodingEnum.GetEncoding(encoding); - return enc.GetString(_postRaw); + _postBody.Position = 0; + using var streamReader = new StreamReader(_postBody, enc); + return streamReader.ReadToEnd(); + } + + public void Dispose() + { + _postBody?.Dispose(); + } + + public async ValueTask DisposeAsync() + { + if (_postBody != null) await _postBody.DisposeAsync(); } } } \ No newline at end of file diff --git a/src/oscript/oscript.csproj b/src/oscript/oscript.csproj index 585a9e3dd..875bdab83 100644 --- a/src/oscript/oscript.csproj +++ b/src/oscript/oscript.csproj @@ -3,54 +3,43 @@ - net452 + $(TargetFrameworkVersion) Exe oscript.Program x86;AnyCPU - Debug;Release + Debug;Release;LinuxDebug + Major + true 1Script console host + - bin\x86\Debug\net452\oscript.xml false - false bin\x86\Debug - 4 - x86 obj\x86\Debug - - - - - + + true + false + + - - - - - - - - - - - + \ No newline at end of file diff --git a/src/oscriptTest/Properties/AssemblyInfo.cs b/src/oscriptTest/Properties/AssemblyInfo.cs deleted file mode 100644 index d37999d95..000000000 --- a/src/oscriptTest/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +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.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("oscriptTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("oscriptTest")] -[assembly: AssemblyCopyright("Copyright © 2017")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] - -[assembly: Guid("69a7869c-203c-4f09-ac3f-04e9b52ad7ab")] - -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/oscriptTest/oscriptTests.cs b/src/oscriptTest/oscriptTests.cs deleted file mode 100644 index 5e8405495..000000000 --- a/src/oscriptTest/oscriptTests.cs +++ /dev/null @@ -1,220 +0,0 @@ -/*---------------------------------------------------------- -This Source Code Form is subject to the terms of the -Mozilla Public License, v.2.0. If a copy of the MPL -was not distributed with this file, You can obtain one -at http://mozilla.org/MPL/2.0/. -----------------------------------------------------------*/ - -using System; -using System.IO; - -using NUnit.Framework; - -namespace oscript -{ - [TestFixture] - public class oscriptTests - { - [SetUp] - public void SetUp() - { - var path = AppDomain.CurrentDomain.BaseDirectory; - _simpleScriptFullPath = Path.Combine(path, "simpleScript.os"); - _uncompilableScriptFullPath = Path.Combine(path, "uncompilableScript.os"); - } - - private string _simpleScriptFullPath; - - private string _uncompilableScriptFullPath; - - private static string GetLastOutput(string text) - { - var array = text.Split(new[] - { - "\r\n" - }, StringSplitOptions.RemoveEmptyEntries); - - return array[array.Length - 1]; - } - - [Test] - [Category("Compile")] - public void BasicCompileBehaviourWorks() - { - var args = new[] - { - "-compile", - _simpleScriptFullPath - }; - - Program.Main(args); - } - - [Test] - [Category("Make")] - public void BasicMakeBehaviourNonsenseFileReturnsError() - { - using (var sw = new StringWriter()) - { - Console.SetOut(sw); - - var tempFolder = Path.GetTempPath(); - const string EXE_NAME = "test.exe"; - - var exeFullPath = Path.Combine(tempFolder, EXE_NAME); - - if (File.Exists(exeFullPath)) - File.Delete(exeFullPath); - - var args = new[] - { - "-make", - "nonsense.os", - exeFullPath - }; - - Program.Main(args); - - var exeExists = File.Exists(exeFullPath); - const string UNEXPECTED = "Make completed"; - var result = GetLastOutput(sw.ToString()); - - Assert.AreNotEqual(UNEXPECTED, result); - Assert.IsFalse(exeExists); - } - } - - [Test] - [Category("Make")] - public void BasicMakeBehaviourWorks() - { - using (var sw = new StringWriter()) - { - Console.SetOut(sw); - - var tempFolder = Path.GetTempPath(); - const string EXE_NAME = "test.exe"; - - var exeFullPath = Path.Combine(tempFolder, EXE_NAME); - - if (File.Exists(exeFullPath)) - File.Delete(exeFullPath); - - var args = new[] - { - "-make", - _simpleScriptFullPath, - exeFullPath - }; - - Program.Main(args); - - var exeExists = File.Exists(exeFullPath); - const string EXPECTED = "Make completed"; - var result = GetLastOutput(sw.ToString()); - - Assert.AreEqual(EXPECTED, result); - Assert.IsTrue(exeExists, "Exptected file: " + exeFullPath); - } - } - - [Test] - [Category("Script execution")] - public void FileNotFoundReturnsError() - { - using (var sw = new StringWriter()) - { - Console.SetOut(sw); - - var args = new[] - { - "nonsense.os" - }; - - Program.Main(args); - const string EXPECTED = "Script file is not found \'nonsense.os\'"; - var result = GetLastOutput(sw.ToString()); - Assert.AreEqual(EXPECTED, result); - } - } - - [Test] - [Category("NoArgs")] - public void ShowUsageBehaviourExecutes() - { - using (var sw = new StringWriter()) - { - Console.SetOut(sw); - - var args = new string[] - { - }; - Program.Main(args); - const string EXPECTED = " Runs as CGI application under HTTP-server (Apache/Nginx/IIS/etc...)"; - var result = GetLastOutput(sw.ToString()); - Assert.AreEqual(EXPECTED, result); - } - } - - [Test] - [Category("Check")] - public void SimpleScriptChecksOut() - { - using (var sw = new StringWriter()) - { - Console.SetOut(sw); - - var args = new[] - { - "-check", - _simpleScriptFullPath - }; - - Program.Main(args); - var result = sw.ToString(); - var expected = $"No errors.{Environment.NewLine}"; - Assert.AreEqual(expected, result); - } - } - - [Test] - [Category("Script execution")] - public void SimpleScriptExecutes() - { - using (var sw = new StringWriter()) - { - Console.SetOut(sw); - - var args = new[] - { - _simpleScriptFullPath - }; - - Program.Main(args); - var result = sw.ToString(); - Assert.IsEmpty(result); - } - } - - [Test] - [Category("Check")] - public void UncompilableScriptFailsCheck() - { - using (var sw = new StringWriter()) - { - Console.SetOut(sw); - - var args = new[] - { - "-check", - _uncompilableScriptFullPath - }; - - Program.Main(args); - var result = sw.ToString(); - var unexpected = $"No errors.{Environment.NewLine}"; - Assert.AreNotEqual(unexpected, result); - } - } - } -} diff --git a/src/oscriptTest/oscriptTests.csproj b/src/oscriptTest/oscriptTests.csproj deleted file mode 100644 index c55b6ec39..000000000 --- a/src/oscriptTest/oscriptTests.csproj +++ /dev/null @@ -1,90 +0,0 @@ - - - - - Debug - AnyCPU - {69A7869C-203C-4F09-AC3F-04E9B52AD7AB} - Library - Properties - oscript - oscriptTest - v4.6.1 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - prompt - MinimumRecommendedRules.ruleset - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - - - - - - - - - - - - - - - - - - {2590e2bb-cc1f-4775-80ed-451f45c9a4f1} - oscript - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file diff --git a/src/oscriptTest/packages.config b/src/oscriptTest/packages.config deleted file mode 100644 index a10d324a0..000000000 --- a/src/oscriptTest/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/oscriptTest/simpleScript.os b/src/oscriptTest/simpleScript.os deleted file mode 100644 index cf5564d43..000000000 --- a/src/oscriptTest/simpleScript.os +++ /dev/null @@ -1 +0,0 @@ -a="b"; \ No newline at end of file diff --git a/src/oscriptTest/uncompilableScript.os b/src/oscriptTest/uncompilableScript.os deleted file mode 100644 index 3d13fb10d..000000000 --- a/src/oscriptTest/uncompilableScript.os +++ /dev/null @@ -1 +0,0 @@ -Саабщить("Шта-та"); \ No newline at end of file diff --git a/tests/.vscode/launch.json b/tests/.vscode/launch.json new file mode 100644 index 000000000..c952b2feb --- /dev/null +++ b/tests/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "LINUX: Run via version 2.0", + "type": "oscript", + "request": "launch", + "program": "${workspaceRoot}/testrunner.os", + "args": ["-run", "${file}"], + "cwd": "${workspaceRoot}", + "env": {}, + "runtimeExecutable": "${workspaceRoot}/../src/oscript/bin/LinuxDebug/net6.0/oscript", + "runtimeArgs": [], + "debugPort": 2801 + }, + { + "name": "WINDOWS: Run via version 2.0", + "type": "oscript", + "request": "launch", + "program": "${workspaceRoot}/testrunner.os", + "args": ["-run", "${file}"], + "cwd": "${workspaceRoot}", + "env": {}, + "runtimeExecutable": "${workspaceRoot}/../src/oscript/bin/Debug/net6.0/oscript", + "runtimeArgs": [], + "debugPort": 2801 + } + ] +} \ No newline at end of file diff --git a/tests/BinaryData-global.os b/tests/BinaryData-global.os index ebc3de208..0de250614 100644 --- a/tests/BinaryData-global.os +++ b/tests/BinaryData-global.os @@ -19,8 +19,16 @@ ВсеТесты.Добавить("ТестДолжен_Проверить_ДвоичныеДанныеИзСтрокиВСтроку"); ВсеТесты.Добавить("ТестДолжен_Проверить_СоединитьДвоичныеДанные"); ВсеТесты.Добавить("ТестДолжен_Проверить_РазбитьДвоичныеДанные"); + ВсеТесты.Добавить("ТестДолжен_Проверить_ПреобразованиеHexСтрокиВДвоичныеДанныеИНаоборот"); ВсеТесты.Добавить("ТестДолжен_Проверить_ВыбросИсключенияПриНевалидномФорматеHexСтроки"); ВсеТесты.Добавить("ТестДолжен_Проверить_ПреобразованиеHexСтрокиВБуферДвоичныхДанныхИНаоборот"); + ВсеТесты.Добавить("ТестДолжен_Проверить_ПреобразованияВСтрокуBase64ИОбратно"); + ВсеТесты.Добавить("ТестДолжен_Проверить_ПреобразованияДвоичныхДанныхВРазныеФорматы"); + ВсеТесты.Добавить("ТестДолжен_Проверить_ПреобразованияБуфераДвоичныхДанныхВРазныеФорматы"); + ВсеТесты.Добавить("ТестДолжен_Проверить_ДобавлениеBOMДляКодировокСBOM"); + ВсеТесты.Добавить("ТестДолжен_Проверить_ОтсутствиеBOMДляКодировокБезBOM"); + ВсеТесты.Добавить("ТестДолжен_Проверить_КодировкиДляПолучитьСтрокуИзДвоичныхДанных"); + ВсеТесты.Добавить("ТестДолжен_Проверить_КодировкиДляПолучитьСтрокуИзБуфераДвоичныхДанных"); Возврат ВсеТесты; КонецФункции @@ -121,6 +129,35 @@ КонецПопытки; юТест.ПроверитьРавенство(Буфер, Неопределено); + Попытка + Буфер = ПолучитьБуферДвоичныхДанныхИзHexСтроки("A1B2C34"); + Исключение + Ошибка = КраткоеПредставлениеОшибки(ИнформацияОбОшибке()); + юТест.ПроверитьВхождение(Ошибка, "Неверный формат шестнадцатеричной строки"); + КонецПопытки; + юТест.ПроверитьРавенство(Буфер, Неопределено); + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_ПреобразованиеHexСтрокиВДвоичныеДанныеИНаоборот() Экспорт + юТест.ПроверитьРавенство( + ПолучитьHexСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки("a1b2c3")), + "A1B2C3"); + юТест.ПроверитьРавенство( + ПолучитьHexСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки("A1B2C3")), + "A1B2C3"); + юТест.ПроверитьРавенство( + ПолучитьHexСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки("A1 B2 C3")), + "A1B2C3"); + юТест.ПроверитьРавенство( + ПолучитьHexСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки("01020304")), + "01020304"); + юТест.ПроверитьРавенство( + ПолучитьHexСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки("01 02 03 04")), + "01020304"); + юТест.ПроверитьРавенство( + ПолучитьHexСтрокуИзДвоичныхДанных(ПолучитьДвоичныеДанныеИзHexСтроки("+01и02 --- 0ю3 0.4")), + "01020304"); КонецПроцедуры Процедура ТестДолжен_Проверить_ПреобразованиеHexСтрокиВБуферДвоичныхДанныхИНаоборот() Экспорт @@ -142,3 +179,295 @@ "01020304"); КонецПроцедуры + +Процедура ТестДолжен_Проверить_ПреобразованияВСтрокуBase64ИОбратно() Экспорт + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки("123"); + юТест.ПроверитьРавенство(ПолучитьBase64СтрокуИзДвоичныхДанных(ДвоичныеДанные), "MTIz", + "Неправильная Base64 строка из ДвоичныхДанных"); + + БуферДД = ПолучитьБуферДвоичныхДанныхИзСтроки("123"); + юТест.ПроверитьРавенство(ПолучитьBase64СтрокуИзБуфераДвоичныхДанных(БуферДД), "MTIz", + "Неправильная Base64 строка из БуфераДвоичныхДанных"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзBase64Строки("MTIz"); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные), "123", + "Неправильные ДвоичныеДанные из Base64 строки"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзBase64Строки("M T" "I z"); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные), "123", + "Неправильные ДвоичныеДанные из Base64 строки с пробелами"); + + БуферДД = ПолучитьБуферДвоичныхДанныхИзBase64Строки("MTIz"); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД), "123", + "Неправильный БуферДвоичныхДанных из Base64 строки"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзBase64Строки("MTIz=="); + юТест.ПроверитьРавенство(ДвоичныеДанные.Размер(), 0, + "Ненулевой размер ДвоичныхДанных из Base64 строки для некорректной строки"); + + БуферДД = ПолучитьБуферДвоичныхДанныхИзBase64Строки("MTI??z"); + юТест.ПроверитьРавенство(БуферДД.Размер, 0, + "Ненулевой размер БуфераДвоичныхДанных Из Base64 строки для некорректной строки"); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_ПреобразованияДвоичныхДанныхВРазныеФорматы() Экспорт + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки("123"); + + ДвоичныеДанные64 = ПолучитьBase64ДвоичныеДанныеИзДвоичныхДанных(ДвоичныеДанные); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные64), "MTIz", + "Неправильные Base64ДвоичныеДанные Из ДвоичныхДанных"); + + ДвоичныеДанные16 = ПолучитьHexДвоичныеДанныеИзДвоичныхДанных(ДвоичныеДанные); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные16), "313233", + "Неправильные HexДвоичныеДанные Из ДвоичныхДанных"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзBase64ДвоичныхДанных(ДвоичныеДанные64); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные), "123", + "Неправильные ДвоичныеДанные Из Base64ДвоичныхДанных"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexДвоичныхДанных(ДвоичныеДанные16); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные), "123", + "Неправильные ДвоичныеДанные Из HexДвоичныхДанных"); + + ДвоичныеДанные64 = ПолучитьДвоичныеДанныеИзСтроки("MTIz??"); + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзBase64ДвоичныхДанных(ДвоичныеДанные64); + юТест.ПроверитьРавенство(ДвоичныеДанные.Размер(), 0, + "Ненулевой размер ДвоичныеДанные Из Base64ДвоичныхДанных для некорректных данных"); + + ДвоичныеДанные16 = ПолучитьДвоичныеДанныеИзСтроки("+31и32 --- 33 3.4"); + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexДвоичныхДанных(ДвоичныеДанные16); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные), "1234", + "Неправильные ДвоичныеДанные Из HexДвоичныхДанных для данных с лишними символами"); + + ДвоичныеДанные16 = ПолучитьДвоичныеДанныеИзСтроки("3132353"); + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexДвоичныхДанных(ДвоичныеДанные16); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные), "125", + "Неправильные ДвоичныеДанные Из HexДвоичныхДанных для нечетной длины цифр"); + + ДвоичныеДанные16 = ПолучитьДвоичныеДанныеИзСтроки("(нет шестнадцатеричных)"); + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexДвоичныхДанных(ДвоичныеДанные16); + юТест.ПроверитьРавенство(ДвоичныеДанные.Размер(), 0, + "Ненулевой размер ДвоичныеДанные Из HexДвоичныхДанных для некорректных данных"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтроки("1234"); + БуферДД = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДвоичныеДанные); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД), "1234", + "Неправильный БуферДвоичныхДанных Из HexДвоичныхДанных"); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_ПреобразованияБуфераДвоичныхДанныхВРазныеФорматы() Экспорт + БуферДД = ПолучитьБуферДвоичныхДанныхИзСтроки("123"); + + БуферДД64 = ПолучитьBase64БуферДвоичныхДанныхИзБуфераДвоичныхДанных(БуферДД); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД64), "MTIz", + "Неправильный Base64БуферДвоичныхДанных Из БуфераДвоичныхДанных"); + + БуферДД16 = ПолучитьHexБуферДвоичныхДанныхИзБуфераДвоичныхДанных(БуферДД); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД16), "313233", + "Неправильный HexБуферДвоичныхДанных Из БуфераДвоичныхДанных"); + + БуферДД = ПолучитьБуферДвоичныхДанныхИзBase64БуфераДвоичныхДанных(БуферДД64); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД), "123", + "Неправильный БуферДвоичныхДанных Из Base64БуфераДвоичныхДанных"); + + БуферДД = ПолучитьБуферДвоичныхДанныхИзHexБуфераДвоичныхДанных(БуферДД16); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД), "123", + "Неправильный БуферДвоичныхДанных Из HexБуфераДвоичныхДанных"); + + БуферДД64 = ПолучитьБуферДвоичныхДанныхИзСтроки("MTIz??"); + БуферДД = ПолучитьБуферДвоичныхДанныхИзBase64БуфераДвоичныхДанных(БуферДД64); + юТест.ПроверитьРавенство(БуферДД.Размер, 0, + "Ненулевой размер БуферДвоичныхДанных Из Base64БуфераДвоичныхДанных для некорректных данных"); + + БуферДД16 = ПолучитьБуферДвоичныхДанныхИзСтроки("+31и32 --- 33 3.4"); + БуферДД = ПолучитьБуферДвоичныхДанныхИзHexБуфераДвоичныхДанных(БуферДД16); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД), "1234", + "Неправильный БуферДвоичныхДанных Из HexБуфераДвоичныхДанных для данных с лишними символами"); + + БуферДД16 = ПолучитьБуферДвоичныхДанныхИзСтроки("3132353"); + БуферДД = ПолучитьБуферДвоичныхДанныхИзHexБуфераДвоичныхДанных(БуферДД16); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(БуферДД), "125", + "Неправильный БуферДвоичныхДанных Из HexБуфераДвоичныхДанных для нечетной длины цифр"); + + БуферДД16 = ПолучитьБуферДвоичныхДанныхИзСтроки("(нет шестнадцатеричных)"); + БуферДД = ПолучитьБуферДвоичныхДанныхИзHexБуфераДвоичныхДанных(БуферДД16); + юТест.ПроверитьРавенство(БуферДД.Размер, 0, + "Ненулевой размер ДвоичныеДанные Из Base64ДвоичныхДанных для некорректных данных"); + + БуферДД = ПолучитьБуферДвоичныхДанныхИзСтроки("1234"); + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(БуферДД); + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные), "1234", + "Неправильные ДвоичныеДанные Из БуфераДвоичныхДанных"); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_ДобавлениеBOMДляКодировокСBOM() Экспорт + Строка = "123"; + + юТест.ПроверитьНеРавенство( + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.UTF8, Ложь), + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.UTF8, Истина) + ); + + юТест.ПроверитьНеРавенство( + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.UTF16, Ложь), + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.UTF16, Истина) + ); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_ОтсутствиеBOMДляКодировокБезBOM() Экспорт + Строка = "123"; + + юТест.ПроверитьРавенство( + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.ANSI, Ложь), + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.ANSI, Истина) + ); + + юТест.ПроверитьРавенство( + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.OEM, Ложь), + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.OEM, Истина) + ); + + юТест.ПроверитьРавенство( + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.UTF8NoBOM, Ложь), + ПолучитьДвоичныеДанныеИзСтроки(Строка, КодировкаТекста.UTF8NoBOM, Истина) + ); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_КодировкиДляПолучитьСтрокуИзДвоичныхДанных() Экспорт + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("EFBBBF2523"); + Кодировка="cp866"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "я╗┐%#", + "Неверная Строка из ДвоичныхДанных для "+Кодировка); + + Кодировка="windows-1251"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "п»ї%#", + "Неверная Строка из ДвоичныхДанных для "+Кодировка); + + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "%#", + "Неверная Строка из ДвоичныхДанных UTF-8 для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "%#", + "Неверная Строка из ДвоичныхДанных UTF-8 для "+Кодировка); + + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "%#", + "Неверная Строка из ДвоичныхДанных UTF-8 без Кодировки"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("FFFE2325"); + Кодировка="windows-1251"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "яю#%", + "Неверная Строка из ДвоичныхДанных для "+Кодировка); + + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + Кодировка="UTF-16BE"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + Кодировка="UTF-32"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 без Кодировки"); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("FF FE 00 00 23 00 00 00"); + + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "#", + "Неверная Строка из ДвоичныхДанных UTF-32 для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "#", + "Неверная Строка из ДвоичныхДанных UTF-32 для "+Кодировка); + + ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("2325"); + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "#%", + "Неверная Строка из ДвоичныхДанных без BOM для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных без BOMдля "+Кодировка); + + Кодировка="UTF-16BE"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзДвоичныхДанных(ДвоичныеДанные, Кодировка), "⌥", + "Неверная Строка из ДвоичныхДанных без BOM для "+Кодировка); +КонецПроцедуры + + +Процедура ТестДолжен_Проверить_КодировкиДляПолучитьСтрокуИзБуфераДвоичныхДанных() Экспорт + ДвоичныеДанные = ПолучитьБуферДвоичныхДанныхИзHexСтроки("EFBBBF2523"); + Кодировка="cp866"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "я╗┐%#", + "Неверная Строка из ДвоичныхДанных для "+Кодировка); + + Кодировка="windows-1251"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "п»ї%#", + "Неверная Строка из ДвоичныхДанных для "+Кодировка); + + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "%#", + "Неверная Строка из ДвоичныхДанных UTF-8 для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "%#", + "Неверная Строка из ДвоичныхДанных UTF-8 для "+Кодировка); + + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "%#", + "Неверная Строка из ДвоичныхДанных UTF-8 без Кодировки"); + + ДвоичныеДанные = ПолучитьБуферДвоичныхДанныхИзHexСтроки("FFFE2325"); + Кодировка="windows-1251"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "яю#%", + "Неверная Строка из ДвоичныхДанных для "+Кодировка); + + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + Кодировка="UTF-16BE"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + Кодировка="UTF-32"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 для "+Кодировка); + + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных UTF-16 без Кодировки"); + + ДвоичныеДанные = ПолучитьБуферДвоичныхДанныхИзHexСтроки("FF FE 00 00 23 00 00 00"); + + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "#", + "Неверная Строка из ДвоичныхДанных UTF-32 для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "#", + "Неверная Строка из ДвоичныхДанных UTF-32 для "+Кодировка); + + ДвоичныеДанные = ПолучитьБуферДвоичныхДанныхИзHexСтроки("2325"); + Кодировка="UTF-8"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "#%", + "Неверная Строка из ДвоичныхДанных без BOM для "+Кодировка); + + Кодировка="UTF-16"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "┣", + "Неверная Строка из ДвоичныхДанных без BOMдля "+Кодировка); + + Кодировка="UTF-16BE"; + юТест.ПроверитьРавенство(ПолучитьСтрокуИзБуфераДвоичныхДанных(ДвоичныеДанные, Кодировка), "⌥", + "Неверная Строка из ДвоичныхДанных без BOM для "+Кодировка); +КонецПроцедуры diff --git a/tests/ValueTableIndex.os b/tests/ValueTableIndex.os new file mode 100644 index 000000000..8305f5862 --- /dev/null +++ b/tests/ValueTableIndex.os @@ -0,0 +1,289 @@ +/////////////////////////////////////////////////////////////////////// +// +// Тест проверки работы таблицы значений +// +// +/////////////////////////////////////////////////////////////////////// + +Перем юТест; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция Версия() Экспорт + Возврат "0.2"; +КонецФункции + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_СоздатьТаблицуЗначенийСИндексами"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСтроковоеПредставлениеИндекса"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоискСИндексом"); + ВсеТесты.Добавить("ТестДолжен_ПоведениеИндексовПриУдаленииКолонок"); + ВсеТесты.Добавить("ТестДолжен_ПоведениеИндексовПриИзмененииЗначенийИндексированныхПолей"); + ВсеТесты.Добавить("ТестДолжен_ПоведениеИндексовПриИзмененииЗначенийИндексированныхПолейПоИндексу"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВыбросИсключений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоискПоИндексированнойКолонкеСЗаданнымТипом"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_СоздатьТаблицуЗначенийСИндексами() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Колонки.Добавить("К3"); + + И1 = Т.Индексы.Добавить("К1"); + И2 = Т.Индексы.Добавить("К1,К2"); + И3 = Т.Индексы.Добавить("К1,К2,К3"); + + юТест.ПроверитьРавенство(Т.Индексы.Количество(), 3); + + Т.Индексы.Удалить(Т.Индексы[0]); + юТест.ПроверитьРавенство(Т.Индексы.Количество(), 2); + + Т.Колонки.К1.Имя = "К_1"; + юТест.ПроверитьРавенство(Т.Индексы[0][0], "К_1"); + + Для Каждого мИндекс Из Т.Индексы Цикл + + Для Каждого мИмяКолонки Из мИндекс Цикл + + Колонка = Т.Колонки.Найти(мИмяКолонки); + юТест.ПроверитьНеравенство(Колонка, Неопределено, "Не найдена колонка " + мИмяКолонки); + + КонецЦикла; + + КонецЦикла; + + Т.Индексы.Очистить(); + юТест.ПроверитьРавенство(Т.Индексы.Количество(), 0); + + ИндексСОдинаковымиПолями = Т.Индексы.Добавить("К2, К2, К2"); + юТест.ПроверитьРавенство(ИндексСОдинаковымиПолями[0], "К2"); + юТест.ПроверитьРавенство(ИндексСОдинаковымиПолями[1], "К2"); + юТест.ПроверитьРавенство(ИндексСОдинаковымиПолями[2], "К2"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСтроковоеПредставлениеИндекса() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + + И1 = Т.Индексы.Добавить("К1"); + И2 = Т.Индексы.Добавить("К1,К2"); + И3 = Т.Индексы.Добавить("К1,К2,К2"); + И4 = Т.Индексы.Добавить("К2,К1,К2"); + + юТест.ПроверитьРавенство(Строка(И1), "К1"); + юТест.ПроверитьРавенство(Строка(И2), "К1, К2"); + юТест.ПроверитьРавенство(Строка(И3), "К1, К2, К2"); + юТест.ПроверитьРавенство(Строка(И4), "К2, К1, К2"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоискСИндексом() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Индексы.Добавить("К1"); + Т.Индексы.Добавить("К2,К1"); + + НекоеБольшоеЧисло = 1000; // не такое уж большое, чтобы тесты проходили быстрее + Для Инд = 1 По НекоеБольшоеЧисло Цикл + + НоваяСтрока = Т.Добавить(); + НоваяСтрока.К1 = НекоеБольшоеЧисло; + НоваяСтрока.К2 = Инд; + НоваяСтрока.К1 = Инд; + + КонецЦикла; + + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Поиск по индексированному полю"); + + Т.Колонки.К1.Имя = "ДругоеИмя"; + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Переименовали проиндексированную колонку"); + юТест.ПроверитьРавенство(Т.Индексы[0][0], "ДругоеИмя"); + + Т.Колонки.ДругоеИмя.Имя = "К1"; + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Вернули имя"); + + Т.Свернуть("К2, К1"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 0 раз"); + + Т.Свернуть("К2", "К1"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 1 раз"); + + Т.Свернуть("К1", "К2"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 2 раз"); + + Т.Свернуть("К1"); + НайтиИПроверить(Т, НекоеБольшоеЧисло, "Свернули 3 раз"); + +КонецПроцедуры + +Процедура ТестДолжен_ПоведениеИндексовПриУдаленииКолонок() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Добавить().К1 = 1; + Т.Добавить().К1 = 2; + Т.Добавить().К1 = 3; + + Т.Индексы.Добавить("К1"); + юТест.ПроверитьРавенство(Строка(Т.Индексы[0]), "К1"); + + Т.Колонки.Очистить(); + юТест.ПроверитьРавенство(Строка(Т.Индексы[0]), ""); // Индекс не удаляется, состав полей пустой + + Т.Колонки.Добавить("К1"); + Т[0].К1 = 3; + Т[1].К1 = 2; + Т[2].К1 = 1; + + СтруктураПоиска = Новый Структура("К1", 1); + НайденныеСтроки = Т.НайтиСтроки(СтруктураПоиска); + юТест.ПроверитьРавенство(НайденныеСтроки.Количество(), 1); + юТест.ПроверитьРавенство(НайденныеСтроки[0], Т[2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПоведениеИндексовПриИзмененииЗначенийИндексированныхПолей() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Индексы.Добавить("К2"); + Т.Индексы.Добавить("К2, К1"); + Т.Индексы.Добавить("К1"); + Т.Добавить().К1 = 1; + Т.Добавить().К1 = 2; + Т.Добавить().К1 = 3; + + Т[0].К1 = 3; + Т[1].К1 = 2; + Т[2].К1 = 1; + + СтруктураПоиска = Новый Структура("К1", 1); + НайденныеСтроки = Т.НайтиСтроки(СтруктураПоиска); + юТест.ПроверитьРавенство(НайденныеСтроки.Количество(), 1); + юТест.ПроверитьРавенство(НайденныеСтроки[0], Т[2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПоведениеИндексовПриИзмененииЗначенийИндексированныхПолейПоИндексу() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Индексы.Добавить("К2"); + Т.Индексы.Добавить("К2, К1"); + Т.Индексы.Добавить("К1"); + Т.Добавить().К1 = 1; + Т.Добавить().К1 = 2; + Т.Добавить().К1 = 3; + + Т[0]["К1"] = 3; + Т[1]["К1"] = 2; + Т[2]["К1"] = 1; + + СтруктураПоиска = Новый Структура("К1", 1); + НайденныеСтроки = Т.НайтиСтроки(СтруктураПоиска); + юТест.ПроверитьРавенство(НайденныеСтроки.Количество(), 1); + юТест.ПроверитьРавенство(НайденныеСтроки[0], Т[2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВыбросИсключений() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1"); + Т.Колонки.Добавить("К2"); + Т.Индексы.Добавить("К2"); + Т.Индексы.Добавить("К2, К1"); + Т.Индексы.Добавить("К1"); + + БылоИсключение = Ложь; + Попытка + Индекс = Т.Индексы[4]; + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Доступ за пределы диапазона"); + + БылоИсключение = Ложь; + Попытка + Индекс = Т.Индексы[-1]; + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Доступ за пределы диапазона"); + + Индекс = Т.Индексы[0]; + Т.Индексы.Удалить(Индекс); + БылоИсключение = Ложь; + Попытка + Т.Индексы.Удалить(Индекс); + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Удаление несуществующего индекса"); + + БылоИсключение = Ложь; + Попытка + Т.Индексы[0] = Индекс; + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Запись по индексу"); + + БылоИсключение = Ложь; + Попытка + Т.Индексы.Добавить("НесуществующееПоле"); + Исключение + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Индекс по несуществующему полю"); + +КонецПроцедуры + +Процедура НайтиИПроверить(Знач Таблица, Знач ИскомоеЗначение, Знач Пояснение) + + // в проверяемом тесте индексированное поле всегда первое + ИндексированноеПоле = Таблица.Колонки[0].Имя; + + Отбор = Новый Структура(ИндексированноеПоле, ИскомоеЗначение); + ПоискПоИндексу = Таблица.НайтиСтроки(Отбор); + + юТест.ПроверитьРавенство(ПоискПоИндексу.Количество(), 1, Пояснение); + юТест.ПроверитьРавенство(ПоискПоИндексу[0][ИндексированноеПоле], ИскомоеЗначение, Пояснение); + +КонецПроцедуры + +// к issue #1573 +Процедура ТестДолжен_ПроверитьПоискПоИндексированнойКолонкеСЗаданнымТипом() Экспорт + + Т = Новый ТаблицаЗначений; + Т.Колонки.Добавить("К1",Новый ОписаниеТипов("Строка")); + Т.Индексы.Добавить("К1"); + + Т.Добавить().К1 = "Стр1"; + Т.Добавить().К1 = "Стр2"; + Т.Добавить().К1 = "Стр3"; + + + СтруктураПоиска = Новый Структура("К1", "Стр2"); + НайденныеСтроки = Т.НайтиСтроки(СтруктураПоиска); + юТест.ПроверитьРавенство(НайденныеСтроки.Количество(), 1); + юТест.ПроверитьРавенство(НайденныеСтроки[0].К1, "Стр2"); + +КонецПроцедуры diff --git a/tests/XMLSchema/Enumerations/test-XSDisallowedSubstitutions.os b/tests/XMLSchema/Enumerations/test-XSDisallowedSubstitutions.os index 373f63504..b5eae9a33 100644 --- a/tests/XMLSchema/Enumerations/test-XSDisallowedSubstitutions.os +++ b/tests/XMLSchema/Enumerations/test-XSDisallowedSubstitutions.os @@ -34,7 +34,7 @@ Процедура TestConstructor() Экспорт - ЮнитТест.ПроверитьРавенство(TypeOf(XSDisallowedSubstitutions), Type("EnumerationXSDisallowedSubstitutions")); + ЮнитТест.ПроверитьРавенство(TypeOf(XSDisallowedSubstitutions), Type("EnumXSDisallowedSubstitutions")); ЮнитТест.ПроверитьРавенство(TypeOf(XSDisallowedSubstitutions.All), Type("XSDisallowedSubstitutions")); ЮнитТест.ПроверитьРавенство(TypeOf(XSDisallowedSubstitutions.Restriction), Type("XSDisallowedSubstitutions")); ЮнитТест.ПроверитьРавенство(TypeOf(XSDisallowedSubstitutions.Substitution), Type("XSDisallowedSubstitutions")); diff --git a/tests/XMLSchema/Enumerations/test-XSForm.os b/tests/XMLSchema/Enumerations/test-XSForm.os index fe902db01..7576ee43f 100644 --- a/tests/XMLSchema/Enumerations/test-XSForm.os +++ b/tests/XMLSchema/Enumerations/test-XSForm.os @@ -32,7 +32,7 @@ Процедура TestConstructor() Экспорт - ЮнитТест.ПроверитьРавенство(TypeOf(XSForm), Type("EnumerationXSForm")); + ЮнитТест.ПроверитьРавенство(TypeOf(XSForm), Type("EnumXSForm")); ЮнитТест.ПроверитьРавенство(TypeOf(XSForm.Qualified), Type("XSForm")); ЮнитТест.ПроверитьРавенство(TypeOf(XSForm.Unqualified), Type("XSForm")); diff --git a/tests/XMLSchema/Enumerations/test-XSSchemaFinal.os b/tests/XMLSchema/Enumerations/test-XSSchemaFinal.os index 9c980934f..89b3557ca 100644 --- a/tests/XMLSchema/Enumerations/test-XSSchemaFinal.os +++ b/tests/XMLSchema/Enumerations/test-XSSchemaFinal.os @@ -35,7 +35,7 @@ Процедура TestConstructor() Экспорт - ЮнитТест.ПроверитьРавенство(TypeOf(XSSchemaFinal), Type("EnumerationXSSchemaFinal")); + ЮнитТест.ПроверитьРавенство(TypeOf(XSSchemaFinal), Type("EnumXSSchemaFinal")); ЮнитТест.ПроверитьРавенство(TypeOf(XSSchemaFinal.All), Type("XSSchemaFinal")); ЮнитТест.ПроверитьРавенство(TypeOf(XSSchemaFinal.Union), Type("XSSchemaFinal")); ЮнитТест.ПроверитьРавенство(TypeOf(XSSchemaFinal.Restriction), Type("XSSchemaFinal")); diff --git a/tests/XMLSchema/Enumerations/test-XSSimpleFinal.os b/tests/XMLSchema/Enumerations/test-XSSimpleFinal.os index ff9ee3ca4..6a7a3d305 100644 --- a/tests/XMLSchema/Enumerations/test-XSSimpleFinal.os +++ b/tests/XMLSchema/Enumerations/test-XSSimpleFinal.os @@ -34,7 +34,7 @@ Процедура TestConstructor() Экспорт - ЮнитТест.ПроверитьРавенство(TypeOf(XSSimpleFinal), Type("EnumerationXSSimpleFinal")); + ЮнитТест.ПроверитьРавенство(TypeOf(XSSimpleFinal), Type("EnumXSSimpleFinal")); ЮнитТест.ПроверитьРавенство(TypeOf(XSSimpleFinal.All), Type("XSSimpleFinal")); ЮнитТест.ПроверитьРавенство(TypeOf(XSSimpleFinal.Union), Type("XSSimpleFinal")); ЮнитТест.ПроверитьРавенство(TypeOf(XSSimpleFinal.Restriction), Type("XSSimpleFinal")); diff --git a/tests/annotations.os b/tests/annotations.os index c5ef73365..b18a75c20 100644 --- a/tests/annotations.os +++ b/tests/annotations.os @@ -11,19 +11,49 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеАннотацийМетода"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеАннотацийПараметров"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотацииПолейЗагрузитьСценарий"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотацииПолейЗагрузитьСценарийИзСтроки"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьАннотациюКакЗначениеПараметраАннотации"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеАннотацийСпискаПеременных"); Возврат ВсеТесты; КонецФункции +&Аннотация(Параметр = &ТожеАннотация(&СТожеПараметромАннотацией, П2 = &СТожеПараметромАннотацией)) +Процедура ТестДолжен_ПроверитьАннотациюКакЗначениеПараметраАннотации() Экспорт + + Рефлектор = Новый Рефлектор; + ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(ЭтотОбъект); + + юТест.ПроверитьНеРавенство(ТаблицаМетодов.Колонки.Найти("Аннотации"), Неопределено, "Есть колонка Аннотации"); + + СтрокаМетода = ТаблицаМетодов.Найти("ТестДолжен_ПроверитьАннотациюКакЗначениеПараметраАннотации", "Имя"); + ПерваяАннотация = СтрокаМетода.Аннотации[0]; + ПервыйПараметрПервойАннотации = ПерваяАннотация.Параметры[0]; + + юТест.ПроверитьТип(ПервыйПараметрПервойАннотации.Значение, Тип("СтрокаТаблицыЗначений")); + + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Имя, "ТожеАннотация"); + юТест.ПроверитьТип(ПервыйПараметрПервойАннотации.Значение.Параметры, Тип("ТаблицаЗначений")); + + юТест.ПроверитьТип(ПервыйПараметрПервойАннотации.Значение.Параметры[0].Значение, Тип("СтрокаТаблицыЗначений")); + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Параметры[0].Значение.Имя, "СТожеПараметромАннотацией"); + + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Параметры[1].Имя, "П2"); + юТест.ПроверитьРавенство(ПервыйПараметрПервойАннотации.Значение.Параметры[1].Значение.Имя, "СТожеПараметромАннотацией"); + +КонецПроцедуры + Процедура САннотированнымиПараметрами( &АннотацияДляПараметра Знач Парам1, - &АннотацияДляПараметра + &АннотацияДляПараметра() &АннотацияДляПараметра1 - &АннотацияДляПараметра2(СПараметрами = 3, 4, 5) + &АннотацияДляПараметра2(СПараметрами = 3, 4, -5) Знач Парам2, Парам3, @@ -36,6 +66,7 @@ &НаКлиентеНаСервереБезКонтекста &НаЧемУгодно(ДажеСПараметром = "Да", СПараметромБезЗначения, "Значение без параметра") &НаЧемУгодно(ДажеДважды = Истина) +&НаЧемУгодно() Процедура ТестДолжен_ПроверитьПолучениеАннотацийМетода() Экспорт Рефлектор = Новый Рефлектор; @@ -47,12 +78,13 @@ юТест.ПроверитьНеРавенство(СтрокаМетода, Неопределено, "Метод с аннотациями есть в таблице рефлектора"); юТест.ПроверитьНеРавенство(СтрокаМетода.Аннотации, Неопределено, "Рефлектор знает про аннотации метода"); - юТест.ПроверитьРавенство(СтрокаМетода.Аннотации.Количество(), 4, "Рефлектор вернул верное количество аннотаций"); + юТест.ПроверитьРавенство(СтрокаМетода.Аннотации.Количество(), 5, "Рефлектор вернул верное количество аннотаций"); юТест.ПроверитьРавенство(СтрокаМетода.Аннотации[0].Имя, "НаСервере", "Рефлектор сохранил порядок указания аннотаций"); юТест.ПроверитьРавенство(СтрокаМетода.Аннотации[1].Имя, "НаКлиентеНаСервереБезКонтекста", "Рефлектор сохранил порядок указания аннотаций"); юТест.ПроверитьРавенство(СтрокаМетода.Аннотации[2].Имя, "НаЧемУгодно", "Рефлектор сохранил порядок указания аннотаций"); юТест.ПроверитьРавенство(СтрокаМетода.Аннотации[3].Имя, "НаЧемУгодно", "Рефлектор сохранил порядок указания аннотаций"); + юТест.ПроверитьРавенство(СтрокаМетода.Аннотации[4].Имя, "НаЧемУгодно", "Рефлектор сохранил порядок указания аннотаций"); Аннотация2 = СтрокаМетода.Аннотации[2]; юТест.ПроверитьНеРавенство(Аннотация2.Параметры, Неопределено, "Есть таблица параметров аннотации"); @@ -87,8 +119,136 @@ юТест.ПроверитьРавенство(Парам2.Аннотации.Получить(2).Параметры.Количество(), 3, "Параметры аннотации параметров"); юТест.ПроверитьРавенство(Парам2.Аннотации.Получить(2).Параметры[0].Значение, 3, "Значения параметров аннотации параметров"); юТест.ПроверитьРавенство(Парам2.Аннотации.Получить(2).Параметры[1].Значение, 4, "Значения параметров аннотации параметров"); - юТест.ПроверитьРавенство(Парам2.Аннотации.Получить(2).Параметры[2].Значение, 5, "Значения параметров аннотации параметров"); + юТест.ПроверитьРавенство(Парам2.Аннотации.Получить(2).Параметры[2].Значение, -5, "Значения параметров аннотации параметров"); юТест.ПроверитьРавенство(Парам3.Аннотации.Количество(), 0); юТест.ПроверитьРавенство(Парам4.Аннотации.Количество(), 0); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьАннотацииПолейЗагрузитьСценарий() Экспорт + + Файл = ПолучитьИмяВременногоФайла(".os"); + + Запись = Новый ЗаписьТекста(Файл); + Запись.Записать(ТекстСценария()); + Запись.Закрыть(); + + Сценарий = ЗагрузитьСценарий(Файл); + + УдалитьФайлы(Файл); + + ПроверитьАннотацииПоляСценария(Сценарий); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьАннотацииПолейЗагрузитьСценарийИзСтроки() Экспорт + + Сценарий = ЗагрузитьСценарийИзСтроки(ТекстСценария()); + + ПроверитьАннотацииПоляСценария(Сценарий); + +КонецПроцедуры + +Функция ТекстСценария() + + Возврат + "&АннотацияБезПараметра + |&АннотацияСПараметром(""Значение"") + |&АннотацияСИменованнымПараметром(ИмяПараметра = ""Значение"") + |Перем Поле Экспорт;"; + +КонецФункции + +Процедура ПроверитьАннотацииПоляСценария(Сценарий) + + Рефлектор = Новый Рефлектор(); + + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Сценарий); + + Если ТаблицаСвойств.Количество() <> 1 Тогда + ВызватьИсключение "Ожидали, что в таблице свойств будет одно свойство а это не так"; + КонецЕсли; + + КоличествоАннотаций = ТаблицаСвойств[0].Аннотации.Количество(); + + Если КоличествоАннотаций <> 3 Тогда + ВызватьИсключение "Ожидали, что в таблице аннотаций свойства будет 3 аннотации а их там " + КоличествоАннотаций; + КонецЕсли; + + ИмяАннотации = ТаблицаСвойств[0].Аннотации[0].Имя; + + Если ИмяАннотации <> "АннотацияБезПараметра" Тогда + ВызватьИсключение "Ожидали, что первой аннотацией свойства будет АннотацияБезПараметра а там " + ИмяАннотации; + КонецЕсли; + + ИмяАннотации = ТаблицаСвойств[0].Аннотации[1].Имя; + + Если ИмяАннотации <> "АннотацияСПараметром" Тогда + ВызватьИсключение "Ожидали, что второй аннотацией свойства будет АннотацияСПараметром а там " + ИмяАннотации; + КонецЕсли; + + КоличествоПараметров = ТаблицаСвойств[0].Аннотации[1].Параметры.Количество(); + + Если КоличествоПараметров <> 1 Тогда + ВызватьИсключение "Ожидали, что количество параметров второй аннотации будет равно 1 а их там " + КоличествоПараметров; + КонецЕсли; + + ПараметрАннотации = ТаблицаСвойств[0].Аннотации[1].Параметры[0]; + + Если ПараметрАннотации.Имя <> Неопределено Или ПараметрАннотации.Значение <> "Значение" Тогда + + ВызватьИсключение + "Ожидали, что у параметра второй аннотации будет имя Неопределено и строка Значение в поле значение, а там: + | Имя = " + ПараметрАннотации.Имя + " Значение = " + ПараметрАннотации.Значение; + + КонецЕсли; + + ИмяАннотации = ТаблицаСвойств[0].Аннотации[2].Имя; + + Если ИмяАннотации <> "АннотацияСИменованнымПараметром" Тогда + ВызватьИсключение "Ожидали, что третьей аннотацией свойства будет АннотацияСИменованнымПараметром а там " + ИмяАннотации; + КонецЕсли; + + КоличествоПараметров = ТаблицаСвойств[0].Аннотации[2].Параметры.Количество(); + + Если КоличествоПараметров <> 1 Тогда + ВызватьИсключение "Ожидали, что количество параметров третьей аннотации будет равно 1 а их там " + КоличествоПараметров; + КонецЕсли; + + ПараметрАннотации = ТаблицаСвойств[0].Аннотации[2].Параметры[0]; + + Если ПараметрАннотации.Имя <> "ИмяПараметра" Или ПараметрАннотации.Значение <> "Значение" Тогда + + ВызватьИсключение + "Ожидали, что у параметра третьей аннотации будет имя ИмяПараметра и строка Значение в поле значение, а там: + | Имя = " + ПараметрАннотации.Имя + " Значение = " + ПараметрАннотации.Значение; + + КонецЕсли; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПолучениеАннотацийСпискаПеременных() Экспорт + + ТекстСценария = "&Аннотация1 + |Перем Поле1 Экспорт, Поле2; + |Перем Поле3; + |"; + + Сценарий = ЗагрузитьСценарийИзСтроки(ТекстСценария); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Сценарий, Истина); + + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 3, "Ожидали, что в таблице свойств будет 3 свойства"); + + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Аннотации.Количество(), 1, "Ожидали, что Поле1 имеет 1 аннотацию"); + ИмяАннотации = ТаблицаСвойств[0].Аннотации[0].Имя; + юТест.ПроверитьРавенство(ИмяАннотации, "Аннотация1", "Ожидали, что Поле1 имеет аннотацию Аннотация1"); + + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Аннотации.Количество(), 1, "Ожидали, что Поле2 имеет 1 аннотацию"); + ИмяАннотации = ТаблицаСвойств[1].Аннотации[0].Имя; + юТест.ПроверитьРавенство(ИмяАннотации, "Аннотация1", "Ожидали, что Поле2 имеет аннотацию Аннотация1"); + + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Аннотации.Количество(), 0, "Ожидали, что Поле3 не имеет аннотаций"); + +КонецПроцедуры diff --git a/tests/binary-objects.os b/tests/binary-objects.os index 130fc076a..f5db51764 100644 --- a/tests/binary-objects.os +++ b/tests/binary-objects.os @@ -11,6 +11,7 @@ ВсеТесты = Новый Массив; ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСоздаетсяБуферДвоичныхДанных"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхНесколькимиБуферами"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноЗаписатьБайты"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноПрочитатьБайты"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМожноЗаписатьПрочитатьЦелое16"); @@ -28,8 +29,13 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоПриЗаписиВБуферСНевернымИндексомВыбрасываетсяИсключение"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоКорректноРаботаютПобитовыеОперацииЗаписиВБуферДвоичныхДанных"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМетодОткрытьПотокДляЧтенияВозвращаетПотокТолькоДляЧтения"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоПолучитьПотокТолькоДляЧтенияЗапрещаетЗапись"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоПолучитьПотокТолькоДляЧтенияРазделяетДанныеИПозицию"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеРаботаетСПотоком"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоХешированиеРаботаетСДвоичнымиДанными"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоПрочитатьБайтНеВыбрасываетИсключениеВКонцеПотока"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхОднимБуфером"); Возврат ВсеТесты; @@ -158,12 +164,34 @@ Процедура ТестДолжен_ПроверитьЧтоСоздаетсяФайловыйПоток() Экспорт - Поток = ФайловыеПотоки.Открыть(ТекущийСценарий().Источник, РежимОткрытияФайла.Открыть); + // Режим открытия + Поток = ФайловыеПотоки.Открыть(РежимОткрытияФайла.Открыть, ТекущийСценарий().Источник); Поток.Закрыть(); Поток = Новый ФайловыйПоток(ТекущийСценарий().Источник, РежимОткрытияФайла.Открыть); Поток.Закрыть(); + // Режим доступа и режим открытия + Поток = ФайловыеПотоки.Открыть(ДоступКФайлу.Чтение, ТекущийСценарий().Источник, РежимОткрытияФайла.Открыть); + Поток.Закрыть(); + + Поток = Новый ФайловыйПоток(ТекущийСценарий().Источник, РежимОткрытияФайла.Открыть, ДоступКФайлу.Чтение); + Поток.Закрыть(); + + // Режим открытия и размер буфера + Поток = ФайловыеПотоки.Открыть(РежимОткрытияФайла.Открыть, ТекущийСценарий().Источник, 4096); + Поток.Закрыть(); + + Поток = Новый ФайловыйПоток(ТекущийСценарий().Источник, РежимОткрытияФайла.Открыть, 4096); + Поток.Закрыть(); + + // Режим доступа, режим открытия и размер буфера + Поток = ФайловыеПотоки.Открыть(ДоступКФайлу.Чтение, ТекущийСценарий().Источник, РежимОткрытияФайла.Открыть, 4096); + Поток.Закрыть(); + + Поток = Новый ФайловыйПоток(ТекущийСценарий().Источник, РежимОткрытияФайла.Открыть, ДоступКФайлу.Чтение, 4096); + Поток.Закрыть(); + КонецПроцедуры Процедура ТестДолжен_ПроверитьЧтоВозможнаЗаписьВФайл() Экспорт @@ -251,7 +279,7 @@ Буфер = ПолучитьБуферДвоичныхДанныхИзHexСтроки("01 02 03"); Буфер.ЗаписатьПобитовоеИсключительноеИли(-1, ВторойБуфер, 3); Исключение - юТест.ПроверитьВхождение(ОписаниеОшибки(), "Значение индекса выходит за границы диапазона"); + юТест.ПроверитьВхождение(ОписаниеОшибки(), "Значение индекса выходит за пределы диапазона"); КонецПопытки; КонецПроцедуры @@ -299,6 +327,112 @@ КонецПроцедуры +Процедура ТестДолжен_ПроверитьЧтоПолучитьПотокТолькоДляЧтенияЗапрещаетЗапись() Экспорт + + Поток = Новый ПотокВПамяти(); + Буфер = Новый БуферДвоичныхДанных(10); + Поток.Записать(Буфер, 0, 10); // Запишем какие-то данные + + // Получим поток только для чтения + ПотокДляЧтения = Поток.ПолучитьПотокТолькоДляЧтения(); + + // Проверим, что поток помечен как read-only + юТест.ПроверитьЛожь(ПотокДляЧтения.ДоступнаЗапись); + + // Попытаемся записать в поток только для чтения - должно выбросить исключение + Попытка + ПотокДляЧтения.Записать(Новый БуферДвоичныхДанных(10), 0, 10); + ВызватьИсключение "Ожидали исключение при записи в поток только для чтения, но его не было"; + Исключение + ОписаниеОшибки = ИнформацияОбОшибке().Описание; + юТест.ПроверитьВхождение(ОписаниеОшибки, "Попытка записи в поток не поддерживающий запись"); + КонецПопытки; + + // Также проверим, что изменение размера тоже запрещено + Попытка + ПотокДляЧтения.УстановитьРазмер(20); + ВызватьИсключение "Ожидали исключение при изменении размера потока только для чтения, но его не было"; + Исключение + ОписаниеОшибки = ИнформацияОбОшибке().Описание; + юТест.ПроверитьВхождение(ОписаниеОшибки, "Попытка записи в поток не поддерживающий запись"); + КонецПопытки; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоПолучитьПотокТолькоДляЧтенияРазделяетДанныеИПозицию() Экспорт + + // Создаем поток с данными + Поток = Новый ПотокВПамяти(); + Буфер = Новый БуферДвоичныхДанных(20); + + // Заполним буфер данными + Для Индекс = 0 По 19 Цикл + Буфер.Установить(Индекс, Индекс + 1); + КонецЦикла; + + Поток.Записать(Буфер, 0, 20); + юТест.ПроверитьРавенство(20, Поток.Размер()); + юТест.ПроверитьРавенство(20, Поток.ТекущаяПозиция()); + + // Сбросим позицию на начало + Поток.Перейти(0, ПозицияВПотоке.Начало); + юТест.ПроверитьРавенство(0, Поток.ТекущаяПозиция()); + + // Получим поток только для чтения + ПотокДляЧтения = Поток.ПолучитьПотокТолькоДляЧтения(); + + // Проверим, что начальная позиция общая + юТест.ПроверитьРавенство(0, ПотокДляЧтения.ТекущаяПозиция()); + + // Прочитаем из основного потока 5 байт - позиция должна сдвинуться в обоих потоках + БуферЧтения = Новый БуферДвоичныхДанных(5); + Поток.Прочитать(БуферЧтения, 0, 5); + + // Проверим, что позиция изменилась в обоих потоках + юТест.ПроверитьРавенство(5, Поток.ТекущаяПозиция()); + юТест.ПроверитьРавенство(5, ПотокДляЧтения.ТекущаяПозиция()); + + // Прочитаем из потока только для чтения еще 3 байта + БуферЧтения2 = Новый БуферДвоичныхДанных(3); + ПотокДляЧтения.Прочитать(БуферЧтения2, 0, 3); + + // Проверим, что позиция изменилась в обоих потоках + юТест.ПроверитьРавенство(8, Поток.ТекущаяПозиция()); + юТест.ПроверитьРавенство(8, ПотокДляЧтения.ТекущаяПозиция()); + + // Проверим, что данные одинаковые - прочитали байты с 6-го по 8-й (значения 6, 7, 8) + юТест.ПроверитьРавенство(6, БуферЧтения2.Получить(0)); + юТест.ПроверитьРавенство(7, БуферЧтения2.Получить(1)); + юТест.ПроверитьРавенство(8, БуферЧтения2.Получить(2)); + + // Изменим позицию в потоке только для чтения + ПотокДляЧтения.Перейти(15, ПозицияВПотоке.Начало); + + // Проверим, что позиция изменилась в обоих потоках + юТест.ПроверитьРавенство(15, Поток.ТекущаяПозиция()); + юТест.ПроверитьРавенство(15, ПотокДляЧтения.ТекущаяПозиция()); + + // Запишем данные в основной поток (в позицию 15) + БуферЗаписи = Новый БуферДвоичныхДанных(2); + БуферЗаписи.Установить(0, 100); + БуферЗаписи.Установить(1, 200); + Поток.Записать(БуферЗаписи, 0, 2); + + // Позиция должна сдвинуться в обоих потоках + юТест.ПроверитьРавенство(17, Поток.ТекущаяПозиция()); + юТест.ПроверитьРавенство(17, ПотокДляЧтения.ТекущаяПозиция()); + + // Вернемся к позиции 15 и прочитаем из потока только для чтения + ПотокДляЧтения.Перейти(15, ПозицияВПотоке.Начало); + БуферЧтения3 = Новый БуферДвоичныхДанных(2); + ПотокДляЧтения.Прочитать(БуферЧтения3, 0, 2); + + // Проверим, что мы прочитали измененные данные - данные общие между потоками + юТест.ПроверитьРавенство(100, БуферЧтения3.Получить(0)); + юТест.ПроверитьРавенство(200, БуферЧтения3.Получить(1)); + +КонецПроцедуры + Процедура ТестДолжен_ПроверитьЧтоХешированиеРаботаетСПотоком() Экспорт ДвоичныеДанные = ПолучитьДвоичныеДанныеИзHexСтроки("1b1e c416 6a11 c03b 3afe faea 442e 7709"); @@ -351,3 +485,78 @@ НРег(ПолучитьHexСтрокуИзДвоичныхДанных(Хеширование.ХешСумма))); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхОднимБуфером() Экспорт + + Подстроки = Новый Массив; + Подстроки.Добавить("Часть1"); + Подстроки.Добавить("Часть2"); + Подстроки.Добавить("Часть3"); + РазделительТекстом = "123"; + + ТестовыйБуфер = ПолучитьБуферДвоичныхДанныхИзСтроки(СтрСоединить(Подстроки, РазделительТекстом)); + Разделитель = ПолучитьБуферДвоичныхДанныхИзСтроки(РазделительТекстом); + + РазделенныеДанные = ТестовыйБуфер.Разделить(Разделитель); + + юТест.ПроверитьРавенство(РазделенныеДанные.Количество(), 3, "два разделителя - три элемента"); + + Для Инд = 0 По Подстроки.ВГраница() Цикл + + СтрокаИзБуфера = ПолучитьСтрокуИзБуфераДвоичныхДанных(РазделенныеДанные[Инд]); + юТест.ПроверитьРавенство(СтрокаИзБуфера, Подстроки[Инд], "сравнение строк после разделения буфера"); + + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазделениеБуфераДвоичныхДанныхНесколькимиБуферами() Экспорт + + Подстроки = Новый Массив; + Подстроки.Добавить("Часть1"); + Подстроки.Добавить("Часть2"); + Подстроки.Добавить("Часть3"); + Подстроки.Добавить("Часть4"); + Подстроки.Добавить("Часть5"); + + ТестоваяСтрока = "Часть1\R\R\NЧасть2\R\NЧасть3\NЧасть4\RЧасть5"; + + ТестовыйБуфер = ПолучитьБуферДвоичныхДанныхИзСтроки(ТестоваяСтрока); + Разделители = Новый Массив; + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\N")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R\N")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R")); + Разделители.Добавить(ПолучитьБуферДвоичныхДанныхИзСтроки("\R\R\N")); + + РазделенныеДанные = ТестовыйБуфер.Разделить(Разделители); + + юТест.ПроверитьРавенство(РазделенныеДанные.Количество(), 5); + + Для Инд = 0 По РазделенныеДанные.ВГраница() Цикл + + СтрокаИзБуфера = ПолучитьСтрокуИзБуфераДвоичныхДанных(РазделенныеДанные[Инд]); + юТест.ПроверитьРавенство(СтрокаИзБуфера, Подстроки[Инд], "сравнение строк после разделения буфера"); + + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоПрочитатьБайтНеВыбрасываетИсключениеВКонцеПотока() Экспорт + + Буфер = ПолучитьБуферСДанными(); + Буфер.УстановитьТолькоЧтение(); + Поток = Новый ПотокВПамяти(Буфер); + ЧтениеДанных = Новый ЧтениеДанных(Поток.ЗакрытьИПолучитьДвоичныеДанные()); + + Сч = 0; + Пока Сч <= Буфер.Размер Цикл // читаем после конца буфера + + Байт = ЧтениеДанных.ПрочитатьБайт(); + Если Байт = Неопределено Тогда + Прервать; // должны зайти сюда + КонецЕсли; + + Сч = Сч + 1; + + КонецЦикла; +КонецПроцедуры diff --git a/tests/cgi-output.os b/tests/cgi-output.os index 2daca2ea3..5c776ceba 100755 --- a/tests/cgi-output.os +++ b/tests/cgi-output.os @@ -25,7 +25,6 @@ Процедура ПередЗапускомТеста() Экспорт Лог = Логирование.ПолучитьЛог("oscript.tests.cgi"); - Лог.УстановитьУровень(УровниЛога.Информация); КонецПроцедуры @@ -43,12 +42,7 @@ Функция СтрокаЗапускаОСкрипта(Знач ПутьКИсполняемомуМодулю) - СИ = Новый СистемнаяИнформация; - Если Найти(СИ.ВерсияОС, "Windows") > 0 Тогда - Возврат """" + ПутьКИсполняемомуМодулю + """"; - КонецЕсли; - - Возврат "mono """ + ПутьКИСполняемомуМодулю + """"; + Возврат "dotnet """ + ПутьКИСполняемомуМодулю + """"; КонецФункции @@ -376,12 +370,12 @@ Данные = СокрЛП(Данные); ПроверочнаяСтрока = СокрЛП(ПроверочнаяСтрока); - юТест.ПроверитьРавенство(Данные, ПроверочнаяСтрока); + юТест.ПроверитьРавенство(ПроверочнаяСтрока, Данные); КонецПроцедуры Функция ПутьОСкрипт() - Возврат КаталогПрограммы() + "/oscript.exe"; + Возврат КаталогПрограммы() + "/oscript.dll"; КонецФункции Функция НормализоватьПереводыСтрок(Знач ИсходнаяСтрока) diff --git a/tests/collections.os b/tests/collections.os new file mode 100644 index 000000000..516e533b5 --- /dev/null +++ b/tests/collections.os @@ -0,0 +1,149 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючНеопределеноВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючNullВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючБулевоВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючЧислоВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючСтрокаВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючДатаВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючУникальныйИдентификаторВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКлючТипВСоответствии"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазличныеКлючиВСоответствии"); + + Возврат ВсеТесты; +КонецФункции + +Процедура ТестДолжен_ПроверитьКлючНеопределеноВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв[Неопределено] = Истина; + юТест.ПроверитьРавенство(1, Соотв.Количество()); + юТест.ПроверитьРавенство(Истина, Соотв[Неопределено]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючNullВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить(Null, "Значение для Null"); + юТест.ПроверитьРавенство(1, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Null", Соотв[Null]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючБулевоВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить(Истина, "Значение для Истина"); + Соотв.Вставить(Ложь, "Значение для Ложь"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Истина", Соотв[Истина]); + юТест.ПроверитьРавенство("Значение для Ложь", Соотв[Ложь]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючЧислоВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить(42, "Значение для 42"); + Соотв.Вставить(0, "Значение для 0"); + Соотв.Вставить(-100, "Значение для -100"); + Соотв.Вставить(3.14, "Значение для 3.14"); + + юТест.ПроверитьРавенство(4, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для 42", Соотв[42]); + юТест.ПроверитьРавенство("Значение для 0", Соотв[0]); + юТест.ПроверитьРавенство("Значение для -100", Соотв[-100]); + юТест.ПроверитьРавенство("Значение для 3.14", Соотв[3.14]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючСтрокаВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Соотв.Вставить("Ключ1", "Значение1"); + Соотв.Вставить("Ключ2", "Значение2"); + Соотв.Вставить("", "Значение для пустой строки"); + + юТест.ПроверитьРавенство(3, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение1", Соотв["Ключ1"]); + юТест.ПроверитьРавенство("Значение2", Соотв["Ключ2"]); + юТест.ПроверитьРавенство("Значение для пустой строки", Соотв[""]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючДатаВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Дата1 = '20151231'; + Дата2 = '20200101'; + + Соотв.Вставить(Дата1, "Значение для Дата1"); + Соотв.Вставить(Дата2, "Значение для Дата2"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Дата1", Соотв[Дата1]); + юТест.ПроверитьРавенство("Значение для Дата2", Соотв[Дата2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючУникальныйИдентификаторВСоответствии() Экспорт + + Соотв = Новый Соответствие; + УИД1 = Новый УникальныйИдентификатор; + УИД2 = Новый УникальныйИдентификатор; + + Соотв.Вставить(УИД1, "Значение для УИД1"); + Соотв.Вставить(УИД2, "Значение для УИД2"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для УИД1", Соотв[УИД1]); + юТест.ПроверитьРавенство("Значение для УИД2", Соотв[УИД2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКлючТипВСоответствии() Экспорт + + Соотв = Новый Соответствие; + Тип1 = Тип("Строка"); + Тип2 = Тип("Число"); + + Соотв.Вставить(Тип1, "Значение для Тип Строка"); + Соотв.Вставить(Тип2, "Значение для Тип Число"); + + юТест.ПроверитьРавенство(2, Соотв.Количество()); + юТест.ПроверитьРавенство("Значение для Тип Строка", Соотв[Тип1]); + юТест.ПроверитьРавенство("Значение для Тип Число", Соотв[Тип2]); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазличныеКлючиВСоответствии() Экспорт + + // Проверка, что различные типы значений могут быть одновременно ключами + Соотв = Новый Соответствие; + + Соотв[Неопределено] = "Неопределено"; + Соотв[Null] = "Null"; + Соотв[Истина] = "Истина"; + Соотв[42] = "Число"; + Соотв["Текст"] = "Строка"; + Соотв['20240101'] = "Дата"; + Соотв[Тип("Строка")] = "Тип"; + + юТест.ПроверитьРавенство(7, Соотв.Количество()); + юТест.ПроверитьРавенство("Неопределено", Соотв[Неопределено]); + юТест.ПроверитьРавенство("Null", Соотв[Null]); + юТест.ПроверитьРавенство("Истина", Соотв[Истина]); + юТест.ПроверитьРавенство("Число", Соотв[42]); + юТест.ПроверитьРавенство("Строка", Соотв["Текст"]); + юТест.ПроверитьРавенство("Дата", Соотв['20240101']); + юТест.ПроверитьРавенство("Тип", Соотв[Тип("Строка")]); + +КонецПроцедуры \ No newline at end of file diff --git a/tests/config.os b/tests/config.os new file mode 100644 index 000000000..e3d33f62e --- /dev/null +++ b/tests/config.os @@ -0,0 +1,129 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолженПроверитьПорядокПрименения_ПеременнаяОкруженияИмеетВысшийПриоритет"); + ВсеТесты.Добавить("ТестДолженПроверитьЧтениеЗначенияПоУмолчанию"); + ВсеТесты.Добавить("ТестДолженПроверитьПереопределениеНесколькихПараметровОдновременно"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолженПроверитьПорядокПрименения_ПеременнаяОкруженияИмеетВысшийПриоритет() Экспорт + + // Получаем исходное значение языка системы + ИсходноеЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + + // Определяем тестовое значение, отличное от текущего + Если ИсходноеЗначениеЯзыка = "ru" Тогда + ТестовоеЗначение = "en"; + Иначе + ТестовоеЗначение = "ru"; + КонецЕсли; + + // Устанавливаем переменную окружения с новым значением + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", "SystemLanguage=" + ТестовоеЗначение); + + Попытка + // Перечитываем конфигурацию + ОбновитьНастройкиСистемы(); + + // Проверяем, что значение изменилось + НовоеЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + + юТест.ПроверитьРавенство(ТестовоеЗначение, НовоеЗначениеЯзыка, + "Переменная окружения OSCRIPT_CONFIG должна иметь наивысший приоритет"); + + Исключение + // Очищаем переменную окружения даже в случае ошибки + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ВызватьИсключение; + КонецПопытки; + + // Очищаем переменную окружения и возвращаем исходное состояние + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ОбновитьНастройкиСистемы(); + + // Проверяем, что значение вернулось к исходному + ВосстановленноеЗначение = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + юТест.ПроверитьРавенство(ИсходноеЗначениеЯзыка, ВосстановленноеЗначение, + "После очистки переменной окружения значение должно вернуться к исходному"); + +КонецПроцедуры + +Процедура ТестДолженПроверитьЧтениеЗначенияПоУмолчанию() Экспорт + + // Проверяем, что можем прочитать существующий параметр + ЗначениеКодировки = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + + // Значение может быть Неопределено, если не задано в конфигурации + // Это нормальное поведение + юТест.ПроверитьИстину( + ЗначениеКодировки = Неопределено ИЛИ ТипЗнч(ЗначениеКодировки) = Тип("Строка"), + "Значение encoding.script должно быть либо строкой, либо Неопределено"); + + // Проверяем чтение несуществующего параметра + НесуществующийПараметр = ПолучитьЗначениеСистемнойНастройки("несуществующий.параметр.test"); + + юТест.ПроверитьРавенство(Неопределено, НесуществующийПараметр, + "Несуществующий параметр должен возвращать Неопределено"); + +КонецПроцедуры + +Процедура ТестДолженПроверитьПереопределениеНесколькихПараметровОдновременно() Экспорт + + // Получаем исходные значения + ИсходныйЯзык = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + ИсходнаяКодировка = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + + // Устанавливаем несколько параметров через точку с запятой + Если ИсходныйЯзык = "ru" Тогда + НовыйЯзык = "en"; + Иначе + НовыйЯзык = "ru"; + КонецЕсли; + + НоваяКодировка = "utf-8"; + + КонфигурационнаяСтрока = "SystemLanguage=" + НовыйЯзык + ";encoding.script=" + НоваяКодировка; + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", КонфигурационнаяСтрока); + + Попытка + // Перечитываем конфигурацию + ОбновитьНастройкиСистемы(); + + // Проверяем, что оба значения изменились + ЗначениеЯзыка = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + ЗначениеКодировки = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + + юТест.ПроверитьРавенство(НовыйЯзык, ЗначениеЯзыка, + "Язык системы должен измениться на " + НовыйЯзык); + + юТест.ПроверитьРавенство(НоваяКодировка, ЗначениеКодировки, + "Кодировка должна измениться на " + НоваяКодировка); + + Исключение + // Очищаем переменную окружения даже в случае ошибки + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ВызватьИсключение; + КонецПопытки; + + // Очищаем переменную окружения + УстановитьПеременнуюСреды("OSCRIPT_CONFIG", ""); + ОбновитьНастройкиСистемы(); + + // Проверяем, что значения вернулись (или стали Неопределено) + ВосстановленныйЯзык = ПолучитьЗначениеСистемнойНастройки("SystemLanguage"); + ВосстановленнаяКодировка = ПолучитьЗначениеСистемнойНастройки("encoding.script"); + + юТест.ПроверитьРавенство(ИсходныйЯзык, ВосстановленныйЯзык, + "Язык системы должен вернуться к исходному значению"); + + юТест.ПроверитьРавенство(ИсходнаяКодировка, ВосстановленнаяКодировка, + "Кодировка должна вернуться к исходному значению"); + +КонецПроцедуры diff --git a/tests/console.os b/tests/console.os new file mode 100644 index 000000000..6162e7622 --- /dev/null +++ b/tests/console.os @@ -0,0 +1,157 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСтандартныйПотокВводаЭтоПоток"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьТаймаутЧтенияСтандартногоПотокВвода"); + #Если Не Linux Тогда + // TODO: https://github.com/EvilBeaver/OneScript/issues/1255 + ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуДанныхВСкриптЧерезСтандартныйПотокВвода"); + #КонецЕсли + ВсеТесты.Добавить("ТестДолжен_ПроверитьПеренаправлениеВывода"); + ВсеТесты.Добавить("ТестДолжен_ПроверитЦветКонсоли"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьЧтоСтандартныйПотокВводаЭтоПоток() Экспорт + + ПотокВвода = Консоль.ОткрытьСтандартныйПотокВвода(); + юТест.ПроверитьРавенство(Тип("Поток"), ТипЗнч(ПотокВвода), "Ошибка открытия стандартного потока ввода"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьТаймаутЧтенияСтандартногоПотокВвода() Экспорт + + ПутьКОскрипт = ОбъединитьПути(КаталогПрограммы(), "oscript.dll"); + + КодСкрипта = "ВходящийПоток = Консоль.ОткрытьСтандартныйПотокВвода(); + |ВходящийПоток.ТаймаутЧтения = 100; + |Чтение = Новый ЧтениеТекста(); + |Чтение.Открыть(ВходящийПоток); + |Попытка + | Сообщить(СокрЛП(Чтение.Прочитать())); + |Исключение + | Сообщить(ИнформацияобОшибке().ПодробноеОписаниеОшибки()); + | ВызватьИсключение; + |КонецПопытки;"; + + ТекстСкрипта = Новый ТекстовыйДокумент(); + ТекстСкрипта.УстановитьТекст(КодСкрипта); + + ВремФайл = ПолучитьИмяВременногоФайла("os"); + + ТекстСкрипта.Записать(ВремФайл); + + ТестовыеДанные = ""; + + ИсполняемаяКоманда = СтрШаблон("dotnet ""%1"" ""%2""", ПутьКОскрипт, ВремФайл); + #Если Windows Тогда + СтрокаЗапуска = СтрШаблон("cmd /c ""%1""", ИсполняемаяКоманда); + #Иначе + СтрокаЗапуска = СтрШаблон("sh -c '%1'", ИсполняемаяКоманда); + #КонецЕсли + Процесс = СоздатьПроцесс(СтрокаЗапуска, , Истина); + Процесс.Запустить(); + + МаксимумОжидания = 1000; + ИнтервалОжидания = 100; + ВсегоОжидание = 0; + Пока НЕ Процесс.Завершен Цикл + Приостановить(ИнтервалОжидания); + ВсегоОжидание = ВсегоОжидание + ИнтервалОжидания; + Если ВсегоОжидание >= МаксимумОжидания Тогда + Процесс.Завершить(); + юТест.ТестПровален("Ошибка чтения пустого стандартного потока ввода. Истекло время ожидания."); + КонецЕсли; + КонецЦикла; + + ВыводКоманды = СокрЛП(Процесс.ПотокВывода.Прочитать()); + + УдалитьФайлы(ВремФайл); + Сообщить(ВыводКоманды); + юТест.ПроверитьРавенство(ВыводКоманды, ТестовыеДанные, "Ошибка чтения пустого стандартного потока ввода."); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПередачуДанныхВСкриптЧерезСтандартныйПотокВвода() Экспорт + + ПутьКОскрипт = ОбъединитьПути(КаталогПрограммы(), "oscript.dll"); + + КодСкрипта = "Чтение = Новый ЧтениеТекста(); + |Чтение.Открыть(Консоль.ОткрытьСтандартныйПотокВвода()); + |Сообщить(СокрЛП(Чтение.Прочитать())); + |"; + + ТекстСкрипта = Новый ТекстовыйДокумент(); + ТекстСкрипта.УстановитьТекст(КодСкрипта); + + ВремФайл = ПолучитьИмяВременногоФайла("os"); + + ТекстСкрипта.Записать(ВремФайл); + + ТестовыеДанные = "12346"; + + ИсполняемаяКоманда = СтрШаблон("echo %1 | dotnet %2 %3", ТестовыеДанные, ПутьКОскрипт, ВремФайл); + #Если Windows Тогда + СтрокаЗапуска = СтрШаблон("cmd /c ""%1""", ИсполняемаяКоманда); + #Иначе + СтрокаЗапуска = СтрШаблон("sh -c '%1'", ИсполняемаяКоманда); + #КонецЕсли + + Процесс = СоздатьПроцесс(СтрокаЗапуска, , Истина); + Процесс.Запустить(); + + Пока НЕ Процесс.Завершен Цикл + Приостановить(100); + КонецЦикла; + + ВыводКоманды = СокрЛП(Процесс.ПотокВывода.Прочитать()); + + УдалитьФайлы(ВремФайл); + + юТест.ПроверитьРавенство(ВыводКоманды, ТестовыеДанные, "Ошибка чтения стандартного потока ввода."); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПеренаправлениеВывода() Экспорт + + ВФ = ПолучитьИмяВременногоФайла(); + Поток = ФайловыеПотоки.ОткрытьДляЗаписи(ВФ); + Консоль.УстановитьПотокВывода(Поток); + Попытка + Сообщить("Привет мир!"); + Исключение + // что-то пошло не так + Консоль.УстановитьПотокВывода(Консоль.ОткрытьСтандартныйПотокВывода()); + ВызватьИсключение; + КонецПопытки; + + Поток.Закрыть(); + Консоль.УстановитьПотокВывода(Консоль.ОткрытьСтандартныйПотокВывода()); + + Чтение = Новый ЧтениеТекста(ВФ, Консоль.КодировкаВыходногоПотока); + Текст = Чтение.Прочитать(); + Чтение.Закрыть(); + + УдалитьФайлы(ВФ); + + юТест.ПроверитьРавенство("Привет мир!", СокрЛП(Текст)); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитЦветКонсоли() Экспорт + + // Получение цвета не должно упасть + + ЦветТекста = Консоль.ЦветТекста; + ЦветФона = Консоль.ЦветФона; + + юТест.ПроверитьТип(ЦветТекста, "ЦветКонсоли"); + юТест.ПроверитьТип(ЦветФона, "ЦветКонсоли"); + +КонецПроцедуры diff --git a/tests/constructor.os b/tests/constructor.os index e8fabfc1c..8f2459640 100644 --- a/tests/constructor.os +++ b/tests/constructor.os @@ -12,6 +12,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеСтандартногоПредставления"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеЗначенияПоУмолчанию"); ВсеТесты.Добавить("ТестДолжен_ПроверитьКорректностьПроверкиУказанияФактическихПараметров"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПропускНеобязательныхПараметров"); Возврат ВсеТесты; @@ -85,6 +86,7 @@ НовыйОбъект = Новый Проверка(1, 2, 3); НовыйОбъект.ПроизвольноеПредставление = "Я специальный объект"; Ожидаем.Что(Строка(НовыйОбъект)).Равно("Я специальный объект"); + Ожидаем.Что("> " + НовыйОбъект).Равно("> Я специальный объект"); КонецПроцедуры @@ -116,4 +118,16 @@ Ожидаем.Что(ОписаниеОшибки()).Содержит("Недостаточно фактических параметров"); КонецПопытки; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПропускНеобязательныхПараметров() Экспорт + + ТекПуть = Новый Файл(ТекущийСценарий().Источник).Путь; + ПодключитьСценарий(ОбъединитьПути(ТекПуть,"testdata", "obj-constructor-with-req-prms-after-opt.os") , "ПроверкаКонструктора"); + + НовыйОбъект = Новый ПроверкаКонструктора(, 23); + + Ожидаем.Что(НовыйОбъект.П1, "Параметр 1 заполнился верно").Равно("1"); + Ожидаем.Что(НовыйОбъект.П2, "Параметр 2 заполнился верно").Равно(23); + КонецПроцедуры \ No newline at end of file diff --git a/tests/customToString.os b/tests/customToString.os new file mode 100644 index 000000000..8703dd7b5 --- /dev/null +++ b/tests/customToString.os @@ -0,0 +1,41 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуВКонтекстПриведенногоКСтрокеЗначения"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПредставлениеОбычногоКласса"); + юТест = ЮнитТестирование; + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьПередачуВКонтекстПриведенногоКСтрокеЗначения() Экспорт + ТекстКласса = + "Процедура ОбработкаПолученияПредставления(Представление, СтандартнаяОбработка) + | СтандартнаяОбработка = Ложь; + | Представление = ""Представление""; + |КонецПроцедуры"; + + КлассИзСтроки = ЗагрузитьСценарийИзСтроки(ТекстКласса); + + // see https://github.com/EvilBeaver/OneScript/issues/1530 + // СтрНайти это подключаемый контекст, который вызывается маршаллером. + юТест.ПроверитьРавенство(1, СтрНайти(КлассИзСтроки, "Представление"), Строка(КлассИзСтроки)); + + // СтрЗаменить это опкод ВМ, который не обрабатывается маршаллером + Замена = СтрЗаменить(КлассИзСтроки, "Пред", "На"); + юТест.ПроверитьРавенство("Наставление", Замена); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПредставлениеОбычногоКласса() Экспорт + ТекстКласса = "Перем А;"; + КлассИзСтроки = ЗагрузитьСценарийИзСтроки(ТекстКласса); + + юТест.ПроверитьРавенство("Сценарий", Строка(КлассИзСтроки)); + юТест.ПроверитьРавенство("Сценарий", СтрЗаменить(КлассИзСтроки, "%%%", "")); + +КонецПроцедуры \ No newline at end of file diff --git a/tests/datahashing-from-stream.os b/tests/datahashing-from-stream.os new file mode 100644 index 000000000..f5a9e5213 --- /dev/null +++ b/tests/datahashing-from-stream.os @@ -0,0 +1,58 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти"); + + Возврат ВсеТесты; + +КонецФункции + +Функция ТестДолжен_ПроверитьХешированниеДанныхИзПотокаВПамяти() Экспорт + + БДД = ПолучитьБуферДвоичныхДанныхИз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 diff --git a/tests/delegate-action.os b/tests/delegate-action.os new file mode 100644 index 000000000..5299edc52 --- /dev/null +++ b/tests/delegate-action.os @@ -0,0 +1,101 @@ +#Использовать asserts + +/////////////////////////////////////////////////////////////////////// +// Тест класса Действие/Action +/////////////////////////////////////////////////////////////////////// + +Перем юТест; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаТеста"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаТестаСНесколькимиПараметрами"); + ВсеТесты.Добавить("ТестНеДолжен_ВыполнитьДействиеОбъектаТеста"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаДвижка"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаДвижкаНаАнглийском"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ВыполнитьДействиеОбъектаТеста() Экспорт + + юТест.ПодробныеОписанияОшибок(Истина); + + Действие = Новый Действие(ЭтотОбъект, "Исполнитель"); + Результат = Действие.Выполнить(); + + Ожидаем.Что(Результат).Равно(111); + +КонецПроцедуры + +Процедура ТестДолжен_ВыполнитьДействиеОбъектаТестаСНесколькимиПараметрами() Экспорт + + юТест.ПодробныеОписанияОшибок(Истина); + + Действие = Новый Действие(ЭтотОбъект, "Исполнитель"); + Результат = Действие.Выполнить(200, 20); + + Ожидаем.Что(Результат).Равно(221); + +КонецПроцедуры + +Процедура ТестНеДолжен_ВыполнитьДействиеОбъектаТеста() Экспорт + + юТест.ПодробныеОписанияОшибок(Истина); + + БылоИсключение = Ложь; + Попытка + Действие = Новый Действие(ЭтотОбъект, "ПриватныйИсполнитель"); + Исключение + БылоИсключение = Истина; + КонецПопытки; + + Ожидаем.Что(БылоИсключение).Равно(Истина); + +КонецПроцедуры + +Процедура ТестДолжен_ВыполнитьДействиеОбъектаДвижка() Экспорт + + юТест.ПодробныеОписанияОшибок(Истина); + + Массив = Новый Массив; + + Действие = Новый Действие(Массив, "Добавить"); + Действие.Выполнить(1); + + Ожидаем.Что(Массив).ИмеетДлину(1); + +КонецПроцедуры + +Процедура ТестДолжен_ВыполнитьДействиеОбъектаДвижкаНаАнглийском() Экспорт + + юТест.ПодробныеОписанияОшибок(Истина); + + Массив = Новый Массив; + + Action = Новый Action(Массив, "Добавить"); + Action.Execute(1); + + Ожидаем.Что(Массив).ИмеетДлину(1); + +КонецПроцедуры + +Функция Исполнитель(Знач П1 = 100, Знач П2 = 10, Знач П3 = 1) Экспорт + + Возврат П1 + П2 + П3; + +КонецФункции + +Функция ПриватныйИсполнитель(Знач П1 = 1, Знач П2 = 10, Знач П3 = 100) + + Возврат П1 + П2 + П3; + +КонецФункции diff --git a/tests/directives.os b/tests/directives.os index 99cb903c6..619e1c9d0 100644 --- a/tests/directives.os +++ b/tests/directives.os @@ -1,4 +1,5 @@ Перем юТест; +Перем мОшибкаКомпиляции; Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт @@ -30,6 +31,7 @@ Попытка ПодключитьСценарий(ПутьКФайлу, ИмяПодключения); Исключение + мОшибкаКомпиляции = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); Возврат Ложь; КонецПопытки; @@ -56,7 +58,8 @@ Функция ТестДолжен_ПроверитьЧтоИсключенныеСтрокиПропускаются() Экспорт юТест.ПроверитьРавенство(ФайлКомпилируется("preprocessor/excluded.os"), Истина, - "Файл excluded.os не компилируется, хотя должен!" + "Файл excluded.os не компилируется, хотя должен! + |" + мОшибкаКомпиляции ); КонецФункции diff --git a/tests/driveinfo.os b/tests/driveinfo.os index 22deea9b5..7eb68410a 100644 --- a/tests/driveinfo.os +++ b/tests/driveinfo.os @@ -66,7 +66,7 @@ Процедура Тест_Должен_ВернутьРазмерДиска() Экспорт ИнформацияОДиске = ПодключенныйДиск(); - Ожидаем.Что(ИнформацияОДиске.РазмерДиска).Больше(0); + Ожидаем.Что(ИнформацияОДиске.РазмерДиска).БольшеИлиРавно(0); КонецПроцедуры Процедура Тест_Должен_ВернутьМеткаТома() Экспорт diff --git a/tests/encodings.os b/tests/encodings.os index 43ce1cccc..612410406 100644 --- a/tests/encodings.os +++ b/tests/encodings.os @@ -5,15 +5,10 @@ юТест = ЮнитТестирование; Си = Новый СистемнаяИнформация; - ЭтоWindows = Найти(Си.ВерсияОС, "Windows") > 0; ВсеТесты = Новый Массив; - Если ЭтоWindows Тогда - ВсеТесты.Добавить("ТестДолженПроверитьЧтоФайлБезБОМСчитываетсяКакANSI_Windows"); - Иначе - ВсеТесты.Добавить("ТестДолженПроверитьЧтоФайлБезБОМСчитываетсяКакUTF_Linux"); - КонецЕсли; + ВсеТесты.Добавить("ТестДолженПроверитьЧтоФайлБезБОМСчитываетсяКакUTF"); Возврат ВсеТесты; @@ -33,7 +28,7 @@ Возврат Файл; КонецФункции -Процедура ТестДолженПроверитьЧтоФайлБезБОМСчитываетсяКакANSI_Windows() Экспорт +Процедура ТестДолженПроверитьЧтоФайлБезБОМСчитываетсяКакANSI() Экспорт Файл = ЗаписатьФайлБезБОМ(КодировкаТекста.ANSI); @@ -45,7 +40,7 @@ КонецПроцедуры -Процедура ТестДолженПроверитьЧтоФайлБезБОМСчитываетсяКакUTF_Linux() Экспорт +Процедура ТестДолженПроверитьЧтоФайлБезБОМСчитываетсяКакUTF() Экспорт Файл = ЗаписатьФайлБезБОМ(КодировкаТекста.UTF8NoBOM); diff --git a/tests/engine-behaviors.os b/tests/engine-behaviors.os index 56ee05a77..7901869f1 100644 --- a/tests/engine-behaviors.os +++ b/tests/engine-behaviors.os @@ -49,7 +49,19 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьМинусВЗначенииПарамераПоУмолчанию"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПрерываниеВложенныхПопыток"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПродолжениеВложенныхПопыток"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИнформацияОбОшибкеВнеИсключения"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуПараметровПоСсылке"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОшибкиСравненияНаБольшеМеньше"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧто_ЭтотОбъект_НедоступенДляЗаписи"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОперацииСДатой_Сложение"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОперацииСДатой_Вычитание"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОперацииСДатой_СложениеДат"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьВложенныеИсключения"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВложенныеИсключенияДелегат"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВложенныеИсключенияФоновоеЗадание"); + Возврат ВсеТесты; КонецФункции @@ -240,7 +252,7 @@ Попытка ПроброситьИсключение(); Исключение - юТест.ПроверитьИстину(ПустаяСтрока(ИнформацияОбОшибке().Описание), "Текст исключения должен быть пустым"); + юТест.ПроверитьРавенство("", ИнформацияОбОшибке().Описание, "Текст исключения должен быть пустым"); КонецПопытки; КонецПроцедуры @@ -465,7 +477,7 @@ юТест.ПроверитьРавенство(П.П1, Неопределено, "Пропущенный параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П2, Неопределено, "Пропущенный параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П3, "1", "Пропущенный параметр со значением по-умолчанию"); - + П = ПропущенныеПараметры1(,,); юТест.ПроверитьРавенство(П.П1, Неопределено, "Пропущенный параметр без значения по-умолчанию"); @@ -477,25 +489,25 @@ юТест.ПроверитьРавенство(П.П1, Неопределено, "Пропущенный параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П2, 2, "Параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П3, "1", "Пропущенный параметр со значением по-умолчанию"); - + П = ПропущенныеПараметры1(3,); юТест.ПроверитьРавенство(П.П1, 3, "Параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П2, Неопределено, "Пропущенный параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П3, "1", "Пропущенный параметр со значением по-умолчанию"); - + П = ПропущенныеПараметры1(3,,); юТест.ПроверитьРавенство(П.П1, 3, "Параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П2, Неопределено, "Пропущенный параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П3, "1", "Пропущенный параметр со значением по-умолчанию"); - + П = ПропущенныеПараметры1(,,"4"); юТест.ПроверитьРавенство(П.П1, Неопределено, "Пропущенный параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П2, Неопределено, "Пропущенный параметр без значения по-умолчанию"); юТест.ПроверитьРавенство(П.П3, "4", "Параметр со значением по-умолчанию"); - + П = ПропущенныеПараметры1(, 2, "6"); юТест.ПроверитьРавенство(П.П1, Неопределено, "Пропущенный параметр без значения по-умолчанию"); @@ -613,4 +625,168 @@ юТест.ПроверитьРавенство(3, Счетчик); -КонецПроцедуры \ No newline at end of file +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИнформацияОбОшибкеВнеИсключения() Экспорт + + ИнформацияОбОшибке = ИнформацияОбОшибке(); + + юТест.ПроверитьРавенство("", ИнформацияОбОшибке.ИмяМодуля); + юТест.ПроверитьРавенство("", ИнформацияОбОшибке.ИсходнаяСтрока); + юТест.ПроверитьРавенство(0, ИнформацияОбОшибке.НомерСтроки); + юТест.ПроверитьРавенство("", ИнформацияОбОшибке.Описание); + юТест.ПроверитьРавенство("", ИнформацияОбОшибке.ПодробноеОписаниеОшибки()); + юТест.ПроверитьРавенство(Неопределено, ИнформацияОбОшибке.Параметры); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПередачуПараметровПоСсылке() Экспорт + + Структура = Новый Структура("Поле", 1); + + Т = 0; + Рез = Структура.Свойство("Поле", Т); + юТест.ПроверитьРавенство(1, Т, "Для переменной"); + + Массив = Новый Массив(1); + Рез = Структура.Свойство("Поле", Массив[0]); + юТест.ПроверитьРавенство(1, Массив[0], "Для элемента массива"); + + Структура2 = Новый Структура("Поле", 2); + Рез = Структура.Свойство("Поле", Структура2.Поле); + юТест.ПроверитьРавенство(1, Структура2.Поле, "Для поля структуры"); + + Рез = Структура.Свойство("Поле", 3); // не должно упасть + юТест.ПроверитьРавенство(истина, Рез); + + Рез = Структура.Свойство("Поле", ТекущаяДата()); // не должно упасть + юТест.ПроверитьРавенство(истина, Рез); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОшибкиСравненияНаБольшеМеньше() Экспорт + + Ошибка = "Сравнение на больше/меньше для типа 'СтатусСообщения' не поддерживается"; + юТест.ПроверитьКодСОшибкой( + "Рез = (СтатусСообщения.БезСтатуса <= СтатусСообщения.БезСтатуса);", + Ошибка, "Для одинаковых значений перечислений" ); + + Ошибка = "Сравнение на больше/меньше типов 'ЦветКонсоли' и 'СтатусСообщения' не поддерживается"; + юТест.ПроверитьКодСОшибкой( + "Рез = (ЦветКонсоли.Черный < СтатусСообщения.БезСтатуса);", + Ошибка, "Для значений перечислений разных типов" ); + + Ошибка = "Сравнение на больше/меньше для типа 'ПеречислениеКодировкаТекста' не поддерживается"; + юТест.ПроверитьКодСОшибкой( + "Рез = (КодировкаТекста >= КодировкаТекста);", + Ошибка, "Для одинаковых перечислений" ); + + Ошибка = "Сравнение на больше/меньше типов 'ПеречислениеХешФункция' и 'ПеречислениеСтатусСообщения' не поддерживается"; + юТест.ПроверитьКодСОшибкой( + "Рез = (ХешФункция > СтатусСообщения);", + Ошибка, "Для перечислений разных типов" ); + + Ошибка = "Сравнение на больше/меньше для типа 'Тип' не поддерживается"; + юТест.ПроверитьКодСОшибкой( + "Рез = (ТипЗнч(истина) > ТипЗнч(0));", + Ошибка, "Для значений типа 'Тип'" ); + + Ошибка = "Сравнение на больше/меньше типов 'Массив' и 'Структура' не поддерживается"; + юТест.ПроверитьКодСОшибкой( + "Массив = Новый Массив(1); + |Структура = Новый Структура; + |Рез = (Массив > Структура);", + Ошибка, "Для объектов разных типов" ); + + Ошибка = "Сравнение на больше/меньше для типа 'Массив' не поддерживается"; + юТест.ПроверитьКодСОшибкой( + "Массив = Новый Массив(1); + |Массив2 = Новый Массив(1);; + |Рез = (Массив <= Массив2);", + Ошибка, "Для одинаковых объектов" ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧто_ЭтотОбъект_НедоступенДляЗаписи() Экспорт + юТест.ПроверитьКодСОшибкой("ЭтотОбъект = 8", "Свойство 'ЭтотОбъект' недоступно для записи"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОперацииСДатой_Сложение() Экспорт + + ТекущееВремя = ТекущаяДата(); + Попытка + ЧерезЧас = ТекущееВремя + "3600"; + юТест.ПроверитьРавенство(ЧерезЧас,ТекущееВремя+3600); + Исключение + ВызватьИсключение("Не работает сложение даты со строкой"); + КонецПопытки; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОперацииСДатой_Вычитание() Экспорт + + ТекущееВремя = ТекущаяДата(); + Попытка + ЧасомРанее = ТекущееВремя - "3600"; + юТест.ПроверитьРавенство(ЧасомРанее,ТекущееВремя-3600); + Исключение + ВызватьИсключение("Не работает вычитание даты и строки"); + КонецПопытки; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОперацииСДатой_СложениеДат() Экспорт + + ТекущееВремя = ТекущаяДата(); + + Попытка + ТекущееВремя = ТекущееВремя + '00010101'; + Исключение + Возврат; + КонецПопытки; + + ВызватьИсключение "Не должно работать сложение дат"; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВложенныеИсключения() Экспорт + ИсключениеВоВложеннойПопытке(); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВложенныеИсключенияДелегат() Экспорт + Действие = Новый Действие(ЭтотОбъект, "ИсключениеВоВложеннойПопытке"); + Действие.Выполнить(); + КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВложенныеИсключенияФоновоеЗадание() Экспорт + + ФоновоеЗадание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ИсключениеВоВложеннойПопытке"); + ФоновоеЗадание.ОжидатьЗавершения(); + + Если ФоновоеЗадание.ИнформацияОбОшибке <> Неопределено Тогда + + ВызватьИсключение СтрШаблон( + "%1 (%2:%3)", + ФоновоеЗадание.ИнформацияОбОшибке.Описание, + ФоновоеЗадание.ИнформацияОбОшибке.ИмяМодуля, + ФоновоеЗадание.ИнформацияОбОшибке.НомерСтроки + ); + + КонецЕсли; + +КонецПроцедуры + +Процедура ИсключениеВоВложеннойПопытке() Экспорт + + Попытка + + Попытка + ВызватьИсключение "Исключение из вложенной попытки должна перехватываться"; + Исключение + КонецПопытки; + + ВызватьИсключение "Исключение из внешней попытки должна перехватываться"; + + Исключение + // Успех + КонецПопытки; + +КонецПроцедуры diff --git a/tests/enum.os b/tests/enum.os index 79027e94b..1a420f6d1 100644 --- a/tests/enum.os +++ b/tests/enum.os @@ -10,6 +10,7 @@ ВсеТесты.Добавить("ТестДолженПроверитьЧтоПростоеПеречислениеПриводитсяКСтроке"); ВсеТесты.Добавить("ТестДолженПроверитьРаботоспособностьСравненияПростогоПеречисления"); ВсеТесты.Добавить("ТестДолженПроверитьКорректностьКонвертацииПростогоПеречислениеСДругимНаименованиемЭлемента"); + ВсеТесты.Добавить("ТестДолженПроверитьЧтоСистемноеПеречислениеМожноПолучитьЧерезРефлектор"); Возврат ВсеТесты; @@ -47,6 +48,14 @@ КонецПроцедуры +Процедура ТестДолженПроверитьЧтоСистемноеПеречислениеМожноПолучитьЧерезРефлектор() Экспорт + + Рефлектор = Новый Рефлектор; + Свойства = Рефлектор.ПолучитьТаблицуСвойств(ДопустимыйЗнак); + юТест.ПроверитьРавенство(2, Свойства.Количество()); + +КонецПроцедуры + Функция ПростоеПеречисление() ТЗ = Новый ТаблицаЗначений(); @@ -54,4 +63,5 @@ Квалификатор = ТЗ.Колонки[0].ТипЗначения.КвалификаторыЧисла; Возврат Квалификатор.ДопустимыйЗнак; -КонецФункции \ No newline at end of file +КонецФункции + diff --git a/tests/eratosthenes.os b/tests/eratosthenes.os new file mode 100644 index 000000000..04594925e --- /dev/null +++ b/tests/eratosthenes.os @@ -0,0 +1,55 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ВыполнитьРешетоЭратосфена"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ВыполнитьРешетоЭратосфена() Экспорт + + Фрагмент = Новый СкомпилированныйФрагмент(); + + Н = 50000; + + Алгоритм = " + |Массив = Новый Массив(); + |Массив.Добавить(Ложь); + |Массив.Добавить(Ложь); + | + |Для индекс = 2 По Н Цикл + | Массив.Добавить(Истина); + |КонецЦикла; + | + |времяНачала = ТекущаяУниверсальнаяДатаВМиллисекундах(); + |Для индекс = 2 По Н Цикл + | Если Массив[индекс] Тогда + | квадрат = индекс * индекс; + | Если квадрат <= Н Тогда + | м = квадрат; + | Пока м <= Н Цикл + | Массив[м] = Ложь; + | м = м + индекс; + | КонецЦикла; + | КонецЕсли; + | КонецЕсли; + |КонецЦикла; + | + |времяОкончания = ТекущаяУниверсальнаяДатаВМиллисекундах(); + |Возврат (времяОкончания - времяНачала)/1000"; + + Фрагмент.ФрагментКода = Алгоритм; + Фрагмент.Параметры.Вставить("Н", Тип("Число")); + + Действие = Фрагмент.Скомпилировать(); + + Время = Действие.Выполнить(Н); + Сообщить(Время); + +КонецПроцедуры \ No newline at end of file diff --git "a/tests/eval-in-libraries/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\234\320\276\320\264\321\203\320\273\321\214.os" "b/tests/eval-in-libraries/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\234\320\276\320\264\321\203\320\273\321\214.os" new file mode 100644 index 000000000..d9ffe9c90 --- /dev/null +++ "b/tests/eval-in-libraries/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\234\320\276\320\264\321\203\320\273\321\214.os" @@ -0,0 +1,3 @@ +Функция Тест() Экспорт + Возврат 1; +КонецФункции diff --git "a/tests/eval-in-libraries/src/\320\242\320\265\321\201\321\202\321\213/\320\242\320\265\321\201\321\202.os" "b/tests/eval-in-libraries/src/\320\242\320\265\321\201\321\202\321\213/\320\242\320\265\321\201\321\202.os" new file mode 100644 index 000000000..fc2518687 --- /dev/null +++ "b/tests/eval-in-libraries/src/\320\242\320\265\321\201\321\202\321\213/\320\242\320\265\321\201\321\202.os" @@ -0,0 +1,10 @@ +#Использовать ".." + +Функция Тест() Экспорт + + Модуль.Тест(); // Работает + + А = Вычислить("Модуль"); // https://github.com/EvilBeaver/OneScript/issues/1451 Неизвестный символ: Модуль + Возврат А.Тест(); + +КонецФункции diff --git a/tests/eval.os b/tests/eval.os index 3724e4149..7170064a6 100644 --- a/tests/eval.os +++ b/tests/eval.os @@ -1,4 +1,5 @@ Перем юТест; +Перем Глобал; Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт @@ -14,7 +15,19 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьОператорВыполнитьСВыбросомИсключения"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВОператореВыполнитьЗапрещенВозврат"); ВсеТесты.Добавить("ТестДолжен_ПроверитьСвойствоЭтотОбъект_issue712"); - + ВсеТесты.Добавить("ТестДолжен_ПроверитьEvalНаРазныхЭкземплярахThis"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКешКомпиляцииВРазныхФреймах"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРекурсивныйВызовВычислить"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСложныеВложенныеВызовыВычислить"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВычислитьВнутриВыполнить"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьАлиасыФункцийИПеременныхВВычислить"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОбработкуИсключенияВВыполнить"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРекурсивныйВызовВыполнить"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВложенныеВызовыВыполнить"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовыВыполнитьСПопытками"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботоспособностьВПодключенныхСценариях"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботоспособностьСПараметрамиПроцедуры"); + Возврат ВсеТесты; КонецФункции @@ -138,3 +151,225 @@ юТест.ПроверитьРавенство("Это класс", Результат); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьEvalНаРазныхЭкземплярахThis() Экспорт + + ПутьСценарий = ОбъединитьПути(ТекущийСценарий().Каталог, "testdata", "evalDifferentThis.os"); + Первый = ЗагрузитьСценарий(ПутьСценарий); + Второй = ЗагрузитьСценарий(ПутьСценарий); + + Первый.УстановитьЗначение("Первый"); + Второй.УстановитьЗначение("Второй"); + + Рез1 = Первый.ПолучитьЧерезВычислить(); + Рез2 = Второй.ПолучитьЧерезВычислить(); + + юТест.ПроверитьРавенство("Первый", Рез1); + юТест.ПроверитьРавенство("Второй", Рез2); + +КонецПроцедуры + +Функция ТестМетод1(П1) + + ЛокальнаяПеременная = 1; + + Возврат Вычислить("ЛокальнаяПеременная + П1 + 1"); + +КонецФункции + +Функция ТестМетод2(П2, П1) + ДругаяПеременная = 3; + ЛокальнаяПеременная = 1; + + Возврат Вычислить("ЛокальнаяПеременная + П1 + 1"); +КонецФункции + +Процедура ТестДолжен_ПроверитьКешКомпиляцииВРазныхФреймах() Экспорт + + Рез1 = ТестМетод1(1); + Рез2 = ТестМетод1(1); + Рез3 = ТестМетод2(Неопределено, 1); + Рез4 = ТестМетод2(Неопределено, 1); + + юТест.ПроверитьРавенство(3, Рез1); + юТест.ПроверитьРавенство(3, Рез2); + юТест.ПроверитьРавенство(3, Рез3); + юТест.ПроверитьРавенство(3, Рез4); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРекурсивныйВызовВычислить() Экспорт + Рез = "Вычислить(1)"; + Для й=1 По 100 Цикл + Рез = "Вычислить("+Рез+")+1"; + КонецЦикла; + + Рез = Вычислить(Рез); + юТест.ПроверитьРавенство(101, Рез); +КонецПроцедуры + + +Функция Один() + Возврат Вычислить("1"); +КонецФункции + +Функция Два() + Один = Вычислить("Один()"); + Возврат Вычислить("Один+Один()"); +КонецФункции + +Функция Шесть() + Три = 3; + Возврат Вычислить("Два()") * Три; +КонецФункции + +Функция СорокДва(Семь) + Возврат Вычислить("Семь * Шесть()"); +КонецФункции + +Функция Ответ() + Возврат Вычислить("Глобал + СорокДва(7)"); +КонецФункции + +Процедура ТестДолжен_ПроверитьСложныеВложенныеВызовыВычислить() Экспорт + Глобал = 0; + Рез = Вычислить("Ответ()"); + юТест.ПроверитьРавенство(42, Рез); + + Глобал = 66; + Рез = Вычислить("Ответ()"); + юТест.ПроверитьРавенство(108, Рез); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВычислитьВнутриВыполнить() Экспорт + Рез = -1; + Выполнить("Для й=1 По 9 Цикл ц = -й; КонецЦикла; Рез = Вычислить(""ц + й"")"); + юТест.ПроверитьРавенство(1, Рез); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьАлиасыФункцийИПеременныхвВычислить() Экспорт + Рез = Вычислить("Лев(""фыв""+Символы.ПС,1)+Left(""fgh""+Chars.LF,1)"); + юТест.ПроверитьРавенство("фf", Рез); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОбработкуИсключенияВВыполнить() Экспорт + + Рез = 1; + Выполнить " + |Попытка + | Рез = 1/0; + |Исключение + | Рез = -1; + |КонецПопытки;"; + юТест.ПроверитьРавенство(-1, Рез); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРекурсивныйВызовВыполнить() Экспорт + + Рез = 1; + Код = "Рез=Рез+1;"; + Для й = 1 По 10 Цикл + Код = СтрЗаменить(Код, """", """"""); + Код = "Выполнить""" + Код + """;"; + КонецЦикла; + Выполнить(Код); + + юТест.ПроверитьРавенство(2, Рез); + +КонецПроцедуры + +Процедура Третья(Пар) + Выполнить "Рез = Пар;"; +КонецПроцедуры + +Процедура Вторая(Пар) + Выполнить "Лок = Пар + 1; Третья(Лок);"; +КонецПроцедуры + +Процедура ЗаменаПараметра(Пар) + Выполнить "Пар = Пар + 1"; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВложенныеВызовыВыполнить() Экспорт + + Рез = 1; + Выполнить "Вторая(2);"; + + юТест.ПроверитьРавенство(1, Рез); + + Выполнить "ЗаменаПараметра(Рез);"; + + юТест.ПроверитьРавенство(2, Рез); + +КонецПроцедуры + +Процедура СИсключением() + Выполнить " + |Попытка + | Рез = Рез + 8; + | ВызватьИсключение 0; + |Исключение + | Рез = Рез + 9; + |КонецПопытки; + |Выполнить(""Рез = Рез + (-10)"");"; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВызовыВыполнитьСПопытками() Экспорт + + Рез = "0"; + попытка + Выполнить(" + |Лок=""1""; + |Попытка + | Рез = 1/0; + |Исключение + | Рез = Рез + Лок; + |КонецПопытки;"); + Выполнить(" + |Попытка + | Рез = Рез + 2/0; + |Исключение + | Выполнить(""Рез = Рез + 3""); + |КонецПопытки; + |Рез = Рез + 4;"); + Выполнить(" + |Попытка + | Рез = Рез + 5; + | СИсключением(); + |Исключение + | Рез = Рез + 6; + |КонецПопытки; + |Рез = Рез + 7;"); + исключение + Рез = Рез+ "!" ; + конецпопытки; + Рез = Рез + "+"; + + юТест.ПроверитьРавенство("0134567+", Рез); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРаботоспособностьВПодключенныхСценариях() Экспорт + + Библиотека = ОбъединитьПути(ТекущийСценарий().Каталог, "eval-in-libraries"); + + ПодключитьСценарий(Библиотека + "/src/Тесты/Тест.os", "ТестEvalInLibraries"); + + А = Новый ТестEvalInLibraries(); + А.Тест(); + +КонецПроцедуры + +Процедура РаботаСПараметрамиПроцедуры(Элемент1, Элемент2) + Рез = ""; + Для Сч = 1 По 2 Цикл + Рез = Рез + Вычислить("Элемент" + Сч); + КонецЦикла; + юТест.ПроверитьРавенство("Поле1Поле2", Рез); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРаботоспособностьСПараметрамиПроцедуры() Экспорт + Структура=Новый Структура("Поле1,Поле2","Поле1","Поле2"); + РаботаСПараметрамиПроцедуры(Структура.Поле1, Структура.Поле2); +КонецПроцедуры diff --git a/tests/events.os b/tests/events.os index 66856e687..7d94060cd 100644 --- a/tests/events.os +++ b/tests/events.os @@ -11,6 +11,12 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьПодпискуНаСобытие"); ВсеТесты.Добавить("ТестДолжен_ПроверитьОтпискуОтСобытия"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПодпискуНаСобытиеВВидеЛокальнойФункции"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОтпискуОтСобытияВВидеЛокальнойФункции"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоПодпискаПоОбъектуВидитТолькоЭкспорт"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПодпискуНаСобытиеВВидеВыражения"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПодпискуСОбработчикомВВидеВыражения"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоУИсточникаМожетБытьНесколькоСобытий"); Возврат ВсеТесты; @@ -25,13 +31,19 @@ ВызватьСобытие(ИмяСобытия, Параметры); КонецПроцедуры -Процедура ОбработчикСобытия(Параметр1, Параметр2) +Процедура ОбработчикСобытия(Параметр1, Параметр2) Экспорт СчетчикВызовов = СчетчикВызовов + 1; ПараметрыВызова = Новый Структура("Параметр1, Параметр2", Параметр1, Параметр2); КонецПроцедуры +Процедура ОбработчикСобытияВнутр(Параметр1, Параметр2) + + ОбработчикСобытия(Параметр1, Параметр2); + +КонецПроцедуры + Процедура ТестДолжен_ПроверитьПодпискуНаСобытие() Экспорт МассивПараметров = Новый Массив; @@ -51,6 +63,44 @@ КонецПроцедуры +Процедура ТестДолжен_ПроверитьПодпискуНаСобытиеВВидеВыражения() Экспорт + + МассивПараметров = Новый Массив; + МассивПараметров.Добавить("П1"); + МассивПараметров.Добавить("П2"); + + Источник = Новый ТестСобытий; + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + + юТест.Проверитьравенство(0, СчетчикВызовов); + ДобавитьОбработчик Источник["Мое"+"Событие"], ЭтотОбъект.ОбработчикСобытия; + + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + юТест.Проверитьравенство(1, СчетчикВызовов); + юТест.ПроверитьРавенство("П1", ПараметрыВызова.Параметр1); + юТест.ПроверитьРавенство("П2", ПараметрыВызова.Параметр2); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПодпискуСОбработчикомВВидеВыражения() Экспорт + + МассивПараметров = Новый Массив; + МассивПараметров.Добавить("П1"); + МассивПараметров.Добавить("П2"); + + Источник = Новый ТестСобытий; + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + + юТест.Проверитьравенство(0, СчетчикВызовов); + ДобавитьОбработчик Источник.МоеСобытие, ЭтотОбъект["Обработчик"+"События"]; + + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + юТест.Проверитьравенство(1, СчетчикВызовов); + юТест.ПроверитьРавенство("П1", ПараметрыВызова.Параметр1); + юТест.ПроверитьРавенство("П2", ПараметрыВызова.Параметр2); + +КонецПроцедуры + Процедура ТестДолжен_ПроверитьОтпискуОтСобытия() Экспорт Источник = Новый ТестСобытий; @@ -72,4 +122,77 @@ КонецПроцедуры +Процедура ТестДолжен_ПроверитьПодпискуНаСобытиеВВидеЛокальнойФункции() Экспорт + + МассивПараметров = Новый Массив; + МассивПараметров.Добавить("П1"); + МассивПараметров.Добавить("П2"); + + Источник = Новый ТестСобытий; + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + + юТест.Проверитьравенство(0, СчетчикВызовов); + ДобавитьОбработчик Источник.МоеСобытие, ОбработчикСобытияВнутр; + + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + юТест.Проверитьравенство(1, СчетчикВызовов); + юТест.ПроверитьРавенство("П1", ПараметрыВызова.Параметр1); + юТест.ПроверитьРавенство("П2", ПараметрыВызова.Параметр2); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОтпискуОтСобытияВВидеЛокальнойФункции() Экспорт + Источник = Новый ТестСобытий; + + ОбработчикСобытияВнутр = 42; // проверим, что все равно выберет метод, а не переменную + ДобавитьОбработчик Источник.МоеСобытие, ОбработчикСобытияВнутр; + + МассивПараметров = Новый Массив; + МассивПараметров.Добавить("П1"); + МассивПараметров.Добавить("П2"); + + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + юТест.ПроверитьРавенство(1, СчетчикВызовов); + + УдалитьОбработчик Источник.МоеСобытие, ОбработчикСобытияВнутр; + + СчетчикВызовов = 0; + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + юТест.ПроверитьРавенство(0, СчетчикВызовов); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоПодпискаПоОбъектуВидитТолькоЭкспорт() Экспорт + + Источник = Новый ТестСобытий; + + Попытка + ДобавитьОбработчик Источник.МоеСобытие, ЭтотОбъект.ОбработчикСобытияВнутр; + Исключение + юТест.ПроверитьБольше(Найти(ОписаниеОшибки(), "Метод объекта не обнаружен"), 0); + Возврат; + КонецПопытки; + + ВызватьИсключение "Ожидали исключение, но его не было"; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоУИсточникаМожетБытьНесколькоСобытий() Экспорт + + Источник = Новый ТестСобытий; + + ДобавитьОбработчик Источник.МоеСобытие, ОбработчикСобытияВнутр; + ДобавитьОбработчик Источник.МоеСобытиеВторое, ОбработчикСобытияВнутр; + + МассивПараметров = Новый Массив; + МассивПараметров.Добавить("П1"); + МассивПараметров.Добавить("П2"); + + Источник.СгенерироватьСобытие("МоеСобытие", МассивПараметров); + юТест.ПроверитьРавенство(1, СчетчикВызовов); + + Источник.СгенерироватьСобытие("МоеСобытиеВторое", МассивПараметров); + юТест.ПроверитьРавенство(2, СчетчикВызовов); + +КонецПроцедуры + ПодключитьСценарий(ТекущийСценарий().Источник, "ТестСобытий"); \ No newline at end of file diff --git a/tests/example-test.os b/tests/example-test.os index 190d26b0d..fff0cd75c 100644 --- a/tests/example-test.os +++ b/tests/example-test.os @@ -1,7 +1,12 @@ -Перем юТест; +&Анно +Перем ПеременнаяСАннотацией Экспорт; +&Анно(1) +Перем ПеременнаяСАннотациейИПараметром Экспорт; +Перем юТест; Перем ЭтотОбъек Экспорт; Перем ЭкспортнаяПеременная Экспорт; Перем Яшма1 Экспорт; +&Анно(Парам1 = 1) Перем Яшма2; Функция Версия() Экспорт @@ -34,6 +39,13 @@ // юТест.ПроверитьРавенство(1,2); // КонецПроцедуры +&Анно(1) Функция Яшма2() Экспорт Возврат Яшма2; КонецФункции + +&Анно +Процедура МетодСРазнымиПараметрами( +Параметр1, Параметр2 = Неопределено, Параметр3 = Истина, Параметр4 = 1, Параметр5 = "Строка", +Параметр6, Параметр7 = Неопределено, Параметр8 = Истина, Параметр9 = 1, Параметр10 = "Строка") Экспорт +КонецПроцедуры \ No newline at end of file diff --git a/tests/find-in-collections.os b/tests/find-in-collections.os new file mode 100644 index 000000000..2fee1d27f --- /dev/null +++ b/tests/find-in-collections.os @@ -0,0 +1,221 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВМассиве"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВМассиве"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВФиксированномМассиве"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВФиксированномМассиве"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВСпискеЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВСпискеЗначений"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВТаблицеЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВТаблицеЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВСтрокахТаблицыЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВСтрокахТаблицыЗначений"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВДеревеЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВДеревеЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВСтрокахДереваЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВСтрокахДереваЗначений"); + + Возврат ВсеТесты; +КонецФункции + +Процедура ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВМассиве() Экспорт + Массив = Новый Массив; + Массив.Добавить(0); + Массив.Добавить(1); + ЕстьИстина = Массив.Найти(Истина) <> Неопределено; + ЕстьЛожь = Массив.Найти(Ложь) <> Неопределено; + юТест.ПроверитьЛожь( ЕстьИстина ); + юТест.ПроверитьЛожь( ЕстьЛожь ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВМассиве() Экспорт + Массив = Новый Массив; + Массив.Добавить(Ложь); + Массив.Добавить(Истина); + Есть1 = Массив.Найти(1) <> Неопределено; + Есть0 = Массив.Найти(0) <> Неопределено; + юТест.ПроверитьЛожь( Есть1 ); + юТест.ПроверитьЛожь( Есть0 ); +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВФиксированномМассиве() Экспорт + Массив = Новый Массив; + Массив.Добавить(0); + Массив.Добавить(1); + ФиксированныйМассив = Новый ФиксированныйМассив(Массив); + ЕстьИстина = ФиксированныйМассив.Найти(Истина) <> Неопределено; + ЕстьЛожь = ФиксированныйМассив.Найти(Ложь) <> Неопределено; + юТест.ПроверитьЛожь( ЕстьИстина ); + юТест.ПроверитьЛожь( ЕстьЛожь ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВФиксированномМассиве() Экспорт + Массив = Новый Массив; + Массив.Добавить(Ложь); + Массив.Добавить(Истина); + ФиксированныйМассив = Новый ФиксированныйМассив(Массив); + Есть1 = ФиксированныйМассив.Найти(1) <> Неопределено; + Есть0 = ФиксированныйМассив.Найти(0) <> Неопределено; + юТест.ПроверитьЛожь( Есть1 ); + юТест.ПроверитьЛожь( Есть0 ); +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВСпискеЗначений() Экспорт + СписокЗначений = Новый СписокЗначений(); + СписокЗначений.Добавить(1); + СписокЗначений.Добавить(0); + ЕстьИстина = СписокЗначений.НайтиПоЗначению(Истина) <> Неопределено; + ЕстьЛожь = СписокЗначений.НайтиПоЗначению(Ложь) <> Неопределено; + юТест.ПроверитьЛожь( ЕстьИстина ); + юТест.ПроверитьЛожь( ЕстьЛожь ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВСпискеЗначений() Экспорт + СписокЗначений = Новый СписокЗначений(); + СписокЗначений.Добавить(Истина); + СписокЗначений.Добавить(Ложь); + Есть1 = СписокЗначений.НайтиПоЗначению(1) <> Неопределено; + Есть0 = СписокЗначений.НайтиПоЗначению(0) <> Неопределено; + юТест.ПроверитьЛожь( Есть1 ); + юТест.ПроверитьЛожь( Есть0 ); +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВТаблицеЗначений() Экспорт + ТаблицаЗначений = Новый ТаблицаЗначений(); + ТаблицаЗначений.Колонки.Добавить("Кол1"); + ТаблицаЗначений.Колонки.Добавить("Кол2"); + Строка1 = ТаблицаЗначений.Добавить(); + Строка2 = ТаблицаЗначений.Добавить(); + Строка1.Кол1 = 1; + Строка1.Кол2 = 0; + Строка2.Кол1 = 0; + Строка2.Кол2 = 1; + ЕстьИстина = ТаблицаЗначений.Найти(Истина) <> Неопределено; + ЕстьЛожь = ТаблицаЗначений.Найти(Ложь) <> Неопределено; + юТест.ПроверитьЛожь( ЕстьИстина ); + юТест.ПроверитьЛожь( ЕстьЛожь ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВТаблицеЗначений() Экспорт + ТаблицаЗначений = Новый ТаблицаЗначений(); + ТаблицаЗначений.Колонки.Добавить("Кол1"); + ТаблицаЗначений.Колонки.Добавить("Кол2"); + Строка1 = ТаблицаЗначений.Добавить(); + Строка2 = ТаблицаЗначений.Добавить(); + Строка1.Кол1 = Истина; + Строка1.Кол2 = Ложь; + Строка2.Кол1 = Ложь; + Строка2.Кол2 = Истина; + Есть1 = ТаблицаЗначений.Найти(1) <> Неопределено; + Есть0 = ТаблицаЗначений.Найти(0) <> Неопределено; + юТест.ПроверитьЛожь( Есть1 ); + юТест.ПроверитьЛожь( Есть0 ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВСтрокахТаблицыЗначений() Экспорт + ТаблицаЗначений = Новый ТаблицаЗначений(); + ТаблицаЗначений.Колонки.Добавить("Кол1"); + ТаблицаЗначений.Колонки.Добавить("Кол2"); + Строка1 = ТаблицаЗначений.Добавить(); + Строка2 = ТаблицаЗначений.Добавить(); + Строка1.Кол1 = 1; + Строка1.Кол2 = 0; + Строка2.Кол1 = 0; + Строка2.Кол2 = 1; + ЕстьИстина = ТаблицаЗначений.НайтиСтроки( Новый Структура("Кол1", Истина) ).Количество() > 0; + ЕстьЛожь = ТаблицаЗначений.НайтиСтроки( Новый Структура("Кол1", Ложь) ).Количество() > 0; + юТест.ПроверитьЛожь( ЕстьИстина ); + юТест.ПроверитьЛожь( ЕстьЛожь ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВСтрокахТаблицыЗначений() Экспорт + ТаблицаЗначений = Новый ТаблицаЗначений(); + ТаблицаЗначений.Колонки.Добавить("Кол1"); + ТаблицаЗначений.Колонки.Добавить("Кол2"); + Строка1 = ТаблицаЗначений.Добавить(); + Строка2 = ТаблицаЗначений.Добавить(); + Строка1.Кол1 = Истина; + Строка1.Кол2 = Ложь; + Строка2.Кол1 = Ложь; + Строка2.Кол2 = Истина; + Есть1 = ТаблицаЗначений.НайтиСтроки( Новый Структура("Кол1", 1) ).Количество() > 0; + Есть0 = ТаблицаЗначений.НайтиСтроки( Новый Структура("Кол1", 0) ).Количество() > 0; + юТест.ПроверитьЛожь( Есть1 ); + юТест.ПроверитьЛожь( Есть0 ); +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВДеревеЗначений() Экспорт + ДеревоЗначений = Новый ДеревоЗначений(); + ДеревоЗначений.Колонки.Добавить("Кол1"); + ДеревоЗначений.Колонки.Добавить("Кол2"); + Строка1 = ДеревоЗначений.Строки.Добавить(); + Строка2 = ДеревоЗначений.Строки.Добавить(); + Строка1.Кол1 = 1; + Строка1.Кол2 = 0; + Строка2.Кол1 = 0; + Строка2.Кол2 = 1; + ЕстьИстина = ДеревоЗначений.Строки.Найти(Истина) <> Неопределено; + ЕстьЛожь = ДеревоЗначений.Строки.Найти(Ложь) <> Неопределено; + юТест.ПроверитьЛожь( ЕстьИстина ); + юТест.ПроверитьЛожь( ЕстьЛожь ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВДеревеЗначений() Экспорт + ДеревоЗначений = Новый ДеревоЗначений(); + ДеревоЗначений.Колонки.Добавить("Кол1"); + ДеревоЗначений.Колонки.Добавить("Кол2"); + Строка1 = ДеревоЗначений.Строки.Добавить(); + Строка2 = ДеревоЗначений.Строки.Добавить(); + Строка1.Кол1 = Истина; + Строка1.Кол2 = Ложь; + Строка2.Кол1 = Ложь; + Строка2.Кол2 = Истина; + Есть1 = ДеревоЗначений.Строки.Найти(1) <> Неопределено; + Есть0 = ДеревоЗначений.Строки.Найти(0) <> Неопределено; + юТест.ПроверитьЛожь( Есть1 ); + юТест.ПроверитьЛожь( Есть0 ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_БулеваСредиЧисел_ВСтрокахДереваЗначений() Экспорт + ДеревоЗначений = Новый ДеревоЗначений(); + ДеревоЗначений.Колонки.Добавить("Кол1"); + ДеревоЗначений.Колонки.Добавить("Кол2"); + Строка1 = ДеревоЗначений.Строки.Добавить(); + Строка2 = ДеревоЗначений.Строки.Добавить(); + Строка1.Кол1 = 1; + Строка1.Кол2 = 0; + Строка2.Кол1 = 0; + Строка2.Кол2 = 1; + ЕстьИстина = ДеревоЗначений.Строки.НайтиСтроки( Новый Структура("Кол1", Истина) ).Количество() > 0; + ЕстьЛожь = ДеревоЗначений.Строки.НайтиСтроки( Новый Структура("Кол1", Ложь) ).Количество() > 0; + юТест.ПроверитьЛожь( ЕстьИстина ); + юТест.ПроверитьЛожь( ЕстьЛожь ); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоиск_ЧислаСредиБулевых_ВСтрокахДереваЗначений() Экспорт + ДеревоЗначений = Новый ДеревоЗначений(); + ДеревоЗначений.Колонки.Добавить("Кол1"); + ДеревоЗначений.Колонки.Добавить("Кол2"); + Строка1 = ДеревоЗначений.Строки.Добавить(); + Строка2 = ДеревоЗначений.Строки.Добавить(); + Строка1.Кол1 = Истина; + Строка1.Кол2 = Ложь; + Строка2.Кол1 = Ложь; + Строка2.Кол2 = Истина; + Есть1 = ДеревоЗначений.Строки.НайтиСтроки( Новый Структура("Кол1", 1) ).Количество() > 0; + Есть0 = ДеревоЗначений.Строки.НайтиСтроки( Новый Структура("Кол1", 0) ).Количество() > 0; + юТест.ПроверитьЛожь( Есть1 ); + юТест.ПроверитьЛожь( Есть0 ); +КонецПроцедуры diff --git a/tests/fixed-structure.os b/tests/fixed-structure.os index 19e00dac9..0374d357f 100644 --- a/tests/fixed-structure.os +++ b/tests/fixed-structure.os @@ -24,6 +24,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСвойство"); ВсеТесты.Добавить("ТестДолжен_ПроверитьОтсутствиеМетодаВставить"); ВсеТесты.Добавить("ТестДолжен_СоздатьСтруктуруПоФиксированнойСтруктуре"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗаполнитьЗначенияСвойствИзФиксированнойСтруктуры"); Возврат ВсеТесты; КонецФункции @@ -165,3 +166,17 @@ Ожидаем.Что(Структура.Ключ2, "значения элементов совпадут").Равно(ФиксированнаяСтруктура.Ключ2); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗаполнитьЗначенияСвойствИзФиксированнойСтруктуры() Экспорт + + Структура1 = Новый ФиксированнаяСтруктура("тест1,тест2,тест3", "тест", Истина); + Структура2 = Новый Структура(Структура1); + + // Проверяем, что метод ЗаполнитьЗначенияСвойств работает с ФиксированнаяСтруктура + ЗаполнитьЗначенияСвойств(Структура2, Структура1); + + Ожидаем.Что(Структура2.тест1, "значения свойства тест1 совпадут").Равно(Структура1.тест1); + Ожидаем.Что(Структура2.тест2, "значения свойства тест2 совпадут").Равно(Структура1.тест2); + Ожидаем.Что(Структура2.тест3, "значения свойства тест3 совпадут").Равно(Структура1.тест3); + +КонецПроцедуры diff --git a/tests/formatting.os b/tests/formatting.os index 769d9d0c9..62744d923 100644 --- a/tests/formatting.os +++ b/tests/formatting.os @@ -18,9 +18,12 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьФорматированиеБольшойРазрядности"); ВсеТесты.Добавить("ТестДолжен_ПроверитьФорматированиеПустаяДата"); ВсеТесты.Добавить("ТестДолжен_ПроверитьФорматированиеЛокальнаяДата"); + #Если Не Linux Тогда + //TODO: https://github.com/EvilBeaver/OneScript/issues/1228 ВсеТесты.Добавить("ТестДолжен_ПроверитьФорматированиеЛокальнаяДатаВремя"); ВсеТесты.Добавить("ТестДолжен_ПроверитьФорматированиеДлинныйФорматЛокальнойДаты"); ВсеТесты.Добавить("ТестДолжен_ПроверитьФорматированиеЛокальноеВремя"); + #КонецЕсли ВсеТесты.Добавить("ТестДолжен_ПроверитьПроизвольноеФорматированиеДат"); ВсеТесты.Добавить("ТестДолжен_ПроверитьФорматированиеДатДоДробей"); diff --git a/tests/global-funcs.os b/tests/global-funcs.os index 81b46c5f3..2acfd4bef 100644 --- a/tests/global-funcs.os +++ b/tests/global-funcs.os @@ -1,4 +1,4 @@ -/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// // // Тест проверки поведения некоторых глобальных функций // @@ -6,12 +6,19 @@ /////////////////////////////////////////////////////////////////////// Перем юТест; +Перем мАдресРесурса; // URL ресурса (хоста) для тестирования запросов //////////////////////////////////////////////////////////////////// // Программный интерфейс Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + Если мАдресРесурса = Неопределено Тогда + ПутьКHttpТесту = ОбъединитьПути(ТекущийСценарий().Каталог, "http.os"); + ПодключитьСценарий(ПутьКHttpТесту, "http_тест"); + мАдресРесурса = Новый http_тест().ВыбратьДоступныйХост(); + КонецЕсли; + юТест = ЮнитТестирование; ВсеТесты = Новый Массив; @@ -43,10 +50,20 @@ ВсеТесты.Добавить("ТестДолжен_Проверить_XMLСтрокаДвоичныеДанные"); ВсеТесты.Добавить("ТестДолжен_Проверить_XMLСтрокаСтрокаИзПеременной"); ВсеТесты.Добавить("ТестДолжен_Проверить_XMLЗначениеДвоичныеДанные"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLСтрокаNULL"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLСтрокаGUID"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLСтрокаПеречисления"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLСтрокаНедопустимыеТипы"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLЗначениеNULL"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLЗначениеGUID"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLЗначениеПеречисления"); + ВсеТесты.Добавить("ТестДолжен_Проверить_XMLЗначениеНедопустимыеТипы"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗаписьВBase64"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтениеИзBase64"); ВсеТесты.Добавить("Тест_ДолженПроверитьМатематическиеФункции"); + ВсеТесты.Добавить("Тест_ДолженПроверитьФункциюОкр"); ВсеТесты.Добавить("Тест_ДолженПроверитьЧтоФункцияМинСравниваетЧисла"); ВсеТесты.Добавить("Тест_ДолженПроверитьЧтоФункцияМаксСравниваетЧисла"); ВсеТесты.Добавить("Тест_ДолженПроверитьЧтоФункцияМинСравниваетСтроки"); @@ -67,9 +84,14 @@ ВсеТесты.Добавить("Тест_ДолженПроверитьИнформацияОбОшибкеОписание"); ВсеТесты.Добавить("Тест_ДолженПроверитьКраткоеПредставлениеОшибки"); ВсеТесты.Добавить("Тест_ДолженПроверитьПодробноеПредставлениеОшибки"); + ВсеТесты.Добавить("Тест_ДолженПроверитьИнформацияОбОшибкеСПричиной"); + ВсеТесты.Добавить("Тест_ДолженПроверитьИнформацияОбОшибкеПробрасываетсяПриВызовеИсключения"); ВсеТесты.Добавить("Тест_ДолженПроверитьОбъединениеПутей"); + + ВсеТесты.Добавить("Тест_ДолженПроверитьФункциюСимвол"); ВсеТесты.Добавить("Тест_ДолженПроверитьНеобязательныйПараметрФункцииКодСимвола"); + ВсеТесты.Добавить("Тест_ДолженПроверитьПараметрФункцииКодСимволаВнеСтроки"); ВсеТесты.Добавить("Тест_ДолженПроверитьФункциюСтрЧислоСтрок"); ВсеТесты.Добавить("Тест_ДолженПроверитьФункциюСтрПолучитьСтроку"); @@ -87,6 +109,8 @@ ВсеТесты.Добавить("Тест_ДолженПроверитьНСтрВозвращаетПервуюСтроку"); ВсеТесты.Добавить("Тест_ДолженПроверитьПолучениеТекущейДатыВМиллисекундах"); ВсеТесты.Добавить("Тест_ДолженПроверитьОкруглениеТекущейДатыДоСекунд"); + + ВсеТесты.Добавить("Тест_ДолженПроверитьСтрЗаменить"); Возврат ВсеТесты; КонецФункции @@ -370,6 +394,122 @@ КонецПроцедуры +Процедура ТестДолжен_Проверить_XMLСтрокаNULL() Экспорт + + юТест.ПроверитьРавенство("", XMLСтрока(NULL)); + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_XMLЗначениеNULL() Экспорт + + Тип = Тип("NULL"); + + Значение = XMLЗначение(Тип, " "); + юТест.ПроверитьТип(Значение, Тип); + + БылоИсключение = Ложь; + Попытка + Значение = XMLЗначение(Тип, "NULL"); + Исключение + БылоИсключение = Истина; + КонецПопытки; + + юТест.ПроверитьИстину(БылоИсключение,"Не было исключения при недопустимом представлении NULL в XMLЗначение"); + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_XMLСтрокаGUID() Экспорт + + Строка = "ae1d6d78-c3d5-4ef0-b096-2fb4f8e4717e"; + GUID = Новый УникальныйИдентификатор(Строка); + юТест.ПроверитьРавенство(Строка, XMLСтрока(GUID)); + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_XMLЗначениеGUID() Экспорт + + Тип = Тип("УникальныйИдентификатор"); + Строка = "ae1d6d78-c3d5-4ef0-b096-2fb4f8e4717e"; + + Значение = XMLЗначение(Тип, Строка); + юТест.ПроверитьТип(Значение, Тип); + юТест.ПроверитьРавенство(Строка(Значение), Строка); + + БылоИсключение = Ложь; + Попытка + Значение = XMLЗначение(Тип, "-a-e1d6d78c3d54ef0b0962fb4f8e4717e"); + Исключение + БылоИсключение = Истина; + КонецПопытки; + + юТест.ПроверитьИстину(БылоИсключение,"Не было исключения при недопустимом представлении GUID в XMLЗначение"); + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_XMLСтрокаПеречисления() Экспорт + + Перечисление = ДопустимыйЗнак.Неотрицательный; + юТест.ПроверитьРавенство("Неотрицательный", XMLСтрока(Перечисление)); // нужно "Nonnegative" + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_XMLЗначениеПеречисления() Экспорт + Тип = Тип("ДопустимаяДлина"); + Значение = XMLЗначение(Тип, "Fixed"); + + юТест.ПроверитьТип(Значение, Тип); + юТест.ПроверитьРавенство(Значение, ДопустимаяДлина.Фиксированная); + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_XMLСтрокаНедопустимыеТипы() Экспорт + + Массив = Новый Массив(1); + БылоИсключение = Ложь; + Попытка + Стр = XMLСтрока(Массив); + Исключение + БылоИсключение = Истина; + КонецПопытки; + + юТест.ПроверитьИстину(БылоИсключение,"Не было исключения при недопустимом типе в XMLСтрока"); + + Перечисление = НаправлениеСортировки.Возр; + БылоИсключение = Ложь; + Попытка + Стр = XMLСтрока(Перечисление); + Исключение + БылоИсключение = Истина; + КонецПопытки; + + юТест.ПроверитьИстину(БылоИсключение,"Не было исключения при недопустимом перечислении в XMLСтрока"); + +КонецПроцедуры + +Процедура ТестДолжен_Проверить_XMLЗначениеНедопустимыеТипы() Экспорт + + БылоИсключение = Ложь; + Попытка + Значение = XMLЗначение(Тип("НаправлениеСортировки"), "Возр"); + Исключение + БылоИсключение = Истина; + КонецПопытки; + + юТест.ПроверитьИстину(БылоИсключение,"Не было исключения при недопустимом типе в XMLСтрока"); + + Перечисление = НаправлениеСортировки.Возр; + БылоИсключение = Ложь; + Попытка + Значение = XMLЗначение(Тип("ДопустимаяДлина"), "Возр"); + Исключение + БылоИсключение = Истина; + КонецПопытки; + + юТест.ПроверитьИстину(БылоИсключение,"Не было исключения при недопустимом значении в XMLСтрока"); + +КонецПроцедуры + + Функция ПрочитатьФайлСкрипта(Знач Файл) Ч = Новый ЧтениеТекста(Файл); @@ -426,6 +566,35 @@ КонецПроцедуры +Процедура Тест_ДолженПроверитьФункциюОкр() Экспорт + + юТест.ПроверитьРавенство( Окр(1234.567), 1235); + юТест.ПроверитьРавенство( Окр(2234.567,2), 2234.57); + юТест.ПроверитьРавенство( Окр(3234,-2), 3200); + юТест.ПроверитьРавенство( Окр(4234.567,-2), 4200); + + юТест.ПроверитьРавенство( Окр(5234.55,1), 5234.6); + юТест.ПроверитьРавенство( Окр(6234.55,1,0), 6234.5); + юТест.ПроверитьРавенство( Окр(7234.55,1,1), 7234.6); + юТест.ПроверитьРавенство( Окр(8234.555,1,0), 8234.6); + юТест.ПроверитьРавенство( Окр(9234.55,1,-4), 9234.6); + + юТест.ПроверитьРавенство( Окр(10250,-2,0), 10200); + юТест.ПроверитьРавенство( Окр(11251,-2,0), 11300); + юТест.ПроверитьРавенство( Окр(12251,-2,1), 12300); + юТест.ПроверитьРавенство( Окр(-13250,-2,0), -13200); + юТест.ПроверитьРавенство( Окр(-14251,-2,0), -14300); + юТест.ПроверитьРавенство( Окр(-15250,-2,1), -15300); + + юТест.ПроверитьРавенство( Окр(-16234.55,1,0), -16234.5); + юТест.ПроверитьРавенство( Окр(-17234.55,1,1), -17234.6); + юТест.ПроверитьРавенство( Окр(-18234.551,1,0), -18234.6); + юТест.ПроверитьРавенство( Окр(-19234.551,1,1), -19234.6); + + юТест.ПроверитьРавенство( Окр("20234.5","0",-1.9), 20235); + +КонецПроцедуры + Процедура Тест_ДолженПроверитьЧтоФункцияМинСравниваетЧисла() Экспорт юТест.ПроверитьРавенство(1, Мин(3,1,2,5,4)); @@ -490,7 +659,7 @@ Приемник = ПолучитьИмяВременногоФайла(); - КопироватьФайл("http://httpbin.org/image", Приемник); + КопироватьФайл(мАдресРесурса + "/image", Приемник); Попытка ДД = Новый ДвоичныеДанные(Приемник); @@ -508,7 +677,7 @@ Приемник = ПолучитьИмяВременногоФайла(); - КопироватьФайл("https://httpbin.org/image", Приемник); + КопироватьФайл(мАдресРесурса + "/image", Приемник); Попытка ДД = Новый ДвоичныеДанные(Приемник); @@ -527,7 +696,7 @@ Приемник = ПолучитьИмяВременногоФайла(); Попытка - ПереместитьФайл("http://httpbin.org/image", Приемник); + ПереместитьФайл(мАдресРесурса + "/image", Приемник); Исключение //Отвалится по 405, значит delete метод отрабатывает Описание = ОписаниеОшибки(); @@ -730,6 +899,85 @@ ВызватьИсключение "Исключение не было брошено"; КонецПроцедуры +Процедура Тест_ДолженПроверитьИнформацияОбОшибкеСПричиной() Экспорт + + Попытка + ВыброситьТестовоеИсключение(); + Исключение + ИнформацияОбОшибкеОригинальная = ИнформацияОбОшибке(); + КонецПопытки; + + юТест.ПроверитьИстину(ЗначениеЗаполнено(ИнформацияОбОшибкеОригинальная), "Исключение не было брошено"); + + Объект = Новый ИнформацияОбОшибке("Я новая ошибка", Новый Массив, ИнформацияОбОшибкеОригинальная); + + Попытка + ВызватьИсключение Объект; + Исключение + ИнформацияОбОшибке = ИнформацияОбОшибке(); + + юТест.ПроверитьИстину( + СтрНайти(ИнформацияОбОшибке.Описание, "Я новая ошибка") > 0, + "Описание в новой информации об ошибке должно быть текстом из конструктора" + ); + + юТест.ПроверитьТип( + ИнформацияОбОшибке.Причина, + "ИнформацияОбОшибке", + "У информации об ошибке которому передали причину в конструкторе, + |должна быть заполнена причина после выброса исключения" + ); + + юТест.ПроверитьРавенство( + ИнформацияОбОшибке.Причина, + ИнформацияОбОшибкеОригинальная, + "Причина в новой информации об ошибке должна соответствовать переданной в конструктор" + ); + + юТест.ПроверитьИстину( + СтрНайти(ИнформацияОбОшибке.Причина.Описание, "тест-тест-тест") > 0, + "Оригинальное сообщение должно содержать текст исключения" + ); + + КонецПопытки; + +КонецПроцедуры + +Процедура Тест_ДолженПроверитьИнформацияОбОшибкеПробрасываетсяПриВызовеИсключения() Экспорт + + Попытка + ВыброситьТестовоеИсключение(); + Исключение + ИнформацияОбОшибке = ИнформацияОбОшибке(); + КонецПопытки; + + юТест.ПроверитьИстину(ЗначениеЗаполнено(ИнформацияОбОшибке), "Исключение не было брошено"); + + Попытка + ВызватьИсключение ИнформацияОбОшибке; + Исключение + ИнформацияОбОшибке = ИнформацияОбОшибке(); + + юТест.ПроверитьИстину( + СтрНайти(ИнформацияОбОшибке.Описание, "тест-тест-тест") > 0, + "Сообщение в проброшенном исключении должно содержать текст оригинального исключения" + ); + + юТест.ПроверитьТип( + ИнформацияОбОшибке.Причина, + "ИнформацияОбОшибке", + "У проброшенного исключения должна быть заполнена причина, оригинальным исключением" + ); + + юТест.ПроверитьИстину( + СтрНайти(ИнформацияОбОшибке.Причина.Описание, "тест-тест-тест") > 0, + "Оригинальное сообщение должно содержать текст исключения" + ); + + КонецПопытки; + +КонецПроцедуры + Процедура Тест_ДолженПроверитьОбъединениеПутей() Экспорт СИ = Новый СистемнаяИнформация(); Если Найти(СИ.ВерсияОС,"Windows") > 0 Тогда @@ -750,6 +998,18 @@ КонецПроцедуры +Процедура Тест_ДолженПроверитьФункциюСимвол() Экспорт + + Строка = Символ(1040); + юТест.ПроверитьРавенство(Строка, "А"); + + Строка = Символ(-2); + юТест.ПроверитьРавенство(Строка, "", "код < 0"); + Строка = Символ(65859); + юТест.ПроверитьРавенство(Строка, "", "код > 65536"); + +КонецПроцедуры + Процедура Тест_ДолженПроверитьНеобязательныйПараметрФункцииКодСимвола() Экспорт Строка = "АБВ"; //Символ(1040) + Символ(1041) + Символ(1042); @@ -761,6 +1021,21 @@ КонецПроцедуры +Процедура Тест_ДолженПроверитьПараметрФункцииКодСимволаВнеСтроки() Экспорт + + Строка = "АБВ"; + юТест.ПроверитьРавенство(-1, КодСимвола(Строка,0), "номер символа = 0"); + юТест.ПроверитьРавенство(-1, КодСимвола(Строка,-3), "номер символа < 0"); + юТест.ПроверитьРавенство(-1, КодСимвола(Строка,7), "номер символа > длины строки"); + + Строка = ""; + юТест.ПроверитьРавенство(-1, КодСимвола(Строка), "пустая строка, без параметра"); + юТест.ПроверитьРавенство(-1, КодСимвола(Строка,0), "пустая строка, номер символа = 0"); + юТест.ПроверитьРавенство(-1, КодСимвола(Строка,-1), "пустая строка, номер символа < 0"); + юТест.ПроверитьРавенство(-1, КодСимвола(Строка,1), "пустая строка, номер символа > 0"); + +КонецПроцедуры + Процедура Тест_ДолженПроверитьФункциюСтрЧислоСтрок() Экспорт СтрокаДляТеста = "Это одна строка"; @@ -810,6 +1085,10 @@ Результат = РаскодироватьСтроку(Строка, СпособКодированияСтроки.КодировкаURL); юТест.ПроверитьРавенство("%1Script рулит!%", Результат); + // Проверить передачу Неопределено и Null (не должно упасть) + РаскодироватьСтроку(Неопределено, СпособКодированияСтроки.КодировкаURL); + РаскодироватьСтроку(Null, СпособКодированияСтроки.КодировкаURL); + КонецПроцедуры Процедура Тест_ДолженПроверитьДекодированиеURLВКодировкеURL() Экспорт @@ -916,3 +1195,28 @@ ДатаСозданнаяВручную = Дата(Формат(ДатаИсходная, "ДФ=ггггММддЧЧммсс")); юТест.ПроверитьРавенство(ДатаИсходная, ДатаСозданнаяВручную); КонецПроцедуры + +Процедура Тест_ДолженПроверитьСтрЗаменить() Экспорт + + Тест = СтрЗаменить("123", "1", "4"); + юТест.ПроверитьРавенство("423", Тест); + + Тест = СтрЗаменить("", "1", "4"); + юТест.ПроверитьРавенство("", Тест); + + Тест = СтрЗаменить("123", "", "4"); + юТест.ПроверитьРавенство("123", Тест); + + Тест = СтрЗаменить("123", "1", ""); + юТест.ПроверитьРавенство("23", Тест); + + Тест = СтрЗаменить("123", "", ""); + юТест.ПроверитьРавенство("123", Тест); + + Тест = СтрЗаменить("", "", "123"); + юТест.ПроверитьРавенство("", Тест); + + Тест = СтрЗаменить("", "", ""); + юТест.ПроверитьРавенство("", Тест); + +КонецПроцедуры diff --git a/tests/global-json.os b/tests/global-json.os new file mode 100644 index 000000000..506df914e --- /dev/null +++ b/tests/global-json.os @@ -0,0 +1,362 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + СписокТестов = Новый Массив; + + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеСтруктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеСоответствия"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеЧисла"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеСтроки"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеПустогоМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеПустойСтруктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеПустогоСоответствия"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеВложенныхМассивов"); + + // issue #1373 + СписокТестов.Добавить("Тест_ДолженПроверить_ОднострочныйКомментарийВКонцеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ОднострочныйКомментарийВнутриСтуктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ОднострочныйКомментарийВНачалеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_МногострочныйКомментарийВнутриСтуктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_МногострочныйКомментарийВнутриМассива"); + + СписокТестов.Добавить("Тест_ДолженПроверить_ПродолжениеЧтения"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПродолжениеЧтенияСИзменениемСтруктуры"); + + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ДляUndefined"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомМассиве"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомОбъекте"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномМассиве"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномОбъекте"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПропущенномЗначенииСвойства"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомКомментарии"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНедопустимомКлючеСтруктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПроизвольныйКлючСоответствия"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПустомЗначенииВнутриМассива"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПустомЗначенииВКонцеМассива"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПустомЗначенииСвойства"); + СписокТестов.Добавить("Тест_ДолженПроверить_ЗапятуюВКонцеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ЗапятуюВКонцеОбъекта"); + + Возврат СписокТестов; + +КонецФункции + +Функция ПрочитатьJSONИзСтроки(Стр, Знач ВСоответствие=Ложь) + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Стр); + Результат = ПрочитатьJSON(Чтение, ВСоответствие); + Чтение.Закрыть(); + Возврат Результат; +КонецФункции + +Процедура Тест_ДолженПроверить_ПолучениеМассива() Экспорт + Текст = "[123,456,789]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(3, Результат.Количество()); + юТест.ПроверитьРавенство(456, Результат[1]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеСтруктуры() Экспорт + Текст = "{""Поле1"":123,""Поле2"":456}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат) ); + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство(456, Результат.Поле2); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеСоответствия() Экспорт + Текст = "{""Поле1"":123,""Поле2"":456}"; + Результат = ПрочитатьJSONИзСтроки(Текст,Истина); + + юТест.ПроверитьРавенство(Тип("Соответствие"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство(456, Результат["Поле2"]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеЧисла() Экспорт + Текст = "321"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Число"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(321, Результат); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеСтроки() Экспорт + Текст = """абв"""; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(ТипЗнч(Результат), Тип("Строка")); + юТест.ПроверитьРавенство(Результат, "абв"); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеПустогоМассива() Экспорт + Текст = "[]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат) ); + юТест.ПроверитьРавенство(0, Результат.Количество()); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеПустойСтруктуры() Экспорт + Текст = "{}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(Результат.Количество(), 0); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеПустогоСоответствия() Экспорт + Текст = "{}"; + Результат = ПрочитатьJSONИзСтроки(Текст,Истина); + + юТест.ПроверитьРавенство(Тип("Соответствие"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(Результат.Количество(), 0); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеВложенныхМассивов() Экспорт + Текст = "[[123,456],789,[-3,-2,""-0""],{""Ф"":[""Ы"",222]}]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(4, Результат.Количество()); + юТест.ПроверитьРавенство(Тип("Число"), ТипЗнч(Результат[1])); + юТест.ПроверитьРавенство(789, Результат[1]); + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат[2])); + юТест.ПроверитьРавенство(3, Результат[2].Количество()); + юТест.ПроверитьРавенство("-0", Результат[2][2]); + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат[3])); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ОднострочныйКомментарийВКонцеМассива() Экспорт + Текст = "[ + |{ + | ""NAME"": ""n1"", + | ""Method"": ""m1"", + | ""PATH"": ""p1"" + |}, + |{ + | ""NAME"": ""n2"", + | //""Method"": ""m2"", + | ""PATH"": ""p2"" + |} + |//}, + |//{ + |// ""NAME"": """", + |]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство("p2", Результат[1].PATH); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ОднострочныйКомментарийВнутриСтуктуры() Экспорт + Текст = " + |{ + | ""test"": //fail + | ""pass"" + |}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("pass", Результат.test); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ОднострочныйКомментарийВНачалеМассива() Экспорт + Текст = " + |[// !!! + |""fail"",""pass""] + |"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("pass", Результат[1]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_МногострочныйКомментарийВнутриСтуктуры() Экспорт + Текст = " + |{ + | ""test"": /*fail + | ""??""*/ ""ok"" + |}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("ok", Результат.test); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_МногострочныйКомментарийВнутриМассива() Экспорт + Текст = " + |[ + | ""test"", /*fail + | ""??""]*/ ""ok"" + |]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("ok", Результат[1]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПродолжениеЧтения() Экспорт + Текст = "[123,456] {""аб"":""вг"",""де"":""жз"",""ик"":""лм""}"; + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Результат1 = ПрочитатьJSON(Чтение); + Результат2 = ПрочитатьJSON(Чтение); + Чтение.Закрыть(); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат1)); + юТест.ПроверитьРавенство(2, Результат1.Количество()); + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат2)); + юТест.ПроверитьРавенство(3, Результат2.Количество()); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПродолжениеЧтенияСИзменениемСтруктуры() Экспорт + Текст = "{""аб"":""вг"",""де"":""жз""} {""ик"":""лм""}"; + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Результат1 = ПрочитатьJSON(Чтение); + Результат2 = ПрочитатьJSON(Чтение,Истина); + Чтение.Закрыть(); + + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат1)); + юТест.ПроверитьРавенство(2, Результат1.Количество()); + юТест.ПроверитьРавенство(Тип("Соответствие"), ТипЗнч(Результат2)); + юТест.ПроверитьРавенство(1, Результат2.Количество()); + юТест.ПроверитьРавенство("лм", Результат2["ик"]); +КонецПроцедуры + + +Процедура Тест_ДолженВызватьОшибку_ДляUndefined() Экспорт + Текст = "[123,undefined,456]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомМассиве() Экспорт + Текст = "[[[123,456]]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомОбъекте() Экспорт + Текст = "{""aбв"":456"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномМассиве() Экспорт + Текст = "{""aбв"":[456,789}"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномОбъекте() Экспорт + Текст = "[{""aбв"":456]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПропущенномЗначенииСвойства() Экспорт + Текст = "{""aбв"":}"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомКомментарии() Экспорт + Текст = "[123,456 /*]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНедопустимомКлючеСтруктуры() Экспорт + Текст = "{""123"":""456""}"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПроизвольныйКлючСоответствия() Экспорт + Текст = "{""%/$!"":""456""}"; + Результат = ПрочитатьJSONИзСтроки(Текст, Истина); + юТест.ПроверитьРавенство("456", Результат["%/$!"]); +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПустомЗначенииВнутриМассива() Экспорт + Текст = "[123,,456]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПустомЗначенииВКонцеМассива() Экспорт + Текст = "[123,456,,]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПустомЗначенииСвойства() Экспорт + Текст = "{""аб"": , ""еж"":""кл""}";; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ЗапятуюВКонцеМассива() Экспорт + Текст = "[123,456,789,]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(3, Результат.Количество()); + юТест.ПроверитьРавенство(789, Результат[2]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ЗапятуюВКонцеОбъекта() Экспорт + Текст = "{""аб"":""гд"",""еж"":""кл"",}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство("кл", Результат["еж"]); +КонецПроцедуры diff --git a/tests/global-new.os b/tests/global-new.os index 096027e53..dc7a8dbe5 100644 --- a/tests/global-new.os +++ b/tests/global-new.os @@ -11,6 +11,10 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьПовторноеПодключениеТогоЖеСценария"); ВсеТесты.Добавить("ТестДолжен_ПроверитьОшибочноеПодключениеТогоЖеИмениТипа"); ВсеТесты.Добавить("ТестДолжен_ПроверитьНепосредственноеСозданиеОбъектаИзФайла"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеМассиваЧерезФункциюНовый"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеЧерезФункциюНовыйИПустойМассивАргументов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеЧерезФункциюНовыйИМассивАргументов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеЧерезФункциюНовыйИФиксированныйМассивАргументов"); ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеОбъектаПоПараметруИмениКласса"); ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеОбъектаПоПараметруИмениКлассаЧерезФункциюНовый"); @@ -36,6 +40,27 @@ ПроверитьСозданиеМассива(Объект); КонецПроцедуры +Процедура ТестДолжен_ПроверитьСозданиеЧерезФункциюНовыйИПустойМассивАргументов() Экспорт + Объект = Новый("Рефлектор", Новый Массив); + юТест.ПроверитьРавенство(Тип("Рефлектор"), ТипЗнч(Объект)); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСозданиеЧерезФункциюНовыйИМассивАргументов() Экспорт + МассивАрг = Новый Массив; + МассивАрг.Добавить(ТекущийСценарий().Источник); + Объект = Новый("Файл", МассивАрг); + юТест.ПроверитьРавенство(Объект.ПолноеИмя, ТекущийСценарий().Источник); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСозданиеЧерезФункциюНовыйИФиксированныйМассивАргументов() Экспорт + Массив = Новый Массив; + Массив.Добавить(20); + МассивАрг = Новый ФиксированныйМассив(Массив); + Объект = Новый("Массив", МассивАрг); + юТест.ПроверитьРавенство(Объект.Количество(), 20); +КонецПроцедуры + + Процедура ТестДолжен_ПроверитьСозданиеОбъектаИзСценария() Экспорт ТекПуть = Новый Файл(ТекущийСценарий().Источник).Путь; ПодключитьСценарий(ТекПуть+"example-test.os", "Пример_example_test"); diff --git a/tests/hash/hash.os b/tests/hash.os similarity index 100% rename from tests/hash/hash.os rename to tests/hash.os diff --git a/tests/http.os b/tests/http.os index eb793c6ba..9e6fb44ee 100644 --- a/tests/http.os +++ b/tests/http.os @@ -1,6 +1,7 @@ Перем юТест; -Перем мАдресРесурса; // URL ресурса (хоста) httpbin.org для тестирования запросов +Перем мАдресРесурса; // URL ресурса (хоста) для тестирования запросов +Перем ПортТестовогоСервера; Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт @@ -8,6 +9,7 @@ ВсеТесты = Новый Массив; + // Отключено временно: см https://github.com/EvilBeaver/OneScript/pull/1018 //ВсеТесты.Добавить("ТестДолжен_ПроверитьОтключениеПеренаправления"); //ВсеТесты.Добавить("ТестДолжен_ПроверитьАвтоматическоеПеренаправление"); ВсеТесты.Добавить("ТестДолжен_ПроверитьАвтоматическуюРаспаковкуGZip"); @@ -35,10 +37,24 @@ ВсеТесты.Добавить("ТестДолженПроверитьЧтоМожноЗадатьТелоЗапросаСПомощьюПотока"); ВсеТесты.Добавить("ТестДолженПроверитьЧтоМетодыБезТелаПриУстановленномТелеУспешноВыполняются"); + ВсеТесты.Добавить("ТестДолженПроверитьЧтоРаботаетПолучениеДвоичныхДанныхИзОтвета"); + + ВсеТесты.Добавить("ТестДолженПроверитьЧтоТелоМожноПолучитьНесколькоРаз"); + ВсеТесты.Добавить("ТестДолженПроверитьПолучениеТелаПослеЗакрытогоПотока"); + ВсеТесты.Добавить("ТестДолженПроверитьНедоступностьЧистогоПотокаПослеПолученияТела"); + + ВсеТесты.Добавить("ТестДолженПроверитьПолучениеStreamEvent"); Возврат ВсеТесты; КонецФункции +Процедура ПередЗапускомТеста() Экспорт + Если мАдресРесурса = Неопределено Тогда + мАдресРесурса = ВыбратьДоступныйХост(); + Сообщить("HTTP тесты: используется ресурс " + мАдресРесурса); + КонецЕсли; +КонецПроцедуры + Процедура ТестДолжен_ПроверитьАвтоматическуюРаспаковкуGZip() Экспорт Запрос = Новый HttpЗапрос("/gzip"); @@ -79,9 +95,9 @@ Процедура ТестДолжен_ПроверитьАвторизациюПрокси() Экспорт - Прокси = Новый ИнтернетПрокси(); + Прокси = Новый ИнтернетПрокси(Ложь); - Прокси.Установить("http","proxy.server.lan", 8080, "someuser", "somepassword"); + Прокси.Установить("http","proxy.server.lan", 8080, "someuser", "somepassword", Ложь); юТест.ПроверитьРавенство("someuser",Прокси.Пользователь("http")); юТест.ПроверитьРавенство("somepassword",Прокси.Пароль("http")); @@ -368,6 +384,157 @@ КонецПроцедуры +Процедура ТестДолженПроверитьЧтоРаботаетПолучениеДвоичныхДанныхИзОтвета() Экспорт + + Запрос = Новый HttpЗапрос("/bytes/10"); + Соединение = Новый HttpСоединение(мАдресРесурса); + Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос); + + ДД = Ответ.ПолучитьТелоКакДвоичныеДанные(); + юТест.ПроверитьТип(ДД, Тип("ДвоичныеДанные")); + юТест.ПроверитьРавенство(ДД.Размер(), 10); + +КонецПроцедуры + + Процедура ТестДолженПроверитьЧтоТелоМожноПолучитьНесколькоРаз() Экспорт + + Запрос = Новый HttpЗапрос("/get?p1=v1&p2=v2"); + + Соединение = Новый HttpСоединение(мАдресРесурса); + Ответ = Соединение.Получить(Запрос); + + юТест.ПроверитьРавенство(200, Ответ.КодСостояния); + + Ответ.ПолучитьТелоКакПоток(); + + юТест.ПроверитьРавенство(Ответ.ПолучитьТелоКакПоток().ДоступноЧтение, Истина); + + юТест.ПроверитьРавенство(ТипЗнч(Ответ.ПолучитьТелоКакДвоичныеДанные()), Тип("ДвоичныеДанные")); + юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p1"": ""v1"""); + юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p2"": ""v2"""); + + юТест.ПроверитьРавенство(ТипЗнч(Ответ.ПолучитьТелоКакДвоичныеДанные()), Тип("ДвоичныеДанные")); + + // Оригинальный поток уже вычитан вернется поддельный + юТест.ПроверитьРавенство(Ответ.ПолучитьТелоКакПоток().ДоступноЧтение, Истина); + +КонецПроцедуры + +Процедура ТестДолженПроверитьПолучениеТелаПослеЗакрытогоПотока() Экспорт + + Запрос = Новый HttpЗапрос("/get?p1=v1&p2=v2"); + + Соединение = Новый HttpСоединение(мАдресРесурса); + Ответ = Соединение.Получить(Запрос); + + юТест.ПроверитьРавенство(200, Ответ.КодСостояния); + + Поток = Ответ.ПолучитьТелоКакПоток(); + Поток.Закрыть(); + + юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p2"": ""v2"""); + +КонецПроцедуры + +Процедура ТестДолженПроверитьНедоступностьЧистогоПотокаПослеПолученияТела() Экспорт + + Запрос = Новый HttpЗапрос("/get?p1=v1&p2=v2"); + + Соединение = Новый HttpСоединение(мАдресРесурса); + Ответ = Соединение.Получить(Запрос); + + юТест.ПроверитьРавенство(200, Ответ.КодСостояния); + + юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p2"": ""v2"""); + + // чистый поток не доступен для чтения + Поток = Ответ.ПолучитьТелоКакПоток(Истина); + юТест.ПроверитьРавенство(Поток.ДоступноЧтение, Ложь); + + // оберточный нормальный + Поток = Ответ.ПолучитьТелоКакПоток(); + юТест.ПроверитьРавенство(Поток.ДоступноЧтение, Истина); + ЧтениеДанных = Новый ЧтениеДанных(Поток); + юТест.ПроверитьВхождение( + ПолучитьСтрокуИзДвоичныхДанных(ЧтениеДанных.Прочитать().ПолучитьДвоичныеДанные()), + """p2"": ""v2""" + ); + +КонецПроцедуры + +Процедура ТестДолженПроверитьПолучениеStreamEvent() Экспорт + + МенеджерФоновыхЗаданий = Новый МенеджерФоновыхЗаданий; + ЗаданиеВебсервера = МенеджерФоновыхЗаданий.Выполнить(ЭтотОбъект, "Вебсервер"); + + ПортТестовогоСервера = 8181; + Соединение = Новый HTTPСоединение("http://127.0.0.1:" + ПортТестовогоСервера); + Запрос = Новый HTTPЗапрос("/"); + + // Подождем пока поднимится сервер + Приостановить(1000); + + Старт = ТекущаяУниверсальнаяДатаВМиллисекундах(); + + Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос); + + Прошло = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт; + ютест.ПроверитьМеньше(Прошло, 600); + + ТелоПоток = Ответ.ПолучитьТелоКакПоток(Истина); + юТест.ПроверитьТип(ТелоПоток, Тип("Поток")); + + ЧтениеДанных = Новый ЧтениеДанных(ТелоПоток); + Таймаут = 600; + Пока Истина Цикл + + ПрочтенныеДанные = ЧтениеДанных.Прочитать(20); + + Строка = ПолучитьСтрокуИзДвоичныхДанных(ПрочтенныеДанные.ПолучитьДвоичныеДанные()); + Прошло = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт; + + ютест.ПроверитьМеньше(Прошло, Таймаут); + + Таймаут = Таймаут + Таймаут; + Если Строка = "" ИЛИ Строка = "data: [DONE]" + Символы.ПС + Символы.ПС Тогда + Прервать; + КонецЕсли; + + юТест.ПроверитьРавенство(Строка, "data: [1]" + Символы.ПС + Символы.ПС); + + КонецЦикла; + + ТелоПоток.Закрыть(); + + МенеджерФоновыхЗаданий.Очистить(); +КонецПроцедуры + +Функция ОбработчикЗапроса(Контекст, СледующийОбработчик) Экспорт + + Контекст.Ответ.КодСостояния = 200; + Контекст.Ответ.ТипКонтента = "text/event-stream"; + + ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [1]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения(); + ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело); + + Приостановить(500); + ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [1]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения(); + ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело); + + Приостановить(500); + ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [DONE]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения(); + ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело); + +КонецФункции + +Процедура Вебсервер() Экспорт + + Вебсервер = Новый ВебСервер(ПортТестовогоСервера); + Вебсервер.ДобавитьОбработчикЗапросов(ЭтотОбъект, "ОбработчикЗапроса"); + Вебсервер.Запустить(); + +КонецПроцедуры + Функция JsonВОбъект(Json) ЧтениеJSON = Новый ЧтениеJSON; @@ -380,8 +547,50 @@ КонецФункции -/////////////////////////////////////////////////////////////////// -/// ИНИЦИАЛИЗАЦИЯ -/////////////////////////////////////////////////////////////////// +Функция ПроверитьДоступность(АдресХоста) + + Попытка + // Короткие таймауты для быстрой проверки доступности + Соединение = Новый HttpСоединение(АдресХоста,,,,,5); + Запрос = Новый HttpЗапрос("/get"); + Ответ = Соединение.Получить(Запрос); + Возврат Ответ.КодСостояния = 200; + Исключение + Сообщить("HTTP тесты: недоступен хост " + АдресХоста + " (" + ОписаниеОшибки() + ")"); + Возврат Ложь; + КонецПопытки; + +КонецФункции -мАдресРесурса = "httpbin.org"; +Функция ВыбратьДоступныйХост() Экспорт + + // Если задана переменная окружения, используем ее при наличии значения + ЗначениеИзОкружения = ПолучитьПеременнуюСреды("OS_HTTP_TEST_HOST"); + Если ЗначениеЗаполнено(ЗначениеИзОкружения) Тогда + Если ПроверитьДоступность(ЗначениеИзОкружения) Тогда + Сообщить("HTTP тесты: выбран хост из переменной окружения OS_HTTP_TEST_HOST = " + ЗначениеИзОкружения); + Возврат ЗначениеИзОкружения; + Иначе + Сообщить("HTTP тесты: хост из переменной окружения недоступен: " + ЗначениеИзОкружения); + КонецЕсли; + КонецЕсли; + + // Пробуем типовые варианты httpbin + Кандидаты = Новый Массив; + Кандидаты.Добавить("http://127.0.0.1:8085"); + Кандидаты.Добавить("https://connectorhttp.ru"); + Кандидаты.Добавить("https://httpbin.org"); + Кандидаты.Добавить("https://httpbingo.org"); + + Для Каждого Хост Из Кандидаты Цикл + Если ПроверитьДоступность(Хост) Тогда + Сообщить("HTTP тесты: автоматически выбран доступный хост " + Хост); + Возврат Хост; + КонецЕсли; + КонецЦикла; + + // По умолчанию сохраняем прежнее поведение + Сообщить("HTTP тесты: не удалось определить доступный хост, используется значение по умолчанию https://httpbin.org"); + Возврат "https://httpbin.org"; + +КонецФункции \ No newline at end of file diff --git a/tests/iterators/annotated_iterator.os b/tests/iterators/annotated_iterator.os new file mode 100644 index 000000000..b27a2b63a --- /dev/null +++ b/tests/iterators/annotated_iterator.os @@ -0,0 +1,24 @@ + +Перем Индекс; +Перем Коллекция; +Перем Размер; + +&Итератор +Процедура ПриСозданииОбъекта(пКоллекция) + Коллекция = пКоллекция; + Индекс = 0; + Размер = Коллекция.Количество(); +КонецПроцедуры + +Функция Следующий() + Если Индекс < Размер-1 Тогда + Индекс = Индекс + 1; + Возврат Истина; + Иначе + Возврат Ложь; + КонецЕсли; +КонецФункции + +Функция ТекущийЭлемент() + Возврат Коллекция.Получить(Индекс); +КонецФункции \ No newline at end of file diff --git a/tests/iterators/collection.os b/tests/iterators/collection.os new file mode 100644 index 000000000..25709f27e --- /dev/null +++ b/tests/iterators/collection.os @@ -0,0 +1,17 @@ +#Обходимое + +Функция ПолучитьИтератор() + Возврат Новый ИтераторКоллекции(ЭтотОбъект); +КонецФункции + +Функция Количество() Экспорт + Возврат 10; +КонецФункции + +Функция Получить(Индекс) Экспорт + Если Индекс < 10 тогда + Возврат Индекс; + КонецЕсли; + + ВызватьИсключение "Индекс выходит за границы коллекции"; +КонецФункции \ No newline at end of file diff --git a/tests/iterators/collection_method_style.os b/tests/iterators/collection_method_style.os new file mode 100644 index 000000000..e55a747dd --- /dev/null +++ b/tests/iterators/collection_method_style.os @@ -0,0 +1,20 @@ + +&Обходимое +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +Функция ПолучитьИтератор() + Возврат Новый ИтераторСАннотацией(ЭтотОбъект); +КонецФункции + +Функция Количество() Экспорт + Возврат 10; +КонецФункции + +Функция Получить(Индекс) Экспорт + Если Индекс < 10 тогда + Возврат Индекс; + КонецЕсли; + + ВызватьИсключение "Индекс выходит за границы коллекции"; +КонецФункции \ No newline at end of file diff --git a/tests/iterators/iterator.os b/tests/iterators/iterator.os new file mode 100644 index 000000000..7868e21dc --- /dev/null +++ b/tests/iterators/iterator.os @@ -0,0 +1,23 @@ + +Перем Индекс; +Перем Коллекция; +Перем Размер; + +Процедура ПриСозданииОбъекта(пКоллекция) + Коллекция = пКоллекция; + Индекс = 0; + Размер = Коллекция.Количество(); +КонецПроцедуры + +Функция Следующий() + Если Индекс < Размер-1 Тогда + Индекс = Индекс + 1; + Возврат Истина; + Иначе + Возврат Ложь; + КонецЕсли; +КонецФункции + +Функция ТекущийЭлемент() + Возврат Коллекция.Получить(Индекс); +КонецФункции \ No newline at end of file diff --git a/tests/iterators/test-iterators.os b/tests/iterators/test-iterators.os new file mode 100644 index 000000000..a54de631a --- /dev/null +++ b/tests/iterators/test-iterators.os @@ -0,0 +1,57 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_ОбойтиКоллекцию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоМодулиСАннотациейКонструктораРаботают"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоИтераторПроверяетсяПриКомпиляции"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ОбойтиКоллекцию() Экспорт + + ТекКаталог = ТекущийСценарий().Каталог; + + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "iterator.os"), "ИтераторКоллекции"); + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "collection.os"), "Коллекция"); + + Коллекция = Новый Коллекция; + + Индекс = 1; + Для Каждого Элемент Из Коллекция цикл + юТест.ПроверитьРавенство(Индекс, Элемент); + Индекс = Индекс + 1; + КонецЦикла; + + юТест.ПроверитьРавенство(10, Индекс); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоМодулиСАннотациейКонструктораРаботают() Экспорт + + ТекКаталог = ТекущийСценарий().Каталог; + + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "collection_method_style.os"), "КоллекцияСАннотациейМетода"); + ПодключитьСценарий(ОбъединитьПути(ТекКаталог, "annotated_iterator.os"), "ИтераторСАннотацией"); + + Коллекция = Новый КоллекцияСАннотациейМетода; + + Индекс = 1; + Для Каждого Элемент Из Коллекция цикл + юТест.ПроверитьРавенство(Индекс, Элемент); + Индекс = Индекс + 1; + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоИтераторПроверяетсяПриКомпиляции() Экспорт + + Скрипт = "#Итератор + |Процедура Привет() КонецПроцедуры"; + + юТест.ПроверитьКодСОшибкой(Скрипт, "Обязательный метод Следующий отсутствует"); +КонецПроцедуры \ No newline at end of file diff --git a/tests/json/json-mock.json b/tests/json/json-mock.json index 519215a78..c570d1e8e 100644 --- a/tests/json/json-mock.json +++ b/tests/json/json-mock.json @@ -1,6 +1,5 @@ { "Null": null, - "Неопределено": undefined, "Ложь": false, "Истина": true, "Число (плавающая точка)": 1.001e-2, @@ -17,7 +16,6 @@ "Пустой объект": {}, "Массив": [ null, - undefined, false, true, 1.001e-2, diff --git a/tests/json/json_dataExtractor.os b/tests/json/json_dataExtractor.os new file mode 100644 index 000000000..6887e8a35 --- /dev/null +++ b/tests/json/json_dataExtractor.os @@ -0,0 +1,374 @@ + +Перем юТест; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИзСтрокиJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИзФайлаJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИзПотокаJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьСтроковоеЗначениеИзJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьЦелоеЧислоИзJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьПлавающееЧислоИзJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьСтрокуJSONИзJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьСоответствиеИзJSON"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИПосчитатьКоличество"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИПосчитатьСумму"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИПосчитатьСреднее"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИПосчитатьМинимум"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИПосчитатьМаксимум"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИВыбратьПервоеЗначение"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИВыбратьПоследнееЗначение"); + ВсеТесты.Добавить("ТестДолжен_ИзвлечьДанныеИВыбратьПоляОбъекта"); + + Возврат ВсеТесты; + +КонецФункции // ПолучитьСписокТестов() + +Процедура ТестДолжен_ИзвлечьДанныеИзСтрокиJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$.Manufacturers[?(@.Name == 'Acme Co')].Name"); + + ТестовоеЗначение = "Acme Co"; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИзСтрокиJSON() + +Процедура ТестДолжен_ИзвлечьДанныеИзФайлаJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + ПутьКТестовомуКаталогу = ОбъединитьПути(ТекущийСценарий().Каталог, "test"); + ОбеспечитьКаталог(ПутьКТестовомуКаталогу); + + ИмяТестовогоФайла = "testFile1.json"; + ПутьКФайлу = ОбъединитьПути(ПутьКТестовомуКаталогу, ИмяТестовогоФайла); + + Текст = Новый ТекстовыйДокумент(); + Текст.УстановитьТекст(ДанныеJSON()); + Текст.Записать(ПутьКФайлу, КодировкаТекста.UTF8); + + ИзвлечениеДанных.ОткрытьФайл(ПутьКФайлу, КодировкаТекста.UTF8); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Name == 'Anvil')].Price"); + + ТестовоеЗначение = 50; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + + УдалитьФайлы(ПутьКТестовомуКаталогу); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИзФайлаJSON() + +Процедура ТестДолжен_ИзвлечьДанныеИзПотокаJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + ПутьКТестовомуКаталогу = ОбъединитьПути(ТекущийСценарий().Каталог, "test"); + ОбеспечитьКаталог(ПутьКТестовомуКаталогу); + + ИмяТестовогоФайла = "testFile1.json"; + ПутьКФайлу = ОбъединитьПути(ПутьКТестовомуКаталогу, ИмяТестовогоФайла); + + Текст = Новый ТекстовыйДокумент(); + Текст.УстановитьТекст(ДанныеJSON()); + Текст.Записать(ПутьКФайлу, КодировкаТекста.UTF8); + + Поток = Новый ФайловыйПоток(ПутьКФайлу, РежимОткрытияФайла.Открыть); + + ИзвлечениеДанных.ОткрытьПоток(Поток, КодировкаТекста.UTF8); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Name == 'Anvil')].Price"); + + ТестовоеЗначение = 50; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + + УдалитьФайлы(ПутьКТестовомуКаталогу); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИзФайлаJSON() + +Процедура ТестДолжен_ИзвлечьСтроковоеЗначениеИзJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Price >= 50 && @.Name == 'Elbow Grease')].Name"); + + ТестовоеЗначение = "Elbow Grease"; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьСтроковоеЗначениеИзJSON() + +Процедура ТестДолжен_ИзвлечьЦелоеЧислоИзJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Name == 'Anvil')].Price"); + + ТестовоеЗначение = 50; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьЦелоеЧислоИзJSON() + +Процедура ТестДолжен_ИзвлечьПлавающееЧислоИзJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Price >= 50 && @.Name == 'Elbow Grease')].Price"); + + ТестовоеЗначение = 99.95; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьПлавающееЧислоИзJSON() + +Процедура ТестДолжен_ИзвлечьСтрокуJSONИзJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Price >= 50)]"); + + ТестовоеЗначение = "[ + |{ + |""Name"": ""Anvil"", + |""Price"": 50 + |}, + |{ + |""Name"": ""Elbow Grease"", + |""Price"": 99.95 + |} + |]"; + + Результат = СтрЗаменить(Результат, Символы.ВК, ""); + ТестовоеЗначение = СтрЗаменить(ТестовоеЗначение, Символы.ВК, ""); + + юТест.ПроверитьРавенство(СокрЛП(Результат), ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьСтрокуJSONИзJSON() + +Процедура ТестДолжен_ИзвлечьСоответствиеИзJSON() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Price >= 50)]", Истина, Ложь); + + // "[ + // | { + // | ""Name"": ""Anvil"", + // | ""Price"": 50 + // | }, + // | { + // | ""Name"": ""Elbow Grease"", + // | ""Price"": 99.95 + // | } + // |]"; + + юТест.ПроверитьРавенство(Результат.Количество(), 2, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьСоответствиеИзJSON() + +Процедура ТестДолжен_ИзвлечьДанныеИПосчитатьКоличество() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Price >= 50)].Price.length()"); + + юТест.ПроверитьРавенство(Результат, 2, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИПосчитатьКоличество() + +Процедура ТестДолжен_ИзвлечьДанныеИПосчитатьСумму() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[*].Price.sum()"); + + юТест.ПроверитьРавенство(Результат, 153.95, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИПосчитатьСумму() + +Процедура ТестДолжен_ИзвлечьДанныеИПосчитатьСреднее() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[*].Price.avg()"); + + юТест.ПроверитьРавенство(Окр(Результат, 3), 51.317, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИПосчитатьСреднее() + +Процедура ТестДолжен_ИзвлечьДанныеИПосчитатьМаксимум() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[*].Price.max()"); + + юТест.ПроверитьРавенство(Результат, 99.95, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИПосчитатьМаксимум() + +Процедура ТестДолжен_ИзвлечьДанныеИПосчитатьМинимум() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[*].Price.min()"); + + юТест.ПроверитьРавенство(Результат, 4, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИПосчитатьМинимум() + +Процедура ТестДолжен_ИзвлечьДанныеИВыбратьПервоеЗначение() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[*].first()"); + Результат = СтрЗаменить(Результат, Символы.ПС, ""); + Результат = СтрЗаменить(Результат, Символы.ВК, ""); + Результат = СокрЛП(Результат); + + ТестовоеЗначение = "{""Name"": ""Anvil"",""Price"": 50}"; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИВыбратьПервоеЗначение() + +Процедура ТестДолжен_ИзвлечьДанныеИВыбратьПоследнееЗначение() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[*].last()"); + Результат = СтрЗаменить(Результат, Символы.ПС, ""); + Результат = СтрЗаменить(Результат, Символы.ВК, ""); + Результат = СокрЛП(Результат); + + ТестовоеЗначение = "{""Name"": ""Headlight Fluid"",""Price"": 4}"; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИВыбратьПоследнееЗначение() + +Процедура ТестДолжен_ИзвлечьДанныеИВыбратьПоляОбъекта() Экспорт + + ИзвлечениеДанных = Новый ИзвлечениеДанныхJSON(); + + СтрокаJSON = ДанныеJSON(); + + ИзвлечениеДанных.УстановитьСтроку(СтрокаJSON); + + Результат = ИзвлечениеДанных.Выбрать("$..Products[?(@.Price > 50)].keys()"); + Результат = СтрЗаменить(Результат, Символы.ПС, ""); + Результат = СтрЗаменить(Результат, Символы.ВК, ""); + Результат = СокрЛП(Результат); + + ТестовоеЗначение = "[""Name"",""Price""]"; + + юТест.ПроверитьРавенство(Результат, ТестовоеЗначение, "Ошибка выборки данных из JSON"); + +КонецПроцедуры // ТестДолжен_ИзвлечьДанныеИВыбратьПоляОбъекта() + +Процедура ОбеспечитьКаталог(ПутьККаталогу) + + ВремКаталог = Новый Файл(ПутьККаталогу); + Если НЕ (ВремКаталог.Существует() И ВремКаталог.ЭтоКаталог()) Тогда + СоздатьКаталог(ПутьККаталогу); + КонецЕсли; + +КонецПроцедуры // ОбеспечитьКаталог() + +Функция ДанныеJSON() + + Возврат "{ + | ""Stores"": [ + | ""Lambton Quay"", + | ""Willis Street"" + | ], + | ""Manufacturers"": [ + | { + | ""Name"": ""Acme Co"", + | ""Products"": [ + | { + | ""Name"": ""Anvil"", + | ""Price"": 50 + | } + | ] + | }, + | { + | ""Name"": ""Contoso"", + | ""Products"": [ + | { + | ""Name"": ""Elbow Grease"", + | ""Price"": 99.95 + | }, + | { + | ""Name"": ""Headlight Fluid"", + | ""Price"": 4 + | } + | ] + | } + | ] + |}"; + +КонецФункции // ДанныеJSON() diff --git a/tests/json/json_reader.os b/tests/json/json_reader.os deleted file mode 100644 index b41f1ac74..000000000 --- a/tests/json/json_reader.os +++ /dev/null @@ -1,120 +0,0 @@ -#Использовать asserts - -Перем юТест; -Перем ТекстПроверки; - -Функция ПолучитьСписокТестов(Тестирование) Экспорт - - юТест = Тестирование; - - СписокТестов = Новый Массив; - - СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSON"); - СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONВСтруктуру"); - СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONМассива"); - СписокТестов.Добавить("Тест_Должен_ПроверитьПолучениеВсехЭлементовПеречисленияТипЗначенияJSON"); - СписокТестов.Добавить("Тест_Должен_СверитьСвойствоСДвойнымиКавычками"); - Возврат СписокТестов; - -КонецФункции - -Процедура Тест_Должен_ПроверитьПолучениеВсехЭлементовПеречисленияТипЗначенияJSON() Экспорт - -юТест.ПроверитьРавенство("Null", Строка(ТипЗначенияJSON.Null)); -юТест.ПроверитьРавенство("Комментарий", Строка(ТипЗначенияJSON.Комментарий)); -юТест.ПроверитьРавенство("ИмяСвойства", Строка(ТипЗначенияJSON.ИмяСвойства)); -юТест.ПроверитьРавенство("КонецМассива", Строка(ТипЗначенияJSON.КонецМассива)); -юТест.ПроверитьРавенство("КонецОбъекта", Строка(ТипЗначенияJSON.КонецОбъекта)); -юТест.ПроверитьРавенство("НачалоМассива", Строка(ТипЗначенияJSON.НачалоМассива)); -юТест.ПроверитьРавенство("НачалоОбъекта", Строка(ТипЗначенияJSON.НачалоОбъекта)); -юТест.ПроверитьРавенство("Ничего", Строка(ТипЗначенияJSON.Ничего)); -юТест.ПроверитьРавенство("Строка", Строка(ТипЗначенияJSON.Строка)); -юТест.ПроверитьРавенство("Число", Строка(ТипЗначенияJSON.Число)); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьСвойствоСДвойнымиКавычками() Экспорт - - СтруктураДанных = ПолучитьСтруктуруДанных("json\json-mock_struct.json"); - - юТест.ПроверитьРавенство(СтруктураДанных.lastName, """Иванов"""); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSON() Экспорт - - СтруктураДанных = ПолучитьСтруктуруДанных("json\json-mock.json", Истина); - - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 4014703816); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONВСтруктуру() Экспорт - - СтруктураДанных = ПолучитьСтруктуруДанных("json\json-mock_struct.json", Ложь); - - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 2800700943); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONМассива() Экспорт - - СтруктураДанных = ПолучитьСтруктуруДанных("json\json-mock_array.json", Истина); - - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 3633637665); - -КонецПроцедуры - -Функция ЗначениеВСтроку(Значение, Уровень = 0) - - Текст = ""; - Отступы = ""; - Для Счетчик = 1 По Уровень Цикл - Отступы = Отступы + Символы.Таб; - КонецЦикла; - - Если ТипЗнч(Значение) = Тип("Массив") Тогда - Для Каждого Элемент Из Значение Цикл - Если ТипЗнч(Элемент) = Тип("Структура") ИЛИ ТипЗнч(Элемент) = Тип("Соответствие") ИЛИ ТипЗнч(Элемент) = Тип("Массив") Тогда - Текст = Текст + ЗначениеВСтроку(Элемент, Уровень + 1); - Иначе - Текст = Текст + Отступы + ?(Элемент = Undefined, "Undefined", Элемент) + Символы.ПС; - КонецЕсли; - КонецЦикла; - Иначе - Для Каждого ТекСтрока Из Значение Цикл - Если ТипЗнч(ТекСтрока.Значение) = Тип("Структура") ИЛИ ТипЗнч(ТекСтрока.Значение) = Тип("Соответствие") ИЛИ ТипЗнч(ТекСтрока.Значение) = Тип("Массив") Тогда - Текст = Текст + Отступы + ТекСтрока.Ключ + ":" + Символы.ПС + ЗначениеВСтроку(ТекСтрока.Значение, Уровень + 1); - Иначе - Текст = Текст + Отступы + ТекСтрока.Ключ + ":" + ?(ТекСтрока.Значение = Undefined, "Undefined", ТекСтрока.Значение) + Символы.ПС; - КонецЕсли; - КонецЦикла; - КонецЕсли; - Возврат Текст; - -КонецФункции - -Функция РассчитатьХешСумму(СтруктураДанных) - - Текст = ЗначениеВСтроку(СтруктураДанных); - - Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); - Хеширование.Добавить(Текст); - Возврат Хеширование.ХешСумма; - -КонецФункции - - -Функция ПолучитьСтруктуруДанных(ПутьКФайлу, КакСоответствие = Ложь) - - Текст = Новый ТекстовыйДокумент(); - Текст.Прочитать(ПутьКФайлу, КодировкаТекста.UTF8); - - Чтение = Новый ЧтениеJSON; - Чтение.УстановитьСтроку(Текст.ПолучитьТекст()); - - Результат = ПрочитатьJSON(Чтение, КакСоответствие); - -Возврат Результат; - -КонецФункции \ No newline at end of file diff --git a/tests/json/json_writer.os b/tests/json/json_writer.os deleted file mode 100644 index 4b7a02891..000000000 --- a/tests/json/json_writer.os +++ /dev/null @@ -1,144 +0,0 @@ -#Использовать asserts - -Перем юТест; -Перем ТекстПроверки; - -Функция ПолучитьСписокТестов(Тестирование) Экспорт - - юТест = Тестирование; - - СписокТестов = Новый Массив; - - СписокТестов.Добавить("Тест_Должен_СверитьСтрокуСоСпецСимволами"); - СписокТестов.Добавить("Тест_Должен_СверитьСтрокуЭкранированнымиКириллическимиСимволами"); - СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуСгенерированнойСтрокиJSON"); - СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктуры"); - СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктурыСФиксированнымиКоллекциями"); - - Возврат СписокТестов; - -КонецФункции - -Процедура Тест_Должен_СверитьХешСуммуСгенерированнойСтрокиJSON() Экспорт - - Запись = Новый ЗаписьJson(); - Запись.УстановитьСтроку(); - Запись.ЗаписатьНачалоОбъекта(); - Запись.ЗаписатьИмяСвойства("firstName"); - Запись.ЗаписатьЗначение("Иван"); - Запись.ЗаписатьИмяСвойства("lastName"); - Запись.ЗаписатьЗначение("""Иванов"""); - - Запись.ЗаписатьИмяСвойства("address"); - Запись.ЗаписатьНачалоОбъекта(); - Запись.ЗаписатьИмяСвойства("streetAddress"); - Запись.ЗаписатьЗначение("Московское ш., 101, кв.101"); - Запись.ЗаписатьИмяСвойства("city"); - Запись.ЗаписатьЗначение("Ленинград"); - Запись.ЗаписатьИмяСвойства("postalCode"); - Запись.ЗаписатьЗначение(101101); - Запись.ЗаписатьИмяСвойства("float"); - Запись.ЗаписатьЗначение(1.54); - Запись.ЗаписатьИмяСвойства("undefined"); - Запись.ЗаписатьЗначение(Неопределено); - Запись.ЗаписатьКонецОбъекта(); - - Запись.ЗаписатьИмяСвойства("phoneNumbers"); - Запись.ЗаписатьНачалоМассива(); - Запись.ЗаписатьЗначение("812 123-1234"); - Запись.ЗаписатьЗначение("916 123-4567"); - Запись.ЗаписатьКонецМассива(); - - Запись.ЗаписатьКонецОбъекта(); - - Текст = Запись.Закрыть(); - - Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); - Хеширование.Добавить(Текст); - - юТест.ПроверитьРавенство(Хеширование.ХешСумма, 2627237007); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьСтрокуЭкранированнымиКириллическимиСимволами() Экспорт - -ЗаписьJSON = Новый ЗаписьJSON; -Параметры = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Windows, , , ЭкранированиеСимволовJSON.СимволыВнеASCII); - -ЗаписьJSON.УстановитьСтроку(Параметры); - -ЗаписатьJSON(ЗаписьJSON, "Символы кириллицы"); - -юТест.ПроверитьРавенство(ЗаписьJSON.Закрыть(), """\\u0421\\u0438\\u043c\\u0432\\u043e\\u043b\\u044b \\u043a\\u0438\\u0440\\u0438\\u043b\\u043b\\u0438\\u0446\\u044b"""); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьСтрокуСоСпецСимволами() Экспорт - -ЗаписьJSON = Новый ЗаписьJSON; -ЗаписьJSON.УстановитьСтроку(); - -ЗаписатьJSON(ЗаписьJSON, Символ(1) + Символ(31)); -юТест.ПроверитьРавенство(ЗаписьJSON.Закрыть(), """\u0001\u001f"""); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктурыСФиксированнымиКоллекциями() Экспорт - - Запись = Новый ЗаписьJson(); - Запись.УстановитьСтроку(); - - Структура = Новый Структура(); - - МассивОбычный = Новый Массив(); - МассивОбычный.Добавить("1"); - - ВложенныйМассив = Новый Массив(); - ВложенныйМассив.Добавить("2.1"); - ВложенныйМассив.Добавить("2.2"); - ФиксМассив = Новый ФиксированныйМассив(ВложенныйМассив); - - МассивОбычный.Добавить(ФиксМассив); - - Структура.Вставить("МассивОбычный", МассивОбычный); - - соответствие = Новый Соответствие(); - соответствие.Вставить("ЭлементСоответствия", "ЗначениеСоовтетствия"); - - ФиксированноеСоответствие = Новый ФиксированноеСоответствие(соответствие); - Структура.Вставить("ФиксированноеСоответствие", ФиксированноеСоответствие); - - ФиксированнаяСтруктура = Новый ФиксированнаяСтруктура(Структура); - - ЗаписатьJSON(Запись, ФиксированнаяСтруктура); - - Текст = Запись.Закрыть(); - - Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); - Хеширование.Добавить(Текст); - - юТест.ПроверитьРавенство(Хеширование.ХешСумма, 3499253905); - -КонецПроцедуры - -Процедура Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктуры() Экспорт - - Текст = Новый ТекстовыйДокумент(); - Текст.Прочитать("json\json-mock_struct.json", КодировкаТекста.UTF8); - - Чтение = Новый ЧтениеJSON; - Чтение.УстановитьСтроку(Текст.ПолучитьТекст()); - Структура = ПрочитатьJSON(Чтение); - - Запись = Новый ЗаписьJson(); - Запись.УстановитьСтроку(); - ЗаписатьJSON(Запись, Структура); - - Текст = Запись.Закрыть(); - - Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); - Хеширование.Добавить(Текст); - - юТест.ПроверитьРавенство(Хеширование.ХешСумма, 3352482152); - -КонецПроцедуры diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os new file mode 100644 index 000000000..a3e98504a --- /dev/null +++ b/tests/json/test-json_reader.os @@ -0,0 +1,221 @@ +#Использовать asserts + +Перем юТест; +Перем ТекстПроверки; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + СписокТестов = Новый Массив; + + СписокТестов.Добавить("Тест_Должен_ПроверитьПолучениеВсехЭлементовПеречисленияТипЗначенияJSON"); + СписокТестов.Добавить("Тест_Должен_СверитьСвойствоСДвойнымиКавычками"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПреобразованиеNullВНеопределено"); + + СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSON"); + СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONВСтруктуру"); + СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONМассива"); + + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ДляUndefined"); + + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускЗначения"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускМассива"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускНезавершенногоМассива"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускМассиваСВложенными"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускОбъектаСВложениями"); + Возврат СписокТестов; + +КонецФункции + +Процедура Тест_Должен_ПроверитьПолучениеВсехЭлементовПеречисленияТипЗначенияJSON() Экспорт + +юТест.ПроверитьРавенство("Null", Строка(ТипЗначенияJSON.Null)); +юТест.ПроверитьРавенство("Комментарий", Строка(ТипЗначенияJSON.Комментарий)); +юТест.ПроверитьРавенство("ИмяСвойства", Строка(ТипЗначенияJSON.ИмяСвойства)); +юТест.ПроверитьРавенство("КонецМассива", Строка(ТипЗначенияJSON.КонецМассива)); +юТест.ПроверитьРавенство("КонецОбъекта", Строка(ТипЗначенияJSON.КонецОбъекта)); +юТест.ПроверитьРавенство("НачалоМассива", Строка(ТипЗначенияJSON.НачалоМассива)); +юТест.ПроверитьРавенство("НачалоОбъекта", Строка(ТипЗначенияJSON.НачалоОбъекта)); +юТест.ПроверитьРавенство("Ничего", Строка(ТипЗначенияJSON.Ничего)); +юТест.ПроверитьРавенство("Строка", Строка(ТипЗначенияJSON.Строка)); +юТест.ПроверитьРавенство("Число", Строка(ТипЗначенияJSON.Число)); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьСвойствоСДвойнымиКавычками() Экспорт + + СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock_struct.json"); + + юТест.ПроверитьРавенство(СтруктураДанных.lastName, """Иванов"""); + +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПреобразованиеNullВНеопределено() Экспорт + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку("{""Null"": null}"); + СтруктураДанных = ПрочитатьJSON(Чтение,Ложь); + + юТест.ПроверитьРавенство(ТипЗнч(СтруктураДанных.Null), Тип("Неопределено")); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSON() Экспорт + + СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock.json", Истина); + + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 960829385); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONВСтруктуру() Экспорт + + СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock_struct.json", Ложь); + + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 2800700943); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONМассива() Экспорт + + СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock_array.json", Истина); + + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 3633637665); + +КонецПроцедуры + +Функция ЗначениеВСтроку(Значение, Уровень = 0) + + Текст = ""; + Отступы = ""; + Для Счетчик = 1 По Уровень Цикл + Отступы = Отступы + Символы.Таб; + КонецЦикла; + + Если ТипЗнч(Значение) = Тип("Массив") Тогда + Для Каждого Элемент Из Значение Цикл + Если ТипЗнч(Элемент) = Тип("Структура") ИЛИ ТипЗнч(Элемент) = Тип("Соответствие") ИЛИ ТипЗнч(Элемент) = Тип("Массив") Тогда + Текст = Текст + ЗначениеВСтроку(Элемент, Уровень + 1); + Иначе + Текст = Текст + Отступы + ?(Элемент = Undefined, "Undefined", Элемент) + Символы.ПС; + КонецЕсли; + КонецЦикла; + Иначе + Для Каждого ТекСтрока Из Значение Цикл + Если ТипЗнч(ТекСтрока.Значение) = Тип("Структура") ИЛИ ТипЗнч(ТекСтрока.Значение) = Тип("Соответствие") ИЛИ ТипЗнч(ТекСтрока.Значение) = Тип("Массив") Тогда + Текст = Текст + Отступы + ТекСтрока.Ключ + ":" + Символы.ПС + ЗначениеВСтроку(ТекСтрока.Значение, Уровень + 1); + Иначе + Текст = Текст + Отступы + ТекСтрока.Ключ + ":" + ?(ТекСтрока.Значение = Undefined, "Undefined", ТекСтрока.Значение) + Символы.ПС; + КонецЕсли; + КонецЦикла; + КонецЕсли; + Возврат Текст; + +КонецФункции + +Функция РассчитатьХешСумму(СтруктураДанных) + + Текст = ЗначениеВСтроку(СтруктураДанных); + + Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); + Хеширование.Добавить(Текст); + Возврат Хеширование.ХешСумма; + +КонецФункции + + +Функция ПолучитьСтруктуруДанных(ПутьКФайлу, КакСоответствие = Ложь) + + Текст = Новый ТекстовыйДокумент(); + Текст.Прочитать(ПутьКФайлу, КодировкаТекста.UTF8); + + Чтение = Новый ЧтениеJSON; + Чтение.УстановитьСтроку(Текст.ПолучитьТекст()); + + Результат = ПрочитатьJSON(Чтение, КакСоответствие); + +Возврат Результат; + +КонецФункции + +Процедура Тест_ДолженВызватьОшибку_ДляUndefined() Экспорт + Текст = "undefined"; + Чтение = Новый ЧтениеJSON; + Чтение.УстановитьСтроку(Текст); + Попытка + Результат = Чтение.Прочитать(); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускЗначения() Экспорт + Текст = "1,2,3,4,5"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); + Результат = Чтение.Пропустить(); + + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(2, Чтение.ТекущееЗначение); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускМассива() Экспорт + Текст = "1,[2,3],4,5"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); // считывает 1 + Чтение.Прочитать(); // считывает [ + Результат = Чтение.Пропустить(); + + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(4, Чтение.ТекущееЗначение); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускНезавершенногоМассива() Экспорт + Текст = "1,[2,"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); + Чтение.Прочитать(); + Результат = Чтение.Пропустить(); + + юТест.ПроверитьРавенство(Ложь, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Ничего, Чтение.ТипТекущегоЗначения); +КонецПроцедуры + + +Процедура Тест_Должен_ПроверитьПропускМассиваСВложенными() Экспорт + Текст = "1,[2,[-1,-2],[-3,-4],3],4,5"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); // считывает 1 + Чтение.Прочитать(); // считывает НачалоМассива + Результат = Чтение.Пропустить(); // должно пропустить масиив от '[2,' до ',3]' и считать 4 + + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(4, Чтение.ТекущееЗначение); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускОбъектаСВложениями() Экспорт + Текст = "1, {""a"":[2,[-1,-2],[-3,-4],3], ""b"":{ ""C"":""D""} }, 4, 5, {}"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); + Чтение.Прочитать(); + Чтение.Пропустить(); + Результат = Чтение.Прочитать(); + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(5, Чтение.ТекущееЗначение); +КонецПроцедуры diff --git a/tests/json/test-json_writer.os b/tests/json/test-json_writer.os new file mode 100644 index 000000000..927a7c186 --- /dev/null +++ b/tests/json/test-json_writer.os @@ -0,0 +1,209 @@ +#Использовать asserts + +Перем юТест; +Перем ТекстПроверки; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + СписокТестов = Новый Массив; + + СписокТестов.Добавить("Тест_Должен_СверитьСтрокуСоСпецСимволами"); + СписокТестов.Добавить("Тест_Должен_СверитьСтрокуЭкранированнымиКириллическимиСимволами"); + СписокТестов.Добавить("Тест_Должен_ПроверитьИсключенияДляНедопустимыхТипов"); + + СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуСгенерированнойСтрокиJSON"); + СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктуры"); + СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктурыСФиксированнымиКоллекциями"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПустуюСтрокуПриЗакрытии"); + СписокТестов.Добавить("Тест_Должен_ПроверитьСохранениеУстановленнойСтроки"); + Возврат СписокТестов; + +КонецФункции + +Процедура Тест_Должен_СверитьХешСуммуСгенерированнойСтрокиJSON() Экспорт + + Запись = Новый ЗаписьJson(); + Запись.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Windows)); + Запись.ЗаписатьНачалоОбъекта(); + Запись.ЗаписатьИмяСвойства("firstName"); + Запись.ЗаписатьЗначение("Иван"); + Запись.ЗаписатьИмяСвойства("lastName"); + Запись.ЗаписатьЗначение("""Иванов"""); + + Запись.ЗаписатьИмяСвойства("address"); + Запись.ЗаписатьНачалоОбъекта(); + Запись.ЗаписатьИмяСвойства("streetAddress"); + Запись.ЗаписатьЗначение("Московское ш., 101, кв.101"); + Запись.ЗаписатьИмяСвойства("city"); + Запись.ЗаписатьЗначение("Ленинград"); + Запись.ЗаписатьИмяСвойства("postalCode"); + Запись.ЗаписатьЗначение(101101); + Запись.ЗаписатьИмяСвойства("float"); + Запись.ЗаписатьЗначение(1.54); + Запись.ЗаписатьИмяСвойства("undefined"); + Запись.ЗаписатьЗначение(Неопределено); + Запись.ЗаписатьКонецОбъекта(); + + Запись.ЗаписатьИмяСвойства("phoneNumbers"); + Запись.ЗаписатьНачалоМассива(); + Запись.ЗаписатьЗначение("812 123-1234"); + Запись.ЗаписатьЗначение("916 123-4567"); + Запись.ЗаписатьКонецМассива(); + + Запись.ЗаписатьКонецОбъекта(); + + Текст = Запись.Закрыть(); + + Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); + Хеширование.Добавить(Текст); + + юТест.ПроверитьРавенство(Хеширование.ХешСумма, 2627237007); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьСтрокуЭкранированнымиКириллическимиСимволами() Экспорт + +ЗаписьJSON = Новый ЗаписьJSON; +Параметры = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Windows, , , ЭкранированиеСимволовJSON.СимволыВнеASCII); + +ЗаписьJSON.УстановитьСтроку(Параметры); + +ЗаписатьJSON(ЗаписьJSON, "Символы кириллицы"); + +юТест.ПроверитьРавенство(ЗаписьJSON.Закрыть(), """\\u0421\\u0438\\u043c\\u0432\\u043e\\u043b\\u044b \\u043a\\u0438\\u0440\\u0438\\u043b\\u043b\\u0438\\u0446\\u044b"""); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьСтрокуСоСпецСимволами() Экспорт + +ЗаписьJSON = Новый ЗаписьJSON; +ЗаписьJSON.УстановитьСтроку(); + +ЗаписатьJSON(ЗаписьJSON, Символ(1) + Символ(31)); +юТест.ПроверитьРавенство(ЗаписьJSON.Закрыть(), """\u0001\u001f"""); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктурыСФиксированнымиКоллекциями() Экспорт + + Запись = Новый ЗаписьJson(); + Запись.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Windows)); + + Структура = Новый Структура(); + + МассивОбычный = Новый Массив(); + МассивОбычный.Добавить("1"); + + ВложенныйМассив = Новый Массив(); + ВложенныйМассив.Добавить("2.1"); + ВложенныйМассив.Добавить("2.2"); + ФиксМассив = Новый ФиксированныйМассив(ВложенныйМассив); + + МассивОбычный.Добавить(ФиксМассив); + + Структура.Вставить("МассивОбычный", МассивОбычный); + + соответствие = Новый Соответствие(); + соответствие.Вставить("ЭлементСоответствия", "ЗначениеСоовтетствия"); + + ФиксированноеСоответствие = Новый ФиксированноеСоответствие(соответствие); + Структура.Вставить("ФиксированноеСоответствие", ФиксированноеСоответствие); + + ФиксированнаяСтруктура = Новый ФиксированнаяСтруктура(Структура); + + ЗаписатьJSON(Запись, ФиксированнаяСтруктура); + + Текст = Запись.Закрыть(); + + Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); + Хеширование.Добавить(Текст); + + юТест.ПроверитьРавенство(Хеширование.ХешСумма, 3499253905); + +КонецПроцедуры + +Процедура Тест_Должен_СверитьХешСуммуСгенерированнойМетодомЗаписатьJSONСтруктуры() Экспорт + + Текст = Новый ТекстовыйДокумент(); + Текст.Прочитать("json/json-mock_struct.json", КодировкаТекста.UTF8); + + Чтение = Новый ЧтениеJSON; + Чтение.УстановитьСтроку(Текст.ПолучитьТекст()); + Структура = ПрочитатьJSON(Чтение); + + Запись = Новый ЗаписьJson(); + Запись.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Windows)); + ЗаписатьJSON(Запись, Структура); + + Текст = Запись.Закрыть(); + + Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32); + Хеширование.Добавить(Текст); + + юТест.ПроверитьРавенство(Хеширование.ХешСумма, 3352482152); + +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПустуюСтрокуПриЗакрытии() Экспорт + Файл = ПолучитьИмяВременногоФайла(".json"); + + Соотв=Новый Соответствие(); + Соотв.Вставить("value",0); + + ЗаписьJSON = Новый ЗаписьJSON; + ЗаписьJSON.УстановитьСтроку(); + ЗаписатьJSON(ЗаписьJSON, Соотв); + + Соотв["value"]=1; + ЗаписьJSON.ОткрытьФайл(Файл); + ЗаписатьJSON(ЗаписьJSON, Соотв); + + Результат = ЗаписьJSON.Закрыть(); + УдалитьФайлы(Файл); + + юТест.ПроверитьРавенство(Результат, ""); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьСохранениеУстановленнойСтроки() Экспорт + Файл = ПолучитьИмяВременногоФайла(".json"); + + ЗаписьJSON = Новый ЗаписьJSON; + ЗаписьJSON.УстановитьСтроку(); + ЗаписьJSON.ЗаписатьБезОбработки("{"); + + Попытка + ЗаписьJSON.ОткрытьФайл(Файл+"/*"); + Исключение + КонецПопытки; + + ЗаписьJSON.ЗаписатьБезОбработки("}"); + Результат = ЗаписьJSON.Закрыть(); + + юТест.ПроверитьРавенство(Результат, "{}"); +КонецПроцедуры + +Процедура ПопыткаЗаписатьЗначение(Зн) + ЗаписьJSON = Новый ЗаписьJSON; + ЗаписьJSON.УстановитьСтроку(); + + Попытка + ЗаписьJSON.ЗаписатьЗначение(зн); + Исключение + ЗаписьJSON.Закрыть(); + возврат; + КонецПопытки; + + ЗаписьJSON.Закрыть(); + ВызватьИсключение "Должно было быть выдано исключение для типа "+ТипЗнч(Зн)+", но его не было"; +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьИсключенияДляНедопустимыхТипов() Экспорт + + Массив = Новый Массив(); + ПопыткаЗаписатьЗначение(Массив); + + Структура = Новый Структура(); + ПопыткаЗаписатьЗначение(Структура); +КонецПроцедуры diff --git a/tests/managed-com.os b/tests/managed-com.os index e92d6d6bc..8cf6bd4db 100644 --- a/tests/managed-com.os +++ b/tests/managed-com.os @@ -14,6 +14,7 @@ Если (Найти(СИ.ВерсияОС, "Windows") > 0) И Не (ПеременныеСреды().Получить("APPVEYOR") = "True") Тогда ВсеТесты.Добавить("ТестДолжен_ПроверитьУстановкуЧисловыхСвойств"); ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовСОпциональнымиПараметрами"); + ВсеТесты.Добавить("ПроверитьВыходныеПараметрыComОбъекта"); КонецЕсли; ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеClrОбъекта"); @@ -24,7 +25,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуGetTypeДляЭлементовСписка"); ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуGetTypeДляCLRКоллекции"); ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуТипаНеИзЯдра"); - + Возврат ВсеТесты; КонецФункции @@ -176,3 +177,16 @@ ПроверитьРаботуGetTypeДляОдногоТипа(Ури, "System", "Uri"); КонецПроцедуры + +Процедура ПроверитьВыходныеПараметрыComОбъекта() Экспорт + // Проверяем, что выходной параметр установился, а также то, что попытки записи во входной параметр не было. + + Рег = Новый ФиксированнаяСтруктура("HKEY_LOCAL_MACHINE", "2147483650"); + ИмяФайлаКласса = ""; + РеестрОС = ПолучитьCOMОбъект("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv"); + Рез=РеестрОС.GetStringValue(Рег.HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows\CurrentVersion","DevicePath", ИмяФайлаКласса); + + юТест.ПроверитьРавенство(0, Рез); + юТест.ПроверитьНеравенство("", ИмяФайлаКласса); + +КонецПроцедуры diff --git a/tests/native-api.os b/tests/native-api.os index cc614dd74..a422d2b40 100644 --- a/tests/native-api.os +++ b/tests/native-api.os @@ -1,40 +1,111 @@ Перем юТест; - -Перем ВнешняяКомпонента; +Перем ЭтоWindows; +Перем DLLПодключенаУспешно; Перем ЗаписьXML, ЗаписьZIP, КаталогСкрипта, КаталогПакета, СуффиксВерсии; Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + ПроверитьСуществованиеПрокси(); + юТест = ЮнитТестирование; ВсеТесты = Новый Массив; - СистемнаяИнформация = Новый СистемнаяИнформация; - ЭтоWindows = Найти(НРег(СистемнаяИнформация.ВерсияОС), "windows") > 0; - Если ЭтоWindows Тогда - ВсеТесты.Добавить("ТестДолжен_ПроверитьПодключениеВнешнейКомпонентыZIP"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьПодключениеВнешнейКомпонентыDLL"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьСвойстваВнешнейКомпоненты"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодыВнешнейКомпоненты"); + // TODO: сделать прогон этого теста еще и на 32-битной архитектуре, см. ошибку #1531 + ВсеТесты.Добавить("ТестДолжен_ПроверитьПодключениеВнешнейКомпонентыZIP"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПодключениеВнешнейКомпонентыDLL"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСвойстваВнешнейКомпоненты"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодыВнешнейКомпоненты"); + + #Если Windows Тогда + ЭтоWindows = Истина; + #ИначеЕсли Linux Тогда + ЭтоWindows = Ложь; + #Иначе + Возврат Новый Массив; // под макос не тестируем + #КонецЕсли + + Возврат ВсеТесты; + +КонецФункции + +Процедура ПроверитьСуществованиеПрокси() + + Каталог = КаталогПрограммы(); + + СистемнаяИнформация = Новый СистемнаяИнформация(); + Если СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86 Тогда + ПутьБиблиотеки = ОбъединитьПути(Каталог, "ScriptEngine.NativeApi32.dll"); + ИначеЕсли СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64 Тогда + ПутьБиблиотеки = ОбъединитьПути(Каталог, "ScriptEngine.NativeApi64.dll"); + ИначеЕсли СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86 Тогда + ПутьБиблиотеки = ОбъединитьПути(Каталог, "ScriptEngine.NativeApi32.so"); + ИначеЕсли СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86_64 Тогда + ПутьБиблиотеки = ОбъединитьПути(Каталог, "ScriptEngine.NativeApi64.so"); + Иначе + ПутьБиблиотеки = ""; + КонецЕсли; + + Сборка = Новый Файл(ПутьБиблиотеки); + + Если Не Сборка.Существует() Тогда + ВызватьИсключение СтрШаблон("Не собрана прокси-библиотека для NativeApi: %1", ПутьБиблиотеки); КонецЕсли; + +КонецПроцедуры + +Функция ПодключитьКомпонентуDLL() - Возврат ВсеТесты; + Если DLLПодключенаУспешно <> Неопределено Тогда + Возврат DLLПодключенаУспешно; + КонецЕсли; + + СистемнаяИнформация = Новый СистемнаяИнформация(); + + КаталогСборок = ОбъединитьПути(ТекущийСценарий().Каталог, "native-api"); + Если СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86 Тогда + ПутьБиблиотеки = ОбъединитьПути(КаталогСборок, "bin", "AddInNativeWin32.dll"); + ИначеЕсли СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Windows_x86_64 Тогда + ПутьБиблиотеки = ОбъединитьПути(КаталогСборок, "bin64", "AddInNativeWin64.dll"); + ИначеЕсли СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86 Тогда + ПутьБиблиотеки = ОбъединитьПути(КаталогСборок, "build32", "AddInNativeLin32.so"); + ИначеЕсли СистемнаяИнформация.ТипПлатформы = ТипПлатформы.Linux_x86_64 Тогда + ПутьБиблиотеки = ОбъединитьПути(КаталогСборок, "build64", "AddInNativeLin64.so"); + Иначе + ВызватьИсключение "Неподдерживаемый тип платформы: " + СистемнаяИнформация.ТипПлатформы; + КонецЕсли; + + Идентификатор = "AddinNativeDLL"; + + ФайлКомпоненты = Новый Файл(ПутьБиблиотеки); + юТест.ПроверитьИстину(ФайлКомпоненты.Существует(), + "Файл компоненты '" + ФайлКомпоненты.ПолноеИмя + "' должен существовать"); + + DLLПодключенаУспешно = ПодключитьВнешнююКомпоненту(ФайлКомпоненты.ПолноеИмя, Идентификатор, ТипВнешнейКомпоненты.Native); + Возврат DLLПодключенаУспешно; + КонецФункции Процедура ТестДолжен_ПроверитьПодключениеВнешнейКомпонентыZIP() Экспорт ИмяФайлаПакета = юТест.ИмяВременногоФайла(); ЗаписьZIP = Новый ЗаписьZipФайла(ИмяФайлаПакета); - Каталог = ТекущийСценарий().Каталог + "\native-api\"; - ЗаписьZIP.Добавить(Каталог + "MANIFEST.XML", РежимСохраненияПутейZIP.НеСохранятьПути); МасивФайлов = Новый Массив; - МасивФайлов.Добавить("bin\AddInNativeWin32.dll"); - МасивФайлов.Добавить("bin64\AddInNativeWin64.dll"); - МасивФайлов.Добавить("build32\libAddInNativeLin32.so"); - МасивФайлов.Добавить("build64\libAddInNativeLin64.so"); + + Если ЭтоWindows Тогда + Каталог = ТекущийСценарий().Каталог + "\native-api\"; + МасивФайлов.Добавить("bin\AddInNativeWin32.dll"); + МасивФайлов.Добавить("bin64\AddInNativeWin64.dll"); + Иначе + Каталог = ТекущийСценарий().Каталог + "/native-api/"; + МасивФайлов.Добавить("build32/AddInNativeLin32.so"); + МасивФайлов.Добавить("build64/AddInNativeLin64.so"); + КонецЕсли; + + ЗаписьZIP.Добавить(Каталог + "MANIFEST.XML", РежимСохраненияПутейZIP.НеСохранятьПути); Для каждого ИмяФайла из МасивФайлов Цикл ПутьФайла = Каталог + ИмяФайла; @@ -55,17 +126,14 @@ юТест.ПроверитьРавенство(Строка(ТипЗнч(ВнешняяКомпонента)), Строка(Тип(ИмяВнешнейКомпоненты))); -КонецПроцедуры +КонецПроцедуры Процедура ТестДолжен_ПроверитьПодключениеВнешнейКомпонентыDLL() Экспорт Идентификатор = "AddinNativeDLL"; - ПутьБиблиотеки = ТекущийСценарий().Каталог + "\native-api\"; - Это32бит = ПодключитьВнешнююКомпоненту(ПутьБиблиотеки + "bin\AddInNativeWin32.dll", Идентификатор, ТипВнешнейКомпоненты.Native); - Это64бит = ПодключитьВнешнююКомпоненту(ПутьБиблиотеки + "bin64\AddInNativeWin64.dll", Идентификатор, ТипВнешнейКомпоненты.Native); - - юТест.ПроверитьРавенство(Это32бит И Это64бит, Ложь); - юТест.ПроверитьРавенство(Это32бит ИЛИ Это64бит, Истина); + + КомпонентаПодключена = ПодключитьКомпонентуDLL(); + юТест.ПроверитьИстину(КомпонентаПодключена, "Компонента должна подключиться"); ИмяВнешнейКомпоненты = "AddIn." + Идентификатор + ".CAddInNative"; ВнешняяКомпонента = Новый(ИмяВнешнейКомпоненты); @@ -80,6 +148,13 @@ Процедура ТестДолжен_ПроверитьСвойстваВнешнейКомпоненты() Экспорт + Идентификатор = "AddinNativeDLL"; + КомпонентаПодключена = ПодключитьКомпонентуDLL(); + юТест.ПроверитьИстину(КомпонентаПодключена, "Компонента должна подключиться"); + + ИмяВнешнейКомпоненты = "AddIn." + Идентификатор + ".CAddInNative"; + ВнешняяКомпонента = Новый(ИмяВнешнейКомпоненты); + ВнешняяКомпонента.Включен = Истина; юТест.ПроверитьРавенство(ВнешняяКомпонента.Включен, Истина); юТест.ПроверитьРавенство(ВнешняяКомпонента.IsEnabled, Истина); @@ -88,6 +163,10 @@ юТест.ПроверитьРавенство(ВнешняяКомпонента.Включен, Ложь); юТест.ПроверитьРавенство(ВнешняяКомпонента.IsEnabled, Ложь); + ВнешняяКомпонента["Включен"] = Истина; + юТест.ПроверитьРавенство(ВнешняяКомпонента["Включен"], Истина); + юТест.ПроверитьРавенство(ВнешняяКомпонента["IsEnabled"], Истина); + СодержимоеСтроки = "Тест строки только на запись"; ВнешняяКомпонента.СтрокаТолькоЗапись = СодержимоеСтроки; юТест.ПроверитьРавенство(ВнешняяКомпонента.СтрокаЧтениеЗапись, СодержимоеСтроки); @@ -123,6 +202,13 @@ Процедура ТестДолжен_ПроверитьМетодыВнешнейКомпоненты() Экспорт + Идентификатор = "AddinNativeDLL"; + КомпонентаПодключена = ПодключитьКомпонентуDLL(); + юТест.ПроверитьИстину(КомпонентаПодключена, "Компонента должна подключиться"); + + ИмяВнешнейКомпоненты = "AddIn." + Идентификатор + ".CAddInNative"; + ВнешняяКомпонента = Новый(ИмяВнешнейКомпоненты); + ВнешняяКомпонента.Включить(); юТест.ПроверитьРавенство(ВнешняяКомпонента.Включен, Истина); @@ -146,6 +232,12 @@ юТест.ТестПройден(); КонецПопытки; + + ПерваяСтрока = "Первая строка"; + ВтораяСтрока = "Вторая тестовая строка"; + КонкатенацияСтрок = ВнешняяКомпонента.КонкатенацияСтрок(ПерваяСтрока, ВтораяСтрока); + юТест.ПроверитьРавенство(КонкатенацияСтрок, ПерваяСтрока + ВтораяСтрока); + СодержимоеФайла = "Тест двоичных данных"; ВременныйФайл = юТест.ИмяВременногоФайла(); ЗаписьТекста = Новый ЗаписьТекста(ВременныйФайл, КодировкаТекста.UTF8); @@ -159,5 +251,4 @@ ЧтениеТекста = Новый ЧтениеТекста(Поток, КодировкаТекста.UTF8); юТест.ПроверитьРавенство(ЧтениеТекста.Прочитать(), СодержимоеФайла); -КонецПроцедуры - +КонецПроцедуры \ No newline at end of file diff --git a/tests/native-api/AddInNative.cpp b/tests/native-api/AddInNative.cpp index 343cc3a35..32fb90e09 100644 --- a/tests/native-api/AddInNative.cpp +++ b/tests/native-api/AddInNative.cpp @@ -47,6 +47,7 @@ static const wchar_t* g_MethodNames[] = { L"LoadPicture", L"ShowMessageBox", L"Exchange", + L"Concatenate", L"Loopback" }; @@ -58,6 +59,7 @@ static const wchar_t* g_MethodNamesRu[] = { L"ЗагрузитьКартинку", L"ПоказатьСообщение", L"ОбменПараметров", + L"КонкатенацияСтрок", L"Петля", }; @@ -345,6 +347,8 @@ long CAddInNative::GetNParams(const long lMethodNum) return 1; case eMethExchange: return 2; + case eMethConcatenate: + return 2; case eMethLoopback: return 1; default: @@ -387,6 +391,7 @@ bool CAddInNative::HasRetVal(const long lMethodNum) case eMethDefaultParam: return true; case eMethLoadPicture: + case eMethConcatenate: case eMethLoopback: return true; default: @@ -480,6 +485,24 @@ bool CAddInNative::CallAsFunc(const long lMethodNum, TV_BOOL(pvarRetValue) = TV_BOOL(paParams); return true; // Method acceps one argument of type BinaryData ant returns its copy + case eMethConcatenate: + { + tVariant* str1 = paParams; + tVariant* str2 = paParams + 1; + if (TV_VT(str1) != VTYPE_PWSTR) return false; + if (TV_VT(str2) != VTYPE_PWSTR) return false; + TV_VT(pvarRetValue) = VTYPE_PWSTR; + TV_WSTR(pvarRetValue) = NULL; + int iActualSize = str1->strLen + str2->strLen + 1; + if (m_iMemory && m_iMemory->AllocMemory((void**)&pvarRetValue->pwstrVal, iActualSize * sizeof(WCHAR_T))) { + memset((void*)pvarRetValue->pstrVal, 0, sizeof(WCHAR_T) * iActualSize); + memcpy((void*)pvarRetValue->pstrVal, (void*)str1->pstrVal, str1->strLen * sizeof(WCHAR_T)); + memcpy((void*)(pvarRetValue->pstrVal + str1->strLen * sizeof(WCHAR_T)), (void*)str2->pstrVal, str2->strLen * sizeof(WCHAR_T)); + pvarRetValue->strLen = paParams->strLen + (paParams + 1)->strLen; + return true; + } + return false; + } case eMethLoopback: { if (lSizeArray != 1 || !paParams) diff --git a/tests/native-api/AddInNative.h b/tests/native-api/AddInNative.h index 3135d87f6..a84cf6561 100644 --- a/tests/native-api/AddInNative.h +++ b/tests/native-api/AddInNative.h @@ -29,6 +29,7 @@ class CAddInNative : public IComponentBase eMethLoadPicture, eMethShowMsgBox, eMethExchange, + eMethConcatenate, eMethLoopback, eMethLast // Always last }; diff --git a/tests/native-api/AddInNative.vcxproj b/tests/native-api/AddInNative.vcxproj index 0f8e36c80..bb47f817d 100644 --- a/tests/native-api/AddInNative.vcxproj +++ b/tests/native-api/AddInNative.vcxproj @@ -9,6 +9,10 @@ Debug x64 + + LinuxDebug + AnyCPU + Release Win32 @@ -22,7 +26,6 @@ {55890DF2-D13E-4C89-A01D-79CAD6726246} testCPP Win32Proj - 10.0.18362.0 diff --git a/tests/native-api/CMakeLists.txt b/tests/native-api/CMakeLists.txt index e99a15581..b1bc69220 100644 --- a/tests/native-api/CMakeLists.txt +++ b/tests/native-api/CMakeLists.txt @@ -14,7 +14,7 @@ SET(AddInNative_SRC dllmain.cpp stdafx.cpp stdafx.h - ) + ) include_directories(${CMAKE_SOURCE_DIR}/include) @@ -22,10 +22,12 @@ SET (CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${CMAKE_SOURCE_DIR}) SET(AddInDef_SRC) if (UNIX) - if (TARGET_ARCH STREQUAL "x86") + if (TARGET_PLATFORM_32) set(CMAKE_CXX_FLAGS "-m32 ${CMAKE_CXX_FLAGS}") + set(MySuffix "Lin32") else(UNIX) set(CMAKE_CXX_FLAGS "-m64 ${CMAKE_CXX_FLAGS}") + set(MySuffix "Lin64") endif () else() SET(AddInDef_SRC AddInNative.def) @@ -37,9 +39,10 @@ endif() add_library(${PROJECT_NAME} SHARED ${AddInNative_SRC} ${AddInDef_SRC}) -set_target_properties( ${PROJECT_NAME} PROPERTIES - OUTPUT_NAME ${PROJECT_NAME}${MySuffix1}${MySuffix2} - POSITION_INDEPENDENT_CODE ON - CXX_STANDARD 11 - CXX_STANDARD_REQUIRED ON - ) +set_target_properties(${PROJECT_NAME} PROPERTIES + PREFIX "" + OUTPUT_NAME ${PROJECT_NAME}${MySuffix} + POSITION_INDEPENDENT_CODE ON + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + ) diff --git a/tests/native-api/MANIFEST.XML b/tests/native-api/MANIFEST.XML index 778ee8567..03bb40552 100644 --- a/tests/native-api/MANIFEST.XML +++ b/tests/native-api/MANIFEST.XML @@ -2,6 +2,6 @@ - - + + diff --git a/tests/native-api/build.sh b/tests/native-api/build.sh new file mode 100755 index 000000000..a2aaacc80 --- /dev/null +++ b/tests/native-api/build.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +cmake -E make_directory build32 +cd build32 +cmake -D CMAKE_BUILD_TYPE:STRING=Release -D TARGET_PLATFORM_32:BOOL=ON --build .. +cmake --build . +cp *.so .. +cd .. + +cmake -E make_directory build64 +cd build64 +cmake -D CMAKE_BUILD_TYPE:STRING=Release -D TARGET_PLATFORM_32:BOOL=OFF --build .. +cmake --build . +chmod +x *.so +cp *.so .. +cd .. diff --git a/tests/native-lib/test-import-native-from-stack.os b/tests/native-lib/test-import-native-from-stack.os new file mode 100644 index 000000000..9722dba00 --- /dev/null +++ b/tests/native-lib/test-import-native-from-stack.os @@ -0,0 +1,36 @@ +#Использовать "." + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ПроверитьОберткуНадРегуляркой"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьОберткуНадРегуляркой() Экспорт + ИсходнаяСтрока = "{20221110000049,N, + |{2444a6a24da10,3d},1,1,1,1803214,81,I,""Первое событие"",599, + |{""U""},""Представление данных"",1,1,9,3,0, + |{0} + |}, + |{20221110000049,U, + |{2444a6a24da10,3d},1,1,1,1803214,81,E,""Второе событие"",599, + |{""U""},""Представление данных2"",1,1,9,3,0, + |{2,1,31,2,31} + |}"; + Регулярка = "\,*\r*\n*\{(\d{14}),(\w),\r*\n\{([0-9a-f]+),([0-9a-f]+)\},(\d+),(\d+),(\d+),(\d+),(\d+),(\w),""([^ꡏ]*?)(?="",\d+,\r*\n)"",(\d+),\r*\n\{([^ꡏ]*?)(?=\},"")\},""([^ꡏ]*?)(?="",\d+)"",(\d+),(\d+),(\d+),(\d+),\d+[,\d+]*,\r*\n\{((\d+)|\d+,(\d+),(\d+),(\d+),(\d+))\}\r*\n\},*\r*\n*"; + + ОберткаНадРегуляркой = Новый ОберткаНадРегуляркой(Регулярка); + + Совпадения = ОберткаНадРегуляркой.НайтиСовпадения(ИсходнаяСтрока); + Совпадение = Совпадения[0]; + + юТест.ПроверитьРавенство(2, Совпадения.Количество()); +КонецПроцедуры + diff --git a/tests/native-lib/test-native-change-array.os b/tests/native-lib/test-native-change-array.os new file mode 100644 index 000000000..9c9845f58 --- /dev/null +++ b/tests/native-lib/test-native-change-array.os @@ -0,0 +1,30 @@ +#native + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ИзменениеМассиваПоСсылке"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ИзменениеМассиваПоСсылке() Экспорт + + МойМассив = Новый Массив; + + ДобавитьВМассив(МойМассив); + + Значение = МойМассив[0]; + + юТест.ПроверитьРавенство(Значение, 1, "Массив изменен корректно"); + +КонецПроцедуры + +Процедура ДобавитьВМассив(Массив) + Массив.Добавить(1); +КонецПроцедуры diff --git a/tests/native-lib/test-native-default-params-in-methods.os b/tests/native-lib/test-native-default-params-in-methods.os new file mode 100644 index 000000000..aafbc6153 --- /dev/null +++ b/tests/native-lib/test-native-default-params-in-methods.os @@ -0,0 +1,74 @@ +#native + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ЗначенияВМетодахПоУмолчаниюСтрока"); + Тесты.Добавить("ТестДолжен_ЗначенияВМетодахПоУмолчаниюБулево"); + Тесты.Добавить("ТестДолжен_ЗначенияВМетодахПоУмолчаниюНеопределено"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ЗначенияВМетодахПоУмолчаниюСтрока() Экспорт + + Результат = МетодСтроки(,"Текст"); + + юТест.ПроверитьРавенство(Результат, "Т1 Текст", "Текст сформирован корректно"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗначенияВМетодахПоУмолчаниюБулево() Экспорт + + Результат1 = МетодБулево(); + Результат2 = МетодБулево(Истина); + Результат3 = МетодБулево(Ложь); + + юТест.ПроверитьРавенство(Результат1, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат2, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат3, 0, "параметр передан корректно"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗначенияВМетодахПоУмолчаниюНеопределено() Экспорт + + Результат1 = МетодНеопределено(); + Результат2 = МетодНеопределено(Неопределено); + Результат3 = МетодНеопределено(Ложь); + + юТест.ПроверитьРавенство(Результат1, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат2, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат3, 0, "параметр передан корректно"); + +КонецПроцедуры + +Функция МетодСтроки(Текст1 = "Т1 ", Текст2 = "Т2 ") + Сообщить(Текст1); + Сообщить(Текст2); + Возврат Текст1 + Текст2; +КонецФункции + +Функция МетодБулево(Булево = Истина) + + Если Булево = Истина Или Булево Тогда + Возврат 1; + Иначе + Возврат 0; + КонецЕсли; + +КонецФункции + +Функция МетодНеопределено(Параметр = Неопределено) + + Если Параметр = Неопределено Тогда + Возврат 1; + Иначе + Возврат 0; + КонецЕсли; + +КонецФункции \ No newline at end of file diff --git a/tests/native-lib/test-native-kolichestvo.os b/tests/native-lib/test-native-kolichestvo.os new file mode 100644 index 000000000..2fa09ba8a --- /dev/null +++ b/tests/native-lib/test-native-kolichestvo.os @@ -0,0 +1,91 @@ +#native +#Использовать "." + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ПроверитьКоличество_Массив"); + Тесты.Добавить("ТестДолжен_ПроверитьКоличество_Структура"); + Тесты.Добавить("ТестДолжен_ПроверитьКоличество_СписокЗначений"); + Тесты.Добавить("ТестДолжен_ПроверитьКоличество_Соответствие"); + Тесты.Добавить("ТестДолжен_ПроверитьКоличество_ТаблицаЗначений"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьКоличество_Массив() Экспорт + + // Дано + Коллекция = Новый Массив(); + + // Когда + Количество = Коллекция.Количество(); + + // Тогда + + юТест.ПроверитьРавенство(Количество, 0, "Количество получено"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКоличество_Структура() Экспорт + + // Дано + Коллекция = Новый Структура(); + + // Когда + Количество = Коллекция.Количество(); + + // Тогда + + юТест.ПроверитьРавенство(Количество, 0, "Количество получено"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКоличество_СписокЗначений() Экспорт + + // Дано + Коллекция = Новый СписокЗначений(); + + // Когда + Количество = Коллекция.Количество(); + + // Тогда + + юТест.ПроверитьРавенство(Количество, 0, "Количество получено"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКоличество_Соответствие() Экспорт + + // Дано + Коллекция = Новый Соответствие(); + + // Когда + Количество = Коллекция.Количество(); + + // Тогда + + юТест.ПроверитьРавенство(Количество, 0, "Количество получено"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКоличество_ТаблицаЗначений() Экспорт + + // Дано + Коллекция = Новый ТаблицаЗначений(); + + // Когда + Количество = Коллекция.Количество(); + КоличествоКолонок = Коллекция.Колонки.Количество(); + + // Тогда + + юТест.ПроверитьРавенство(Количество, 0, "Количество получено"); + юТест.ПроверитьРавенство(КоличествоКолонок, 0, "Количество получено"); + +КонецПроцедуры diff --git a/tests/native-lib/test-native-search-in-array.os b/tests/native-lib/test-native-search-in-array.os new file mode 100644 index 000000000..d3a2e1f33 --- /dev/null +++ b/tests/native-lib/test-native-search-in-array.os @@ -0,0 +1,40 @@ +#native + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ПроверитьПоискВМассиве"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьПоискВМассиве() Экспорт + + Массив = Новый Массив(); + Массив.Добавить("1"); + Массив.Добавить("2"); + Массив.Добавить("3"); + + Если Массив.Найти("1") <> Неопределено Тогда + НашлиЗначение1 = Истина; + Иначе + НашлиЗначение1 = Ложь; + КонецЕсли; + + Если Массив.Найти("9") <> Неопределено Тогда + НашлиЗначение9 = Истина; + Иначе + НашлиЗначение9 = Ложь; + КонецЕсли; + + юТест.ПроверитьРавенство(НашлиЗначение1, Истина, "Нашли 1"); + юТест.ПроверитьРавенство(НашлиЗначение9, Ложь, "Не нашли 9"); + юТест.ПроверитьРавенство(Массив.Найти("2"), 1, "Индекс элемента по значению"); + +КонецПроцедуры + diff --git a/tests/native-lib/test-native-use-default-params-bool.os b/tests/native-lib/test-native-use-default-params-bool.os new file mode 100644 index 000000000..526a080de --- /dev/null +++ b/tests/native-lib/test-native-use-default-params-bool.os @@ -0,0 +1,29 @@ +#native +#Использовать "." + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ЗначенияВМетодахПоУмолчаниюИспользоватьБулево"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ЗначенияВМетодахПоУмолчаниюИспользоватьБулево() Экспорт + + Сценарий = Новый ПараметрыПоУмолчанию; + + Результат1 = Сценарий.МетодБулево(); + Результат2 = Сценарий.МетодБулево(Истина); + Результат3 = Сценарий.МетодБулево(Ложь); + + юТест.ПроверитьРавенство(Результат1, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат2, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат3, 0, "параметр передан корректно"); + +КонецПроцедуры diff --git a/tests/native-lib/test-native-use-default-params-string.os b/tests/native-lib/test-native-use-default-params-string.os new file mode 100644 index 000000000..ae5102c99 --- /dev/null +++ b/tests/native-lib/test-native-use-default-params-string.os @@ -0,0 +1,25 @@ +#native +#Использовать "." + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ЗначенияВМетодахПоУмолчаниюИспользоватьСтрока"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ЗначенияВМетодахПоУмолчаниюИспользоватьСтрока() Экспорт + + Сценарий = Новый ПараметрыПоУмолчанию; + + Результат = Сценарий.МетодСтроки(,"Текст"); + + юТест.ПроверитьРавенство(Результат, "Т1 Текст", "Текст сформирован корректно"); + +КонецПроцедуры diff --git a/tests/native-lib/test-native-use-default-params-undefinde.os b/tests/native-lib/test-native-use-default-params-undefinde.os new file mode 100644 index 000000000..d0ce0e2aa --- /dev/null +++ b/tests/native-lib/test-native-use-default-params-undefinde.os @@ -0,0 +1,29 @@ +#native +#Использовать "." + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ЗначенияВМетодахПоУмолчаниюИспользоватьНеопределено"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ЗначенияВМетодахПоУмолчаниюИспользоватьНеопределено() Экспорт + + Сценарий = Новый ПараметрыПоУмолчанию; + + Результат1 = Сценарий.МетодНеопределено(); + Результат2 = Сценарий.МетодНеопределено(Неопределено); + Результат3 = Сценарий.МетодНеопределено(Ложь); + + юТест.ПроверитьРавенство(Результат1, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат2, 1, "параметр передан корректно"); + юТест.ПроверитьРавенство(Результат3, 0, "параметр передан корректно"); + +КонецПроцедуры diff --git a/tests/native-lib/test-native-use-params-in-constructor.os b/tests/native-lib/test-native-use-params-in-constructor.os new file mode 100644 index 000000000..dce112f4f --- /dev/null +++ b/tests/native-lib/test-native-use-params-in-constructor.os @@ -0,0 +1,23 @@ +#native +#Использовать "." + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ПроверитьПередачуЗначенияВКонструктор"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьПередачуЗначенияВКонструктор() Экспорт + + НужныйОбъект = Новый СценарийСТекстомВКонструкторе("1"); + + юТест.ПроверитьРавенство(НужныйОбъект.Поле1, "1", "параметр передан корректно"); + +КонецПроцедуры diff --git a/tests/native-lib/test-native-use.os b/tests/native-lib/test-native-use.os new file mode 100644 index 000000000..07488fe90 --- /dev/null +++ b/tests/native-lib/test-native-use.os @@ -0,0 +1,94 @@ +#native +#Использовать "." + +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + Тесты = Новый Массив; + Тесты.Добавить("ТестДолжен_ПроверитьСозданиеОбъекта_ИзНативногоКласса"); + Тесты.Добавить("ТестДолжен_ПроверитьПодключениеСценария"); + Тесты.Добавить("ТестДолжен_ПроверитьВызовМетодовСПараметрамиПоУмолчанию"); + Тесты.Добавить("ТестДолжен_ПроверитьВызовМетодовСПропущеннымиПараметрами"); + Тесты.Добавить("ТестДолжен_ПроверитьВызовМетодовСОшибочноПропущеннымиПараметрами"); + Тесты.Добавить("ТестДолжен_ПроверитьВызовМетодовСЛишнимиПараметрами"); + Тесты.Добавить("ТестДолжен_ПроверитьВызовКонструктораСПараметрамиПоУмолчанию"); + + Возврат Тесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьСозданиеОбъекта_ИзНативногоКласса() Экспорт + ТестовыйСценарий = Новый ТестовыйСценарий; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПодключениеСценария() Экспорт + ПодключитьСценарий(ОбъединитьПути(ТекущийСценарий().Каталог, "Классы", "ТестовыйСценарий.os"), "ПодключенныйСценарий"); + + ТестовыйСценарий = Новый ПодключенныйСценарий; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВызовМетодовСПараметрамиПоУмолчанию() Экспорт + Сценарий = Новый ПараметрыПоУмолчанию; + + Результат1 = Сценарий.МетодБулево(); + Результат2 = Сценарий.МетодБулево(Истина); + Результат3 = Сценарий.МетодБулево(Ложь); + + Если НЕ Результат1 = 1 Тогда + ВызватьИсключение "Ошибка передачи параметра"; + КонецЕсли; + + Если НЕ Результат2 = 1 Тогда + ВызватьИсключение "Ошибка передачи параметра"; + КонецЕсли; + + Если НЕ Результат3 = 0 Тогда + ВызватьИсключение "Ошибка передачи параметра"; + КонецЕсли; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВызовМетодовСПропущеннымиПараметрами() Экспорт + Сценарий = Новый ПараметрыПоУмолчанию; + + Результат = Сценарий.МетодЧисло(,2); + + юТест.ПроверитьРавенство(Результат, 1, "Ошибка передачи пропущенного параметра"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВызовМетодовСОшибочноПропущеннымиПараметрами() Экспорт + Сценарий = Новый ПараметрыПоУмолчанию; + + ОК = Ложь; + Ошибка = ""; + Попытка + Результат = Сценарий.МетодЧисло(1,); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + ОК = СтрНайти(Ошибка,"Пропущен") > 0; + КонецПопытки; + + юТест.ПроверитьИстину(ОК, "Не было исключения о пропущенном параметре."+Ошибка); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВызовМетодовСЛишнимиПараметрами() Экспорт + Сценарий = Новый ПараметрыПоУмолчанию; + + ОК = Ложь; + Ошибка = ""; + Попытка + Результат = Сценарий.МетодЧисло(1,2,3); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + ОК = СтрНайти(Ошибка,"Слишком много") > 0; + КонецПопытки; + + юТест.ПроверитьИстину(ОК, "Не было исключения о лишнем параметре."+Ошибка); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВызовКонструктораСПараметрамиПоУмолчанию() Экспорт + Сценарий = Новый ПараметрыКонструктораПоУмолчанию("Парам1"); +КонецПроцедуры diff --git "a/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\261\320\265\321\200\321\202\320\272\320\260\320\235\320\260\320\264\320\240\320\265\320\263\321\203\320\273\321\217\321\200\320\272\320\276\320\271.os" "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\261\320\265\321\200\321\202\320\272\320\260\320\235\320\260\320\264\320\240\320\265\320\263\321\203\320\273\321\217\321\200\320\272\320\276\320\271.os" new file mode 100644 index 000000000..fb82ecfa0 --- /dev/null +++ "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\261\320\265\321\200\321\202\320\272\320\260\320\235\320\260\320\264\320\240\320\265\320\263\321\203\320\273\321\217\321\200\320\272\320\276\320\271.os" @@ -0,0 +1,15 @@ +#native + +Перем РегулярноеВыражение; + +Процедура ПриСозданииОбъекта(ТекстРегулярки) + РегулярноеВыражение = Новый РегулярноеВыражение(ТекстРегулярки); +КонецПроцедуры + +Функция НайтиСовпадения(Текст) Экспорт + + КоллекцияСовпадений = РегулярноеВыражение.НайтиСовпадения(Текст); + + Возврат КоллекцияСовпадений; + +КонецФункции \ No newline at end of file diff --git "a/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\232\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\202\320\276\321\200\320\260\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\232\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\202\320\276\321\200\320\260\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" new file mode 100644 index 000000000..f654e0ee9 --- /dev/null +++ "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\232\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\202\320\276\321\200\320\260\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" @@ -0,0 +1,6 @@ +#native + +Процедура ПриСозданииОбъекта(Параметр1, Параметр2="Парам2") + +КонецПроцедуры + diff --git "a/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" new file mode 100644 index 000000000..b943f980f --- /dev/null +++ "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\320\260\321\200\320\260\320\274\320\265\321\202\321\200\321\213\320\237\320\276\320\243\320\274\320\276\320\273\321\207\320\260\320\275\320\270\321\216.os" @@ -0,0 +1,39 @@ +#native + +Процедура ПриСозданииОбъекта() + +КонецПроцедуры + +Функция МетодСтроки(Текст1 = "Т1 ", Текст2 = "Т2 ") Экспорт + Возврат Текст1 + Текст2; +КонецФункции + +Функция МетодБулево(Булево = Истина) Экспорт + + Если Булево = Истина Или Булево Тогда + Возврат 1; + Иначе + Возврат 0; + КонецЕсли; + +КонецФункции + +Функция МетодНеопределено(Параметр = Неопределено) Экспорт + + Если Параметр = Неопределено Тогда + Возврат 1; + Иначе + Возврат 0; + КонецЕсли; + +КонецФункции + +Функция МетодЧисло(Параметр1 = -1, Параметр2 ) Экспорт + + Если Параметр1 = -1 Тогда + Возврат 1; + Иначе + Возврат 0 * Параметр2; + КонецЕсли; + +КонецФункции \ No newline at end of file diff --git "a/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\241\321\206\320\265\320\275\320\260\321\200\320\270\320\271\320\241\320\242\320\265\320\272\321\201\321\202\320\276\320\274\320\222\320\232\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\202\320\276\321\200\320\265.os" "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\241\321\206\320\265\320\275\320\260\321\200\320\270\320\271\320\241\320\242\320\265\320\272\321\201\321\202\320\276\320\274\320\222\320\232\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\202\320\276\321\200\320\265.os" new file mode 100644 index 000000000..3aaddf5de --- /dev/null +++ "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\241\321\206\320\265\320\275\320\260\321\200\320\270\320\271\320\241\320\242\320\265\320\272\321\201\321\202\320\276\320\274\320\222\320\232\320\276\320\275\321\201\321\202\321\200\321\203\320\272\321\202\320\276\321\200\320\265.os" @@ -0,0 +1,7 @@ +#native + +Перем Поле1 Экспорт; + +Процедура ПриСозданииОбъекта(Параметр1) + Поле1 = Параметр1; +КонецПроцедуры \ No newline at end of file diff --git "a/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\241\321\206\320\265\320\275\320\260\321\200\320\270\320\271.os" "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\241\321\206\320\265\320\275\320\260\321\200\320\270\320\271.os" new file mode 100644 index 000000000..a9f126b28 --- /dev/null +++ "b/tests/native-lib/\320\232\320\273\320\260\321\201\321\201\321\213/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\241\321\206\320\265\320\275\320\260\321\200\320\270\320\271.os" @@ -0,0 +1,18 @@ +#native + +Перем Поле1 Экспорт; + +Перем Поле2; + +Процедура ПриСозданииОбъекта() + Поле1 = 1; +КонецПроцедуры + + +Процедура Метод1() + +КонецПроцедуры + +Процедура Метод2() Экспорт + +КонецПроцедуры \ No newline at end of file diff --git a/tests/native-module.os b/tests/native-module.os new file mode 100644 index 000000000..08a4f7a67 --- /dev/null +++ b/tests/native-module.os @@ -0,0 +1,55 @@ +#native + +Перем юТест; +Перем Лог; +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетода"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовАссерта"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтениеЗаписьПоИндексу"); + //FIXME #1531 + //ВсеТесты.Добавить("ТестДолжен_ПроверитьДелегат"); + + Возврат ВсеТесты; + +КонецФункции + +// Вызовется со стороны основного рантайма +Процедура ТестДолжен_ПроверитьВызовМетода() Экспорт + // просто не должно упасть + Сообщить("Вызвано со стороны testrunner") +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВызовАссерта() Экспорт + // должно суметь вызвать основной рантайм (юТест.*) + юТест.ПроверитьРавенство(1, 1); + Сообщить("Выполнен вызов в сторону testrunner"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтениеЗаписьПоИндексу() Экспорт + + Массив = Новый Массив; + Массив.Добавить(1); + + юТест.ПроверитьРавенство(1, Массив[0]); + Массив[0] = "Привет"; + + юТест.ПроверитьРавенство("Привет", Массив[0]); + +КонецПроцедуры + +Процедура ПроверитьДелегат() Экспорт + + //Текст = "Функция Петля(Знач Значение) Экспорт Возврат Значение; КонецФункции"; + //Сценарий = ЗагрузитьСценарийИзСтроки(Текст); + // + //Делегат = Новый Действие(Сценарий, "Петля"); + //Результат = Делегат.Выполнить("Привет"); + // + //юТест.ПроверитьРавенство("Привет", Результат); + +КонецПроцедуры diff --git a/tests/null-character-handling.os b/tests/null-character-handling.os new file mode 100644 index 000000000..631f7cc7c --- /dev/null +++ b/tests/null-character-handling.os @@ -0,0 +1,110 @@ +Перем юТест; + +Функция Версия() Экспорт + Возврат "0.1"; +КонецФункции + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_СоздатьФайлСНулевымСимволом"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьНайтиФайлыСНулевымСимволом"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьСвойстваФайлаСНулевымСимволом"); + + Возврат ВсеТесты; +КонецФункции + +Процедура ТестДолжен_СоздатьФайлСНулевымСимволом() Экспорт + + ВремФайл = ПолучитьИмяВременногоФайла("txt"); + ЗаписьТекста = Новый ЗаписьТекста(ВремФайл); + ЗаписьТекста.ЗаписатьСтроку("тестовое содержимое"); + ЗаписьТекста.Закрыть(); + + Попытка + // Добавляем нулевой символ в путь, как это делает Windows WebDAV клиент + ПутьСНулевымСимволом = ВремФайл + Символ(0); + Файл = Новый Файл(ПутьСНулевымСимволом); + + // Проверяем, что файл доступен и не выбрасывает исключение + юТест.ПроверитьИстину(Файл.Существует(), "Файл должен существовать"); + юТест.ПроверитьРавенство("txt", Прав(Файл.Расширение, 3), "Расширение должно быть txt"); + + Исключение + УдалитьФайлы(ВремФайл); + ВызватьИсключение; + КонецПопытки; + + УдалитьФайлы(ВремФайл); + +КонецПроцедуры + +Процедура ТестДолжен_ВыполнитьНайтиФайлыСНулевымСимволом() Экспорт + + ВремКаталог = КаталогВременныхФайлов(); + ИмяТестовогоФайла = "test_null_char_" + Формат(ТекущаяДата(), "ДФ=yyyyMMddHHmmss") + ".txt"; + ВремФайл = ОбъединитьПути(ВремКаталог, ИмяТестовогоФайла); + + ЗаписьТекста = Новый ЗаписьТекста(ВремФайл); + ЗаписьТекста.ЗаписатьСтроку("тестовое содержимое"); + ЗаписьТекста.Закрыть(); + + Попытка + // Добавляем нулевой символ в путь к каталогу + ПутьКаталогаСНулевымСимволом = ВремКаталог + Символ(0); + + // Проверяем, что НайтиФайлы не выбрасывает исключение + НайденныеФайлы = НайтиФайлы(ПутьКаталогаСНулевымСимволом, ИмяТестовогоФайла); + + // Проверяем, что файл был найден + юТест.ПроверитьРавенство(1, НайденныеФайлы.Количество(), "Должен быть найден один файл"); + юТест.ПроверитьРавенство(ИмяТестовогоФайла, НайденныеФайлы[0].Имя, "Имя файла должно совпадать"); + + Исключение + УдалитьФайлы(ВремФайл); + ВызватьИсключение; + КонецПопытки; + + УдалитьФайлы(ВремФайл); + +КонецПроцедуры + +Процедура ТестДолжен_ПолучитьСвойстваФайлаСНулевымСимволом() Экспорт + + ВремФайл = ПолучитьИмяВременногоФайла("txt"); + ЗаписьТекста = Новый ЗаписьТекста(ВремФайл); + ЗаписьТекста.ЗаписатьСтроку("тестовое содержимое"); + ЗаписьТекста.Закрыть(); + + Попытка + // Добавляем нулевой символ в конец пути + ПутьСНулевымСимволом = ВремФайл + Символ(0); + Файл = Новый Файл(ПутьСНулевымСимволом); + + // Проверяем, что все свойства доступны без исключений + ПолноеИмя = Файл.ПолноеИмя; + юТест.ПроверитьНеравенство("", ПолноеИмя, "ПолноеИмя не должно быть пустым"); + + Имя = Файл.Имя; + юТест.ПроверитьНеравенство("", Имя, "Имя не должно быть пустым"); + + Путь = Файл.Путь; + юТест.ПроверитьНеравенство("", Путь, "Путь не должен быть пустым"); + + Расширение = Файл.Расширение; + юТест.ПроверитьРавенство(".txt", Расширение, "Расширение должно быть .txt"); + + Размер = Файл.Размер(); + юТест.ПроверитьБольше(Размер, 0, "Размер должен быть больше 0"); + + Исключение + УдалитьФайлы(ВремФайл); + ВызватьИсключение; + КонецПопытки; + + УдалитьФайлы(ВремФайл); + +КонецПроцедуры diff --git a/tests/postdataparser.os b/tests/postdataparser.os index 4d0779487..f82519a84 100644 --- a/tests/postdataparser.os +++ b/tests/postdataparser.os @@ -13,7 +13,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьПростойРазборСтрокиЗапроса"); Каталог = КаталогПрограммы(); - ПодключитьВнешнююКомпоненту(ОбъединитьПути(Каталог, "oscript.exe")); + ПодключитьВнешнююКомпоненту(ОбъединитьПути(Каталог, "oscript.dll")); Возврат ВсеТесты; diff --git a/tests/process.os b/tests/process.os index e80ec4890..44da651b5 100644 --- a/tests/process.os +++ b/tests/process.os @@ -6,9 +6,8 @@ ВсеТесты = Новый Массив; - ВсеТесты.Добавить("ТестДолжен_ПолучитьПутьКOscript"); - СИ = Новый СистемнаяИнформация; + Сообщить(СИ.ВерсияОС); Если Найти(СИ.ВерсияОС, "Windows") > 0 Тогда ВсеТесты.Добавить("ТестДолжен_ПрочитатьВыводOscriptСразу"); ВсеТесты.Добавить("ТестДолжен_ПрочитатьВыводOscriptПострочно"); @@ -19,25 +18,18 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьЗапускСАргументамиСодержащимиПробелы"); ВсеТесты.Добавить("ТестДолжен_ЗапуститьПроцессСПередачейПеременныхСреды"); ВсеТесты.Добавить("ТестДолжен_ЗапуститьПроцессСПередачейПеременныхСредыИКодировкой"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоСобственныйPIDПроцессаСуществует"); КонецЕсли; Возврат ВсеТесты; КонецФункции -Процедура ТестДолжен_ПолучитьПутьКOscript() Экспорт - - Путь = Новый Файл(ПутьОСкрипт()); - - юТест.ПроверитьИстину(Путь.Существует()); - -КонецПроцедуры - Процедура ТестДолжен_ПрочитатьВыводOscriptСразу() Экспорт Путь = ПутьОСкрипт(); - Процесс = СоздатьПроцесс("""" + Путь + """",,Истина); + Процесс = СоздатьПроцесс(Путь,,Истина); Процесс.Запустить(); Поток = Процесс.ПотокВывода; Процесс.ОжидатьЗавершения(); @@ -52,7 +44,7 @@ Путь = ПутьОСкрипт(); - Процесс = СоздатьПроцесс("""" + Путь + """",,Истина); + Процесс = СоздатьПроцесс(Путь,,Истина); Процесс.Запустить(); Поток = Процесс.ПотокВывода; @@ -76,7 +68,6 @@ Процедура ТестДолжен_ЗаписатьВоВходнойПотокПроцесса() Экспорт - Консоль = Новый Консоль(); КодировкаПоУмолчанию = Консоль.КодировкаВходногоПотока; Если Консоль.КодировкаВходногоПотока = КодировкаТекста.UTF8 Тогда Консоль.КодировкаВходногоПотока = КодировкаТекста.UTF8NoBOM; @@ -102,16 +93,20 @@ КонецПроцедуры +Функция ИмяПроцесса() + Возврат ТекущийПроцесс().Имя; +КонецФункции + Процедура ТестДолжен_НайтиПроцессПоИмени() Экспорт - МассивПроцессов = НайтиПроцессыПоИмени("oscript"); + МассивПроцессов = НайтиПроцессыПоИмени(ИмяПроцесса()); юТест.ПроверитьБольшеИлиРавно(МассивПроцессов.Количество(), 1); КонецПроцедуры Процедура ТестДолжен_НайтиПроцессПоPID() Экспорт - МассивПроцессов = НайтиПроцессыПоИмени("oscript"); + МассивПроцессов = НайтиПроцессыПоИмени(ИмяПроцесса()); ИД = МассивПроцессов[0].Идентификатор; @@ -121,6 +116,26 @@ КонецПроцедуры +Процедура ТестДолжен_ПроверитьЧтоСобственныйPIDПроцессаСуществует() Экспорт + + МассивПроцессов = НайтиПроцессыПоИмени(ИмяПроцесса()); + юТест.ПроверитьНеравенство(0, МассивПроцессов.Количество()); + + СобственныйPID = ТекущийПроцесс().Идентификатор; + + PIDПроцесса = Неопределено; + + Для Каждого ТекПроцесс Из МассивПроцессов Цикл + Если ТекПроцесс.Идентификатор = СобственныйPID Тогда + PIDПроцесса = ТекПроцесс.Идентификатор; + Прервать; + КонецЕсли; + КонецЦикла; + + юТест.ПроверитьРавенство(СобственныйPID, PIDПроцесса); + +КонецПроцедуры + Функция ШтатныйВыводOscript() СИ = Новый СистемнаяИнформация; @@ -129,32 +144,36 @@ "1Script Execution Engine. Version " + СИ.Версия + " | |Usage: + | oscript.exe [options] [script_arguments...] + | oscript.exe [mode_options] [script_arguments...] | - |I. Script execution: oscript.exe [script arguments..] - | - |II. Special mode: oscript.exe [script arguments..] - |Mode can be one of these: - | -measure measures execution time - | -compile shows compiled module without execution - | -check [-env=]provides syntax check - | -check -cgi provides syntax check in CGI-mode - | -version output version string - | - | -encoding= set output encoding - | -codestat= write code statistics + |Modes: + | -measure Measures script execution time. + | -compile Shows compiled module without execution. + | -check Provides syntax check. + | Options: + | -cgi Syntax check in CGI-mode. + | -env= Path to entrypoint file for context. + | -debug Runs script in debug mode. + | Options: + | -port= Debugger port (default is 2801). + | -noWait Do not wait for debugger connection. + | -version, -v Output version string. | - |III. Build standalone executable: oscript.exe -make - | Builds a standalone executable module based on script specified + |Options: + | -encoding= Set output encoding (e.g. utf-8). + | -codestat= Write code execution statistics to file. | - |IV. Run as CGI application: oscript.exe -cgi [script arguments..] - | Runs as CGI application under HTTP-server (Apache/Nginx/IIS/etc...)"; + |CGI Mode: + | oscript.exe -cgi [script_arguments...] + | Runs as CGI application under HTTP-server."; Возврат НормализоватьПереводыСтрок(Текст); КонецФункции Функция ПутьОСкрипт() - Возврат КаталогПрограммы() + "/oscript.exe"; + Возврат "dotnet """ + КаталогПрограммы() + "/oscript.dll"""; КонецФункции Функция НормализоватьПереводыСтрок(Знач ИсходнаяСтрока) @@ -168,7 +187,7 @@ УдалитьФайлы(ФайлВывода); - ПриложениеДляЗапуска = """" + КаталогПрограммы() + "/oscript.exe"""; //TODO: для Mono должно быть иначе + ПриложениеДляЗапуска = ПутьОСкрипт(); //TODO: для Mono должно быть иначе ЗапуститьПриложение(ПриложениеДляЗапуска + " """ + ФайлЗапускаемогоСкрипта + """ """ + ФайлВывода + """ ""арг у мент1"" аргумент2", КаталогВременныхФайлов(), Истина); УдалитьФайлы(ФайлЗапускаемогоСкрипта); ФайлВыводаОбъект = Новый Файл(ФайлВывода); @@ -192,7 +211,7 @@ Путь = ПутьОСкрипт(); - Процесс = СоздатьПроцесс("""" + Путь + """",,Истина); + Процесс = СоздатьПроцесс(Путь,,Истина); Процесс.Запустить(); Процесс.ОжидатьЗавершения(); @@ -210,7 +229,7 @@ Путь = ПутьОСкрипт(); - Процесс = СоздатьПроцесс("""" + Путь + """",,Истина, Ложь,, ПеременныеСреды()); + Процесс = СоздатьПроцесс(Путь,,Истина, Ложь,, ПеременныеСреды()); Процесс.Запустить(); Поток = Процесс.ПотокВывода; Процесс.ОжидатьЗавершения(); @@ -225,7 +244,7 @@ Путь = ПутьОСкрипт(); - Процесс = СоздатьПроцесс("""" + Путь + """",,Истина, Ложь, КодировкаТекста.UTF8, ПеременныеСреды()); + Процесс = СоздатьПроцесс(Путь,,Истина, Ложь, КодировкаТекста.UTF8, ПеременныеСреды()); Процесс.Запустить(); Поток = Процесс.ПотокВывода; Процесс.ОжидатьЗавершения(); diff --git a/tests/random.os b/tests/random.os index 518c76607..a4591bb67 100644 --- a/tests/random.os +++ b/tests/random.os @@ -22,6 +22,7 @@ ВсеТесты.Добавить("ТестДолжен_СоздатьГенератор"); ВсеТесты.Добавить("ТестДолжен_ПроверитьДоступныеГраницы"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьГраницыНачальногоЗначения"); Возврат ВсеТесты; @@ -86,3 +87,22 @@ КонецЦикла; КонецПроцедуры + +Процедура ТестДолжен_ПроверитьГраницыНачальногоЗначения() Экспорт + + ГсчМин1 = Новый ГенераторСлучайныхЧисел(-36893488147419103232); + ГсчМин2 = Новый ГенераторСлучайныхЧисел(-36893488147419103232); + + юТест.ПроверитьРавенство(ГсчМин1.СлучайноеЧисло(), ГсчМин2.СлучайноеЧисло()); + + ГсчМин1 = Новый ГенераторСлучайныхЧисел(36893488147419103232); + ГсчМин2 = Новый ГенераторСлучайныхЧисел(36893488147419103232); + + юТест.ПроверитьРавенство(ГсчМин1.СлучайноеЧисло(), ГсчМин2.СлучайноеЧисло()); + + ГсчМин1 = Новый ГенераторСлучайныхЧисел(36893488147419103.232); + ГсчМин2 = Новый ГенераторСлучайныхЧисел(36893488147419103.232); + + юТест.ПроверитьРавенство(ГсчМин1.СлучайноеЧисло(), ГсчМин2.СлучайноеЧисло()); + +КонецПроцедуры diff --git a/tests/reflector.os b/tests/reflector.os index 4c1b9582e..59984dec3 100644 --- a/tests/reflector.os +++ b/tests/reflector.os @@ -24,15 +24,43 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьМетод_ПолучитьТаблицуСвойств"); ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаСПараметрамиПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаБезПараметровПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуПараметровПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСуществуетДляТипа"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуМетодовДляТипа"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляСистемногоТипа"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляТипа"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляТипа_ПроверкаАннотаций"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоУПараметровЕстьИмена"); ВсеТесты.Добавить("ТестДолжен_ПроверитьУстановкуПриватногоСвойства"); ВсеТесты.Добавить("ТестДолжен_ПроверитьУстановкуПубличногоСвойства"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеПриватногоСвойства"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучениеПубличногоСвойства"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетод_ПолучитьТаблицуМетодов_ПроверитьЗначенияПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетод_ПолучитьТаблицуМетодов_ПроверитьЗначенияПоУмолчанию_ИзТипаОбъекта"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРефлексиюДля_ЭтотОбъект"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПустыеАннотации"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПриватныеПоля"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы_СОтборомКоллекция"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПримитивный"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПользовательский"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуВнешнихПараметров"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРефлексиюВнешнегоПараметра"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРефлексиюВнешнегоПараметраВРежимеПриватных"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляСтруктуры"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляФиксированнойСтруктуры"); + +#Если Windows Тогда + ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаComОбъекта"); +#КонецЕсли + Возврат ВсеТесты; КонецФункции @@ -115,7 +143,7 @@ ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(Пример); юТест.ПроверитьРавенство(Строка(ТипЗнч(ТаблицаМетодов)), "ТаблицаЗначений", "ТаблицаМетодов"); - юТест.ПроверитьРавенство(7, ТаблицаМетодов.Количество(), "ТаблицаМетодов.Количество()"); + юТест.ПроверитьРавенство(8, ТаблицаМетодов.Количество(), "ТаблицаМетодов.Количество()"); Метод0 = ТаблицаМетодов.Найти("Версия", "Имя"); юТест.ПроверитьНеРавенство(Неопределено, Метод0, "Метод0"); @@ -206,7 +234,7 @@ ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(Пример); юТест.ПроверитьРавенство(Строка(ТипЗнч(ТаблицаМетодов)), "ТаблицаЗначений", "ТаблицаМетодов"); - юТест.ПроверитьРавенство(6, ТаблицаМетодов.Количество(), "ТаблицаМетодов.Количество()"); + юТест.ПроверитьРавенство(7, ТаблицаМетодов.Количество(), "ТаблицаМетодов.Количество()"); Метод0 = ТаблицаМетодов.Найти("ВызватьМетод", "Имя"); юТест.ПроверитьНеРавенство(Неопределено, Метод0, "Метод0"); @@ -232,7 +260,7 @@ МассивОбъектов.Добавить(Новый РегулярноеВыражение("а")); МассивОбъектов.Добавить(Новый ЗаписьXML); МассивОбъектов.Добавить(Новый ЧтениеXML); - МассивОбъектов.Добавить(Новый Консоль); + МассивОбъектов.Добавить(Консоль); МассивОбъектов.Добавить(Новый Файл("ыв")); МассивОбъектов.Добавить(Новый ГенераторСлучайныхЧисел); МассивОбъектов.Добавить(Новый СистемнаяИнформация); @@ -291,15 +319,48 @@ Рефлектор = Новый Рефлектор; ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Пример); - юТест.ПроверитьРавенство(3, ТаблицаСвойств.Количество()); + юТест.ПроверитьРавенство(5, ТаблицаСвойств.Количество()); - юТест.ПроверитьРавенство("ЭкспортнаяПеременная", ТаблицаСвойств[1].Имя); + юТест.ПроверитьРавенство("ЭкспортнаяПеременная", ТаблицаСвойств[3].Имя); ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Рефлектор); юТест.ПроверитьРавенство(0, ТаблицаСвойств.Количество()); КонецПроцедуры +Процедура ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляТипа() Экспорт + + ОбъектДляПроверки = ПолучитьОбъектДляПроверки("test_export_props_type"); + Тип = ТипЗнч(ОбъектДляПроверки); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Тип, Истина); + юТест.ПроверитьРавенство(7, ТаблицаСвойств.Количество()); + + юТест.ПроверитьРавенство("ЭкспортнаяПеременная", ТаблицаСвойств[4].Имя); + юТест.ПроверитьРавенство("юТест", ТаблицаСвойств[2].Имя); + юТест.ПроверитьЛожь(ТаблицаСвойств[2].Экспорт); + юТест.ПроверитьИстину(ТаблицаСвойств[3].Экспорт); + юТест.ПроверитьИстину(ТаблицаСвойств[4].Экспорт); + юТест.ПроверитьИстину(ТаблицаСвойств[5].Экспорт); + юТест.ПроверитьЛожь(ТаблицаСвойств[6].Экспорт); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляТипа_ПроверкаАннотаций() Экспорт + + ОбъектДляПроверки = ПолучитьОбъектДляПроверки("test_export_props_type_annotations"); + Тип = ТипЗнч(ОбъектДляПроверки); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Тип, Истина); + + юТест.ПроверитьРавенство(1, ТаблицаСвойств[0].Аннотации.Количество()); + юТест.ПроверитьРавенство(1, ТаблицаСвойств[1].Аннотации[0].Параметры[0].Значение); + юТест.ПроверитьРавенство("Парам1", ТаблицаСвойств[6].Аннотации[0].Параметры[0].Имя); + +КонецПроцедуры + Функция ПолучитьОбъектДляПроверки(ИмяКласса) ТекПуть = Новый Файл(ТекущийСценарий().Источник).Путь; ПодключитьСценарий(ТекПуть+"example-test.os", ИмяКласса); @@ -373,6 +434,47 @@ КонецФункции +Процедура ТестДолжен_ПроверитьВызовМетодаБезПараметровПоУмолчанию() Экспорт + Рефлектор = Новый Рефлектор; + + Массив = Новый Массив(1); + Арг = Новый Массив; + Арг.Добавить(0); + + Ошибка = Ложь; + Попытка + Рефлектор.ВызватьМетод(Массив, "Установить", Арг); + Ч = Массив[0]; + Ошибка = Истина; + Исключение + Сообщение = ОписаниеОшибки(); + Если СтрНайти(Сообщение,"NullReferenceException") <> 0 Тогда + Ошибка = Истина; + КонецЕсли; + КонецПопытки; + + Если Ошибка Тогда + юТест.ТестПровален("Вызов рефлектора с недостаточным количеством параметров без умолчания"); + КонецЕсли; + +КонецПроцедуры + +Процедура ТестПередачиПараметровПоУмолчанию(Пар1, Пар2="п2") Экспорт + юТест.ПроверитьРавенство(Пар1, "п1", "Тест передачи параметров: параметр 1"); + юТест.ПроверитьРавенство(Пар2, "п2", "Тест передачи параметров: параметр 2"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПередачуПараметровПоУмолчанию() Экспорт + Рефлектор = Новый Рефлектор; + + Массив = Новый Массив(1); + Арг = Новый Массив; + Арг.Добавить("п1"); + Рефлектор.ВызватьМетод(ЭтотОбъект, "ТестПередачиПараметровПоУмолчанию", Арг); + +КонецПроцедуры + + Процедура ТестДолжен_ПроверитьМетодСуществуетДляТипа() Экспорт Рефлектор = Новый Рефлектор; @@ -390,10 +492,24 @@ юТест.ПроверитьНеРавенство(Неопределено, Таблица.Найти("Размер")); юТест.ПроверитьНеРавенство(Неопределено, Таблица.Найти("Записать")); юТест.ПроверитьНеРавенство(Неопределено, Таблица.Найти("ОткрытьПотокДляЧтения")); - + КонецПроцедуры -Процедура ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляТипа() Экспорт +Процедура ТестДолжен_ПроверитьЧтоУПараметровЕстьИмена() Экспорт + Рефлектор = Новый Рефлектор(); + Методы = Рефлектор.ПолучитьТаблицуМетодов(ЭтотОбъект); + МетодСПараметрами = Методы.Найти("ФункцияСПараметрамиПоУмолчанию"); + + юТест.ПроверитьНеРавенство(Неопределено, МетодСПараметрами); + Параметры = МетодСПараметрами.Параметры; + + юТест.ПроверитьРавенство("П1", Параметры[0].Имя); + юТест.ПроверитьРавенство("П2", Параметры[1].Имя); + юТест.ПроверитьРавенство("П3", Параметры[2].Имя); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляСистемногоТипа() Экспорт Рефлектор = Новый Рефлектор; Таблица = Рефлектор.ПолучитьТаблицуСвойств(Тип("Файл")); юТест.ПроверитьНеРавенство(0, Таблица.Количество()); @@ -455,4 +571,379 @@ юТест.Проверить(НовоеЗначение = Рефлектор.ПолучитьСвойство(Пример, "Яшма2"), "Значения должны совпадать"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьМетод_ПолучитьТаблицуМетодов_ПроверитьЗначенияПоУмолчанию() Экспорт + Пример = ПолучитьОбъектДляПроверки("test_reflector"); + + Рефлектор = Новый Рефлектор; + ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(Пример); + + МетодСРазнымиПараметрами = ТаблицаМетодов.Найти("МетодСРазнымиПараметрами", "Имя"); + Параметры = МетодСРазнымиПараметрами.Параметры; + юТест.ПроверитьРавенство(Параметры[0].ЕстьЗначениеПоУмолчанию, Ложь); + юТест.ПроверитьРавенство(Параметры[0].ЗначениеПоУмолчанию, Неопределено); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[0].ЗначениеПоУмолчанию), Тип("Неопределено")); + юТест.ПроверитьРавенство(Параметры[1].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[1].ЗначениеПоУмолчанию, Неопределено); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[1].ЗначениеПоУмолчанию), Тип("Неопределено")); + юТест.ПроверитьРавенство(Параметры[2].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[2].ЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[2].ЗначениеПоУмолчанию), Тип("Булево")); + юТест.ПроверитьРавенство(Параметры[3].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[3].ЗначениеПоУмолчанию, 1); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[3].ЗначениеПоУмолчанию), Тип("Число")); + юТест.ПроверитьРавенство(Параметры[4].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[4].ЗначениеПоУмолчанию, "Строка"); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[4].ЗначениеПоУмолчанию), Тип("Строка")); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьМетод_ПолучитьТаблицуМетодов_ПроверитьЗначенияПоУмолчанию_ИзТипаОбъекта() Экспорт + Пример = ПолучитьОбъектДляПроверки("test_reflector"); + + Рефлектор = Новый Рефлектор; + ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(ТипЗнч(Пример)); + + МетодСРазнымиПараметрами = ТаблицаМетодов.Найти("МетодСРазнымиПараметрами", "Имя"); + Параметры = МетодСРазнымиПараметрами.Параметры; + юТест.ПроверитьРавенство(Параметры[0].ЕстьЗначениеПоУмолчанию, Ложь); + юТест.ПроверитьРавенство(Параметры[0].ЗначениеПоУмолчанию, Неопределено); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[0].ЗначениеПоУмолчанию), Тип("Неопределено")); + юТест.ПроверитьРавенство(Параметры[1].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[1].ЗначениеПоУмолчанию, Неопределено); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[1].ЗначениеПоУмолчанию), Тип("Неопределено")); + юТест.ПроверитьРавенство(Параметры[2].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[2].ЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[2].ЗначениеПоУмолчанию), Тип("Булево")); + юТест.ПроверитьРавенство(Параметры[3].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[3].ЗначениеПоУмолчанию, 1); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[3].ЗначениеПоУмолчанию), Тип("Число")); + юТест.ПроверитьРавенство(Параметры[4].ЕстьЗначениеПоУмолчанию, Истина); + юТест.ПроверитьРавенство(Параметры[4].ЗначениеПоУмолчанию, "Строка"); + юТест.ПроверитьРавенство(ТипЗнч(Параметры[4].ЗначениеПоУмолчанию), Тип("Строка")); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПустыеАннотации() Экспорт + Пример = ПолучитьОбъектДляПроверки("test_reflector"); + + Рефлектор = Новый Рефлектор; + ТаблицаМетодов = Рефлектор.ПолучитьТаблицуМетодов(Пример); + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Пример); + + МетодСРазнымиПараметрами = ТаблицаМетодов.Найти("МетодСРазнымиПараметрами", "Имя"); + Яшма2 = ТаблицаМетодов.Найти("Яшма2", "Имя"); + ПеременнаяСАннотацией = ТаблицаСвойств.Найти("ПеременнаяСАннотацией", "Имя"); + ПеременнаяСАннотациейИПараметром = ТаблицаСвойств.Найти("ПеременнаяСАннотациейИПараметром", "Имя"); + + юТест.ПроверитьРавенство(типЗнч(МетодСРазнымиПараметрами.Аннотации[0].Параметры), Тип("ТаблицаЗначений")); + юТест.ПроверитьРавенство(типЗнч(Яшма2.Аннотации[0].Параметры), Тип("ТаблицаЗначений")); + юТест.ПроверитьРавенство(типЗнч(ПеременнаяСАннотацией.Аннотации[0].Параметры), Тип("ТаблицаЗначений")); + юТест.ПроверитьРавенство(типЗнч(ПеременнаяСАннотациейИПараметром.Аннотации[0].Параметры), Тип("ТаблицаЗначений")); + + юТест.ПроверитьРавенство(МетодСРазнымиПараметрами.Аннотации[0].Параметры.Количество(), 0); + юТест.ПроверитьРавенство(Яшма2.Аннотации[0].Параметры.Количество(), 1); + юТест.ПроверитьРавенство(ПеременнаяСАннотацией.Аннотации[0].Параметры.Количество(), 0); + юТест.ПроверитьРавенство(ПеременнаяСАннотациейИПараметром.Аннотации[0].Параметры.Количество(), 1); + + юТест.ПроверитьРавенство(Яшма2.Аннотации[0].Параметры[0].Значение, 1); + юТест.ПроверитьРавенство(ПеременнаяСАннотациейИПараметром.Аннотации[0].Параметры[0].Значение, 1); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПриватныеПоля() Экспорт + Пример = ПолучитьОбъектДляПроверки("test_reflector"); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Пример, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 7); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "юТест"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Ложь); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Имя, "Яшма2"); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Экспорт, Ложь); + + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(Пример); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 5); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); + + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(ТипЗнч(Пример), Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 7); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "юТест"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Ложь); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[5].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Имя, "Яшма2"); + юТест.ПроверитьРавенство(ТаблицаСвойств[6].Экспорт, Ложь); + + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(ТипЗнч(Пример)); + юТест.ПроверитьРавенство(ТаблицаСвойств.Количество(), 5); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Имя, "ПеременнаяСАннотацией"); + юТест.ПроверитьРавенство(ТаблицаСвойств[0].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Имя, "ПеременнаяСАннотациейИПараметром"); + юТест.ПроверитьРавенство(ТаблицаСвойств[1].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Имя, "ЭтотОбъек"); + юТест.ПроверитьРавенство(ТаблицаСвойств[2].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Имя, "ЭкспортнаяПеременная"); + юТест.ПроверитьРавенство(ТаблицаСвойств[3].Экспорт, Истина); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Имя, "Яшма1"); + юТест.ПроверитьРавенство(ТаблицаСвойств[4].Экспорт, Истина); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРефлексиюДля_ЭтотОбъект() Экспорт + ОбъектПроверки = ЭтотОбъект; + РефлекторПроверки = Новый Рефлектор; + + ТаблицаМетодов = РефлекторПроверки.ПолучитьТаблицуМетодов(ОбъектПроверки); + ТаблицаСвойств = РефлекторПроверки.ПолучитьТаблицуСвойств(ТипЗнч(ОбъектПроверки), Истина); + + юТест.ПроверитьНеравенство(0, ТаблицаМетодов.Количество()); + юТест.ПроверитьНеравенство(0, ТаблицаСвойств.Количество()); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + КонецЦикла; + + ОписаниеТипаЧисло = ИзвестныеТипы.Найти(Тип("Число")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаЧисло); + юТест.ПроверитьТип(ОписаниеТипаЧисло, "СтрокаТаблицыЗначений"); + юТест.ПроверитьИстину(ОписаниеТипаЧисло.Примитивный); + юТест.ПроверитьЛожь(ОписаниеТипаЧисло.Пользовательский); + юТест.ПроверитьЛожь(ОписаниеТипаЧисло.Коллекция); + + ОписаниеТипаМассив = ИзвестныеТипы.Найти(Тип("Массив")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМассив); + юТест.ПроверитьТип(ОписаниеТипаМассив, "СтрокаТаблицыЗначений"); + юТест.ПроверитьЛожь(ОписаниеТипаМассив.Примитивный); + юТест.ПроверитьЛожь(ОписаниеТипаМассив.Пользовательский); + юТест.ПроверитьИстину(ОписаниеТипаМассив.Коллекция); + + ОписаниеТипаМойКлассныйТип = ИзвестныеТипы.Найти(Тип("МойКлассныйТип")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМойКлассныйТип); + юТест.ПроверитьТип(ОписаниеТипаМойКлассныйТип, "СтрокаТаблицыЗначений"); + юТест.ПроверитьЛожь(ОписаниеТипаМойКлассныйТип.Примитивный); + юТест.ПроверитьИстину(ОписаниеТипаМойКлассныйТип.Пользовательский); + юТест.ПроверитьЛожь(ОписаниеТипаМойКлассныйТип.Коллекция); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы_СОтборомКоллекция() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + Отбор = Новый Структура("Коллекция", Истина); + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(Отбор); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + + юТест.ПроверитьИстину(ИзвестныйТип.Коллекция); + юТест.ПроверитьЛожь(ИзвестныйТип.Примитивный); + юТест.ПроверитьЛожь(ИзвестныйТип.Пользовательский); + + КонецЦикла; + + ОписаниеТипаМассив = ИзвестныеТипы.Найти(Тип("Массив")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМассив); + юТест.ПроверитьТип(ОписаниеТипаМассив, "СтрокаТаблицыЗначений"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПримитивный() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + Отбор = Новый Структура("Примитивный", Истина); + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(Отбор); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + + юТест.ПроверитьЛожь(ИзвестныйТип.Коллекция); + юТест.ПроверитьИстину(ИзвестныйТип.Примитивный); + юТест.ПроверитьЛожь(ИзвестныйТип.Пользовательский); + + КонецЦикла; + + ОписаниеТипаЧисло = ИзвестныеТипы.Найти(Тип("Число")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаЧисло); + юТест.ПроверитьТип(ОписаниеТипаЧисло, "СтрокаТаблицыЗначений"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвестныеТипы_СОтборомПользовательский() Экспорт + + ПолучитьОбъектДляПроверки("МойКлассныйТип"); + + Рефлектор = Новый Рефлектор; + + Отбор = Новый Структура("Пользовательский", Истина); + + ИзвестныеТипы = Рефлектор.ИзвестныеТипы(Отбор); + + ПроверитьТипИзвестныхТипов(ИзвестныеТипы); + + Для Каждого ИзвестныйТип Из ИзвестныеТипы Цикл + + ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип); + + юТест.ПроверитьЛожь(ИзвестныйТип.Коллекция); + юТест.ПроверитьЛожь(ИзвестныйТип.Примитивный); + юТест.ПроверитьИстину(ИзвестныйТип.Пользовательский); + + КонецЦикла; + + ОписаниеТипаМойКлассныйТип = ИзвестныеТипы.Найти(Тип("МойКлассныйТип")); + + юТест.ПроверитьЗаполненность(ОписаниеТипаМойКлассныйТип); + юТест.ПроверитьТип(ОписаниеТипаМойКлассныйТип, "СтрокаТаблицыЗначений"); + +КонецПроцедуры + +Процедура ПроверитьТипИзвестныхТипов(ИзвестныеТипы) + + юТест.ПроверитьТип(ИзвестныеТипы, "ТаблицаЗначений"); + юТест.ПроверитьНеРавенство(ИзвестныеТипы.Количество(), 0); + + юТест.ПроверитьРавенство(ИзвестныеТипы.Колонки.Количество(), 5); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Имя")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Значение")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Примитивный")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Пользовательский")); + юТест.ПроверитьЗаполненность(ИзвестныеТипы.Колонки.Найти("Коллекция")); + +КонецПроцедуры + +Процедура ПроверитьСтрокуИзвестныхТипов(ИзвестныйТип) + + юТест.ПроверитьТип(ИзвестныйТип.Имя, "Строка"); + юТест.ПроверитьТип(ИзвестныйТип.Значение, "Тип"); + юТест.ПроверитьТип(ИзвестныйТип.Примитивный, "Булево"); + юТест.ПроверитьТип(ИзвестныйТип.Пользовательский, "Булево"); + юТест.ПроверитьТип(ИзвестныйТип.Коллекция, "Булево"); + + юТест.ПроверитьЗаполненность(ИзвестныйТип.Имя); + юТест.ПроверитьЗаполненность(ИзвестныйТип.Значение); + +КонецПроцедуры + +#Если Windows Тогда +Процедура ТестДолжен_ПроверитьВызовМетодаComОбъекта() Экспорт + FSO = ПолучитьCOMОбъект("", "Scripting.FileSystemObject"); + Рез = FSO.DriveExists("C"); + юТест.ПроверитьТип(Рез, "Булево"); +КонецПроцедуры +#КонецЕсли + +Процедура ТестДолжен_ПроверитьПередачуВнешнихПараметров() Экспорт + Код = "Перем Результат Экспорт; Результат = Множитель * 5"; + Параметры = Новый Структура("Множитель", 2); + + ЗагружаемыйСценарий = ЗагрузитьСценарийИзСтроки(Код, Параметры); + + юТест.ПроверитьРавенство(10, ЗагружаемыйСценарий.Результат); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРефлексиюВнешнегоПараметра() Экспорт + Код = "Перем Результат Экспорт; Результат = Множитель * 5"; + Параметры = Новый Структура("Множитель", 2); + + ЗагружаемыйСценарий = ЗагрузитьСценарийИзСтроки(Код, Параметры); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(ЗагружаемыйСценарий); + + юТест.ПроверитьРавенство(1, ТаблицаСвойств.Количество()); + юТест.ПроверитьРавенство("Результат", ТаблицаСвойств[0].Имя); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРефлексиюВнешнегоПараметраВРежимеПриватных() Экспорт + Код = "Перем Результат Экспорт; Результат = Множитель * 5"; + Параметры = Новый Структура("Множитель", 2); + + ЗагружаемыйСценарий = ЗагрузитьСценарийИзСтроки(Код, Параметры); + + Рефлектор = Новый Рефлектор; + ТаблицаСвойств = Рефлектор.ПолучитьТаблицуСвойств(ЗагружаемыйСценарий, Истина); + ТаблицаСвойств.Сортировать("Имя"); + + юТест.ПроверитьРавенство(2, ТаблицаСвойств.Количество()); + юТест.ПроверитьРавенство("Множитель", ТаблицаСвойств[0].Имя); + юТест.ПроверитьЛожь(ТаблицаСвойств[0].Экспорт); + + юТест.ПроверитьРавенство("Результат", ТаблицаСвойств[1].Имя); + юТест.ПроверитьИстину(ТаблицаСвойств[1].Экспорт); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляСтруктуры() Экспорт + Рефлектор = Новый Рефлектор; + Структура = Новый Структура("Поле1, Поле2", -1, -2); + Таблица = Рефлектор.ПолучитьТаблицуСвойств(Структура); + юТест.ПроверитьРавенство(2, Таблица.Количество()); + юТест.ПроверитьНеРавенство(Неопределено, Таблица.Найти("Поле1")); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПолучитьТаблицуСвойствДляФиксированнойСтруктуры() Экспорт + Рефлектор = Новый Рефлектор; + ФиксСтруктура = Новый ФиксированнаяСтруктура("Поле1, Поле2", -1, -2); + Таблица = Рефлектор.ПолучитьТаблицуСвойств(ФиксСтруктура); + юТест.ПроверитьРавенство(2, Таблица.Количество()); + юТест.ПроверитьНеРавенство(Неопределено, Таблица.Найти("Поле1")); КонецПроцедуры \ No newline at end of file diff --git a/tests/run-bsl-tests.cmd b/tests/run-bsl-tests.cmd new file mode 100644 index 000000000..6f15f2a3f --- /dev/null +++ b/tests/run-bsl-tests.cmd @@ -0,0 +1,40 @@ +@echo off +setlocal + +rem Remove trailing backslash from %~dp0 +set "SCRIPT_DIR=%~dp0" +if "%SCRIPT_DIR:~-1%"=="\" set "SCRIPT_DIR=%SCRIPT_DIR:~0,-1%" +set OVM_LIB_PATH=%USERPROFILE%\AppData\Local\ovm\current\lib + +set OSCRIPT_CONFIG=lib.system=%OVM_LIB_PATH% + +set OSCRIPT_BIN=%~f1 +if defined OSCRIPT_BIN goto validate_bin + +if defined OSCRIPT_EXE ( + set OSCRIPT_BIN=%OSCRIPT_EXE% + goto validate_bin +) + +for /f "usebackq delims=" %%i in (`where oscript.exe 2^>nul`) do ( + set "OSCRIPT_BIN=%%i" + goto validate_bin +) + +echo [ERROR] Failed to determine path to oscript.exe. >&2 +echo Pass the path as the first parameter or set OSCRIPT_EXE variable. >&2 +exit /b 1 + +:validate_bin +if not exist "%OSCRIPT_BIN%" ( + echo [ERROR] File "%OSCRIPT_BIN%" not found. >&2 + exit /b 1 +) + +pushd "%SCRIPT_DIR%" >nul +"%OSCRIPT_BIN%" testrunner.os -runAll . +set EXIT_CODE=%ERRORLEVEL% +popd >nul + +exit /b %EXIT_CODE% + diff --git a/tests/run-bsl-tests.sh b/tests/run-bsl-tests.sh new file mode 100644 index 000000000..111b14b89 --- /dev/null +++ b/tests/run-bsl-tests.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OVM_LIB_PATH="${HOME}/.local/share/ovm/current/lib" + +if [[ -z "${OSCRIPT_CONFIG:-}" ]]; then + export OSCRIPT_CONFIG="lib.system=${OVM_LIB_PATH}" +fi + +OSCRIPT_BIN="${1:-${OSCRIPT_EXE:-}}" +if [[ -z "${OSCRIPT_BIN}" ]]; then + OSCRIPT_BIN="$(command -v oscript 2>/dev/null || true)" +fi + +if [[ -z "${OSCRIPT_BIN}" ]]; then + echo "[ERROR] Failed to determine path to oscript." >&2 + echo "Pass it as the first parameter or set OSCRIPT_EXE variable." >&2 + exit 1 +fi + +if [[ ! -x "${OSCRIPT_BIN}" ]]; then + echo "[ERROR] File '${OSCRIPT_BIN}' does not exist or is not executable." >&2 + exit 1 +fi + +cd "${SCRIPT_DIR}" +"${OSCRIPT_BIN}" testrunner.os -runAll . + diff --git a/tests/showversion.os b/tests/showversion.os index 978a8dafd..4beaf301c 100644 --- a/tests/showversion.os +++ b/tests/showversion.os @@ -13,8 +13,8 @@ Функция ПутьОСкрипт() - Возврат """" + ОбъединитьПути(КаталогПрограммы(), "oscript.exe") + """"; - + Возврат "dotnet """ + ОбъединитьПути(КаталогПрограммы(), "oscript.dll") + """"; + КонецФункции diff --git a/tests/source-licensing.os b/tests/source-licensing.os index c928f160c..1de20457f 100644 --- a/tests/source-licensing.os +++ b/tests/source-licensing.os @@ -89,7 +89,7 @@ |" + СокрП(Документ.ПолучитьСтроку(5)) + " |" + СокрП(Документ.ПолучитьСтроку(6)); - Если СтрНайти(СтрокаВФайле, "") > 0 Тогда + Если СтрНайти(СтрокаВФайле, "") > 0 или СтрНайти(СтрокаВФайле, "OSCRIPT_SKIP_TESTING_MPL2_PREFIX") > 0 Тогда Возврат Истина; КонецЕсли; diff --git a/tests/start-all.cmd b/tests/start-all.cmd deleted file mode 100644 index e1dcfa305..000000000 --- a/tests/start-all.cmd +++ /dev/null @@ -1,39 +0,0 @@ -@echo off - -chcp 866 > nul - -setlocal -set pathdir=%~dp0 - -rem echo ᠬ %CD% -rem echo ਯ ஢ %pathdir% - -set progfiles=%ProgramFiles(x86)% -if NOT EXIST ProgramFiles(x86) set progfiles=%ProgramFiles% - -@echo on -"%progfiles%\OneScript\bin\oscript.exe" "%pathdir%testrunner.os" -runall %1 %2 %3 %4 %5 -@echo off - -rem echo %ERRORLEVEL% -if %ERRORLEVEL%==2 GOTO pending_exit -if NOT %ERRORLEVEL%==0 GOTO bad_exit - -:success_exit - -exit /B 0 - -:pending_exit - -exit /B 2 - -:bad_exit -if "-1"=="%success%" GOTO success_exit -echo . -echo ᪮쪮 ⮢ 㯠 -echo 㤠. ᭠ -echo 騥 : -type %logfile% - -if ".%1"=="." pause -exit /B 1 \ No newline at end of file diff --git a/tests/start-all.sh b/tests/start-all.sh deleted file mode 100755 index efb317bb5..000000000 --- a/tests/start-all.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -../src/oscript/bin/Debug/oscript.exe testrunner.os -runall $@ - -if [ $? = 0 ]; then - # success - exit -fi - -if [ $? = -1 ]; then - # strange success - exit -fi - -exit 1 - diff --git a/tests/start.cmd b/tests/start.cmd deleted file mode 100644 index 1f8668728..000000000 --- a/tests/start.cmd +++ /dev/null @@ -1,30 +0,0 @@ -@echo off - -setlocal -set pathdir=%~dp0 - -rem echo ᠬ� ���� %CD% -rem echo �ਯ�� ���஢���� %pathdir% - -set progfiles=%ProgramFiles(x86)% -if NOT EXIST "%progfiles%" set progfiles=%ProgramFiles% - -echo on -"%progfiles%\OneScript\bin\oscript.exe" "%pathdir%testrunner.os" -run %1 %2 %3 %4 %5 -@echo off - -rem echo %ERRORLEVEL% -if NOT %ERRORLEVEL%==0 GOTO bad_exit - -:success_exit -rem echo �ᯥ譮 -rem pause - -exit /B 0 - -:bad_exit -if %ERRORLEVEL%==-1 GOTO success_exit -echo Tests failed - -pause -exit /B 1 \ No newline at end of file diff --git a/tests/start.sh b/tests/start.sh deleted file mode 100755 index 4f5f049bd..000000000 --- a/tests/start.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -../src/oscript/bin/Debug/oscript.exe testrunner.os -run $@ - -if [ $? = 0 ]; then - # success - exit -fi - -if [ $? = -1 ]; then - # strange success - exit -fi - -echo Tests failed - -exit 1 - diff --git a/tests/stringoperations.os b/tests/stringoperations.os index b9b389b6b..ff8880e1d 100644 --- a/tests/stringoperations.os +++ b/tests/stringoperations.os @@ -33,6 +33,11 @@ ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_ИсключениеПриНомереБольше10"); ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_КорректнаяЗамена"); ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_СЭкранированием"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_СПропущеннымиПараметрами"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_СПередачейПеременныхНеопределено"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_ИсключениеПриЛишнихПараметрах"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_СНомеромВСкобках"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрШаблон_СПустымШаблоном"); ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрСоединить_СРазделителем"); ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрСоединить_БезРазделителя"); @@ -43,6 +48,13 @@ ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_ВтороеВхождениеСНачала"); ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_ВтороеВхождениеСКонца"); ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_ВтороеВхождениеСКонцаНачинаяСПредпоследнегоСимвола"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_СПустойСтрокой"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_СПустойПодстрокойПоиска"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_СПропущеннымиСтроками"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_СНевернойНачальнойПозицией"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_СНулевымЧисломВхождений"); + ВсеТесты.Добавить("ТестДолжен_ВызватьМетод_СтрНайти_СОтрицательнымЧисломВхождений"); + ВсеТесты.Добавить("Тест_ДолженПроверитьНСтрВозвращаетПервуюСтроку"); ВсеТесты.Добавить("ТестДолжен_Проверить_Что_НСТР_С_СуществующимПараметром_ВозвращаетНужнуюСтроку"); ВсеТесты.Добавить("ТестДолжен_Проверить_Что_НСТР_С_НесуществующимПараметром_ВозвращаетПустуюСтроку"); @@ -278,7 +290,7 @@ Процедура ТестДолжен_ВызватьМетод_СтрШаблон_ИсключениеПриНомереБольше10() Экспорт Попытка - а = СтрШаблон("тест %12 тест"); + а = СтрШаблон("тест %11 тест"); Исключение юТест.ТестПройден(); Возврат; @@ -308,6 +320,76 @@ КонецПроцедуры +Процедура ТестДолжен_ВызватьМетод_СтрШаблон_СПропущеннымиПараметрами() Экспорт + + Результат = СтрШаблон("тест %1 тест %2 тест %3", "1", ,"3"); + юТест.ПроверитьРавенство("тест 1 тест тест 3", Результат); + + Результат = СтрШаблон("тест %1 тест %2", ,"2", ,); + юТест.ПроверитьРавенство("тест тест 2", Результат); + + Результат = СтрШаблон("тест %1 тест %2 тест %3 тест %4", Неопределено, ,"3", , , ); + юТест.ПроверитьРавенство("тест тест тест 3 тест ", Результат); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрШаблон_СПередачейПеременныхНеопределено() Экспорт + + Перем п1,п2,п3,п4,п5,п6; + + п2 = "Значение"; + + Результат = СтрШаблон("тест %1 тест %2 тест %3", п1,п2,п3,п4,п5,п6); + юТест.ПроверитьРавенство("тест тест Значение тест ", Результат); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрШаблон_ИсключениеПриЛишнихПараметрах() Экспорт + + Попытка + а = СтрШаблон("тест %1 тест", 1, 2); + Исключение + юТест.ТестПройден(); + Возврат; + КонецПопытки; + + юТест.ТестПровален("Ожидаемое исключение не возникло."); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрШаблон_СНомеромВСкобках() Экспорт + + Результат = СтрШаблон("тест %(1)0", "="); + юТест.ПроверитьРавенство("тест =0", Результат); + + Попытка + а = СтрШаблон("тест %(11)0", 1); + Исключение + юТест.ТестПройден(); + Возврат; + КонецПопытки; + + юТест.ТестПровален("Ожидаемое исключение не возникло."); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрШаблон_СПустымШаблоном() Экспорт + + Результат = СтрШаблон(,); + юТест.ПроверитьРавенство("", Результат); + + Попытка + а = СтрШаблон(,1); + Исключение + юТест.ТестПройден(); + Возврат; + КонецПопытки; + + юТест.ТестПровален("Ожидаемое исключение не возникло."); + +КонецПроцедуры + + Процедура ТестДолжен_ВызватьМетод_СтрСоединить_БезРазделителя() Экспорт Массив = Новый Массив; @@ -384,6 +466,69 @@ КонецПроцедуры +Процедура ТестДолжен_ВызватьМетод_СтрНайти_СПустойСтрокой() Экспорт + + ГдеИщем = ""; + ЧтоИщем = ","; + + юТест.ПроверитьРавенство(0, СтрНайти(ГдеИщем, ЧтоИщем)); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрНайти_СПустойПодстрокойПоиска() Экспорт + + ГдеИщем = "Один,Два,Три,"; + ЧтоИщем = ""; + + юТест.ПроверитьРавенство(1, СтрНайти(ГдеИщем, ЧтоИщем)); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрНайти_СПропущеннымиСтроками() Экспорт + юТест.ПроверитьРавенство(1, СтрНайти(,)); +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрНайти_СНевернойНачальнойПозицией() Экспорт + + ГдеИщем = "Один,Два,Три,"; + ЧтоИщем = ","; + + Попытка + СтрНайти(ГдеИщем, ЧтоИщем, , 999); + Исключение + юТест.ТестПройден(); + Возврат; + КонецПопытки; + + юТест.ТестПровален("Ожидаемое исключение не возникло."); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрНайти_СНулевымЧисломВхождений() Экспорт + + ГдеИщем = "Один,Два,Три,"; + ЧтоИщем = ","; + + Попытка + СтрНайти(ГдеИщем, ЧтоИщем,,, 0); + Исключение + юТест.ТестПройден(); + Возврат; + КонецПопытки; + + юТест.ТестПровален("Ожидаемое исключение не возникло."); + +КонецПроцедуры + +Процедура ТестДолжен_ВызватьМетод_СтрНайти_СОтрицательнымЧисломВхождений() Экспорт + + ГдеИщем = "Один,Два,Три,"; + ЧтоИщем = ","; + + юТест.ПроверитьРавенство(0, СтрНайти(ГдеИщем, ЧтоИщем,,, -2)); + +КонецПроцедуры + Процедура Тест_ДолженПроверитьНСтрВозвращаетПервуюСтроку() Экспорт Стр = НСтр("ru = 'Строка1'; en = 'Строка2'", "ru"); diff --git a/tests/sysinfo.os b/tests/sysinfo.os index 286598cc9..4180d7e05 100644 --- a/tests/sysinfo.os +++ b/tests/sysinfo.os @@ -24,10 +24,13 @@ // Значения зависят от машины запуска ВсеТесты.Добавить("ТестДолжен_ПолучитьЭто64БитнаяОперационнаяСистема"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьТипПлатформы"); ВсеТесты.Добавить("ТестДолжен_ПолучитьКоличествоПроцессоров"); ВсеТесты.Добавить("ТестДолжен_ПолучитьРазмерСистемнойСтраницы"); ВсеТесты.Добавить("ТестДолжен_ПолучитьВремяРаботыСМоментаЗагрузки"); ВсеТесты.Добавить("ТестДолжен_ПолучитьИменаЛогическихДисков"); + #Если Windows Тогда + // Пути к папкам ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_РепозиторийДокументов"); ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_ДанныеПриложений"); @@ -36,9 +39,12 @@ ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_КаталогРабочийСтол"); ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_МояМузыка"); ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_МоиРисунки"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_Шаблоны"); ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_МоиВидеозаписи"); ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_ОбщиеШаблоны"); + + #КонецЕсли ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_ПрофильПользователя"); ВсеТесты.Добавить("ТестДолжен_ПолучитьПолучитьПутьПапки_ОбщийКаталогДанныхПриложения"); @@ -87,6 +93,23 @@ юТест.ПроверитьИстину(Си.Это64БитнаяОперационнаяСистема); КонецПроцедуры +Процедура ТестДолжен_ПолучитьТипПлатформы() Экспорт + СИ = Новый СистемнаяИнформация; + Если Найти(СИ.ВерсияОС, "Windows") > 0 Тогда + Если Си.Это64БитнаяОперационнаяСистема Тогда + юТест.ПроверитьРавенство(СИ.ТипПлатформы, ТипПлатформы.Windows_x86_64); + Иначе + юТест.ПроверитьРавенство(СИ.ТипПлатформы, ТипПлатформы.Windows_x86); + КонецЕсли; + Иначе + Если Си.Это64БитнаяОперационнаяСистема Тогда + юТест.ПроверитьРавенство(СИ.ТипПлатформы, ТипПлатформы.Linux_x86_64); + Иначе + юТест.ПроверитьРавенство(СИ.ТипПлатформы, ТипПлатформы.Linux_x86); + КонецЕсли; + КонецЕсли; +КонецПроцедуры + Процедура ТестДолжен_ПолучитьКоличествоПроцессоров() Экспорт Си = Новый СистемнаяИнформация(); юТест.ПроверитьБольше(Си.КоличествоПроцессоров,0); diff --git a/tests/tasks.os b/tests/tasks.os new file mode 100644 index 000000000..1cc7f878b --- /dev/null +++ b/tests/tasks.os @@ -0,0 +1,312 @@ +Перем юТест; + +Перем глБлокировка; +Перем глСумма; + +Перем СобытиеВызвано; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеЗаданий"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПоискЗаданий"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОжиданиеВсехЗаданий"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОжиданиеЛюбогоИзЗаданий"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьОжиданиеКонкретногоЗадания"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВЗаданииПроставленаИнформацияОбОшибке"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозвращаетсяРезультат"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозвращаетсяРезультатДелегата"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоРаботаетБлокировка"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоКодМожетОпределитьИДЗадания"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВИнформацииОбОшибкеЕстьСтекВызовов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоОбработчикиСобытийВызываютсяВФоновомЗадании"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ПередЗапускомТеста() Экспорт + ФоновыеЗадания.Очистить(); +КонецПроцедуры + +Процедура ПроцедураБезПараметров() Экспорт + Приостановить(500); +КонецПроцедуры + +Процедура ПроцедураСПараметрами(Интервал) Экспорт + Приостановить(Интервал); +КонецПроцедуры + +Процедура ПроцедураСИсключением() Экспорт + Приостановить(500); + ВызватьИсключение "Я ошибка в коде"; +КонецПроцедуры + +Функция ПодсчетСуммы(Знач Начало, Знач Конец) Экспорт + + //дадим всем заданиям шанс начаться + Приостановить(500); + + СуммаНаОтрезке = 0; + Для Сч = Начало По Конец Цикл + СуммаНаОтрезке = СуммаНаОтрезке + Сч; + КонецЦикла; + + Возврат СуммаНаОтрезке; + +КонецФункции + +Процедура ПодсчетГлобальнойСуммы(Знач Начало, Знач Конец) Экспорт + + СуммаНаОтрезке = ПодсчетСуммы(Начало, Конец); + + глБлокировка.Заблокировать(); + глСумма = глСумма + СуммаНаОтрезке; + глБлокировка.Разблокировать(); + +КонецПроцедуры + +Процедура Событие() Экспорт + СобытиеВызвано = Истина; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСозданиеЗаданий() Экспорт + + Задание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров"); + юТест.ПроверитьИстину(ЗначениеЗаполнено(Задание.УникальныйИдентификатор)); + юТест.ПроверитьРавенство("ПроцедураБезПараметров", Задание.ИмяМетода); + юТест.ПроверитьРавенство(ЭтотОбъект, Задание.Объект); + юТест.ПроверитьРавенство(Неопределено, Задание.Параметры); + + Задание.ОжидатьЗавершения(); + + юТест.ПроверитьРавенство(СостояниеФоновогоЗадания.Завершено, Задание.Состояние); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПоискЗаданий() Экспорт + + З = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров"); + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров"); + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров"); + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров"); + + Все = ФоновыеЗадания.ПолучитьФоновыеЗадания(); + юТест.ПроверитьРавенство(4, Все.Количество()); + + ПоИд = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("UUID", З.УникальныйИдентификатор)); + юТест.ПроверитьРавенство(1, ПоИд.Количество()); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОжиданиеВсехЗаданий() Экспорт + + МассивПараметров = Новый Массив; + МассивПараметров.Добавить(3000); + + МассивЗаданий = Новый Массив; + + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураСПараметрами", МассивПараметров)); + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров")); + + ФоновыеЗадания.ОжидатьВсе(МассивЗаданий); + + Завершенные = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Состояние", СостояниеФоновогоЗадания.Завершено)); + юТест.ПроверитьРавенство(2, Завершенные.Количество()); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОжиданиеЛюбогоИзЗаданий() Экспорт + + МассивПараметров = Новый Массив; + МассивПараметров.Добавить(3000); + + МассивЗаданий = Новый Массив; + + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураСПараметрами", МассивПараметров)); + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров")); + + ФоновыеЗадания.ОжидатьЛюбое(МассивЗаданий); + + Завершенные = ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Состояние", СостояниеФоновогоЗадания.Завершено)); + юТест.ПроверитьМеньшеИлиРавно(Завершенные.Количество(), 2); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОжиданиеКонкретногоЗадания() Экспорт + + Задание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураБезПараметров"); + Задание.ОжидатьЗавершения(); + + юТест.ПроверитьРавенство(СостояниеФоновогоЗадания.Завершено, Задание.Состояние); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоВЗаданииПроставленаИнформацияОбОшибке() Экспорт + + Задание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураСИсключением"); + Задание.ОжидатьЗавершения(); + + юТест.ПроверитьРавенство(СостояниеФоновогоЗадания.ЗавершеноАварийно, Задание.Состояние); + юТест.ПроверитьРавенство("Я ошибка в коде", Задание.ИнформацияОбОшибке.Описание); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоВозвращаетсяРезультат() Экспорт + + МассивПараметров = Новый Массив(2); + МассивПараметров[0] = 1; + МассивПараметров[1] = 100; + + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПодсчетСуммы", МассивПараметров); + + МассивПараметров[0] = 101; + МассивПараметров[1] = 200; + + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПодсчетСуммы", МассивПараметров); + + МассивПараметров[0] = 201; + МассивПараметров[1] = 300; + + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПодсчетСуммы", МассивПараметров); + + ВсеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(); + ФоновыеЗадания.ОжидатьВсе(ВсеЗадания); + + Сумма = 0; + Для Каждого Задание Из ВсеЗадания Цикл + Сумма = Сумма + Задание.Результат; + КонецЦикла; + + юТест.ПроверитьРавенство(45150, Сумма); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоВозвращаетсяРезультатДелегата() Экспорт + + Делегат = Новый Действие(ЭтотОбъект, "ПодсчетСуммы"); + + МассивПараметров = Новый Массив(2); + МассивПараметров[0] = 1; + МассивПараметров[1] = 5; + + ФЗ = ФоновыеЗадания.Выполнить(Делегат, "Выполнить", МассивПараметров); + + ФЗ.ОжидатьЗавершения(); + + юТест.ПроверитьРавенство(15, ФЗ.Результат); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоРаботаетБлокировка() Экспорт + + глБлокировка = Новый БлокировкаРесурса; + глСумма = 0; + + МассивПараметров = Новый Массив(2); + МассивПараметров[0] = 1; + МассивПараметров[1] = 1000; + + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПодсчетГлобальнойСуммы", МассивПараметров); + + МассивПараметров[0] = 1001; + МассивПараметров[1] = 2000; + + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПодсчетГлобальнойСуммы", МассивПараметров); + + МассивПараметров[0] = 2001; + МассивПараметров[1] = 3000; + + ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПодсчетГлобальнойСуммы", МассивПараметров); + + Попытка + ФоновыеЗадания.ОжидатьЗавершенияЗадач(); + Исключение + МассивОшибок = ИнформацияОбОшибке().Параметры; + Если МассивОшибок <> Неопределено Тогда + Для Каждого Задание Из МассивОшибок Цикл + Сообщить(Задание.ИнформацияОбОшибке.ПодробноеОписаниеОшибки()); + КонецЦикла; + КонецЕсли; + КонецПопытки; + + юТест.ПроверитьРавенство(4501500, глСумма); + +КонецПроцедуры + +Функция ПроверитьИдентификаторЗадания() Экспорт + + Приостановить(500); + Задание = ФоновыеЗадания.ПолучитьТекущее(); + Если Задание <> Неопределено Тогда + Возврат Задание.УникальныйИдентификатор; + КонецЕсли; + + Возврат Неопределено; + +КонецФункции + +Процедура ТестДолжен_ПроверитьЧтоКодМожетОпределитьИДЗадания() Экспорт + + МассивЗаданий = Новый Массив; + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроверитьИдентификаторЗадания")); + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроверитьИдентификаторЗадания")); + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроверитьИдентификаторЗадания")); + МассивЗаданий.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроверитьИдентификаторЗадания")); + + ФоновыеЗадания.ОжидатьВсе(МассивЗаданий); + + Для Каждого Задание Из МассивЗаданий Цикл + юТест.ПроверитьРавенство(Задание.УникальныйИдентификатор, Задание.Результат); + юТест.ПроверитьНеРавенство(Неопределено, Задание.Результат); + КонецЦикла; + + ЗаданиеГлавногоПотока = ФоновыеЗадания.ПолучитьТекущее(); + юТест.ПроверитьРавенство(Неопределено, ЗаданиеГлавногоПотока); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоВИнформацииОбОшибкеЕстьСтекВызовов() Экспорт + + Задание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ПроцедураСИсключением"); + Задание.ОжидатьЗавершения(); + + СтекВызовов = Задание.ИнформацияОбОшибке.ПолучитьСтекВызовов(); + + юТест.ПроверитьТип(СтекВызовов, "КоллекцияКадровСтекаВызовов"); + + юТест.ПроверитьРавенство(СтекВызовов.Количество(), 1); + + юТест.ПроверитьРавенство(СтекВызовов[0].Метод, "ПроцедураСИсключением"); + юТест.ПроверитьЗаполненность(СтекВызовов[0].НомерСтроки); + юТест.ПроверитьРавенство(СтекВызовов[0].ИмяМодуля, ТекущийСценарий().Источник); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоОбработчикиСобытийВызываютсяВФоновомЗадании() Экспорт + + СобытиеВызвано = Ложь; + + Параметры = Новый Массив; + Параметры.Добавить("ЯСобытие"); + Параметры.Добавить(Новый Массив); + + ДобавитьОбработчик ЭтотОбъект.ЯСобытие, Событие; + + ФоновоеЗадание = ФоновыеЗадания.Выполнить(ЭтотОбъект, "ВызватьСобытие", Параметры); + ФоновоеЗадание.ОжидатьЗавершения(); + + УдалитьОбработчик ЭтотОбъект.ЯСобытие, Событие; + + юТест.ПроверитьИстину( + СобытиеВызвано, + "Ожидали что при вызове события в фоновом задании вызовется, а это не так" + ); + +КонецПроцедуры diff --git a/tests/testdata/evalDifferentThis.os b/tests/testdata/evalDifferentThis.os new file mode 100644 index 000000000..0478536ff --- /dev/null +++ b/tests/testdata/evalDifferentThis.os @@ -0,0 +1,10 @@ +Перем Значение Экспорт; + +Процедура УстановитьЗначение(НовоеЗначение) Экспорт + Значение = НовоеЗначение; +КонецПроцедуры + +Функция ПолучитьЧерезВычислить() Экспорт + Возврат Вычислить("Значение"); +КонецФункции + diff --git a/tests/testdata/magic-object.os b/tests/testdata/magic-object.os index 08ebe52e4..a4bd36228 100644 --- a/tests/testdata/magic-object.os +++ b/tests/testdata/magic-object.os @@ -1,6 +1,10 @@ Перем ПроизвольноеПредставление Экспорт; Перем ЗначПоУмолчанию Экспорт; +Процедура ПриСозданииОбъекта(Прм1, Прм2, Прм3, Прм4 = 4) + ЗначПоУмолчанию = Прм4; +КонецПроцедуры + Процедура ОбработкаПолученияПредставления(Строка, СтандартнаяОбработка) Если ПроизвольноеПредставление = Неопределено Тогда @@ -12,6 +16,3 @@ КонецПроцедуры -Процедура ПриСозданииОбъекта(Прм1, Прм2, Прм3, Прм4 = 4) - ЗначПоУмолчанию = Прм4; -КонецПроцедуры diff --git a/tests/testrunner.os b/tests/testrunner.os index e8fce2ac3..6c413590b 100644 --- a/tests/testrunner.os +++ b/tests/testrunner.os @@ -82,6 +82,18 @@ КонецЕсли; КонецПроцедуры +Процедура ПроверитьРавенствоСтрокБезУчетаРегистра(ПервоеЗначение, ВтороеЗначение, ДопСообщениеОшибки = "") Экспорт + Если нрег(ПервоеЗначение) <> нрег(ВтороеЗначение) Тогда + Если ТипЗнч(ПервоеЗначение) = Тип("Строка") и ТипЗнч(ВтороеЗначение) = Тип("Строка") Тогда + ВызватьИсключение ИсключениеНеравенстваСтрок(ПервоеЗначение, ВтороеЗначение, Истина) + " + |" + ДопСообщениеОшибки; + КонецЕсли; + + СообщениеОшибки = "Сравниваемые значения ("+ПервоеЗначение+"; "+ВтороеЗначение+") не равны, а хотели, чтобы были равны." + ФорматДСО(ДопСообщениеОшибки); + ВызватьИсключение(СообщениеОшибки); + КонецЕсли; +КонецПроцедуры + Процедура ПроверитьНеРавенство(ПервоеЗначение, ВтороеЗначение, ДопСообщениеОшибки = "") Экспорт Если ПервоеЗначение = ВтороеЗначение Тогда СообщениеОшибки = "Сравниваемые значения ("+ПервоеЗначение+"; "+ВтороеЗначение+") равны, а хотели, чтобы были не равны." + ФорматДСО(ДопСообщениеОшибки); @@ -192,33 +204,57 @@ ВызватьИсключение(СообщениеОшибки); КонецПроцедуры -Функция ИсключениеНеравенстваСтрок(ПервоеЗначение, ВтороеЗначение) - - СтрокаНабора = " "; - ДиапазонОтНачала = 5; - ДиапазонВКонце = 5; - Для Сч = 1 По Мин(СтрДлина(ПервоеЗначение), СтрДлина(ВтороеЗначение)) Цикл - Символ1 = Сред(ПервоеЗначение,Сч,1); - Символ2 = Сред(ВтороеЗначение,Сч,1); +Функция ЭкранироватьПереводыСтрок(Знач Строка) + Возврат СтрЗаменить(СтрЗаменить(Строка, Символы.ПС, "\n"), Символы.ВК, "\r"); +КонецФункции + +Функция ИсключениеНеравенстваСтрок(ПервоеЗначение, ВтороеЗначение, Знач БезУчетаРегистра = Ложь) + ДиапазонОтНачала = 17; + ДиапазонВКонце = 17; + СтрокаНабора = СтрСоединить(Новый Массив(2*ДиапазонОтНачала), " "); + + МинДлина = Мин(СтрДлина(ПервоеЗначение), СтрДлина(ВтороеЗначение)); + ИндексРазличия = МинДлина + 1; + Для Сч = 1 По МинДлина Цикл + + Символ1 = Сред(ПервоеЗначение, Сч, 1); + Символ2 = Сред(ВтороеЗначение, Сч, 1); + + Если БезУчетаРегистра Тогда + Символ1 = нрег(Символ1); + Символ2 = нрег(Символ2); + КонецЕсли; + Если Символ1 <> Символ2 Тогда ИндексРазличия = Сч; - ОтступНачала = ИндексРазличия - ДиапазонОтНачала; - Если ОтступНачала < 0 Тогда - ОтступНачала = 1; - КонецЕсли; - - НачалоПервой = Сред(ПервоеЗначение, ОтступНачала, ДиапазонОтНачала); - НачалоВторой = Сред(ВтороеЗначение, ОтступНачала, ДиапазонОтНачала); - - Префикс = 9; // Строка Х: - - Возврат "Различия в позиции " + ИндексРазличия + ". - |Строка 1:" + НачалоПервой + СтрЗаменить(Сред(ПервоеЗначение, ОтступНачала+ДиапазонОтНачала, ДиапазонВКонце), Символы.ПС, "\n") + " - | " + Лев(СтрокаНабора, СтрДлина(НачалоПервой)) + "^ - |Строка 2:" + НачалоВторой + СтрЗаменить(Сред(ВтороеЗначение, ОтступНачала+ДиапазонОтНачала, ДиапазонВКонце), Символы.ПС, "\n"); + Прервать; КонецЕсли; + КонецЦикла; + + ОтступНачала = ИндексРазличия - ДиапазонОтНачала; + Если ОтступНачала <= 0 Тогда + ОтступНачала = 1; + КонецЕсли; + ФрагментПервой = Сред(ПервоеЗначение, ОтступНачала, ДиапазонОтНачала); + ФрагментПервойЭкр = ЭкранироватьПереводыСтрок(ФрагментПервой); + + Префикс = ДиапазонОтНачала; + Если ИндексРазличия <= ДиапазонОтНачала Тогда + Префикс = ИндексРазличия - 1; + КонецЕсли; + Префикс = Префикс + СтрДлина(ФрагментПервойЭкр) - СтрДлина(ФрагментПервой); + + ФрагментПервойЭкр = ФрагментПервойЭкр + + ЭкранироватьПереводыСтрок(Сред(ПервоеЗначение, ОтступНачала+ДиапазонОтНачала, ДиапазонВКонце)); + + ФрагментВторойЭкр = ЭкранироватьПереводыСтрок(Сред(ВтороеЗначение, ОтступНачала, ДиапазонОтНачала+ДиапазонВКонце)); + + Возврат "Различия в позиции " + ИндексРазличия + ". + |Ожидали: `" + ФрагментПервойЭкр + "` + | " + Лев(СтрокаНабора, Префикс) + "^ + |Получено:`" + ФрагментВторойЭкр + "`"; КонецФункции //} @@ -346,6 +382,21 @@ Возврат ПараметрыЗапуска; КонецФункции +Процедура НайтиТестыВПодкаталоге(Подкаталог, Вложенный = Ложь) + + Файлы = НайтиФайлы(Подкаталог, ПолучитьМаскуВсеФайлы()); + Для Каждого Файл Из Файлы Цикл + Если Файл.ЭтоКаталог() Тогда + НайтиТестыВПодкаталоге(Файл.ПолноеИмя, Истина); + ИначеЕсли Файл.Расширение = ".os" И Файл.ИмяБезРасширения <> "testrunner" Тогда + Если Не Вложенный ИЛИ СтрНачинаетсяС(Файл.ИмяБезРасширения, "test-") Тогда + Пути.Добавить(Файл.ПолноеИмя); + КонецЕсли; + КонецЕсли; + КонецЦикла; + +КонецПроцедуры + Функция ОбработатьПараметрыЗапуска(МассивПараметров) ПараметрыЗапуска = ПолучитьПараметрыЗапуска(МассивПараметров); @@ -368,12 +419,7 @@ ИначеЕсли КомандаЗапуска = СтруктураПараметровЗапуска.ПоказатьСписок Тогда Пути.Добавить(ПутьКТестам); ИначеЕсли КомандаЗапуска = СтруктураПараметровЗапуска.ЗапуститьКаталог Тогда - Файлы = НайтиФайлы(ПутьКТестам, "*.os"); - Для Каждого Файл Из Файлы Цикл - Если Файл.ИмяБезРасширения <> "testrunner" Тогда - Пути.Добавить(Файл.ПолноеИмя); - КонецЕсли; - КонецЦикла; + НайтиТестыВПодкаталоге(ПутьКТестам); КонецЕсли; Если КомандаЗапуска = СтруктураПараметровЗапуска.ПоказатьСписок Тогда @@ -436,7 +482,7 @@ Если ВыводитьОшибкиПодробно Тогда текстОшибки = ИнфоОшибки.ПодробноеОписаниеОшибки(); Иначе - текстОшибки = ОписаниеОшибки(); + текстОшибки = ПодробноеПредставлениеОшибки(ИнфоОшибки); КонецЕсли; Сообщить("Не удалось загрузить тест "+ПолноеИмяТестовогоСлучая+Символы.ПС+ текстОшибки); @@ -471,7 +517,12 @@ НомерТеста = НаборТестов.Количество()-1; Если КомандаЗапуска = СтруктураПараметровЗапуска.ПоказатьСписок Тогда - Сообщить(" Имя теста <"+ИмяТестовогоСлучая+">, №теста <"+НомерТеста+">"); + + Шаблон = СтрШаблон("%1: %2", + Формат(НомерТеста, "ЧЦ=4; ЧВН=; ЧГ=0; ЧН=0000"), + ИмяТестовогоСлучая); + + Сообщить(Шаблон); ИначеЕсли КомандаЗапуска = СтруктураПараметровЗапуска.Запустить или КомандаЗапуска = СтруктураПараметровЗапуска.ЗапуститьКаталог Тогда Если НаименованиеТестаДляЗапуска = Неопределено Тогда @@ -534,9 +585,12 @@ Сообщить(" "); Сообщить("Тестов пройдено: " + пройденоТестов, СтатусСообщения.Информация); + КоличествоОшибок = НаборОшибок.Количество(); Сообщить(" "); - Сообщить("Тестов не пройдено: " + НаборОшибок.Количество(), СтатусСообщения.Важное); - Если НаборОшибок.Количество() > 0 Тогда + Сообщить("Тестов не пройдено: " + КоличествоОшибок, + ?(КоличествоОшибок>0, СтатусСообщения.Важное, СтатусСообщения.Информация) ); + + Если КоличествоОшибок > 0 Тогда Сч = 0; Для Каждого КлючЗначение Из НаборОшибок Цикл Сч = Сч + 1; @@ -545,9 +599,12 @@ КонецЦикла; КонецЕсли; + КоличествоНереализованных = НаборНереализованныхТестов.Количество(); Сообщить(" "); - Сообщить("Тестов не реализовано \ пропущено: " + НаборНереализованныхТестов.Количество(), СтатусСообщения.Внимание); - Если НаборНереализованныхТестов.Количество() > 0 Тогда + Сообщить("Тестов не реализовано \ пропущено: " + КоличествоНереализованных, + ?(КоличествоНереализованных>0, СтатусСообщения.Внимание, СтатусСообщения.Информация) ); + + Если КоличествоНереализованных > 0 Тогда Сч = 0; Для Каждого КлючЗначение Из НаборНереализованныхТестов Цикл Сч = Сч + 1; @@ -807,8 +864,10 @@ ЕстьОшибка_МетодОбъектаНеОбнаружен = ЕстьОшибка_МетодОбъектаНеОбнаружен(текстОшибки, "ПолучитьСписокТестов"); Если НЕ ЕстьОшибка_МетодОбъектаНеОбнаружен Тогда + текстОшибки = ?(ВыводитьОшибкиПодробно, ИнформацияОбОшибке().ПодробноеОписаниеОшибки(), ОписаниеОшибки()); + ВывестиОшибку("Набор тестов не загружен: " + ПолноеИмяОбъекта + " - | Ошибка получения списка тестовых случаев: " + ОписаниеОшибки()); + | Ошибка получения списка тестовых случаев: " + текстОшибки); ТестОбъект = Неопределено; КонецЕсли; @@ -974,7 +1033,7 @@ РезультатТестирования = ЗначенияСостоянияТестов.НеВыполнялся; КонецПроцедуры -ВыводитьОшибкиПодробно = Ложь; +ВыводитьОшибкиПодробно = ПолучитьПеременнуюСреды("TESTRUNNER_VERBOSE") = "1";; ВыполнитьТесты(АргументыКоманднойСтроки); diff --git a/tests/text-write.os b/tests/text-write.os index 665730312..f050e067e 100644 --- a/tests/text-write.os +++ b/tests/text-write.os @@ -6,6 +6,7 @@ ВсеТесты = Новый Массив; + // Работа с файлом ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозможнаЗаписьUTF8БезBOMСтандартно"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозможнаЗаписьUTF8БезBOMСтандартноСПеречислениемКодировкаТекста"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозможнаЗаписьUTF8БезBOMСПеречислениемКодировкаТекста"); @@ -13,11 +14,24 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоНеоткрытыйФайлБросаетПравильноеИсключениеВМетодеЗаписатьСтроку"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоРаботаетКонструкторПоИмениФайлаСНезаполненнымиНеобязательнымиПолями"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоЗаписьСтрокиВФайлПроисходитСразуБезКеширования"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуРазделителей"); ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуНеобычныхРазделителей1"); ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуНеобычныхРазделителей2"); ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуНеобычныхРазделителей3"); ВсеТесты.Добавить("ТестДолжен_ПрочитатьИзФайлаСРазнымиПереводамиСтрок"); + + // Работа с потоком + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозможнаЗаписьВПотокПамяти"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозможнаЗаписьВПотокСКодировкой"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоВозможнаЗаписьВПотокСМеткойBOM"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьУстановкуПотокаДляЗаписи"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоКонструкторНеБроситИсключениеДляЗакрытогоПотока"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоНеоткрытыйПотокПриЗаписиБросаетПравильноеИсключение"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоПотокДляЧтенияПриЗаписиБросаетПравильноеИсключение"); + + // Работа с файлом и потоком + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереоткрытиеСФайлаНаПоток"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереоткрытиеСПотокаНаФайл"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуРазделителей"); Возврат ВсеТесты; @@ -172,55 +186,6 @@ КонецПроцедуры -Функция ЭкранироватьПереносыСтрок(Знач Строка1) - - Возврат СтрЗаменить(СтрЗаменить(Строка1, Символы.ПС, ""), Символы.ВК, ""); - -КонецФункции - -Процедура ПодробноеСравнениеСтрок(Знач Строка1, Знач Строка2, Знач ТекстСообщения) - - Если Строка1 = Строка2 Тогда - Возврат; - КонецЕсли; - - Строка1 = ЭкранироватьПереносыСтрок(Строка1); - Строка2 = ЭкранироватьПереносыСтрок(Строка2); - - юТест.ПроверитьРавенство(Строка1, Строка2, ТекстСообщения); - -КонецПроцедуры - -Процедура ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(Знач Текст, Знач РазделительСтрокВФайле, Знач НаименованиеТеста) - - ИмяФайла = ПолучитьИмяВременногоФайла("txt"); - Запись = Новый ЗаписьТекста; - Запись.Открыть(ИмяФайла, "UTF-8", Символы.ПС, Ложь, РазделительСтрокВФайле); - Запись.Записать(Текст); - Запись.Закрыть(); - - Чтение = Новый ЧтениеТекста; - Чтение.Открыть(ИмяФайла, "UTF-8", Символы.ПС, РазделительСтрокВФайле); - - ПроверяемыйТекст = Чтение.Прочитать(); - - ПодробноеСравнениеСтрок(ПроверяемыйТекст, Текст, "Не сработало запись-чтение текста (" + НаименованиеТеста + ")"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьРаботуРазделителей() Экспорт - - ТекстДляПроверки = "1 - |2 - |3" - ; - - ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(ТекстДляПроверки, Символы.ПС, "ПС"); - ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(ТекстДляПроверки, Символы.ВК + Символы.ПС, "ВК+ПС"); - ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(ТекстДляПроверки, Символы.ВК, "ВК"); - -КонецПроцедуры - Процедура ТестДолжен_ПроверитьРаботуНеобычныхРазделителей1() Экспорт ИмяФайла = ПолучитьИмяВременногоФайла("txt"); @@ -313,3 +278,255 @@ юТест.ПроверитьРавенство(125, СтрДлина(Стр)); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоВозможнаЗаписьВПотокПамяти() Экспорт + + // Дано + Поток = Новый ПотокВПамяти(); + + // Когда + ЗаписьТекста = Новый ЗаписьТекста(Поток); + ЗаписьТекста.Записать("Привет"); + ЗаписьТекста.Закрыть(); + + // Тогда + ЧтениеТекста = Новый ЧтениеТекста(Поток); + Поток.Перейти(0, ПозицияВПотоке.Начало); + + юТест.ПроверитьРавенство(ЧтениеТекста.ПрочитатьСтроку(), "Привет"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоВозможнаЗаписьВПотокСКодировкой() Экспорт + + // Дано + Поток = Новый ПотокВПамяти(); + + // Когда + ЗаписьТекста = Новый ЗаписьТекста(Поток, "windows-1251"); + ЗаписьТекста.Записать("Привет"); + ЗаписьТекста.Закрыть(); + + // Тогда + ЧтениеТекста = Новый ЧтениеТекста(Поток, "windows-1251"); + Поток.Перейти(0, ПозицияВПотоке.Начало); + + юТест.ПроверитьРавенство(ЧтениеТекста.ПрочитатьСтроку(), "Привет"); + юТест.ПроверитьРавенство(Поток.Размер(), 6); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоВозможнаЗаписьВПотокСМеткойBOM() Экспорт + + // Дано + Поток = Новый ПотокВПамяти(); + + // Когда + ЗаписьТекста = Новый ЗаписьТекста(Поток, , , , Истина); + ЗаписьТекста.Записать("Привет"); + ЗаписьТекста.Закрыть(); + + // Тогда + ЧтениеТекста = Новый ЧтениеТекста(Поток); + Поток.Перейти(0, ПозицияВПотоке.Начало); + + юТест.ПроверитьРавенство(ЧтениеТекста.ПрочитатьСтроку(), "Привет"); + юТест.ПроверитьРавенство(Поток.Размер(), 15); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьУстановкуПотокаДляЗаписи() Экспорт + + // Дано + Поток = Новый ПотокВПамяти(); + ЗаписьТекста = Новый ЗаписьТекста(); + + // Когда + ЗаписьТекста.Открыть(Поток); + ЗаписьТекста.Записать("Привет"); + ЗаписьТекста.Закрыть(); + + // Тогда + ЧтениеТекста = Новый ЧтениеТекста(Поток); + Поток.Перейти(0, ПозицияВПотоке.Начало); + + юТест.ПроверитьРавенство(ЧтениеТекста.ПрочитатьСтроку(), "Привет"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоКонструкторНеБроситИсключениеДляЗакрытогоПотока() Экспорт + + // Дано + Поток = Новый ПотокВПамяти(); + Поток.Закрыть(); + + // Когда + ЗаписьТекста = Новый ЗаписьТекста(Поток); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоНеоткрытыйПотокПриЗаписиБросаетПравильноеИсключение() Экспорт + + Сценарий = " + |Поток = Новый ПотокВПамяти(); + |Поток.Закрыть(); + | + |ЗаписьТекста = Новый ЗаписьТекста(Поток); + |ЗаписьТекста.Записать(""Привет"")"; + + юТест.ПроверитьКодСОшибкой(Сценарий, "Ошибка обращения к закрытому потоку"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЧтоПотокДляЧтенияПриЗаписиБросаетПравильноеИсключение() Экспорт + + Сценарий = " + |Поток = Новый ПотокВПамяти(); + |ПотокДляЧтения = Поток.ПолучитьПотокТолькоДляЧтения(); + | + |ЗаписьТекста = Новый ЗаписьТекста(ПотокДляЧтения); + | + |ЗаписьТекста.Записать(""Привет"")"; + + юТест.ПроверитьКодСОшибкой(Сценарий, "Попытка записи в поток не поддерживающий запись"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПереоткрытиеСФайлаНаПоток() Экспорт + + // Дано + Поток = Новый ПотокВПамяти(); + ИмяФайла = ПолучитьИмяВременногоФайла("txt"); + ЗаписьТекста = Новый ЗаписьТекста(); + + // Когда + ЗаписьТекста.Открыть(ИмяФайла); + ЗаписьТекста.Записать("Привет1"); + + ЗаписьТекста.Открыть(Поток); + ЗаписьТекста.Записать("Привет2"); + + ЗаписьТекста.Закрыть(); + + // Тогда + ЧтениеТекстаИзФайла = Новый ЧтениеТекста(ИмяФайла); + ЧтениеТекстаИзПотока = Новый ЧтениеТекста(Поток); + + Поток.Перейти(0, ПозицияВПотоке.Начало); + + юТест.ПроверитьРавенство(ЧтениеТекстаИзФайла.Прочитать(), "Привет1"); + юТест.ПроверитьРавенство(ЧтениеТекстаИзПотока.Прочитать(), "Привет2"); + + ЧтениеТекстаИзФайла.Закрыть(); + УдалитьФайлы(ИмяФайла); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПереоткрытиеСПотокаНаФайл() Экспорт + + // Дано + Поток = Новый ПотокВПамяти(); + ИмяФайла = ПолучитьИмяВременногоФайла("txt"); + ЗаписьТекста = Новый ЗаписьТекста(); + + // Когда + ЗаписьТекста.Открыть(Поток); + ЗаписьТекста.Записать("Привет1"); + + ЗаписьТекста.Открыть(ИмяФайла); + ЗаписьТекста.Записать("Привет2"); + + ЗаписьТекста.Закрыть(); + + // Тогда + ЧтениеТекстаИзФайла = Новый ЧтениеТекста(ИмяФайла); + ЧтениеТекстаИзПотока = Новый ЧтениеТекста(Поток); + + Поток.Перейти(0, ПозицияВПотоке.Начало); + + юТест.ПроверитьРавенство(ЧтениеТекстаИзПотока.Прочитать(), "Привет1"); + юТест.ПроверитьРавенство(ЧтениеТекстаИзФайла.Прочитать(), "Привет2"); + + ЧтениеТекстаИзФайла.Закрыть(); + УдалитьФайлы(ИмяФайла); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРаботуРазделителей() Экспорт + + ТекстДляПроверки = "1 + |2 + |3" + ; + + ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(ТекстДляПроверки, Символы.ПС, "ПС"); + ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(ТекстДляПроверки, Символы.ВК + Символы.ПС, "ВК+ПС"); + ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(ТекстДляПроверки, Символы.ВК, "ВК"); + ПроверитьЗаписьТекстаСРазделителемСтрокВПотоке(ТекстДляПроверки, Символы.ПС, "ПС"); + ПроверитьЗаписьТекстаСРазделителемСтрокВПотоке(ТекстДляПроверки, Символы.ВК + Символы.ПС, "ВК+ПС"); + ПроверитьЗаписьТекстаСРазделителемСтрокВПотоке(ТекстДляПроверки, Символы.ВК, "ВК"); + +КонецПроцедуры + +Процедура ПроверитьЗаписьТекстаСРазделителемСтрокВФайле(Знач Текст, Знач РазделительСтрокВФайле, Знач НаименованиеТеста) + + // Дано + ИмяФайла = ПолучитьИмяВременногоФайла("txt"); + Запись = Новый ЗаписьТекста; + + // Когда + Запись.Открыть(ИмяФайла, "UTF-8", Символы.ПС, Ложь, РазделительСтрокВФайле); + Запись.Записать(Текст); + Запись.Закрыть(); + + // Тогда + Чтение = Новый ЧтениеТекста; + Чтение.Открыть(ИмяФайла, "UTF-8", Символы.ПС, РазделительСтрокВФайле); + + ПроверяемыйТекст = Чтение.Прочитать(); + + ПодробноеСравнениеСтрок(ПроверяемыйТекст, Текст, "Не сработало запись-чтение текста в файл (" + НаименованиеТеста + ")"); + +КонецПроцедуры + +Процедура ПроверитьЗаписьТекстаСРазделителемСтрокВПотоке(Знач Текст, Знач РазделительСтрокВФайле, Знач НаименованиеТеста) + + // Дано + Поток = Новый ПотокВПамяти(); + Запись = Новый ЗаписьТекста; + + // Когда + Запись.Открыть(Поток, "UTF-8", Символы.ПС, РазделительСтрокВФайле); + Запись.Записать(Текст); + Запись.Закрыть(); + + // Тогда + Чтение = Новый ЧтениеТекста; + Чтение.Открыть(Поток, "UTF-8", Символы.ПС, РазделительСтрокВФайле); + + Поток.Перейти(0, ПозицияВПотоке.Начало); + + ПроверяемыйТекст = Чтение.Прочитать(); + + ПодробноеСравнениеСтрок(ПроверяемыйТекст, Текст, "Не сработало запись-чтение текста в поток (" + НаименованиеТеста + ")"); + +КонецПроцедуры + +Процедура ПодробноеСравнениеСтрок(Знач Строка1, Знач Строка2, Знач ТекстСообщения) + + Если Строка1 = Строка2 Тогда + Возврат; + КонецЕсли; + + Строка1 = ЭкранироватьПереносыСтрок(Строка1); + Строка2 = ЭкранироватьПереносыСтрок(Строка2); + + юТест.ПроверитьРавенство(Строка1, Строка2, ТекстСообщения); + +КонецПроцедуры + +Функция ЭкранироватьПереносыСтрок(Знач Строка1) + + Возврат СтрЗаменить(СтрЗаменить(Строка1, Символы.ПС, ""), Символы.ВК, ""); + +КонецФункции \ No newline at end of file diff --git a/tests/timezones.os b/tests/timezones.os index b0f46ffee..1f54c7d38 100644 --- a/tests/timezones.os +++ b/tests/timezones.os @@ -14,6 +14,8 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьПреобразованиеВремениБезПараметров"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПреобразованиеВремениСПараметрами"); ВсеТесты.Добавить("ТестДолжен_ПроверитьСмещениеUTC"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьНачалоНедели"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьКонецНедели"); Возврат ВсеТесты; @@ -115,4 +117,67 @@ юТест.Проверить(Смещение1 = Смещение2, "Смещения часового пояса должны быть равны"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьНачалоНедели() Экспорт + + ТекущаяДата = ТекущаяДата(); + + А = НачалоНедели(ТекущаяДата); + Б = BegOfWeek(ТекущаяДата); + + юТест.ПроверитьРавенство(А, Б, "Английский и русский терм совпадают"); + + СерединаНедели = '2021-06-24'; + НачалоНедели = '2021-06-21'; + КонецНедели = '2021-06-27'; + + Эталон = НачалоНедели; + + Результат1 = НачалоНедели(СерединаНедели); + Результат2 = НачалоНедели(НачалоНедели); + Результат3 = НачалоНедели(КонецНедели); + + юТест.ПроверитьРавенство(Результат1, Эталон, "Из середины"); + юТест.ПроверитьРавенство(Результат2, Эталон, "От начала"); + юТест.ПроверитьРавенство(Результат3, Эталон, "С конца"); + + // с переходом через месяц + Дата = '2021-07-03'; + Эталон = '2021-06-28'; + НачалоНедели = НачалоНедели(Дата); + юТест.ПроверитьРавенство(НачалоНедели, Эталон, "С переходом через месяц"); + + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьКонецНедели() Экспорт + + ТекущаяДата = ТекущаяДата(); + + А = КонецНедели(ТекущаяДата); + Б = EndOfWeek(ТекущаяДата); + + юТест.ПроверитьРавенство(А, Б, "Английский и русский терм совпадают"); + + СерединаНедели = '2021-06-24 12:24:08'; + НачалоНедели = '2021-06-21'; + КонецНедели = '2021-06-27 23:59:59'; + + Эталон = КонецНедели; + + Результат1 = КонецНедели(СерединаНедели); + Результат2 = КонецНедели(НачалоНедели); + Результат3 = КонецНедели(КонецНедели); + + юТест.ПроверитьРавенство(Результат1, Эталон, "Из середины"); + юТест.ПроверитьРавенство(Результат2, Эталон, "От начала"); + юТест.ПроверитьРавенство(Результат3, Эталон, "С конца"); + + // с переходом через месяц + Дата = '2021-06-28'; + Эталон = '2021-07-04 23:59:59'; + КонецНедели = КонецНедели(Дата); + юТест.ПроверитьРавенство(КонецНедели, Эталон, "С переходом через месяц"); + КонецПроцедуры \ No newline at end of file diff --git a/tests/typedescription.os b/tests/typedescription.os index e58c5d980..8a58a8228 100644 --- a/tests/typedescription.os +++ b/tests/typedescription.os @@ -14,11 +14,31 @@ ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповПоКвалификаторам"); ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповПоДругомуОписанию"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповСПропускомПараметров"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповСПараметрамиНеопределено"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповСПроизвольнымПорядкомКвалификаторов"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповСПовторяющимисяКвалификаторами"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповПоДругомуОписаниюСПроизвольнымиКвалификаторами"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповИзМассива"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповПоДругомуОписаниюСМассивамиИзменяемыхТипов"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповСПовторяющимисяТипами"); + ВсеТесты.Добавить("ТестДолжен_СоздатьОписаниеТиповПоДругомуОписаниюСПустымиПараметрами"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуНеверныхПараметров"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПриведениеСтрок"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПриведениеБулева"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПриведениеДат"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПриведениеБезПриведения"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПриведениеЧисел"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПриведениеНеопределено"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПриведениеОбъектов"); + + ВсеТесты.Добавить("ТестДолжен_Преобразование_ОписаниеБезТипов"); + ВсеТесты.Добавить("ТестДолжен_Преобразование_ОписаниеБезТипов_КвалификаторыИгнорируются"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьОдинаковыйПорядокТипов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПорядокПриведенияТипов"); Возврат ВсеТесты; @@ -55,8 +75,8 @@ Процедура ТестДолжен_ПроверитьКвалификаторыДаты() Экспорт К1 = Новый КвалификаторыДаты(); - К2 = Новый КвалификаторыДаты(ЧастиДаты.Дата); - К3 = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя); + К2 = Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя); + К3 = Новый КвалификаторыДаты(ЧастиДаты.Дата); К4 = Новый КвалификаторыДаты(ЧастиДаты.Время); юТест.ПроверитьРавенство (К1, К2, "Квалификаторы с одинаковыми параметрами равны"); @@ -83,7 +103,8 @@ Описание3 = Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(10)); Описание4 = Новый ОписаниеТипов("Число,Строка", Новый КвалификаторыЧисла(20)); - юТест.ПроверитьРавенство(Описание1.КвалификаторыЧисла, Описание2.КвалификаторыЧисла); + юТест.ПроверитьНеРавенство(Описание1.КвалификаторыЧисла, Описание2.КвалификаторыЧисла); + юТест.ПроверитьРавенство(Описание2.КвалификаторыЧисла, Новый КвалификаторыЧисла()); // Значения по умолчанию должны совпасть юТест.ПроверитьРавенство(Описание1.КвалификаторыСтроки, Описание2.КвалификаторыСтроки); @@ -110,11 +131,15 @@ Описание1 = Новый ОписаниеТипов("Строка,Число", КЧ1, КС1, КД1, КДД1); Описание2 = Новый ОписаниеТипов(Описание1, "Массив", "Число", КЧ2, КС2, КД2, КДД2); + // остались Строка, Массив + + юТест.ПроверитьНеРавенство(Описание1.КвалификаторыЧисла, Описание2.КвалификаторыЧисла); юТест.ПроверитьНеРавенство(Описание1.КвалификаторыЧисла, Описание2.КвалификаторыЧисла); + юТест.ПроверитьРавенство(Описание2.КвалификаторыЧисла, Новый КвалификаторыЧисла()); юТест.ПроверитьНеРавенство(Описание1.КвалификаторыСтроки, Описание2.КвалификаторыСтроки); - юТест.ПроверитьНеРавенство(Описание1.КвалификаторыДаты, Описание2.КвалификаторыДаты); - юТест.ПроверитьНеРавенство(Описание1.КвалификаторыДвоичныхДанных, Описание2.КвалификаторыДвоичныхДанных); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДаты, Описание2.КвалификаторыДаты); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДвоичныхДанных, Описание2.КвалификаторыДвоичныхДанных); юТест.ПроверитьРавенство(Описание2.Типы().Найти(Тип("Число")), Неопределено); юТест.ПроверитьНеРавенство(Описание2.Типы().Найти(Тип("Массив")), Неопределено); @@ -124,6 +149,198 @@ КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповСПропускомПараметров() Экспорт + КЧ1 = Новый КвалификаторыЧисла(10); + КС1 = Новый КвалификаторыСтроки(10); + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + КДД1 = Новый КвалификаторыДвоичныхДанных(10); + + Описание1 = Новый ОписаниеТипов("Строка,Число,Дата", , ,КЧ1, КС1, КД1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыЧисла, КЧ1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыСтроки, КС1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДаты, КД1); +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповСПараметрамиНеопределено() Экспорт + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + + Описание1 = Новый ОписаниеТипов("Дата", Неопределено, Неопределено, КД1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДаты, КД1); +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповСПроизвольнымПорядкомКвалификаторов() Экспорт + КЧ1 = Новый КвалификаторыЧисла(10); + КС1 = Новый КвалификаторыСтроки(10); + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + КДД1 = Новый КвалификаторыДвоичныхДанных(10); + + Описание1 = Новый ОписаниеТипов("Строка,Число,Дата", КС1, КД1, КДД1, КЧ1 ); + юТест.ПроверитьРавенство(Описание1.КвалификаторыЧисла, КЧ1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыСтроки, КС1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДаты, КД1); + юТест.ПроверитьНеРавенство(Описание1.КвалификаторыДвоичныхДанных, КДД1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДвоичныхДанных, Новый КвалификаторыДвоичныхДанных()); +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповСПовторяющимисяКвалификаторами() Экспорт + КЧ1 = Новый КвалификаторыЧисла(10); + КЧ2 = Новый КвалификаторыЧисла(20); + КС1 = Новый КвалификаторыСтроки(10); + КС2 = Новый КвалификаторыСтроки(20); + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + + Описание1 = Новый ОписаниеТипов("Строка,Число,Дата", КС1, КЧ1, КС2, КД1, , КЧ2 ); + юТест.ПроверитьРавенство(Описание1.КвалификаторыЧисла, КЧ2); + юТест.ПроверитьРавенство(Описание1.КвалификаторыСтроки, КС2); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДаты, КД1); +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповПоДругомуОписаниюСПроизвольнымиКвалификаторами() Экспорт + + КЧ1 = Новый КвалификаторыЧисла(10); + КЧ2 = Новый КвалификаторыЧисла(20); + КС1 = Новый КвалификаторыСтроки(10); + КС2 = Новый КвалификаторыСтроки(20); + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + КД2 = Новый КвалификаторыДаты(ЧастиДаты.Время); + КДД1 = Новый КвалификаторыДвоичныхДанных(10); + КДД2 = Новый КвалификаторыДвоичныхДанных(20); + + Описание1 = Новый ОписаниеТипов("Строка,Число", КЧ1, КС1, КД1); + Описание2 = Новый ОписаниеТипов(Описание1, "Дата", "Число", КС2, КЧ2, КД1, КД2); + + юТест.ПроверитьНеРавенство(Описание2.КвалификаторыЧисла, КЧ2); + юТест.ПроверитьРавенство(Описание2.КвалификаторыЧисла, Новый КвалификаторыЧисла()); + юТест.ПроверитьРавенство(Описание2.КвалификаторыСтроки, КС2); + юТест.ПроверитьРавенство(Описание2.КвалификаторыДаты, КД2); + +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповИзМассива() Экспорт + КЧ1 = Новый КвалификаторыЧисла(10); + КС1 = Новый КвалификаторыСтроки(10); + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + КДД1 = Новый КвалификаторыДвоичныхДанных(10); + + Массив = Новый Массив(); + Массив.Добавить(Тип("Строка")); + Массив.Добавить(Тип("Дата")); + Массив.Добавить(Тип("Число")); + + Описание1 = Новый ОписаниеТипов(Массив, КЧ1, КС1, КД1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыЧисла, КЧ1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыСтроки, КС1); + юТест.ПроверитьРавенство(Описание1.КвалификаторыДаты, КД1); +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповПоДругомуОписаниюСМассивамиИзменяемыхТипов() Экспорт + + КЧ1 = Новый КвалификаторыЧисла(10); + КЧ2 = Новый КвалификаторыЧисла(20); + КС1 = Новый КвалификаторыСтроки(10); + КС2 = Новый КвалификаторыСтроки(20); + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + КД2 = Новый КвалификаторыДаты(ЧастиДаты.Время); + КДД1 = Новый КвалификаторыДвоичныхДанных(10); + КДД2 = Новый КвалификаторыДвоичныхДанных(20); + + МассивДобавляемых = Новый Массив(); + МассивДобавляемых.Добавить(Тип("ДвоичныеДанные")); + МассивДобавляемых.Добавить(Тип("Структура")); + + МассивВычитаемых = Новый Массив(); + МассивВычитаемых.Добавить(Тип("Число")); + + Описание1 = Новый ОписаниеТипов("Строка,Число,Дата", КЧ1, КС1, КД1); + Описание2 = Новый ОписаниеТипов(Описание1, МассивДобавляемых, МассивВычитаемых, КС2, КЧ2, КД2, КДД2); + + юТест.ПроверитьРавенство(Описание2.Типы().Количество(), 4); + юТест.ПроверитьНеРавенство(Описание2.КвалификаторыЧисла, КЧ2); + юТест.ПроверитьРавенство(Описание2.КвалификаторыЧисла, Новый КвалификаторыЧисла()); + юТест.ПроверитьРавенство(Описание2.КвалификаторыСтроки, КС2); + юТест.ПроверитьРавенство(Описание2.КвалификаторыДаты, КД2); + юТест.ПроверитьРавенство(Описание2.КвалификаторыДвоичныхДанных, КДД2); + +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповСПовторяющимисяТипами() Экспорт + + Описание = Новый ОписаниеТипов("Строка,Число,Число,Дата,Строка"); + + юТест.ПроверитьРавенство(Описание.Типы().Количество(), 3); + +КонецПроцедуры + +Процедура ТестДолжен_СоздатьОписаниеТиповПоДругомуОписаниюСПустымиПараметрами() Экспорт + + КЧ1 = Новый КвалификаторыЧисла(10); + КЧ2 = Новый КвалификаторыЧисла(20); + КС1 = Новый КвалификаторыСтроки(10); + КС2 = Новый КвалификаторыСтроки(20); + КД1 = Новый КвалификаторыДаты(ЧастиДаты.Дата); + КД2 = Новый КвалификаторыДаты(ЧастиДаты.Время); + КДД1 = Новый КвалификаторыДвоичныхДанных(10); + КДД2 = Новый КвалификаторыДвоичныхДанных(20); + + Описание1 = Новый ОписаниеТипов("Строка,Число", КЧ1, КС1, КД1); + + Описание2 = Новый ОписаниеТипов(Описание1, "Дата", Неопределено); + юТест.ПроверитьРавенство(Описание2.Типы().Количество(), 3); + + Описание2 = Новый ОписаниеТипов(Описание1, "Дата", ""); + юТест.ПроверитьРавенство(Описание2.Типы().Количество(), 3); + + Описание2 = Новый ОписаниеТипов(Описание1, "Дата", ", , Число,"); + юТест.ПроверитьРавенство(Описание2.Типы().Количество(), 2); + + Описание2 = Новый ОписаниеТипов(Описание1, ,"Число"); + юТест.ПроверитьРавенство(Описание2.Типы().Количество(), 1); + + Описание2 = Новый ОписаниеТипов(Описание1, ",", "Число"); + юТест.ПроверитьРавенство(Описание2.Типы().Количество(), 1); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПередачуНеверныхПараметров() Экспорт + + Ошибка = "Передача неверного значения типа"; + Попытка + Описание1 = Новый ОписаниеТипов("НеБулево"); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Тип не зарегистрирован (НеБулево)"); + + Описание1 = Новый ОписаниеТипов("Строка,Число"); + + Ошибка = "Передача неверного типа в список добавляемых"; + Попытка + Описание2 = Новый ОписаниеТипов(Описание1, NULL); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Неверный тип аргумента номер 2"); + + Ошибка = "Передача неверного типа в список удаляемых"; + Попытка + Описание2 = Новый ОписаниеТипов(Описание1, "Булево", Ложь); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Неверный тип аргумента номер 3"); + + Ошибка = "Передача неверного значения в список удаляемых"; + Попытка + Описание2 = Новый ОписаниеТипов(Описание1, "Булево", "НеБулево"); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Тип не зарегистрирован (НеБулево)"); + +КонецПроцедуры + + Процедура ТестДолжен_ПроверитьПриведениеСтрок() Экспорт Описание1 = Новый ОписаниеТипов("Строка",, Новый КвалификаторыСтроки(5, ДопустимаяДлина.Фиксированная)); @@ -236,3 +453,184 @@ юТест.ПроверитьРавенство(Описание.ПривестиЗначение(12345.555), 999.99, "Забивает девятками"); КонецПроцедуры + +Процедура ТестДолжен_Преобразование_ОписаниеБезТипов() Экспорт + + МассивТиповНеопределено = Новый Массив; + МассивТиповНеопределено.Добавить(Тип("Неопределено")); + + ПроверяемыеОписания = Новый СписокЗначений; + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов, + "Новый ОписаниеТипов" + ); + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов("Неопределено"), + "Новый ОписаниеТипов(""Неопределено"")" + ); + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов(МассивТиповНеопределено), + "Новый ОписаниеТипов(МассивТиповНеопределено)" + ); + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов("Undefined"), + "Новый ОписаниеТипов(""Undefined"")" + ); + + Для Каждого мЭлементПроверки Из ПроверяемыеОписания Цикл + + ОписаниеБезТипов = мЭлементПроверки.Значение; + ОписаниеСлучая = мЭлементПроверки.Представление; + + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(), Неопределено, "Приведение без параметра. " + ОписаниеСлучая); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(1), 1, "Приведение числа. " + ОписаниеСлучая); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение("1"), "1", "Приведение строки. " + ОписаниеСлучая); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(Тип("Строка")), Тип("Строка"), "Приведение Типа. " + ОписаниеСлучая); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение('20230817104356'), '20230817104356', "Приведение даты. " + ОписаниеСлучая); + + ДД = ПолучитьДвоичныеДанныеИзСтроки("Строка"); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(ДД), ДД, "Двоичные данные не ломаются. " + ОписаниеСлучая); + + юТест.ПроверитьРавенство(ОписаниеБезТипов.Типы().Количество(), 0, "Типы() пустой. " + ОписаниеСлучая); + // юТест.ПроверитьЛожь(ОписаниеБезТипов.СодержитТип(Тип("Неопределено")), "Нет типа Неопределено. " + ОписаниеСлучая); + + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_Преобразование_ОписаниеБезТипов_КвалификаторыИгнорируются() Экспорт + + КЧ = Новый КвалификаторыЧисла(1); + КС = Новый КвалификаторыСтроки(1); + КД = Новый КвалификаторыДаты(ЧастиДаты.Время); + + МассивТиповНеопределено = Новый Массив; + МассивТиповНеопределено.Добавить(Тип("Неопределено")); + + ПроверяемыеОписания = Новый СписокЗначений; + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов(, КЧ, КС, КД), + "Новый ОписаниеТипов(, КЧ, КС, КД)" + ); + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов("Неопределено", КЧ, КС, КД), + "Новый ОписаниеТипов(""Неопределено"", КЧ, КС, КД)" + ); + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов(МассивТиповНеопределено, КЧ, КС, КД), + "Новый ОписаниеТипов(МассивТиповНеопределено, КЧ, КС, КД)" + ); + ПроверяемыеОписания.Добавить( + Новый ОписаниеТипов("Undefined", КЧ, КС, КД), + "Новый ОписаниеТипов(""Undefined"", КЧ, КС, КД)" + ); + + Для Каждого мЭлементПроверки Из ПроверяемыеОписания Цикл + + ОписаниеБезТипов = мЭлементПроверки.Значение; + ОписаниеСлучая = мЭлементПроверки.Представление; + + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(), Неопределено, "Приведение без параметра. " + ОписаниеСлучая); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(123), 123, "Число не режется. " + ОписаниеСлучая); // не 1 + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение("123"), "123", "Строка не режется. " + ОписаниеСлучая); // не "1" + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(Тип("Строка")), Тип("Строка"), "Значение типа Тип не ломается. " + ОписаниеСлучая); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение('20230817104356'), '20230817104356', "Дата не режется. " + ОписаниеСлучая); // не `00010101104356` + + ДД = ПолучитьДвоичныеДанныеИзСтроки("Строка"); + юТест.ПроверитьРавенство(ОписаниеБезТипов.ПривестиЗначение(ДД), ДД, "Двоичные данные не ломаются. " + ОписаниеСлучая); + + юТест.ПроверитьРавенство(ОписаниеБезТипов.Типы().Количество(), 0, "Типы() пустой. " + ОписаниеСлучая); + юТест.ПроверитьЛожь(ОписаниеБезТипов.СодержитТип(Тип("Неопределено")), "Нет типа Неопределено. " + ОписаниеСлучая); + + КонецЦикла; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПорядокПриведенияТипов() Экспорт + + ОписаниеТипов = Новый ОписаниеТипов("Число, Строка", Новый КвалификаторыСтроки(1)); + + юТест.ПроверитьРавенство(ОписаниеТипов.ПривестиЗначение(Истина), "Д"); // TODO: локализация + юТест.ПроверитьРавенство(ОписаниеТипов.ПривестиЗначение("Строка"), "С"); + юТест.ПроверитьРавенство(ОписаниеТипов.ПривестиЗначение(15), 15); + юТест.ПроверитьРавенство(ОписаниеТипов.ПривестиЗначение('20230911'), "1"); // TODO: локализация + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьОдинаковыйПорядокТипов() Экспорт + + ОТ = Новый ОписаниеТипов("Число, Строка"); + ПроверитьПорядокТипов(ОТ.Типы(), "Строка, Число"); + + ОТ = Новый ОписаниеТипов("Строка, Число"); + ПроверитьПорядокТипов(ОТ.Типы(), "Строка, Число"); + + ОТ = Новый ОписаниеТипов("Строка, Число, Строка"); + ПроверитьПорядокТипов(ОТ.Типы(), "Строка, Число"); + + ОТ = Новый ОписаниеТипов("Число, Число, Строка"); + ПроверитьПорядокТипов(ОТ.Типы(), "Строка, Число"); + + ОТ = Новый ОписаниеТипов("Число, Число, Строка, Дата, Булево, Неопределено"); + ПроверитьПорядокТипов(ОТ.Типы(), "Булево, Строка, Дата, Число"); + + ОТ = Новый ОписаниеТипов("Неопределено, Число, Дата, Строка, Дата, Булево"); + ПроверитьПорядокТипов(ОТ.Типы(), "Булево, Строка, Дата, Число"); + + ОТ = Новый ОписаниеТипов("ДвоичныеДанные, Неопределено, Тип, NULL, Число, Дата, Строка, Дата, Булево"); + ПроверитьПорядокТипов(ОТ.Типы(), "Булево, ДвоичныеДанные, Строка, Дата, Null, Число, Тип"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПриведениеНеопределено() Экспорт + + ОТ = Новый ОписаниеТипов("Число, Строка"); + юТест.ПроверитьИстину(ОТ.СодержитТип(Тип("Неопределено")), "Неопределено содержится в описании типов"); + юТест.ПроверитьРавенство(ОТ.Типы().Найти(Тип("Неопределено")), Неопределено, "Неопределено не содержится в типах"); + юТест.ПроверитьРавенство(ОТ.ПривестиЗначение(Неопределено), Неопределено, "Неопределено в неопределено"); + + ОТ = Новый ОписаниеТипов; + юТест.ПроверитьРавенство(ОТ.ПривестиЗначение(Неопределено), Неопределено, "Неопределено в неопределено"); + юТест.ПроверитьЛожь(ОТ.СодержитТип(Тип("Неопределено")), "Неопределено не содержится в описании типов (без типов)"); + юТест.ПроверитьРавенство(ОТ.Типы().Найти(Тип("Неопределено")), Неопределено, "Неопределено не содержится в типах"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПриведениеОбъектов() Экспорт + + ОТ = Новый ОписаниеТипов("Массив, Структура, Строка"); + + М1 = Новый Массив; + М1.Добавить(1); + М1.Добавить(2); + + М2 = ОТ.ПривестиЗначение(М1); + юТест.ПроверитьРавенство(М2, М1, "Массив тот же самый"); + +КонецПроцедуры + +Процедура ПроверитьПорядокТипов(Знач ТипыОписанияТипов, Знач ОжидаемыйПорядок, Знач Текст = Неопределено) + + Если Текст = Неопределено Тогда + Текст = СтрШаблон("Ожидаемый порядок: %1 + |Полученный порядок: %2", ОжидаемыйПорядок, СтрСоединить(ТипыОписанияТипов, ", ") + ); + КонецЕсли; + + ТипыСтроками = СтрРазделить(ОжидаемыйПорядок, ","); + + юТест.ПроверитьБольшеИлиРавно(ТипыОписанияТипов.Количество(), ТипыСтроками.Количество(), Текст); + + ИндексТипа = 0; + Для Каждого мТипСтрокой Из ТипыСтроками Цикл + + ТипОписания = ТипыОписанияТипов[ИндексТипа]; + ТипОжидаемый = Тип(СокрЛП(мТипСтрокой)); + + юТест.ПроверитьРавенство(ТипОписания, ТипОжидаемый); + + ИндексТипа = ИндексТипа + 1; + + КонецЦикла; + +КонецПроцедуры diff --git a/tests/types.os b/tests/types.os index 0ed5ac9fd..2940e39e7 100644 --- a/tests/types.os +++ b/tests/types.os @@ -22,6 +22,7 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьАнглийскиеИменаПростыхТипов"); ВсеТесты.Добавить("ТестДолжен_ПроверитьНеравенствоРазличныхТипов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьТипСценарий"); Возврат ВсеТесты; КонецФункции @@ -83,3 +84,10 @@ КонецПроцедуры +Процедура ТестДолжен_ПроверитьТипСценарий() Экспорт + + Сценарий = ЗагрузитьСценарийИзСтроки("Перем Привет;"); + юТест.ПроверитьРавенство(Тип("Сценарий"), ТипЗнч(Сценарий)); + +КонецПроцедуры + diff --git a/tests/value-list.os b/tests/value-list.os index 6e74a82ac..2dbf03562 100644 --- a/tests/value-list.os +++ b/tests/value-list.os @@ -1,385 +1,413 @@ -Перем юТест; - -Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт - - юТест = ЮнитТестирование; - - ВсеТесты = Новый Массив; - ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеСпискаЗначений"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьДобавлениеЭлементов"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьДоступКСвойствамЭлементаСписка"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьЗаписьСвойствЭлементаСписка"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьИтерациюПоСписку"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьДоступПоИндексу"); - - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодИндексСпискаЗначений"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодВставитьСпискаЗначений"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСдвинутьСпискаЗначений"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСортироватьПоЗначениюСпискаЗначений"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСортироватьПоПредставлениюСпискаЗначений"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодНайтиСпискаЗначений"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодУдалитьСпискаЗначений"); - Возврат ВсеТесты; - -КонецФункции - -Процедура ТестДолжен_ПроверитьСозданиеСпискаЗначений() Экспорт - СЗ = Новый СписокЗначений; - юТест.ПроверитьРавенство(Тип("СписокЗначений"), ТипЗнч(СЗ)); -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьДобавлениеЭлементов() Экспорт - СЗ = Новый СписокЗначений; - СЗ.Добавить("Один"); - СЗ.Добавить("Два"); - юТест.ПроверитьРавенство(2, СЗ.Количество()); - юТест.ПроверитьРавенство(Тип("ЭлементСпискаЗначений"), ТипЗнч(СЗ.Получить(0))); -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьДоступКСвойствамЭлементаСписка() Экспорт - - СЗ = Новый СписокЗначений; - СЗ.Добавить(1,"Представление"); - СЗ.Добавить(2,"Представление2", Истина); - СЗ.Добавить(3,"Представление3", Истина, "Тут должна быть картинка, но сейчас может быть любое значение"); - СЗ.Добавить(1); - - Элемент = СЗ[0]; - юТест.ПроверитьРавенство(1, Элемент.Значение, "Значение элемента 0 должно быть установлено"); - юТест.ПроверитьРавенство("Представление", Элемент.Представление, "Представление элемента 0 должно быть установлено"); - юТест.ПроверитьРавенство(Ложь, Элемент.Пометка, "Пометка элемента 0 не должна быть установлена"); - юТест.ПроверитьРавенство(Неопределено, Элемент.Картинка, "Картинка элемента 0 не должна быть установлена"); - - Элемент = СЗ[1]; - юТест.ПроверитьРавенство(2, Элемент.Значение, "Значение элемента 1 должно быть установлено"); - юТест.ПроверитьРавенство("Представление2", Элемент.Представление, "Представление элемента 1 должно быть установлено"); - юТест.ПроверитьРавенство(Истина, Элемент.Пометка, "Пометка элемента 1 должна быть установлена"); - - Элемент = СЗ[2]; - юТест.ПроверитьРавенство(3, Элемент.Значение, "Значение элемента 2 должно быть установлено"); - юТест.ПроверитьРавенство("Представление3", Элемент.Представление, "Представление элемента 2 должно быть установлено"); - юТест.ПроверитьРавенство(Истина, Элемент.Пометка, "Пометка элемента 2 должна быть установлена"); - юТест.ПроверитьРавенство("Тут должна быть картинка, но сейчас может быть любое значение", Элемент.Картинка, "Картинка элемента 2 должна быть установлена"); - - юТест.ПроверитьРавенство("", СЗ[3].Представление, "Должно быть пустое представление по умолчанию"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьЗаписьСвойствЭлементаСписка() Экспорт - СЗ = Новый СписокЗначений; - Элемент = СЗ.Добавить(1); - - Элемент.Значение = 2; - Элемент.Представление = "Привет"; - Элемент.Пометка = Истина; - Элемент.Картинка = "---"; - - юТест.ПроверитьРавенство(2, Элемент.Значение, "Значение элемента должно быть установлено"); - юТест.ПроверитьРавенство("Привет", Элемент.Представление, "Представление элемента должно быть установлено"); - юТест.ПроверитьРавенство(Истина, Элемент.Пометка, "Пометка элемента должна быть установлена"); - юТест.ПроверитьРавенство("---", Элемент.Картинка, "Картинка элемента должна быть установлена"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьИтерациюПоСписку() Экспорт - СЗ = Новый СписокЗначений; - СЗ.Добавить(1); - СЗ.Добавить(2); - СЗ.Добавить(3); - СЗ.Добавить(4); - Сч = 0; - Для Каждого Элемент Из СЗ Цикл - Сч = Сч + 1; - КонецЦикла; - - юТест.ПроверитьРавенство(4, Сч); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьДоступПоИндексу() Экспорт - СЗ = Новый СписокЗначений; - СЗ.Добавить(1); - - юТест.ПроверитьРавенство(1, СЗ[0].Значение); - Попытка - СЗ[0] = СЗ[0]; - Исключение - юТест.ПроверитьРавенство("Индексированное значение доступно только для чтения", ИнформацияОбОшибке().Описание); - Возврат; - КонецПопытки; - - ВызватьИсключение "Должно было быть выброшено исключение, но это не произошло"; - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьМетодИндексСпискаЗначений() Экспорт - - СЗ = Новый СписокЗначений; - - СЗ.Добавить(1); - СЗ.Добавить(2); - СЗ.Добавить(3); - СЗ.Добавить(4); - - юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[0]), 0, "Метод Индекс() списка значений"); - юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[1]), 1, "Метод Индекс() списка значений"); - юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[2]), 2, "Метод Индекс() списка значений"); - юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[3]), 3, "Метод Индекс() списка значений"); - - ДругойСЗ = Новый СписокЗначений; - ДругойСЗ.Добавить(1); - - юТест.ПроверитьРавенство(СЗ.Индекс(ДругойСЗ[0]), -1, "Метод Индекс() списка значений: несуществующий элемент"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьМетодВставитьСпискаЗначений() Экспорт - - СЗ = Новый СписокЗначений; - - Э3 = СЗ.Вставить(0, 3); // Вставка в пустой список - Э1 = СЗ.Вставить(0, 1); // Вставка в начало - Э2 = СЗ.Вставить(1, 2); // Вставка в середину - Э4 = СЗ.Вставить(3, 4); // Вставка в конец - - юТест.ПроверитьРавенство(СЗ.Индекс(Э1), 0, "Метод Вставить() списка значений"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э2), 1, "Метод Вставить() списка значений"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э3), 2, "Метод Вставить() списка значений"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э4), 3, "Метод Вставить() списка значений"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьМетодУдалитьСпискаЗначений() Экспорт - - СЗ = Новый СписокЗначений; - Для Инд = 1 По 5 Цикл - СЗ.Добавить(Инд); - КонецЦикла; - Э1 = СЗ[0]; - Э2 = СЗ[1]; - Э3 = СЗ[2]; - Э4 = СЗ[3]; - Э5 = СЗ[4]; - - СЗ.Удалить(1); - юТест.ПроверитьРавенство(СЗ.Количество(),4, "Удаление по индексу"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э2),-1, "Удаление по индексу"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э4),2, "Удаление по индексу"); - - СЗ.Удалить(Э1); - юТест.ПроверитьРавенство(СЗ.Количество(),3, "Удаление элемента"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э1),-1, "Удаление элемента"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э4),1, "Удаление элемента"); - - СЗ.Удалить("1"); - юТест.ПроверитьРавенство(СЗ.Количество(),2, "Удаление по индексу типа строка"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э4),-1, "Удаление по индексу типа строка"); - юТест.ПроверитьРавенство(СЗ.Индекс(Э5),1, "Удаление по индексу типа строка"); - - // Проверим ошибки - Ошибка = "Удаление с неверным индексом"; - Попытка - СЗ.Удалить(10); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Значение индекса выходит за пределы диапазона"); - - Ошибка = "Удаление несуществующего элемента"; - ДругойСЗ = Новый СписокЗначений; - ДругойСЗ.Добавить(1); - Попытка - СЗ.Удалить(ДругойСЗ[0]); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Элемент не принадлежит списку значений"); - - Ошибка = "Удаление с неверным типом параметра"; - Попытка - СЗ.Удалить("ё"); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Неверный тип аргумента"); - -КонецПроцедуры - - -Функция ПроверитьПорядок(Знач СЗ, Знач П1, - Знач П2 = Неопределено, - Знач П3 = Неопределено, - Знач П4 = Неопределено, - Знач П5 = Неопределено) - - Массив = Новый Массив; - Массив.Добавить(П1); - - Если П2 <> Неопределено Тогда - Массив.Добавить(П2); - КонецЕсли; - Если П3 <> Неопределено Тогда - Массив.Добавить(П3); - КонецЕсли; - Если П4 <> Неопределено Тогда - Массив.Добавить(П4); - КонецЕсли; - Если П5 <> Неопределено Тогда - Массив.Добавить(П5); - КонецЕсли; - - Для Инд = 0 По Массив.ВГраница() Цикл - - Если СЗ[Инд].Значение <> Массив[Инд] Тогда - Возврат Ложь; - КонецЕсли; - - КонецЦикла; - - Возврат Истина; - -КонецФункции - -Процедура ТестДолжен_ПроверитьМетодСдвинутьСпискаЗначений() Экспорт - - СЗ = Новый СписокЗначений; - - Для Инд = 1 По 5 Цикл - СЗ.Добавить(Инд); - КонецЦикла; - - С1 = СЗ[0]; - С2 = СЗ[1]; - С3 = СЗ[2]; - С4 = СЗ[3]; - С5 = СЗ[4]; - - // Проверим обычное перемещение - СЗ.Сдвинуть(С2, -1); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 3, 4, 5), "Сдвиг вверх"); - - СЗ.Сдвинуть(С1, 2); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 3, 4, 1, 5), "Сдвиг вниз"); - - СЗ.Сдвинуть(3, -2); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 3, 4, 5), "Сдвиг по индексу вверх"); - - СЗ.Сдвинуть(1, 2); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 3, 4, 1, 5), "Сдвиг по индексу вниз"); - - // Проверим приведение типа - СЗ.Сдвинуть("2", 2); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 3, 1, 5, 4), "Сдвиг, индекс - строка"); - - СЗ.Сдвинуть("1", "2"); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 5, 3, 4), "Сдвиг, индекс и смещение - строки"); - - // Проверим крайние случаи - СЗ.Сдвинуть(0, 4); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 5, 3, 4, 2), "Сдвиг по индексу вниз"); - - СЗ.Сдвинуть(4, -4); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 5, 3, 4), "Сдвиг по индексу вниз"); - - // Проверим ошибки - Ошибка = "Сдвиг за пределы вниз"; - Попытка - СЗ.Сдвинуть(0, 5); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Неверное значение аргумента номер 2"); - - Ошибка = "Сдвиг за пределы вверх"; - Попытка - СЗ.Сдвинуть(1, -3); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Неверное значение аргумента номер 2"); - - Ошибка = "Сдвиг с неверным индексом"; - Попытка - СЗ.Сдвинуть(10, 2); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Значение индекса выходит за пределы диапазона"); - - Ошибка = "Сдвиг несуществующего элемента"; - ДругойСЗ = Новый СписокЗначений; - ДругойСЗ.Добавить(1); - Попытка - СЗ.Сдвинуть(ДругойСЗ[0], 2); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Элемент не принадлежит списку значений"); - - Ошибка = "Сдвиг с неверным типом параметра"; - Попытка - СЗ.Сдвинуть("ё", 2); - Исключение - Ошибка = ИнформацияОбОшибке().Описание; - КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Неверный тип аргумента"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьМетодСортироватьПоЗначениюСпискаЗначений() Экспорт - - СЗ = Новый СписокЗначений; - СЗ.Добавить(4); - СЗ.Добавить(3); - СЗ.Добавить(2); - СЗ.Добавить(1); - - СЗ.СортироватьПоЗначению(); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 2, 3, 4), "Сортировка по-умолчанию"); - - СЗ.СортироватьПоЗначению(НаправлениеСортировки.Убыв); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 4, 3, 2, 1), "Сортировка по убыванию"); - - СЗ.СортироватьПоЗначению(НаправлениеСортировки.Возр); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 2, 3, 4), "Сортировка по возрастанию"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьМетодСортироватьПоПредставлениюСпискаЗначений() Экспорт - - СЗ = Новый СписокЗначений; - СЗ.Добавить(4,"14"); - СЗ.Добавить(3,"3"); - СЗ.Добавить(2,"2"); - СЗ.Добавить(1,"1"); - - СЗ.СортироватьПоПредставлению(); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 4, 2, 3), "Сортировка по-умолчанию"); - - СЗ.СортироватьПоПредставлению(НаправлениеСортировки.Убыв); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 3, 2, 4, 1), "Сортировка по убыванию"); - - СЗ.СортироватьПоПредставлению(НаправлениеСортировки.Возр); - юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 4, 2, 3), "Сортировка по возрастанию"); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьМетодНайтиСпискаЗначений() Экспорт - - СЗ = Новый СписокЗначений; - Э0 = СЗ.Добавить(1.2); - Э1 = СЗ.Добавить("абв"); - Э2 = СЗ.Добавить('20161009'); - Э3 = СЗ.Добавить(Истина); - Э4 = СЗ.Добавить(Null); - Э5 = СЗ.Добавить(Неопределено); - - юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(1.2) , Э0, "Поиск числа"); - юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению("абв") , Э1, "Поиск строки"); - юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению("АБВ") , Неопределено, "Поиск строки в другом регистре"); - юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению('20161009') , Э2, "Поиск даты"); - юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(Истина) , Э3, "Поиск Истины"); - юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(Null) , Э4, "Поиск Null"); - юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(Неопределено), Э5, "Поиск Неопределено"); - -КонецПроцедуры +Перем юТест; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьДобавлениеЭлементов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьДоступКСвойствамЭлементаСписка"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗаписьСвойствЭлементаСписка"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтерациюПоСписку"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьДоступПоИндексу"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодИндексСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодВставитьСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСдвинутьСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСортироватьПоЗначениюСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСортироватьПоПредставлениюСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодНайтиСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодУдалитьСпискаЗначений"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПреобразованиеЭлементаСпискаВСтроку"); + Возврат ВсеТесты; + +КонецФункции + +Процедура ТестДолжен_ПроверитьСозданиеСпискаЗначений() Экспорт + СЗ = Новый СписокЗначений; + юТест.ПроверитьРавенство(Тип("СписокЗначений"), ТипЗнч(СЗ)); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьДобавлениеЭлементов() Экспорт + СЗ = Новый СписокЗначений; + СЗ.Добавить("Один"); + СЗ.Добавить("Два"); + юТест.ПроверитьРавенство(2, СЗ.Количество()); + юТест.ПроверитьРавенство(Тип("ЭлементСпискаЗначений"), ТипЗнч(СЗ.Получить(0))); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьДоступКСвойствамЭлементаСписка() Экспорт + + СЗ = Новый СписокЗначений; + СЗ.Добавить(1,"Представление"); + СЗ.Добавить(2,"Представление2", Истина); + СЗ.Добавить(3,"Представление3", Истина, "Тут должна быть картинка, но сейчас может быть любое значение"); + СЗ.Добавить(1); + + Элемент = СЗ[0]; + юТест.ПроверитьРавенство(1, Элемент.Значение, "Значение элемента 0 должно быть установлено"); + юТест.ПроверитьРавенство("Представление", Элемент.Представление, "Представление элемента 0 должно быть установлено"); + юТест.ПроверитьРавенство(Ложь, Элемент.Пометка, "Пометка элемента 0 не должна быть установлена"); + юТест.ПроверитьРавенство(Неопределено, Элемент.Картинка, "Картинка элемента 0 не должна быть установлена"); + + Элемент = СЗ[1]; + юТест.ПроверитьРавенство(2, Элемент.Значение, "Значение элемента 1 должно быть установлено"); + юТест.ПроверитьРавенство("Представление2", Элемент.Представление, "Представление элемента 1 должно быть установлено"); + юТест.ПроверитьРавенство(Истина, Элемент.Пометка, "Пометка элемента 1 должна быть установлена"); + + Элемент = СЗ[2]; + юТест.ПроверитьРавенство(3, Элемент.Значение, "Значение элемента 2 должно быть установлено"); + юТест.ПроверитьРавенство("Представление3", Элемент.Представление, "Представление элемента 2 должно быть установлено"); + юТест.ПроверитьРавенство(Истина, Элемент.Пометка, "Пометка элемента 2 должна быть установлена"); + юТест.ПроверитьРавенство("Тут должна быть картинка, но сейчас может быть любое значение", Элемент.Картинка, "Картинка элемента 2 должна быть установлена"); + + юТест.ПроверитьРавенство("", СЗ[3].Представление, "Должно быть пустое представление по умолчанию"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗаписьСвойствЭлементаСписка() Экспорт + СЗ = Новый СписокЗначений; + Элемент = СЗ.Добавить(1); + + Элемент.Значение = 2; + Элемент.Представление = "Привет"; + Элемент.Пометка = Истина; + Элемент.Картинка = "---"; + + юТест.ПроверитьРавенство(2, Элемент.Значение, "Значение элемента должно быть установлено"); + юТест.ПроверитьРавенство("Привет", Элемент.Представление, "Представление элемента должно быть установлено"); + юТест.ПроверитьРавенство(Истина, Элемент.Пометка, "Пометка элемента должна быть установлена"); + юТест.ПроверитьРавенство("---", Элемент.Картинка, "Картинка элемента должна быть установлена"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтерациюПоСписку() Экспорт + СЗ = Новый СписокЗначений; + СЗ.Добавить(1); + СЗ.Добавить(2); + СЗ.Добавить(3); + СЗ.Добавить(4); + Сч = 0; + Для Каждого Элемент Из СЗ Цикл + Сч = Сч + 1; + КонецЦикла; + + юТест.ПроверитьРавенство(4, Сч); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьДоступПоИндексу() Экспорт + СЗ = Новый СписокЗначений; + СЗ.Добавить(1); + + юТест.ПроверитьРавенство(1, СЗ[0].Значение); + Попытка + СЗ[0] = СЗ[0]; + Исключение + юТест.ПроверитьРавенство("Индексированное значение доступно только для чтения", ИнформацияОбОшибке().Описание); + Возврат; + КонецПопытки; + + ВызватьИсключение "Должно было быть выброшено исключение, но это не произошло"; + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьМетодИндексСпискаЗначений() Экспорт + + СЗ = Новый СписокЗначений; + + СЗ.Добавить(1); + СЗ.Добавить(2); + СЗ.Добавить(3); + СЗ.Добавить(4); + + юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[0]), 0, "Метод Индекс() списка значений"); + юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[1]), 1, "Метод Индекс() списка значений"); + юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[2]), 2, "Метод Индекс() списка значений"); + юТест.ПроверитьРавенство(СЗ.Индекс(СЗ[3]), 3, "Метод Индекс() списка значений"); + + ДругойСЗ = Новый СписокЗначений; + ДругойСЗ.Добавить(1); + + юТест.ПроверитьРавенство(СЗ.Индекс(ДругойСЗ[0]), -1, "Метод Индекс() списка значений: несуществующий элемент"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьМетодВставитьСпискаЗначений() Экспорт + + СЗ = Новый СписокЗначений; + + Э3 = СЗ.Вставить(0, 3); // Вставка в пустой список + Э1 = СЗ.Вставить(0, 1); // Вставка в начало + Э2 = СЗ.Вставить(1, 2); // Вставка в середину + Э4 = СЗ.Вставить(3, 4); // Вставка в конец + + юТест.ПроверитьРавенство(СЗ.Индекс(Э1), 0, "Метод Вставить() списка значений"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э2), 1, "Метод Вставить() списка значений"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э3), 2, "Метод Вставить() списка значений"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э4), 3, "Метод Вставить() списка значений"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьМетодУдалитьСпискаЗначений() Экспорт + + СЗ = Новый СписокЗначений; + Для Инд = 1 По 5 Цикл + СЗ.Добавить(Инд); + КонецЦикла; + Э1 = СЗ[0]; + Э2 = СЗ[1]; + Э3 = СЗ[2]; + Э4 = СЗ[3]; + Э5 = СЗ[4]; + + СЗ.Удалить(1); + юТест.ПроверитьРавенство(СЗ.Количество(),4, "Удаление по индексу"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э2),-1, "Удаление по индексу"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э4),2, "Удаление по индексу"); + + СЗ.Удалить(Э1); + юТест.ПроверитьРавенство(СЗ.Количество(),3, "Удаление элемента"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э1),-1, "Удаление элемента"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э4),1, "Удаление элемента"); + + СЗ.Удалить("1"); + юТест.ПроверитьРавенство(СЗ.Количество(),2, "Удаление по индексу типа строка"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э4),-1, "Удаление по индексу типа строка"); + юТест.ПроверитьРавенство(СЗ.Индекс(Э5),1, "Удаление по индексу типа строка"); + + // Проверим ошибки + Ошибка = "Удаление с неверным индексом"; + Попытка + СЗ.Удалить(10); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Значение индекса выходит за пределы диапазона"); + + Ошибка = "Удаление несуществующего элемента"; + ДругойСЗ = Новый СписокЗначений; + ДругойСЗ.Добавить(1); + Попытка + СЗ.Удалить(ДругойСЗ[0]); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Элемент не принадлежит списку значений"); + + Ошибка = "Удаление с неверным типом параметра"; + Попытка + СЗ.Удалить("ё"); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Неверный тип аргумента"); + +КонецПроцедуры + + +Функция ПроверитьПорядок(Знач СЗ, Знач П1, + Знач П2 = Неопределено, + Знач П3 = Неопределено, + Знач П4 = Неопределено, + Знач П5 = Неопределено) + + Массив = Новый Массив; + Массив.Добавить(П1); + + Если П2 <> Неопределено Тогда + Массив.Добавить(П2); + КонецЕсли; + Если П3 <> Неопределено Тогда + Массив.Добавить(П3); + КонецЕсли; + Если П4 <> Неопределено Тогда + Массив.Добавить(П4); + КонецЕсли; + Если П5 <> Неопределено Тогда + Массив.Добавить(П5); + КонецЕсли; + + Для Инд = 0 По Массив.ВГраница() Цикл + + Если СЗ[Инд].Значение <> Массив[Инд] Тогда + Возврат Ложь; + КонецЕсли; + + КонецЦикла; + + Возврат Истина; + +КонецФункции + +Процедура ТестДолжен_ПроверитьМетодСдвинутьСпискаЗначений() Экспорт + + СЗ = Новый СписокЗначений; + + Для Инд = 1 По 5 Цикл + СЗ.Добавить(Инд); + КонецЦикла; + + С1 = СЗ[0]; + С2 = СЗ[1]; + С3 = СЗ[2]; + С4 = СЗ[3]; + С5 = СЗ[4]; + + // Проверим обычное перемещение + СЗ.Сдвинуть(С2, -1); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 3, 4, 5), "Сдвиг вверх"); + + СЗ.Сдвинуть(С1, 2); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 3, 4, 1, 5), "Сдвиг вниз"); + + СЗ.Сдвинуть(3, -2); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 3, 4, 5), "Сдвиг по индексу вверх"); + + СЗ.Сдвинуть(1, 2); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 3, 4, 1, 5), "Сдвиг по индексу вниз"); + + // Проверим приведение типа + СЗ.Сдвинуть("2", 2); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 3, 1, 5, 4), "Сдвиг, индекс - строка"); + + СЗ.Сдвинуть("1", "2"); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 5, 3, 4), "Сдвиг, индекс и смещение - строки"); + + // Проверим крайние случаи + СЗ.Сдвинуть(0, 4); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 5, 3, 4, 2), "Сдвиг по индексу вниз"); + + СЗ.Сдвинуть(4, -4); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 2, 1, 5, 3, 4), "Сдвиг по индексу вниз"); + + // Проверим ошибки + Ошибка = "Сдвиг за пределы вниз"; + Попытка + СЗ.Сдвинуть(0, 5); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Неверное значение аргумента номер 2"); + + Ошибка = "Сдвиг за пределы вверх"; + Попытка + СЗ.Сдвинуть(1, -3); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Неверное значение аргумента номер 2"); + + Ошибка = "Сдвиг с неверным индексом"; + Попытка + СЗ.Сдвинуть(10, 2); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Значение индекса выходит за пределы диапазона"); + + Ошибка = "Сдвиг несуществующего элемента"; + ДругойСЗ = Новый СписокЗначений; + ДругойСЗ.Добавить(1); + Попытка + СЗ.Сдвинуть(ДругойСЗ[0], 2); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Элемент не принадлежит списку значений"); + + Ошибка = "Сдвиг с неверным типом параметра"; + Попытка + СЗ.Сдвинуть("ё", 2); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + КонецПопытки; + юТест.ПроверитьРавенство(Ошибка, "Неверный тип аргумента"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьМетодСортироватьПоЗначениюСпискаЗначений() Экспорт + + СЗ = Новый СписокЗначений; + СЗ.Добавить(4); + СЗ.Добавить(3); + СЗ.Добавить(2); + СЗ.Добавить(1); + + СЗ.СортироватьПоЗначению(); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 2, 3, 4), "Сортировка по-умолчанию"); + + СЗ.СортироватьПоЗначению(НаправлениеСортировки.Убыв); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 4, 3, 2, 1), "Сортировка по убыванию"); + + СЗ.СортироватьПоЗначению(НаправлениеСортировки.Возр); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 2, 3, 4), "Сортировка по возрастанию"); + + СЗ = Новый СписокЗначений; + СЗ.Добавить("аааааб"); + СЗ.Добавить("аааааа"); + СЗ.Добавить("Бааааа"); + + СЗ.СортироватьПоЗначению(); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, "аааааа", "аааааб", "Бааааа"), "Сортировка по регистру"); + +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьМетодСортироватьПоПредставлениюСпискаЗначений() Экспорт + + СЗ = Новый СписокЗначений; + СЗ.Добавить(4,"14"); + СЗ.Добавить(3,"3"); + СЗ.Добавить(2,"2"); + СЗ.Добавить(1,"1"); + + СЗ.СортироватьПоПредставлению(); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 4, 2, 3), "Сортировка по-умолчанию"); + + СЗ.СортироватьПоПредставлению(НаправлениеСортировки.Убыв); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 3, 2, 4, 1), "Сортировка по убыванию"); + + СЗ.СортироватьПоПредставлению(НаправлениеСортировки.Возр); + юТест.ПроверитьИстину(ПроверитьПорядок(СЗ, 1, 4, 2, 3), "Сортировка по возрастанию"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьМетодНайтиСпискаЗначений() Экспорт + + СЗ = Новый СписокЗначений; + Э0 = СЗ.Добавить(1.2); + Э1 = СЗ.Добавить("абв"); + Э2 = СЗ.Добавить('20161009'); + Э3 = СЗ.Добавить(Истина); + Э4 = СЗ.Добавить(Null); + Э5 = СЗ.Добавить(Неопределено); + + юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(1.2) , Э0, "Поиск числа"); + юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению("абв") , Э1, "Поиск строки"); + юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению("АБВ") , Неопределено, "Поиск строки в другом регистре"); + юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению('20161009') , Э2, "Поиск даты"); + юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(Истина) , Э3, "Поиск Истины"); + юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(Null) , Э4, "Поиск Null"); + юТест.ПроверитьРавенство(СЗ.НайтиПоЗначению(Неопределено), Э5, "Поиск Неопределено"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПреобразованиеЭлементаСпискаВСтроку() Экспорт + СЗ = Новый СписокЗначений; + СЗ.Добавить(0); + СЗ.Добавить(1, ""); + СЗ.Добавить(2, Неопределено); + СЗ.Добавить(3, " "); + + юТест.ПроверитьРавенство("0", Строка(СЗ[0]), "Строка, без указания"); + юТест.ПроверитьРавенство("", СЗ[0].Представление, "Представление, без указания"); + юТест.ПроверитьРавенство("1", Строка(СЗ[1]), "Строка, с пустой"); + юТест.ПроверитьРавенство("", СЗ[1].Представление, "Представление, с пустой"); + юТест.ПроверитьРавенство("2", Строка(СЗ[2]), "Строка, Неопределено"); + юТест.ПроверитьРавенство("", СЗ[2].Представление, "Представление, Неопределено"); + юТест.ПроверитьРавенство(" ", Строка(СЗ[3]), "Строка, пробел"); + юТест.ПроверитьРавенство(" ", СЗ[3].Представление, "Представление, пробел"); + +КонецПроцедуры \ No newline at end of file diff --git a/tests/valuetable.os b/tests/valuetable.os index fd165afa2..3dcf51b80 100644 --- a/tests/valuetable.os +++ b/tests/valuetable.os @@ -30,7 +30,9 @@ ВсеТесты.Добавить("ТестДолжен_НайтиНесколькоСтрокВТаблице"); ВсеТесты.Добавить("ТестДолжен_СкопироватьТаблицуПолностью"); ВсеТесты.Добавить("ТестДолжен_СкопироватьТаблицуПоМассивуСтрок"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИсключениеПриКопированииТаблицыПоМассивуСтрок"); ВсеТесты.Добавить("ТестДолжен_СкопироватьТаблицуНесколькоКолонок"); + ВсеТесты.Добавить("ТестДолжен_СкопироватьТаблицуПоОтбору"); ВсеТесты.Добавить("ТестДолжен_ПроверитьСверткуБезУказанияКолонок"); ВсеТесты.Добавить("ТестДолжен_ЗагрузитьКолонку"); @@ -43,6 +45,47 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьСверткуПоКолонкамСуммированияСРазнымиТипами"); ВсеТесты.Добавить("ТестДолжен_ПроверитьСверткуПоОднойКолонкеСРазнымиТипами"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировку_Чисел"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировку_Булевых"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировку_Дат"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировку_Строк"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировку_СтрокРазныхАлфавитов"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировкуНеупорядочиваемыхТипов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировкуРазныхТипов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировкуПоПредставлению"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьВставкуВнеРазмераТаблицы"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИсключениеПриЗагрузкеКолонки"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИсключениеПриНеверномПараметреСортировки"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСортировкуСЛишнимиПробеламиВПараметрах"); + + ВсеТесты.Добавить("ТестДолжен_Проверить_ЗаполнитьЗначения_СПустымСпискомКолонок"); + ВсеТесты.Добавить("ТестДолжен_Проверить_Найти_СПустымСпискомКолонок"); + ВсеТесты.Добавить("ТестДолжен_Проверить_СкопироватьКолонки_СПустымСпискомКолонок"); + ВсеТесты.Добавить("ТестДолжен_Проверить_Скопировать_СПустымСпискомКолонок"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_Строка"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаЧисло"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаДата"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаБезЧисел"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаЧислоБезЧисел"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаДатаБезЧисел"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИтогПоКолонкеБезОписанияТипов_БезЧисел"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьИсключениеПриНеверномИмениКолонкиВОтборе"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗапятуюВИменахКолонок_БезПустых"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗапятуюВИменахКолонок_СПустыми"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИсключениеДляИндекса"); + + ВсеТесты.Добавить("ТестДолжен_ВызватьИсключениеПриДобавленииКолонкиСНевернымИменем"); + ВсеТесты.Добавить("ТестДолжен_ВызватьИсключениеПриВставкеКолонкиСНевернымИменем"); + ВсеТесты.Добавить("ТестДолжен_ВызватьИсключениеНаПоискеИндексаКолонкиСНевернымПримитивнымТипом"); + ВсеТесты.Добавить("ТестДолжен_ВызватьИсключениеНаПоискеИндексаКолонкиСНевернымОбъектнымТипом"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьИсключениеПолученияКолонкиСНевернымНомером"); + + Возврат ВсеТесты; КонецФункции @@ -258,7 +301,7 @@ Исключение Ошибка = ИнформацияОбОшибке().Описание; КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Неверное значение аргумента номер 2"); + юТест.ПроверитьРавенство(Ошибка, "Неправильное смещение внутри коллекции"); Ошибка = "Сдвиг за пределы вверх"; Попытка @@ -266,7 +309,7 @@ Исключение Ошибка = ИнформацияОбОшибке().Описание; КонецПопытки; - юТест.ПроверитьРавенство(Ошибка, "Неверное значение аргумента номер 2"); + юТест.ПроверитьРавенство(Ошибка, "Неправильное смещение внутри коллекции"); Ошибка = "Сдвиг с неверным индексом"; Попытка @@ -518,6 +561,44 @@ КонецПроцедуры +Процедура ТестДолжен_СкопироватьТаблицуПоОтбору() Экспорт + + Т = СоздатьТаблицуСДанными(); + Т.Добавить().Значение = 4; + + Отбор = Новый Структура("Значение", 4); + Т2 = Т.Скопировать(Отбор,"Ключ"); + + юТест.ПроверитьНеравенство(Т, Т2); + юТест.ПроверитьРавенство(2, Т2.Количество()); + юТест.ПроверитьРавенство(1, Т2.Колонки.Количество()); + юТест.ПроверитьРавенство("Ключ4", Т2[0].Ключ); + юТест.ПроверитьРавенство(Неопределено, Т2[1].Ключ); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИсключениеПриКопированииТаблицыПоМассивуСтрок() Экспорт + + Т = СоздатьТаблицуСДанными(); + + Т0 = Новый ТаблицаЗначений(); + Т0.Колонки.Добавить("Тест"); + Т0.Добавить().Тест = 1; + + МассивСтрок = Новый Массив; + МассивСтрок.Добавить(Т0[0]); + + Попытка + Т2 = Т.Скопировать(МассивСтрок); + Исключение + Возврат; + КонецПопытки; + + ВызватьИсключение "Ожидали исключение, но его не было"; + +КонецПроцедуры + + Процедура ТестДолжен_ПроверитьСверткуБезУказанияКолонок() Экспорт Перем Т; @@ -754,7 +835,7 @@ Сч=0; Для каждого Элемент Из СтрРазделить(Строка, ",") Цикл СтрТЗ = ТЗ.Добавить(); - СтрТЗ.Значение1 = ?(Сч=0,Число(Элемент),СокрЛП(Элемент));; + СтрТЗ.Значение1 = ?(Сч=0,Число(Элемент),СокрЛП(Элемент)); СтрТЗ.Количество1 = 1; Сч=(Сч+1)%2; КонецЦикла; @@ -779,3 +860,460 @@ юТест.ПроверитьРавенство( СтрТЗ2[КолонкаСуммы], Количество, СтрШаблон("Сумма колонки %1 для значения %2 типа %3", КолонкаСуммы, Значение, ТипЗнч(Значение)) ); КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСортировку_Чисел() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТЗ.Добавить().Тест = 5; + ТЗ.Добавить().Тест = 10; + ТЗ.Добавить().Тест = -2; + + ТЗ.Сортировать("Тест"); + + юТест.ПроверитьРавенство(ТЗ[0].Тест, -2, "ТЗ[0]"); + юТест.ПроверитьРавенство(ТЗ[1].Тест, 5, "ТЗ[1]"); + юТест.ПроверитьРавенство(ТЗ[2].Тест, 10, "ТЗ[2]"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСортировку_Булевых() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТЗ.Добавить().Тест = Истина; + ТЗ.Добавить().Тест = Ложь; + ТЗ.Добавить().Тест = Истина; + + ТЗ.Сортировать("Тест"); + + юТест.ПроверитьРавенство(ТЗ[0].Тест, Ложь, "ТЗ[0]"); + юТест.ПроверитьРавенство(ТЗ[1].Тест, Истина, "ТЗ[1]"); + юТест.ПроверитьРавенство(ТЗ[2].Тест, Истина, "ТЗ[2]"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСортировку_Дат() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + Сегодня = ТекущаяДата(); + Пустая = '0001-01-01 00:00'; + ТЗ.Добавить().Тест = Сегодня; + ТЗ.Добавить().Тест = Сегодня + 2; + ТЗ.Добавить().Тест = Сегодня - 2; + ТЗ.Добавить().Тест = Пустая; + + ТЗ.Сортировать("Тест"); + + юТест.ПроверитьРавенство(ТЗ[0].Тест, Пустая, "ТЗ[0]"); + юТест.ПроверитьРавенство(ТЗ[1].Тест, Сегодня - 2, "ТЗ[1]"); + юТест.ПроверитьРавенство(ТЗ[2].Тест, Сегодня, "ТЗ[2]"); + юТест.ПроверитьРавенство(ТЗ[3].Тест, Сегодня + 2, "ТЗ[3]"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСортировку_Строк() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТЗ.Добавить().Тест = "Ели"; + ТЗ.Добавить().Тест = "ежи"; + ТЗ.Добавить().Тест = "Ёлки"; + ТЗ.Добавить().Тест = "ёжики"; + + ТЗ.Сортировать("Тест"); + + юТест.ПроверитьРавенство(ТЗ[0].Тест, "ежи", "ТЗ[0]"); + юТест.ПроверитьРавенство(ТЗ[1].Тест, "ёжики", "ТЗ[1]"); + юТест.ПроверитьРавенство(ТЗ[2].Тест, "Ели", "ТЗ[2]"); + юТест.ПроверитьРавенство(ТЗ[3].Тест, "Ёлки", "ТЗ[3]"); +КонецПроцедуры + + +// к issue #1577 +Процедура ТестДолжен_ПроверитьСортировкуНеупорядочиваемыхТипов() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТЗ.Добавить().Тест = Неопределено; + ТЗ.Добавить().Тест = Неопределено; + + ТЗ.Сортировать("Тест"); // не должно упасть + + юТест.ПроверитьРавенство(ТЗ[0].Тест, Неопределено); + юТест.ПроверитьРавенство(ТЗ[1].Тест, Неопределено); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСортировкуРазныхТипов() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТЗ.Добавить().Тест = Тип("Строка"); + ТЗ.Добавить().Тест = "Стр2"; + ТЗ.Добавить().Тест = "СтрЪ"; + ТЗ.Добавить().Тест = Новый Структура; + ТЗ.Добавить().Тест = Новый Массив; + ТЗ.Добавить().Тест = null; + ТЗ.Добавить().Тест = Тип("Булево"); + ТЗ.Добавить().Тест = Тип("Массив"); + + ТЗ.Сортировать("Тест"); // не должно упасть + + юТест.ПроверитьРавенство(ТЗ[0].Тест, Null); + юТест.ПроверитьРавенство(ТЗ[1].Тест, "Стр2"); + юТест.ПроверитьРавенство(ТЗ[2].Тест, "СтрЪ"); + юТест.ПроверитьРавенство(ТипЗнч(ТЗ[3].Тест), Тип("Массив")); + юТест.ПроверитьРавенство(ТипЗнч(ТЗ[4].Тест), Тип("Структура")); + юТест.ПроверитьРавенство(ТЗ[5].Тест, Тип("Булево")); + юТест.ПроверитьРавенство(ТЗ[6].Тест, Тип("Массив")); + юТест.ПроверитьРавенство(ТЗ[7].Тест, Тип("Строка")); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСортировкуПоПредставлению() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТекстКласса = + "Процедура ОбработкаПолученияПредставления(Представление, СтандартнаяОбработка) + | СтандартнаяОбработка = Ложь; + | Представление = ""Представление0""; + |КонецПроцедуры"; + КлассИзСтроки0 = ЗагрузитьСценарийИзСтроки(ТекстКласса); + + ТекстКласса = + "Процедура ОбработкаПолученияПредставления(Представление, СтандартнаяОбработка) + | СтандартнаяОбработка = Ложь; + | Представление = ""Представление1""; + |КонецПроцедуры"; + КлассИзСтроки1 = ЗагрузитьСценарийИзСтроки(ТекстКласса); + + ТекстКласса = + "Процедура ОбработкаПолученияПредставления(Представление, СтандартнаяОбработка) + | СтандартнаяОбработка = Ложь; + | Представление = ""Представление3""; + |КонецПроцедуры"; + КлассИзСтроки3 = ЗагрузитьСценарийИзСтроки(ТекстКласса); + + ТЗ.Добавить().Тест = КлассИзСтроки3; + ТЗ.Добавить().Тест = "Представление2"; + ТЗ.Добавить().Тест = КлассИзСтроки0; + ТЗ.Добавить().Тест = КлассИзСтроки1; + + ТЗ.Сортировать("Тест"); + юТест.ПроверитьРавенство(ТЗ[0].Тест, "Представление2", "строка"); + юТест.ПроверитьРавенство(Строка(ТЗ[1].Тест), "Представление0", "сценарий 0"); + юТест.ПроверитьРавенство(Строка(ТЗ[2].Тест), "Представление1", "сценарий 1"); + юТест.ПроверитьРавенство(Строка(ТЗ[3].Тест), "Представление3", "сценарий 3"); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСортировку_СтрокРазныхАлфавитов() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТЗ.Добавить().Тест = "Е"; // cyrillic + ТЗ.Добавить().Тест = "E"; // latin + ТЗ.Добавить().Тест = "Ε"; // greek + ТЗ.Добавить().Тест = "∃"; // math + ТЗ.Добавить().Тест = "E"; // full-width + + ТЗ.Сортировать("Тест"); + + юТест.ПроверитьРавенство(КодСимвола(ТЗ[0].Тест,1), 8707, "math"); + юТест.ПроверитьРавенство(КодСимвола(ТЗ[1].Тест,1), 69, "latin"); + юТест.ПроверитьРавенство(КодСимвола(ТЗ[2].Тест,1), 65317, "full-width"); + юТест.ПроверитьРавенство(КодСимвола(ТЗ[3].Тест,1), 917, "greek"); + юТест.ПроверитьРавенство(КодСимвола(ТЗ[4].Тест,1), 1045, "cyrillic"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВставкуВнеРазмераТаблицы() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + ТЗ.Добавить().Тест = -1; + ТЗ.Вставить(8).Тест = -2; + + юТест.ПроверитьРавенство(ТЗ[1].Тест, -2); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИсключениеПриЗагрузкеКолонки() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + + Попытка + ТЗ.ЗагрузитьКолонку(8,"Тест"); + Исключение + юТест.ПроверитьРавенство(Найти(ИнформацияОбОшибке().Описание,"NullReferenceException"), 0, "NRE"); + Возврат; + КонецПопытки; + + ВызватьИсключение "Ожидали исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИсключениеПриНеверномПараметреСортировки() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = 1; + ТЗ.Добавить().Тест = 2; + + Попытка + ТЗ.Сортировать("Тест УБЫВ и_лишнее_через_пробел"); + Исключение + Возврат; + КонецПопытки; + + ВызватьИсключение "Ожидали исключение, но его не было"; +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьСортировкуСЛишнимиПробеламиВПараметрах() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Колонки.Добавить("Тест2"); + ТЗ.Добавить().Тест = 1; + ТЗ.Добавить().Тест2 = 2; + + ТЗ.Сортировать(" Тест УБЫВ , Тест2 ВОЗР "); +КонецПроцедуры + + +Процедура ТестДолжен_Проверить_ЗаполнитьЗначения_СПустымСпискомКолонок() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = 1; + ТЗ.ЗаполнитьЗначения(3,""); + + юТест.ПроверитьРавенство(ТЗ[0].Тест, 3); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_Найти_СПустымСпискомКолонок() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = -3; + ТЗ.Добавить().Тест = 7; + Рез = ТЗ.Найти(7,""); + + юТест.ПроверитьНеРавенство(Рез, Неопределено); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_СкопироватьКолонки_СПустымСпискомКолонок() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = 1; + ТЗ2 = ТЗ.СкопироватьКолонки(); + + юТест.ПроверитьРавенство(ТЗ2.Колонки.Количество(), 1); + юТест.ПроверитьРавенство(ТЗ2.Колонки[0].Имя, "Тест"); +КонецПроцедуры + +Процедура ТестДолжен_Проверить_Скопировать_СПустымСпискомКолонок() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = 1; + ТЗ.Добавить().Тест = 2; + ТЗ2 = ТЗ.Скопировать(,""); + + юТест.ПроверитьРавенство(ТЗ2.Количество(), 2); + юТест.ПроверитьРавенство(ТЗ2.Колонки.Количество(), 1); + юТест.ПроверитьРавенство(ТЗ2.Колонки[0].Имя, "Тест"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_Строка() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест", Новый ОписаниеТипов("Строка")); + ТЗ.Добавить().Тест = -3; + ТЗ.Добавить().Тест = "7"; + ТЗ.Добавить().Тест = "ц5"; + + юТест.ПроверитьРавенство(ТЗ.Итог("Тест"), 4); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаЧисло() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест", Новый ОписаниеТипов("Строка,Число")); + ТЗ.Добавить().Тест = -3; + ТЗ.Добавить().Тест = "7"; + ТЗ.Добавить().Тест = "ц5"; + + юТест.ПроверитьРавенство(ТЗ.Итог("Тест"), -3); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаДата() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест", Новый ОписаниеТипов("Строка,Дата")); + ТЗ.Добавить().Тест = -3; + ТЗ.Добавить().Тест = "7"; + ТЗ.Добавить().Тест = "ц5"; + + юТест.ПроверитьРавенство(ТЗ.Итог("Тест"), Неопределено); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаБезЧисел() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест", Новый ОписаниеТипов("Строка")); + ТЗ.Добавить().Тест = "без"; + ТЗ.Добавить().Тест = "числовых"; + ТЗ.Добавить().Тест = "значений"; + + юТест.ПроверитьРавенство(ТЗ.Итог("Тест"), 0); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаЧислоБезЧисел() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест", Новый ОписаниеТипов("Строка,Число")); + ТЗ.Добавить().Тест = "без"; + ТЗ.Добавить().Тест = "числовых"; + ТЗ.Добавить().Тест = "значений"; + + юТест.ПроверитьРавенство(ТЗ.Итог("Тест"), 0); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтогПоКолонкеСОписаниемТипов_СтрокаДатаБезЧисел() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест", Новый ОписаниеТипов("Строка,Дата")); + ТЗ.Добавить().Тест = "без"; + ТЗ.Добавить().Тест = "числовых"; + ТЗ.Добавить().Тест = "значений"; + + юТест.ПроверитьРавенство(ТЗ.Итог("Тест"), Неопределено); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИтогПоКолонкеБезОписанияТипов_БезЧисел() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = "без"; + ТЗ.Добавить().Тест = "числовых"; + ТЗ.Добавить().Тест = "значений"; + + юТест.ПроверитьРавенство(ТЗ.Итог("Тест"), 0); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИсключениеПриНеверномИмениКолонкиВОтборе() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = -3; + ТЗ.Добавить().Тест = 7; + Фильтр = Новый Структура("Тест,Тест2",7,7); + + Попытка + Рез = ТЗ.НайтиСтроки(Фильтр); + Исключение + юТест.ПроверитьРавенство(Найти(ИнформацияОбОшибке().Описание,"NullException"), 0, "ArgumentNull"); + Возврат; + КонецПопытки; + + ВызватьИсключение "Ожидали исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗапятуюВИменахКолонок_БезПустых() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Колонки.Добавить("Тест2"); + ТЗ.Добавить().Тест = -3; + ТЗ.Добавить().Тест = 7; + ТЗ.Свернуть(", Тест" , "Тест2,"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗапятуюВИменахКолонок_СПустыми() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Колонки.Добавить("Тест2"); + ТЗ.Добавить().Тест = -3; + ТЗ.Добавить().Тест = 7; + + Попытка + ТЗ.Свернуть("Тест, " , "Тест2"); + Исключение + Возврат; + КонецПопытки; + + ВызватьИсключение "Ожидали исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИсключениеДляИндекса() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + ТЗ.Добавить().Тест = -1; + + Попытка + Индекс = ТЗ.Индекс("Тест"); + Исключение + Возврат; + КонецПопытки; + + ВызватьИсключение "Ожидали исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ВызватьИсключениеПриДобавленииКолонкиСНевернымИменем() Экспорт + ТЗ = Новый ТаблицаЗначений(); + Попытка + ТЗ.Колонки.Добавить("!@#"); + Исключение + Возврат; + КонецПопытки; + + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ВызватьИсключениеПриВставкеКолонкиСНевернымИменем() Экспорт + ТЗ = Новый ТаблицаЗначений(); + Попытка + ТЗ.Колонки.Вставить(0,"!@#"); + Исключение + Возврат; + КонецПопытки; + + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ВызватьИсключениеНаПоискеИндексаКолонкиСНевернымПримитивнымТипом() Экспорт + ТЗ = Новый ТаблицаЗначений(); + Попытка + ТЗ.Колонки.Индекс(0); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + юТест.ПроверитьНеРавенство(Найти(Ошибка,"Неверный тип аргумента"), 0, "Неверный вид ошибки: "+Ошибка); + Возврат; + КонецПопытки; + + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ВызватьИсключениеНаПоискеИндексаКолонкиСНевернымОбъектнымТипом() Экспорт + ТЗ = Новый ТаблицаЗначений(); + Попытка + ТЗ.Колонки.Индекс(ТЗ); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + юТест.ПроверитьНеРавенство(Найти(Ошибка,"Неверный тип аргумента"), 0, "Неверный вид ошибки: "+Ошибка); + Возврат; + КонецПопытки; + + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИсключениеПолученияКолонкиСНевернымНомером() Экспорт + ТЗ = Новый ТаблицаЗначений(); + ТЗ.Колонки.Добавить("Тест"); + СтрокаТЗ = ТЗ.Добавить(); + + БылоИсключение = Ложь; + Попытка + Рез = СтрокаТЗ.Получить(-1); + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + юТест.ПроверитьНеРавенство(Найти(Ошибка,"Значение индекса выходит за пределы"), 0, "Неверный вид ошибки: "+Ошибка); + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Получение колонки с неверным номером"); + + БылоИсключение = Ложь; + Попытка + Рез = СтрокаТЗ[-1]; + Исключение + Ошибка = ИнформацияОбОшибке().Описание; + юТест.ПроверитьНеРавенство(Найти(Ошибка,"Значение индекса выходит за пределы"), 0, "Неверный вид ошибки: "+Ошибка); + БылоИсключение = Истина; + КонецПопытки; + юТест.ПроверитьИстину(БылоИсключение, "Получение колонки по неверному индексу"); + +КонецПроцедуры diff --git a/tests/xmlread.os b/tests/xmlread.os index 21146f2da..b4c874b97 100644 --- a/tests/xmlread.os +++ b/tests/xmlread.os @@ -1,7 +1,6 @@ /////////////////////////////////////////////////////////////////////// // // Приемочные тесты объекта ЧтениеXML -// // /////////////////////////////////////////////////////////////////////// @@ -11,7 +10,7 @@ // Программный интерфейс Функция Версия() Экспорт - Возврат "0.1"; + Возврат "0.3"; КонецФункции Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт @@ -19,6 +18,11 @@ юТест = ЮнитТестирование; ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_СоздатьПараметрыЧтенияПоУмолчанию"); + ВсеТесты.Добавить("ТестДолжен_СоздатьПараметрыЧтения"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьПараметрыЧтенияНеинициализированного"); + ВсеТесты.Добавить("ТестДолжен_ПолучитьПараметрыЧтенияИнициализированного"); ВсеТесты.Добавить("ТестДолжен_ПрочитатьЭлементыИзСтроки"); ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтениеПустыхЭлементов"); @@ -28,9 +32,90 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтоПропускПереходитНаКонецЭлемента"); ВсеТесты.Добавить("ТестДолжен_ПроверитьРаботуКонтекстаПространствИмен"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеСПараметрами_Комментарий"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеСПараметрами_ПробельныеСимволы1"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеСПараметрами_ПробельныеСимволы2"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьСозданиеСПараметрами_ПробельныеСимволы3"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереключениеПробельныхСимволов"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереключениеПробельныхСимволов2"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПереключениеПробельныхСимволов3"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗначенияСвойствНеинициализированного"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗначенияСвойствИнициализированного"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьЗначенияСвойствАтрибута"); + Возврат ВсеТесты; КонецФункции +Процедура ТестДолжен_СоздатьПараметрыЧтенияПоУмолчанию() Экспорт + ПараметрыЧтения = Новый ПараметрыЧтенияXML(); + + юТест.ПроверитьРавенство(ПараметрыЧтения.Версия,"1.0","Версия"); + юТест.ПроверитьРавенство(ПараметрыЧтения.Язык,"","Язык"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ПробельныеСимволы,ПробельныеСимволыXML.ПоУмолчанию,"ПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ТипПроверкиПравильности,ТипПроверкиXML.НетПроверки,"ТипПроверкиПравильности"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьОбъявлениеXML,Истина,"ИгнорироватьОбъявлениеXML"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьТипДокумента,Истина,"ИгнорироватьТипДокумента"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьИнструкцииОбработки,Ложь,"ИгнорироватьИнструкцииОбработки"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьКомментарии,Ложь,"ИгнорироватьКомментарии"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьПробельныеСимволы,Истина,"ИгнорироватьПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.СекцииCDATAКакТекст,Ложь,"СекцииCDATAКакТекст"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИспользоватьИгнорируемыеПробельныеСимволы,Ложь,"ИспользоватьИгнорируемыеПробельныеСимволы"); +КонецПроцедуры + +Процедура ТестДолжен_СоздатьПараметрыЧтения() Экспорт + ПараметрыЧтения = Новый ПараметрыЧтенияXML( "1.1", "ru", ПробельныеСимволыXML.Сохранять, + ТипПроверкиXML.СхемаXML, Ложь, Ложь, Истина, Истина, Ложь, Истина, Истина ); + + юТест.ПроверитьРавенство(ПараметрыЧтения.Версия,"1.1","Версия"); + юТест.ПроверитьРавенство(ПараметрыЧтения.Язык,"ru","Язык"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ПробельныеСимволы,ПробельныеСимволыXML.Сохранять,"ПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ТипПроверкиПравильности,ТипПроверкиXML.СхемаXML,"ТипПроверкиПравильности"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьОбъявлениеXML,Ложь,"ИгнорироватьОбъявлениеXML"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьТипДокумента,Ложь,"ИгнорироватьТипДокумента"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьИнструкцииОбработки,Истина,"ИгнорироватьИнструкцииОбработки"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьКомментарии,Истина,"ИгнорироватьКомментарии"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьПробельныеСимволы,Ложь,"ИгнорироватьПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.СекцииCDATAКакТекст,Истина,"СекцииCDATAКакТекст"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИспользоватьИгнорируемыеПробельныеСимволы,Истина,"ИспользоватьИгнорируемыеПробельныеСимволы"); +КонецПроцедуры + +Процедура ТестДолжен_ПолучитьПараметрыЧтенияНеинициализированного() Экспорт + ЧтениеXML = Новый ЧтениеXML; + ПараметрыЧтения = ЧтениеXML.Параметры; + + юТест.ПроверитьРавенство(ПараметрыЧтения.Версия,"1.0","Версия"); + юТест.ПроверитьРавенство(ПараметрыЧтения.Язык,"","Язык"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ПробельныеСимволы,ПробельныеСимволыXML.ПоУмолчанию,"ПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ТипПроверкиПравильности,ТипПроверкиXML.НетПроверки,"ТипПроверкиПравильности"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьОбъявлениеXML,Истина,"ИгнорироватьОбъявлениеXML"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьТипДокумента,Истина,"ИгнорироватьТипДокумента"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьИнструкцииОбработки,Ложь,"ИгнорироватьИнструкцииОбработки"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьКомментарии,Ложь,"ИгнорироватьКомментарии"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьПробельныеСимволы,Истина,"ИгнорироватьПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.СекцииCDATAКакТекст,Ложь,"СекцииCDATAКакТекст"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИспользоватьИгнорируемыеПробельныеСимволы,Ложь,"ИспользоватьИгнорируемыеПробельныеСимволы"); +КонецПроцедуры + +Процедура ТестДолжен_ПолучитьПараметрыЧтенияИнициализированного() Экспорт + ЧтениеXML = Новый ЧтениеXML; + ЧтениеXML.УстановитьСтроку(СтрокаXML()); + ПараметрыЧтения = ЧтениеXML.Параметры; + + юТест.ПроверитьРавенство(ПараметрыЧтения.Версия,"1.0","Версия"); + юТест.ПроверитьРавенство(ПараметрыЧтения.Язык,"","Язык"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ПробельныеСимволы,ПробельныеСимволыXML.ПоУмолчанию,"ПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ТипПроверкиПравильности,ТипПроверкиXML.НетПроверки,"ТипПроверкиПравильности"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьОбъявлениеXML,Истина,"ИгнорироватьОбъявлениеXML"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьТипДокумента,Истина,"ИгнорироватьТипДокумента"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьИнструкцииОбработки,Ложь,"ИгнорироватьИнструкцииОбработки"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьКомментарии,Истина,"ИгнорироватьКомментарии"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИгнорироватьПробельныеСимволы,Истина,"ИгнорироватьПробельныеСимволы"); + юТест.ПроверитьРавенство(ПараметрыЧтения.СекцииCDATAКакТекст,Истина,"СекцииCDATAКакТекст"); + юТест.ПроверитьРавенство(ПараметрыЧтения.ИспользоватьИгнорируемыеПробельныеСимволы,Ложь,"ИспользоватьИгнорируемыеПробельныеСимволы"); +КонецПроцедуры + + Процедура ТестДолжен_ПрочитатьЭлементыИзСтроки() Экспорт ЧтениеXML = Новый ЧтениеXML; @@ -258,3 +343,294 @@ //установленные в начале элемента. КонецПроцедуры + + +Функция СтрокаXMLСКомментарием() + Возврат + " + | + | + | + |Текст1 + | Текст2 "; +КонецФункции + +Процедура ТестДолжен_ПроверитьСозданиеСПараметрами_Комментарий() Экспорт + + ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,Ложь); // не игнорировать комментарии + + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXMLСКомментарием(), ПараметрыЧтения); + + ТипыУзлов = Новый Массив; + Пока Ч.Прочитать() Цикл + ТипыУзлов.Добавить(Ч.ТипУзла); + КонецЦикла; + + юТест.ПроверитьРавенство(ТипыУзлов.Количество(),9); + юТест.ПроверитьРавенство(ТипыУзлов[0], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[1], ТипУзлаXML.Комментарий); + юТест.ПроверитьРавенство(ТипыУзлов[2], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[3], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[4], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[5], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[6], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[7], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[8], ТипУзлаXML.КонецЭлемента); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСозданиеСПараметрами_ПробельныеСимволы1() Экспорт + + ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,,Ложь,,Ложь); // не игнорировать, но не использовать пробельные сиволы + + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXMLСКомментарием(), ПараметрыЧтения); + + ТипыУзлов = Новый Массив; + Пока Ч.Прочитать() Цикл + ТипыУзлов.Добавить(Ч.ТипУзла); + КонецЦикла; + + юТест.ПроверитьРавенство(ТипыУзлов.Количество(),13); + юТест.ПроверитьРавенство(ТипыУзлов[0], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[1], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[2], ТипУзлаXML.Комментарий); + юТест.ПроверитьРавенство(ТипыУзлов[3], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[4], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[5], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[6], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[7], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[8], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[9], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[10], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[11], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[12], ТипУзлаXML.КонецЭлемента); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСозданиеСПараметрами_ПробельныеСимволы2() Экспорт + + ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,,Ложь,,Истина); // не игнорировать и использовать пробельные сиволы + + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXMLСКомментарием(), ПараметрыЧтения); + + ТипыУзлов = Новый Массив; + Пока Ч.Прочитать() Цикл + ТипыУзлов.Добавить(Ч.ТипУзла); + КонецЦикла; + + юТест.ПроверитьРавенство(ТипыУзлов.Количество(),13); + юТест.ПроверитьРавенство(ТипыУзлов[0], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[1], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[2], ТипУзлаXML.Комментарий); + юТест.ПроверитьРавенство(ТипыУзлов[3], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[4], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[5], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[6], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[7], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[8], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[9], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[10], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[11], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[12], ТипУзлаXML.КонецЭлемента); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьСозданиеСПараметрами_ПробельныеСимволы3() Экспорт + + ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,,Истина,,Истина); // игнорировать, но использовать пробельные сиволы + + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXMLСКомментарием(), ПараметрыЧтения); + + ТипыУзлов = Новый Массив; + Пока Ч.Прочитать() Цикл + ТипыУзлов.Добавить(Ч.ТипУзла); + КонецЦикла; + + юТест.ПроверитьРавенство(ТипыУзлов.Количество(),13); + юТест.ПроверитьРавенство(ТипыУзлов[0], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[1], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[2], ТипУзлаXML.Комментарий); + юТест.ПроверитьРавенство(ТипыУзлов[3], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[4], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[5], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[6], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[7], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[8], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[9], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[10], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[11], ТипУзлаXML.ПробельныеСимволы); + юТест.ПроверитьРавенство(ТипыУзлов[12], ТипУзлаXML.КонецЭлемента); + +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьПереключениеПробельныхСимволов() Экспорт + + ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,,Ложь,,Ложь); // не игнорировать и не использовать пробельные сиволы + + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXMLСКомментарием(), ПараметрыЧтения); + + ТипыУзлов = Новый Массив; + Счетчик = 0; + Пока Ч.Прочитать() Цикл + Счетчик = Счетчик+1; + ТипыУзлов.Добавить(Ч.ТипУзла); + Если Счетчик = 4 Тогда + Ч.ИгнорироватьПробелы = Истина; + КонецЕсли; + КонецЦикла; + + юТест.ПроверитьРавенство(ТипыУзлов.Количество(),11); + юТест.ПроверитьРавенство(ТипыУзлов[0], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[1], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[2], ТипУзлаXML.Комментарий); + юТест.ПроверитьРавенство(ТипыУзлов[3], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[4], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[5], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[6], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[7], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[8], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[9], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[10], ТипУзлаXML.КонецЭлемента); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПереключениеПробельныхСимволов2() Экспорт + + ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,,Ложь,,Ложь); // не игнорировать и не использовать пробельные сиволы + + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXMLСКомментарием(), ПараметрыЧтения); + + ТипыУзлов = Новый Массив; + Счетчик = 0; + Пока Ч.Прочитать() Цикл + Счетчик = Счетчик+1; + ТипыУзлов.Добавить(Ч.ТипУзла); + Если Счетчик >= 3 Тогда + Ч.ИгнорироватьПробелы = Истина; + КонецЕсли; + КонецЦикла; + + юТест.ПроверитьРавенство(ТипыУзлов.Количество(),10); + юТест.ПроверитьРавенство(ТипыУзлов[0], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[1], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[2], ТипУзлаXML.Комментарий); + юТест.ПроверитьРавенство(ТипыУзлов[3], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[4], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[5], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[6], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[7], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[8], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[9], ТипУзлаXML.КонецЭлемента); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПереключениеПробельныхСимволов3() Экспорт + + ПараметрыЧтения = Новый ПараметрыЧтенияXML(,,,,,,,,Ложь,,Ложь); // не игнорировать и не использовать пробельные сиволы + + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXMLСКомментарием(), ПараметрыЧтения); + + ТипыУзлов = Новый Массив; + Счетчик = 0; + Пока Ч.Прочитать() Цикл + Счетчик = Счетчик+1; + ТипыУзлов.Добавить(Ч.ТипУзла); + Если Счетчик >= 3 Тогда + Ч.ИгнорироватьПробелы = Не Ч.ИгнорироватьПробелы; + КонецЕсли; + КонецЦикла; + + юТест.ПроверитьРавенство(ТипыУзлов.Количество(),12); + юТест.ПроверитьРавенство(ТипыУзлов[0], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[1], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[2], ТипУзлаXML.Комментарий); + юТест.ПроверитьРавенство(ТипыУзлов[3], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[4], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[5], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[6], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[7], ТипУзлаXML.НачалоЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[8], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[9], ТипУзлаXML.КонецЭлемента); + юТест.ПроверитьРавенство(ТипыУзлов[10], ТипУзлаXML.Текст); + юТест.ПроверитьРавенство(ТипыУзлов[11], ТипУзлаXML.КонецЭлемента); + +КонецПроцедуры + + +Процедура ТестДолжен_ПроверитьЗначенияСвойствНеинициализированного() Экспорт + Ч = Новый ЧтениеXml(); + + юТест.ПроверитьРавенство(Ч.ВерсияXML,"1.0","ВерсияXML"); + юТест.ПроверитьРавенство(Ч.Язык,"","Язык"); + юТест.ПроверитьРавенство(Ч.ИгнорироватьПробелы,Истина,"ИгнорироватьПробелы"); + юТест.ПроверитьРавенство(Ч.КодировкаXML,"UTF-8","КодировкаXML"); + + юТест.ПроверитьРавенство(Ч.ТипУзла,ТипУзлаXML.Ничего,"ТипУзла"); + юТест.ПроверитьРавенство(Ч.URIПространстваИмен,"","URIПространстваИмен"); + юТест.ПроверитьРавенство(Ч.БазовыйURI,"","БазовыйURI"); + юТест.ПроверитьРавенство(Ч.ИмеетИмя,Ложь,"ИмеетИмя"); + юТест.ПроверитьРавенство(Ч.Имя,"","Имя"); + юТест.ПроверитьРавенство(Ч.ЛокальноеИмя,"","ЛокальноеИмя"); + юТест.ПроверитьРавенство(Ч.ИмеетЗначение,Ложь,"ИмеетЗначение"); + юТест.ПроверитьРавенство(Ч.Значение,"","Значение"); + юТест.ПроверитьРавенство(Ч.КонтекстПространствИмен,Неопределено,"КонтекстПространствИмен"); + юТест.ПроверитьРавенство(Ч.ЭтоАтрибутПоУмолчанию,Неопределено,"ЭтоАтрибутПоУмолчанию"); + юТест.ПроверитьРавенство(Ч.ЭтоСимвольныеДанные,Ложь,"ЭтоСимвольныеДанные"); + + юТест.ПроверитьРавенство(Ч.ИмяАтрибута(0),"","ИмяАтрибута"); + юТест.ПроверитьРавенство(Ч.ЗначениеАтрибута(0),"","ЗначениеАтрибута"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗначенияСвойствИнициализированного() Экспорт + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(СтрокаXML()); + + юТест.ПроверитьРавенство(Ч.ВерсияXML,"1.0","ВерсияXML"); + юТест.ПроверитьРавенство(Ч.Язык,"","Язык"); + юТест.ПроверитьРавенство(Ч.ИгнорироватьПробелы,Истина,"ИгнорироватьПробелы"); + юТест.ПроверитьРавенство(Ч.КодировкаXML,"UTF-8","КодировкаXML"); + + юТест.ПроверитьРавенство(Ч.ТипУзла,ТипУзлаXML.Ничего,"ТипУзла"); + юТест.ПроверитьРавенство(Ч.URIПространстваИмен,"","URIПространстваИмен"); + юТест.ПроверитьРавенство(Ч.БазовыйURI,"","БазовыйURI"); + юТест.ПроверитьРавенство(Ч.ИмеетИмя,Ложь,"ИмеетИмя"); + юТест.ПроверитьРавенство(Ч.Имя,"","Имя"); + юТест.ПроверитьРавенство(Ч.ЛокальноеИмя,"","ЛокальноеИмя"); + юТест.ПроверитьРавенство(Ч.ИмеетЗначение,Ложь,"ИмеетЗначение"); + юТест.ПроверитьРавенство(Ч.Значение,"","Значение"); + юТест.ПроверитьРавенство(ТипЗнч(Ч.КонтекстПространствИмен),Тип("КонтекстПространствИменXML"),"КонтекстПространствИмен"); + юТест.ПроверитьРавенство(Ч.ЭтоАтрибутПоУмолчанию,Неопределено,"ЭтоАтрибутПоУмолчанию"); + юТест.ПроверитьРавенство(Ч.ЭтоСимвольныеДанные,Ложь,"ЭтоСимвольныеДанные"); + + юТест.ПроверитьРавенство(Ч.ИмяАтрибута(0),"","ИмяАтрибута"); + юТест.ПроверитьРавенство(Ч.ЗначениеАтрибута(0),Неопределено,"ЗначениеАтрибута"); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьЗначенияСвойствАтрибута() Экспорт + Ч = Новый ЧтениеXml(); + Ч.УстановитьСтроку(""); + + Ч.Прочитать(); + юТест.ПроверитьРавенство(Ч.КоличествоАтрибутов(), 1,"КоличествоАтрибутов"); + + Если Ч.ПрочитатьАтрибут() Тогда + юТест.ПроверитьРавенство(Ч.ТипУзла, ТипУзлаXML.Атрибут,"ТипУзла"); + юТест.ПроверитьРавенство(Ч.ЭтоАтрибутПоУмолчанию,Ложь,"ЭтоАтрибутПоУмолчанию"); + + юТест.ПроверитьРавенство(Ч.ИмяАтрибута(0),"attr","ИмяАтрибута"); + юТест.ПроверитьРавенство(Ч.ЗначениеАтрибута(0),"123","ЗначениеАтрибута"); + + юТест.ПроверитьРавенство(Ч.ИмяАтрибута(2),"","ИмяАтрибута(2)"); + Иначе + ВызватьИсключение("Атрибут не прочитан"); + КонецЕсли +КонецПроцедуры + diff --git a/tests/xmlwrite.os b/tests/xmlwrite.os index 5d594c3b1..5b3e4665f 100644 --- a/tests/xmlwrite.os +++ b/tests/xmlwrite.os @@ -26,8 +26,26 @@ ВсеТесты.Добавить("ТестДолжен_ЗаписатьВСтрокуБезПробелаПередЗакрытием"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПравильныйВыводПробелСлэша"); ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекст"); + + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_БезЧтения"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_НачалоЭлемента"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_НачалоВложенногоЭлемента"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлемента"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлементаСОпределениемПространстваИмен"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлементаСПерекрывающимОпределениемПространстваИмен"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлементаСЛишнимПереопределениемПространстваИмен"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_КонецЭлемента"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_Текст"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_CDATA"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_Комментарий"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_СсылкаНаСушность"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_ПолноеКопирование"); + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_ИнструкцииОбработки"); + + ВсеТесты.Добавить("ТестДолжен_ЗаписатьТекущий_ОбъявлениеИТипДокумента"); Возврат ВсеТесты; + КонецФункции Процедура ТестДолжен_ЗаписатьВСтроку() Экспорт @@ -272,3 +290,472 @@ юТест.ПроверитьРавенство(Результат, "123"); КонецПроцедуры + + +Процедура ТестДолжен_ЗаписатьТекущий_БезЧтения() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("", ПолученныйТекст, "Ничего не прочитано - ничего не пишется"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_НачалоЭлемента() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("", // dotnet автоматом закроет элемент + ПолученныйТекст, + "Первый элемент"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_НачалоВложенногоЭлемента() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("", // dotnet автоматом закроет элемент + ПолученныйТекст, + "Второй элемент"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлемента() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЧтениеXML.ПервыйАтрибут(); + ЧтениеXML.СледующийАтрибут(); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("<эл inner2=""456"" />", // dotnet автоматом закроет элемент + ПолученныйТекст, + "Второй атрибут"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлементаСОпределениемПространстваИмен() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ЧтениеXML.Пропустить(); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЧтениеXML.ПервыйАтрибут(); + ЧтениеXML.СледующийАтрибут(); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство( + "<эл xmlns:ins=""inner namespace mapping"" ins:attr=""1"" />", // dotnet автоматом закроет элемент + ПолученныйТекст, + "Автоматически добавляется определение пространства имен"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлементаСЛишнимПереопределениемПространстваИмен() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ЧтениеXML.Пропустить(); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЧтениеXML.ПервыйАтрибут(); + ЧтениеXML.СледующийАтрибут(); + + ЗаписьXML.ЗаписатьНачалоЭлемента("корень"); + ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("ins", "inner namespace mapping"); + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство( + "<корень xmlns:ins=""inner namespace mapping""><эл ins:attr=""1"" />", // dotnet автоматом закроет элементы + ПолученныйТекст, + "Определение пространства имен не добавляется"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_АтрибутВнутриЭлементаСПерекрывающимОпределениемПространстваИмен() Экспорт + + // BSLLS:LineLength-off + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ЧтениеXML.Пропустить(); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЧтениеXML.ПервыйАтрибут(); + ЧтениеXML.СледующийАтрибут(); + + ЗаписьXML.ЗаписатьНачалоЭлемента("корень"); + ЗаписьXML.ЗаписатьСоответствиеПространстваИмен("ins2", "inner namespace mapping"); + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство( + "<корень xmlns:ins2=""inner namespace mapping""><эл xmlns:ins=""inner namespace mapping"" ins:attr=""1"" />", // dotnet автоматом закроет элементы + ПолученныйТекст, + "Определение пространства имен добавляется, так как не совпадают префиксы"); + // BSLLS:LineLength-on +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_КонецЭлемента() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЧтениеXML.ПервыйАтрибут(); + ЧтениеXML.СледующийАтрибут(); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.Текст, "Some Value"); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.КонецЭлемента, ""); + + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("<эл inner2=""456""/>", + ПолученныйТекст, + "Закрытие элемента"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_Текст() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.Текст, "Спозиционированы на текст Some text"); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("<эл>Some Text", + ПолученныйТекст, + "Текст"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_CDATA() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + ДойтиДоИмени(ЧтениеXML, "cdata"); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.СекцияCDATA); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ЗаписьXML.ЗаписатьСекциюCDATA(", к которому добавили ещё текст"); + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("<эл>", + ПолученныйТекст, + "CDATA"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_Комментарий() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + ДойтиДоИмени(ЧтениеXML, "БлокСКомментарием"); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ПробельныеСимволы); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.Комментарий); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + + ЗаписьXML.ЗаписатьТекст("Текст"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); // можем записать ещё раз + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("<эл> Текст", + ПолученныйТекст, + "Комментарий"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_ПолноеКопирование() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + Пока ЧтениеXML.Прочитать() Цикл + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + КонецЦикла; + + ЗаписьXML.Закрыть(); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_ОбъявлениеИТипДокумента() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОбъявлениеXML, ""); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ОпределениеТипаДокумента, ""); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.НачалоЭлемента, ""); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенствоСтрокБезУчетаРегистра( + "<эл/>", + ПолученныйТекст, + "Объявления и тип документа"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_СсылкаНаСушность() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ДойтиДоИмени(ЧтениеXML, "entity"); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.СсылкаНаСущность); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("<эл>&СсылкаНаСущность;", + ПолученныйТекст, + "Ссылка на сущность"); + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ДойтиДоИмени(ЧтениеXML, "entity2"); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.Текст); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.СсылкаНаСущность); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + юТест.ПроверитьРавенство("<эл>Это &СсылкаНаСущность;", + ПолученныйТекст, + "Ссылка на сущность внутри текста"); + +КонецПроцедуры + +Процедура ТестДолжен_ЗаписатьТекущий_ИнструкцииОбработки() Экспорт + + Перем ЧтениеXML, ЗаписьXML; + + ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML); + + ДойтиДоИмени(ЧтениеXML, "PI"); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ИнструкцияОбработки); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + + // Несовместимость: dotnet всегда добавляет пробел, даже если нет текста инструкции + юТест.ПроверитьРавенство("<эл>", + // ^ + ПолученныйТекст, + "Инструкция обработки"); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.КонецЭлемента); + ЧтениеXML.Прочитать(); + + // + + ПодготовитьЗаписьXML(ЗаписьXML); + ДойтиДоИмени(ЧтениеXML, "PI"); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ИнструкцияОбработки); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + + юТест.ПроверитьРавенство("<эл>", + ПолученныйТекст, + "Инструкция обработки с текстом"); + + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.КонецЭлемента); + ЧтениеXML.Прочитать(); + + // + + ПодготовитьЗаписьXML(ЗаписьXML); + ДойтиДоИмени(ЧтениеXML, "PI"); + ПрочитатьИПроверитьТип(ЧтениеXML, ТипУзлаXML.ИнструкцияОбработки); + + ЗаписьXML.ЗаписатьНачалоЭлемента("эл"); + ЗаписьXML.ЗаписатьТекущий(ЧтениеXML); + ЗаписьXML.ЗаписатьКонецЭлемента(); + + ПолученныйТекст = ЗаписьXML.Закрыть(); + + юТест.ПроверитьРавенство("<эл>", + ПолученныйТекст, + "Инструкция обработки с текстом и служебными символами"); + +КонецПроцедуры + +Процедура ПодготовитьТестовыеДанныеДляЗаписиТекущего(ЧтениеXML, ЗаписьXML) + + ПодопытныйТекст = + " + | + | + | Some Text + | + | + | <БлокСКомментарием> + | &СсылкаНаСущность; + | Это &СсылкаНаСущность; + | + | + | + |" + ; + + ПараметрыЧтения = Новый ПараметрыЧтенияXml( + , // Версия + , // Язык + , // ПробельныеСимволы + , // ТипПроверкиПравильности, + Ложь, // ИгнорироватьОбъявлениеXML + Ложь, // ИгнорироватьТипДокумента + Ложь, // ИгнорироватьИнструкцииОбработки + Ложь, // ИгнорироватьКомментарии + Ложь, // ИгнорироватьПробельныеСимволы + Ложь, // СекцииCDATAКакТекст + Истина // ИспользоватьИгнорируемыеПробельныеСимволы + ); + + ЧтениеXML = Новый ЧтениеXML; + ЧтениеXML.УстановитьСтроку(ПодопытныйТекст, ПараметрыЧтения); + + ПодготовитьЗаписьXML(ЗаписьXML); + +КонецПроцедуры + +Процедура ПодготовитьЗаписьXML(ЗаписьXML) + + ПараметрыЗаписи = Новый ПараметрыЗаписиXml(, , Ложь); // без переносов + + ЗаписьXML = Новый ЗаписьXML; + ЗаписьXML.УстановитьСтроку(ПараметрыЗаписи); + +КонецПроцедуры + +Функция ДойтиДоИмени(Знач ЧтениеXML, Знач Имя) + + Пока ЧтениеXML.Имя <> Имя Цикл + + Если Не ЧтениеXML.Прочитать() Тогда + Возврат Ложь; + КонецЕсли; + + КонецЦикла; + + Возврат Истина; + +КонецФункции + +Процедура ПрочитатьИПроверитьТип(Знач ЧтениеXML, Знач ТипУзла, Знач Сообщение = Неопределено) + ЧтениеXML.Прочитать(); + юТест.ПроверитьРавенство(ЧтениеXML.ТипУзла, ТипУзла, Сообщение); +КонецПроцедуры diff --git a/tests/xslt/test-XSLTransform.os b/tests/xslt/test-XSLTransform.os new file mode 100644 index 000000000..cc49bec17 --- /dev/null +++ b/tests/xslt/test-XSLTransform.os @@ -0,0 +1,273 @@ +Перем юТест; + +//////////////////////////////////////////////////////////////////// +// Программный интерфейс + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_СоздатьКомпоненту"); + ВсеТесты.Добавить("ТестДолжен_УстановитьСтилиИзСтроки"); + ВсеТесты.Добавить("ТестДолжен_УстановитьСтилиИзФайла"); + ВсеТесты.Добавить("ТестДолжен_УстановитьСтилиИзУзла"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьПараметр"); + ВсеТесты.Добавить("ТестДолжен_УдалитьПараметр"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьПреобразованиеИзСтроки"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьПреобразованиеИзФайла"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьПреобразованиеИзУзла"); + ВсеТесты.Добавить("ТестДолжен_ВыполнитьПреобразованиеСПараметрами"); + + Возврат ВсеТесты; + +КонецФункции // ПолучитьСписокТестов() + +Процедура ТестДолжен_СоздатьКомпоненту() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + +КонецПроцедуры // ТестДолжен_СоздатьКомпоненту() + +Процедура ТестДолжен_УстановитьСтилиИзСтроки() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + + Шаблон = ПолучитьДанныеИзФайла("xslt/xslt-mock_template.xslt"); + + Попытка + Преобразование.ЗагрузитьТаблицуСтилейXSLИзСтроки(Шаблон); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить загрузить таблицу стилей из строки %1""%2"":%1%3", + Символы.ПС, + Шаблон, + ТекстОшибки); + КонецПопытки; + +КонецПроцедуры // ТестДолжен_УстановитьСтилиИзСтроки() + +Процедура ТестДолжен_УстановитьСтилиИзФайла() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + + ПутьКФайлу = "xslt/xslt-mock_template.xslt"; + + Попытка + Преобразование.ЗагрузитьТаблицуСтилейXSLИзФайла(ПутьКФайлу); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить загрузить таблицу стилей из файла ""%1"":%2%3", + ПутьКФайлу, + Символы.ПС, + ТекстОшибки); + КонецПопытки; + +КонецПроцедуры // ТестДолжен_УстановитьСтилиИзФайла() + +Процедура ТестДолжен_ДобавитьПараметр() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + + ИмяПараметра = "testParam"; + ЗначениеПараметра = "items"; + Попытка + Преобразование.ДобавитьПараметр(ИмяПараметра, ЗначениеПараметра); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить параметр ""%1"" со значением ""%2"":%3%4", + ИмяПараметра, + ЗначениеПараметра, + Символы.ПС, + ТекстОшибки); + КонецПопытки; + +КонецПроцедуры // ТестДолжен_ДобавитьПараметр() + +Процедура ТестДолжен_УдалитьПараметр() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + + ИмяПараметра = "testParam"; + ЗначениеПараметра = "items"; + + Попытка + Преобразование.ДобавитьПараметр(ИмяПараметра, ЗначениеПараметра); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить параметр ""%1"" со значением ""%2"":%3%4", + ИмяПараметра, + ЗначениеПараметра, + Символы.ПС, + ТекстОшибки); + КонецПопытки; + + Попытка + Преобразование.УдалитьПараметр(ИмяПараметра); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось удалить параметр ""%1"":%2%3", + ИмяПараметра, + Символы.ПС, + ТекстОшибки); + КонецПопытки; + +КонецПроцедуры // ТестДолжен_УдалитьПараметр() + +Процедура ТестДолжен_ВыполнитьПреобразованиеИзСтроки() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + + Шаблон = ПолучитьДанныеИзФайла("xslt/xslt-mock_template.xslt"); + ИсходныеДанные = ПолучитьДанныеИзФайла("xslt/xslt-mock_data.xml"); + ТестоваяСтрока = "item3"; + + Попытка + Преобразование.ЗагрузитьТаблицуСтилейXSLИзСтроки(Шаблон); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить загрузить таблицу стилей из строки %1""%2"":%1%3", + Символы.ПС, + Шаблон, + ТекстОшибки); + КонецПопытки; + + Попытка + Результат = Преобразование.ПреобразоватьИзСтроки(ИсходныеДанные); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось выполнить преобразование строки %1""%2""%1 по шаблону %1""%3"":%1%4", + Символы.ПС, + ИсходныеДанные, + Шаблон, + ТекстОшибки); + КонецПопытки; + + ТекстОшибки = СтрШаблон("Не удалось выполнить преобразование строки %1""%2""%1 по шаблону %1""%3""", + Символы.ПС, + ИсходныеДанные, + Шаблон); + юТест.ПроверитьРавенство(СокрЛП(Результат), ТестоваяСтрока, ТекстОшибки); + +КонецПроцедуры // ТестДолжен_ВыполнитьПреобразованиеИзСтроки() + +Процедура ТестДолжен_ВыполнитьПреобразованиеИзФайла() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + + Шаблон = ПолучитьДанныеИзФайла("xslt/xslt-mock_template.xslt"); + ПутьКФайлу = "xslt/xslt-mock_data.xml"; + ТестоваяСтрока = "item3"; + + Попытка + Преобразование.ЗагрузитьТаблицуСтилейXSLИзСтроки(Шаблон); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить загрузить таблицу стилей из строки %1""%2"":%1%3", + Символы.ПС, + Шаблон, + ТекстОшибки); + КонецПопытки; + + Попытка + Результат = Преобразование.ПреобразоватьИзФайла(ПутьКФайлу); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось выполнить преобразование файла ""%1"" по шаблону %2""%3"":%2%4", + ПутьКФайлу, + Символы.ПС, + Шаблон, + ТекстОшибки); + КонецПопытки; + + ТекстОшибки = СтрШаблон("Не удалось выполнить преобразование файла ""%1"" по шаблону %2""%3""", + ПутьКФайлу, + Символы.ПС, + Шаблон); + юТест.ПроверитьРавенство(СокрЛП(Результат), ТестоваяСтрока, ТекстОшибки); + +КонецПроцедуры // ТестДолжен_ВыполнитьПреобразованиеИзФайла() + +Процедура ТестДолжен_ВыполнитьПреобразованиеСПараметрами() Экспорт + + Преобразование = Новый ПреобразованиеXSL(); + юТест.ПроверитьРавенство(ТипЗнч(Преобразование), + Тип("ПреобразованиеXSL"), + "Не удалось создать объект ПреобразованиеXSL"); + + Шаблон = ПолучитьДанныеИзФайла("xslt/xslt-mock_template_w_param.xslt"); + ИсходныеДанные = ПолучитьДанныеИзФайла("xslt/xslt-mock_data.xml"); + ТестоваяСтрока = "items\item3"; + ИмяПараметра = "testParam"; + ЗначениеПараметра = "items"; + + Попытка + Преобразование.ЗагрузитьТаблицуСтилейXSLИзСтроки(Шаблон); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить загрузить таблицу стилей из строки %1""%2"":%1%3", + Символы.ПС, + Шаблон, + ТекстОшибки); + КонецПопытки; + + Попытка + Преобразование.ДобавитьПараметр(ИмяПараметра, ЗначениеПараметра); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось добавить параметр ""%1"" со значением ""%2"":%3%4", + ИмяПараметра, + ЗначениеПараметра, + Символы.ПС, + ТекстОшибки); + КонецПопытки; + + Попытка + Результат = Преобразование.ПреобразоватьИзСтроки(ИсходныеДанные); + Исключение + ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()); + ВызватьИсключение СтрШаблон("Не удалось выполнить преобразование строки %1""%2""%1 по шаблону %1""%3"":%1%4", + Символы.ПС, + ИсходныеДанные, + Шаблон, + ТекстОшибки); + КонецПопытки; + + ТекстОшибки = СтрШаблон("Не удалось выполнить преобразование строки %1""%2""%1 по шаблону %1""%3""", + Символы.ПС, + ИсходныеДанные, + Шаблон); + юТест.ПроверитьРавенство(СокрЛП(Результат), ТестоваяСтрока, ТекстОшибки); + +КонецПроцедуры // ТестДолжен_ВыполнитьПреобразованиеСПараметрами() + +Функция ПолучитьДанныеИзФайла(ПутьКФайлу) + + Текст = Новый ТекстовыйДокумент(); + Текст.Прочитать(ПутьКФайлу, КодировкаТекста.UTF8); + + Возврат Текст.ПолучитьТекст(); + +КонецФункции // ПолучитьДанныеИзФайла() diff --git a/tests/xslt/xslt-mock_data.xml b/tests/xslt/xslt-mock_data.xml new file mode 100644 index 000000000..deb3a9a8a --- /dev/null +++ b/tests/xslt/xslt-mock_data.xml @@ -0,0 +1,7 @@ + + + + item1 + item2 + item3 + diff --git a/tests/xslt/xslt-mock_template.xslt b/tests/xslt/xslt-mock_template.xslt new file mode 100644 index 000000000..f71b71bd7 --- /dev/null +++ b/tests/xslt/xslt-mock_template.xslt @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tests/xslt/xslt-mock_template_w_param.xslt b/tests/xslt/xslt-mock_template_w_param.xslt new file mode 100644 index 000000000..f6a4c269b --- /dev/null +++ b/tests/xslt/xslt-mock_template_w_param.xslt @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/zip.os b/tests/zip.os deleted file mode 100644 index 065fbea65..000000000 --- a/tests/zip.os +++ /dev/null @@ -1,684 +0,0 @@ -Перем юТест; -Перем ТекущийКаталогСохр; -Перем Чтение; - -Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт - - юТест = ЮнитТестирование; - - ВсеТесты = Новый Массив; - - ВсеТесты.Добавить("ТестДолжен_СоздатьАрхивЧерезКонструкторИмениФайла"); - ВсеТесты.Добавить("ТестДолжен_СоздатьАрхивЧерезКонструкторСНеобязательнымиПараметрами"); - ВсеТесты.Добавить("ТестДолжен_СоздатьАрхивЧерезМетодОткрыть"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивОдиночныйФайлБезПутей"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивОдиночныйСПолнымПутем"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивКаталогТестов"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивКаталогТестовПоМаске"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивКаталогСОтносительнымиПутями"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивВложенныйКаталогСОтносительнымиПутями"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивНесколькоВложенныхКаталоговСОтносительнымиПутями"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивФайлСОтносительнымиПутями"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменем"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменем"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменемНеИзТекущегоКаталога"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиВложенныйФайлСПолнымИменемНеИзТекущегоКаталога"); - ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменемНеИзТекущегоКаталога"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвлечениеБезПутей"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвлечениеБезПутейДляОдиночногоЭлемента"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьРазмерИзвлеченногоОдиночногоЭлемента"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьПарольАрхива"); - ВсеТесты.Добавить("ТестДолжен_ПроверитьПарольРусскиеБуквыФайловАрхива"); - - Возврат ВсеТесты; - -КонецФункции - -Функция СоздатьВременныйФайл(Знач Расширение = "tmp") - Возврат юТест.ИмяВременногоФайла(Расширение); -КонецФункции - -Процедура ПередЗапускомТеста() Экспорт - ТекущийКаталогСохр = ТекущийКаталог(); -КонецПроцедуры - -Процедура ПослеЗапускаТеста() Экспорт - УстановитьТекущийКаталог(ТекущийКаталогСохр); - - Если ЗначениеЗаполнено(Чтение) Тогда - Чтение.Закрыть(); - КонецЕсли; - - юТест.УдалитьВременныеФайлы(); -КонецПроцедуры - -Процедура ТестДолжен_СоздатьАрхивЧерезКонструкторИмениФайла() Экспорт - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(ИмяАрхива); - Архив.Записать(); - - Файл = Новый Файл(ИмяАрхива); - юТест.ПроверитьИстину(Файл.Существует()); - -КонецПроцедуры - -Процедура ТестДолжен_СоздатьАрхивЧерезКонструкторСНеобязательнымиПараметрами() Экспорт - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(ИмяАрхива, "123", 456); - Архив.Записать(); - - Файл = Новый Файл(ИмяАрхива); - юТест.ПроверитьИстину(Файл.Существует()); - -КонецПроцедуры - -Процедура ТестДолжен_СоздатьАрхивЧерезМетодОткрыть() Экспорт - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(); - Архив.Открыть(ИмяАрхива,,"Это комментарий",,УровеньСжатияZip.Максимальный); - Архив.Записать(); - - Файл = Новый Файл(ИмяАрхива); - юТест.ПроверитьИстину(Файл.Существует()); - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивОдиночныйФайлБезПутей() Экспорт - - ФайлСкрипта = ТекущийСценарий().Источник; - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(); - Архив.Открыть(ИмяАрхива,,"Это комментарий",,УровеньСжатияZip.Максимальный); - Архив.Добавить(ФайлСкрипта, РежимСохраненияПутейZip.НеСохранятьПути); - Архив.Записать(); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - - Попытка - юТест.ПроверитьРавенство("", Чтение.Элементы[0].Путь); - юТест.ПроверитьРавенство("zip.os", Чтение.Элементы[0].Имя); - юТест.ПроверитьРавенство("zip", Чтение.Элементы[0].ИмяБезРасширения); - Исключение - Чтение.Закрыть(); - ВызватьИсключение; - КонецПопытки; - - Чтение.Закрыть(); - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивОдиночныйСПолнымПутем() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(); - Архив.Открыть(ИмяАрхива); - Архив.Добавить(ФайлСкрипта.ПолноеИмя, РежимСохраненияПутейZip.СохранятьПолныеПути); - Архив.Записать(); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - - СИ = Новый СистемнаяИнформация; - Если Найти(СИ.ВерсияОС, "Windows") > 0 Тогда - ИмяБезДиска = Сред(ФайлСкрипта.Путь, Найти(ФайлСкрипта.Путь, "\")+1); - Иначе - ИмяБезДиска = Сред(ФайлСкрипта.Путь,2); - КонецЕсли; - - Попытка - юТест.ПроверитьРавенство(ИмяБезДиска, Чтение.Элементы[0].Путь); - Исключение - Чтение.Закрыть(); - УдалитьФайлы(ИмяАрхива); - ВызватьИсключение; - КонецПопытки; - - Чтение.Закрыть(); - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивКаталогТестов() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); - - ВременныйКаталог = СоздатьВременныйФайл(); - КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); - СоздатьКаталог(КаталогКопииТестов); - - ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.os"); - Для Каждого Файл Из ВсеФайлы Цикл - Если Файл.ЭтоФайл() Тогда - КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); - КонецЕсли; - КонецЦикла; - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(); - Архив.Открыть(ИмяАрхива); - - Архив.Добавить(КаталогКопииТестов + ПолучитьРазделительПути(), РежимСохраненияПутейZip.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); - Архив.Записать(); - - ОжидаемоеИмя = КаталогСкрипта.Имя + ПолучитьРазделительПути(); - Попытка - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - Для Каждого Элемент Из Чтение.Элементы Цикл - юТест.ПроверитьРавенство(ОжидаемоеИмя, Элемент.Путь, "Проверка элемента zip: " + Элемент.ПолноеИмя); - КонецЦикла; - Исключение - Чтение.Закрыть(); - ВызватьИсключение; - КонецПопытки; - юТест.ПроверитьРавенство(ВсеФайлы.Количество(), Чтение.Элементы.Количество()); - Чтение.Закрыть(); -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивКаталогТестовПоМаске() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); - - ВременныйКаталог = СоздатьВременныйФайл(); - КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); - СоздатьКаталог(КаталогКопииТестов); - ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.*"); - РасширениеТестов = ".os"; - КоличествоТестов = 0; - - ДопК = ОбъединитьПути(КаталогКопииТестов, "add"); - СоздатьКаталог(ДопК); - - Для Каждого Файл Из ВсеФайлы Цикл - Если Файл.ЭтоКаталог() Тогда - Продолжить; - КонецЕсли; - - Если Файл.Расширение = РасширениеТестов Тогда - КоличествоТестов = КоличествоТестов + 2; - КонецЕсли; - КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); - КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(ДопК, Файл.Имя)); - КонецЦикла; - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(ИмяАрхива); - Архив.Добавить(ВременныйКаталог + ПолучитьРазделительПути() + "*.os", РежимСохраненияПутейZip.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); - Архив.Записать(); - - ОжидаемоеИмяКорень = КаталогСкрипта.Имя + ПолучитьРазделительПути(); - ОжидаемоеИмяДоп = ОбъединитьПути(КаталогСкрипта.Имя, "add") + ПолучитьРазделительПути(); - Попытка - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - Для Каждого Элемент Из Чтение.Элементы Цикл - юТест.Проверить(Элемент.Путь = ОжидаемоеИмяКорень или Элемент.Путь = ОжидаемоеИмяДоп, "Проверка для пути: " + Элемент.Путь); - юТест.ПроверитьРавенство(РасширениеТестов, Элемент.Расширение); - КонецЦикла; - Исключение - Чтение.Закрыть(); - ВызватьИсключение; - КонецПопытки; - - юТест.ПроверитьИстину(КоличествоТестов > 0); - юТест.ПроверитьРавенство(КоличествоТестов, Чтение.Элементы.Количество()); - - Чтение.Закрыть(); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьИзвлечениеБезПутей() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); - - ВременныйКаталог = СоздатьВременныйФайл(); - КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); - СоздатьКаталог(КаталогКопииТестов); - ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.os"); - Для Каждого Файл Из ВсеФайлы Цикл - Если Файл.ЭтоФайл() Тогда - КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); - КонецЕсли; - КонецЦикла; - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(); - Архив.Открыть(ИмяАрхива); - Архив.Добавить(ВременныйКаталог,РежимСохраненияПутейZip.СохранятьОтносительныеПути,РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); - Архив.Записать(); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - КаталогИзвлечения = СоздатьВременныйФайл(); - СоздатьКаталог(КаталогИзвлечения); - Чтение.ИзвлечьВсе(КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); - Чтение.Закрыть(); - ИзвлеченныеФайлы = НайтиФайлы(КаталогИзвлечения, "*.*"); - - юТест.ПроверитьНеравенство(0, ИзвлеченныеФайлы.Количество()); - юТест.ПроверитьРавенство(ВсеФайлы.Количество(), ИзвлеченныеФайлы.Количество()); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьИзвлечениеБезПутейДляОдиночногоЭлемента() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); - - ВременныйКаталог = СоздатьВременныйФайл(); - КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); - СоздатьКаталог(КаталогКопииТестов); - ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.os"); - Для Каждого Файл Из ВсеФайлы Цикл - Если Файл.ЭтоФайл() Тогда - КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); - КонецЕсли; - КонецЦикла; - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(); - Архив.Открыть(ИмяАрхива); - Архив.Добавить(ВременныйКаталог,РежимСохраненияПутейZip.СохранятьОтносительныеПути,РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); - Архив.Записать(); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - КаталогИзвлечения = СоздатьВременныйФайл(); - СоздатьКаталог(КаталогИзвлечения); - Элемент = Чтение.Элементы.Найти(ФайлСкрипта.Имя); - - Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); - Чтение.Закрыть(); - ИзвлеченныеФайлы = НайтиФайлы(КаталогИзвлечения, "*.os"); - - юТест.ПроверитьНеравенство(0, ИзвлеченныеФайлы.Количество()); - юТест.ПроверитьРавенство(ФайлСкрипта.Имя, ИзвлеченныеФайлы[0].Имя); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьРазмерИзвлеченногоОдиночногоЭлемента() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(); - Архив.Открыть(ИмяАрхива); - Архив.Добавить(ФайлСкрипта.ПолноеИмя); - Архив.Записать(); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - КаталогИзвлечения = СоздатьВременныйФайл(); - СоздатьКаталог(КаталогИзвлечения); - Элемент = Чтение.Элементы.Найти(ФайлСкрипта.Имя); - Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); - Чтение.Закрыть(); - - ИзвлеченныйФайл = Новый Файл(ОбъединитьПути(КаталогИзвлечения, ФайлСкрипта.Имя)); - - юТест.ПроверитьРавенство(ФайлСкрипта.Размер(), ИзвлеченныйФайл.Размер()); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьПарольАрхива() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - - Пароль = "password"; - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(ИмяАрхива, Пароль); - Архив.Добавить(ФайлСкрипта.ПолноеИмя); - Архив.Записать(); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива, Пароль); - КаталогИзвлечения = СоздатьВременныйФайл(); - СоздатьКаталог(КаталогИзвлечения); - Элемент = Чтение.Элементы.Найти(ФайлСкрипта.Имя); - Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); - Чтение.Закрыть(); - - ИзвлеченныйФайл = Новый Файл(ОбъединитьПути(КаталогИзвлечения, ФайлСкрипта.Имя)); - - юТест.ПроверитьРавенство(ФайлСкрипта.Размер(), ИзвлеченныйФайл.Размер()); - -КонецПроцедуры - -Процедура ТестДолжен_ПроверитьПарольРусскиеБуквыФайловАрхива() Экспорт - - ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); - - ИмяРусскогоФайла = "ОбщиеФункции.os"; - - ПутьРусскогоФайла = ОбъединитьПути(ФайлСкрипта.Путь, "testlib", ИмяРусскогоФайла); - - ИмяАрхива = СоздатьВременныйФайл("zip"); - Архив = Новый ЗаписьZipФайла(ИмяАрхива); - Архив.Добавить(ПутьРусскогоФайла); - Архив.Записать(); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - КаталогИзвлечения = СоздатьВременныйФайл(); - СоздатьКаталог(КаталогИзвлечения); - Элемент = Чтение.Элементы.Найти(ИмяРусскогоФайла); - Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); - Чтение.Закрыть(); - - ИзвлеченныйФайл = Новый Файл(ОбъединитьПути(КаталогИзвлечения, ИмяРусскогоФайла)); - - юТест.ПроверитьИстину(ИзвлеченныйФайл.Существует()); - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивКаталогСОтносительнымиПутями() Экспорт - ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Истина, Истина); -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивФайлСОтносительнымиПутями() Экспорт - ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Ложь, Истина); -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменем() Экспорт - ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Истина, Ложь); -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменем() Экспорт - ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Ложь, Ложь); -КонецПроцедуры - -Процедура ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Знач ПередатьКаталог, Знач ПередатьОтносительныйПуть) Экспорт - УстановитьВременныйКаталогКакТекущий(); - - ИМЯ_КАТАЛОГА = "РодительскийКаталог"; - ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; - - ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ИМЯ_КАТАЛОГА, ИМЯ_ФАЙЛА); - ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; - ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; - ПолныйПутьКаталога = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьКаталога); - ПолныйПутьФайла = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьФайла); - - Если ПередатьКаталог Тогда - Если ПередатьОтносительныйПуть Тогда - Путь = ОтносительныйПутьКаталога; - Иначе - Путь = ПолныйПутьКаталога; - КонецЕсли; - Иначе - Если ПередатьОтносительныйПуть Тогда - Путь = ОтносительныйПутьФайла; - Иначе - Путь = ПолныйПутьФайла; - КонецЕсли; - КонецЕсли; - - ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(Путь); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - - юТест.ПроверитьРавенство(Чтение.Элементы.Количество(), 1, "Количество элементов zip: "); - Элемент = Чтение.Элементы[0]; - юТест.ПроверитьРавенство(СтрЗаменить(ОтносительныйПутьФайла, "\", "/"), - Элемент.ПолноеИмя, "Проверка элемента zip: " + Элемент.ПолноеИмя); - - Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); - - НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); - ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); - - ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); - ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Каталог должен был существовать в корне, а его нет."); - - ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); - ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивВложенныйКаталогСОтносительнымиПутями() Экспорт - УстановитьВременныйКаталогКакТекущий(); - - ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА = "РодительскийКаталог"; - ИМЯ_КАТАЛОГА = "ВложенныйКаталог"; - ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; - - СоздатьКаталог(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); - - ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ОбъединитьПути(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА, ИМЯ_КАТАЛОГА), ИМЯ_ФАЙЛА); - ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; - ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; - - ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(ОтносительныйПутьКаталога); - Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - - НеверныйПутьКаталогаВКорне = ОбъединитьПути(Распаковка, ИМЯ_КАТАЛОГА); - ПроверитьОтсутствиеФайла(НеверныйПутьКаталогаВКорне, "Вложенный каталог не должен был существовать в корне, а он есть."); - - НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); - ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); - - ИскомыйПутьРодительскогоКаталога = ОбъединитьПути(Распаковка, ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); - ПроверитьНаличиеФайла(ИскомыйПутьРодительскогоКаталога, "Родительский каталог должен был существовать в корне, а его нет."); - - ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); - ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Вложенный каталог должен был существовать в каталоге родителя, а его нет."); - - ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); - ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивНесколькоВложенныхКаталоговСОтносительнымиПутями() Экспорт - УстановитьВременныйКаталогКакТекущий(); - - ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА = "РодительскийКаталог"; - ИМЯ_КАТАЛОГА = "ВложенныйКаталог"; - ИМЯ_КАТАЛОГА2 = "ВложенныйКаталог2"; - ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; - ИМЯ_ФАЙЛА2 = "ВложенныйФайл2.txt"; - - СоздатьКаталог(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); - - ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ОбъединитьПути(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА, ИМЯ_КАТАЛОГА), ИМЯ_ФАЙЛА); - ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; - ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; - - ОписаниеКаталога2 = ПодготовитьФайлВоВложенномКаталоге(ОбъединитьПути(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА, ИМЯ_КАТАЛОГА2), ИМЯ_ФАЙЛА2); - ОтносительныйПутьКаталога2 = ОписаниеКаталога2.ПутьКаталога; - ОтносительныйПутьФайла2 = ОписаниеКаталога2.ПутьФайла; - - МассивПутей = Новый Массив; - МассивПутей.Добавить(ОтносительныйПутьКаталога); - МассивПутей.Добавить(ОтносительныйПутьКаталога2); - - ИмяАрхива = ДобавитьКоллекциюФайловИлиКаталоговВАрхивСОтносительнымиПутями(МассивПутей); - Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - - НеверныйПутьКаталогаВКорне = ОбъединитьПути(Распаковка, ИМЯ_КАТАЛОГА); - ПроверитьОтсутствиеФайла(НеверныйПутьКаталогаВКорне, "Вложенный каталог не должен был существовать в корне, а он есть."); - - НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); - ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); - - НеверныйПутьКаталогаВКорне2 = ОбъединитьПути(Распаковка, ИМЯ_КАТАЛОГА2); - ПроверитьОтсутствиеФайла(НеверныйПутьКаталогаВКорне2, "Второй вложенный каталог не должен был существовать в корне, а он есть."); - - НеверныйПутьФайлаВКорне2 = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА2); - ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне2, "Второй файл не должен был существовать в корне распаковки, а он есть."); - - ИскомыйПутьРодительскогоКаталога = ОбъединитьПути(Распаковка, ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); - ПроверитьНаличиеФайла(ИскомыйПутьРодительскогоКаталога, "Родительский каталог должен был существовать в корне, а его нет."); - - ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); - ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Вложенный каталог должен был существовать в каталоге родителя, а его нет."); - - ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); - ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); - - ИскомыйПутьКаталога2 = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога2); - ПроверитьНаличиеФайла(ИскомыйПутьКаталога2, "Второй вложенный каталог должен был существовать в каталоге родителя, а его нет."); - - ИскомыйПутьФайла2 = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла2); - ПроверитьНаличиеФайла(ИскомыйПутьФайла2, "Второй файл должен был существовать во втором вложенном каталоге, а его нет."); - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменемНеИзТекущегоКаталога() Экспорт - ДобавитьВАрхивСОтносительнымиПутямиФайлИлиКаталогСПолнымИменемНеИзТекущегоКаталога(Истина); -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиВложенныйФайлСПолнымИменемНеИзТекущегоКаталога() Экспорт - ДобавитьВАрхивСОтносительнымиПутямиФайлИлиКаталогСПолнымИменемНеИзТекущегоКаталога(Ложь); -КонецПроцедуры - -Процедура ДобавитьВАрхивСОтносительнымиПутямиФайлИлиКаталогСПолнымИменемНеИзТекущегоКаталога(Знач ПередатьКаталог) Экспорт - УстановитьВременныйКаталогКакТекущий(); - - ИМЯ_КАТАЛОГА = "ДругойРодительскийКаталог"; - ИМЯ_ФАЙЛА = "ДругойВложенныйФайл.txt"; - - ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ИМЯ_КАТАЛОГА, ИМЯ_ФАЙЛА); - ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; - ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; - ПолныйПутьКаталога = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьКаталога); - ПолныйПутьФайла = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьФайла); - - УстановитьТекущийКаталог(ТекущийКаталогСохр); - - Если ПередатьКаталог Тогда - Путь = ПолныйПутьКаталога; - Иначе - Путь = ПолныйПутьФайла; - КонецЕсли; - - ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(Путь); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - - юТест.ПроверитьРавенство(Чтение.Элементы.Количество(), 1, "Количество элементов zip: "); - Элемент = Чтение.Элементы[0]; - Если ПередатьКаталог Тогда - юТест.ПроверитьРавенство(ОтносительныйПутьКаталога + ПолучитьРазделительПути(), Элемент.Путь, "Проверка элемента zip: " + Элемент.ПолноеИмя); - Иначе - юТест.ПроверитьРавенство(ИМЯ_ФАЙЛА, Элемент.ПолноеИмя, "Проверка элемента zip: " + Элемент.ПолноеИмя); - юТест.ПроверитьРавенство("", Элемент.Путь, "Проверка пути элемента zip: " + Элемент.ПолноеИмя); - КонецЕсли; - - Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); - - Если ПередатьКаталог Тогда - НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); - ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); - - ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); - ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Каталог должен был существовать в корне, а его нет."); - - ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); - ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); - Иначе - ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); - ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать в корне распаковки, а его нет."); - КонецЕсли; - -КонецПроцедуры - -Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменемНеИзТекущегоКаталога() Экспорт - УстановитьВременныйКаталогКакТекущий(); - - ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; - - ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге("", ИМЯ_ФАЙЛА); - ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; - ПолныйПутьФайла = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьФайла); - УстановитьТекущийКаталог(ТекущийКаталогСохр); - - ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(ПолныйПутьФайла); - - Чтение = Новый ЧтениеZipФайла(ИмяАрхива); - - юТест.ПроверитьРавенство(Чтение.Элементы.Количество(), 1, "Количество элементов zip: "); - Элемент = Чтение.Элементы[0]; - юТест.ПроверитьРавенство(ИМЯ_ФАЙЛА, Элемент.ПолноеИмя, "Проверка элемента zip: " + Элемент.ПолноеИмя); - - Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); - - ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); - ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать в корне распаковки, а его нет."); -КонецПроцедуры - -Функция УстановитьВременныйКаталогКакТекущий() - ИмяКаталогаКорня = юТест.ИмяВременногоФайла(); - СоздатьКаталог(ИмяКаталогаКорня); - УстановитьТекущийКаталог(ИмяКаталогаКорня); - Возврат ТекущийКаталог(); -КонецФункции - -Функция ПодготовитьФайлВоВложенномКаталоге(Знач ИмяКаталога, Знач ИмяФайла) - ПутьКаталога = ИмяКаталога; - Если Не ПустаяСтрока(ИмяКаталога) Тогда - СоздатьКаталог(ПутьКаталога); - ПутьФайла = ОбъединитьПути(ПутьКаталога, ИмяФайла); - Иначе - ПутьФайла = ИмяФайла; - КонецЕсли; - - СоздатьФайл(ПутьФайла); - - Возврат Новый Структура( - "ПутьКаталога, ПутьФайла", ПутьКаталога, ПутьФайла); -КонецФункции - -Функция СоздатьФайл(Знач ПутьФайла = "") - Если ПутьФайла = "" Тогда - ПутьФайла = СоздатьВременныйФайл(); - КонецЕсли; - - ЗТ = Новый ЗаписьТекста(ПутьФайла); - ЗТ.Записать("Привет"); - ЗТ.Закрыть(); - Возврат ПутьФайла; -КонецФункции - -Функция ДобавитьКоллекциюФайловИлиКаталоговВАрхивСОтносительнымиПутями(Знач МассивПутей) - ИмяАрхива = юТест.ИмяВременногоФайла("zip"); - Архив = Новый ЗаписьZIPФайла(ИмяАрхива); - - Для каждого ОтносительныйПуть Из МассивПутей Цикл - Архив.Добавить(ОтносительныйПуть, РежимСохраненияПутейZIP.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); - КонецЦикла; - - Архив.Записать(); - Возврат ИмяАрхива; -КонецФункции - -Функция ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(Знач ОтносительныйПуть) - МассивПутей = Новый Массив; - МассивПутей.Добавить(ОтносительныйПуть); - Возврат ДобавитьКоллекциюФайловИлиКаталоговВАрхивСОтносительнымиПутями(МассивПутей); -КонецФункции - -Функция ИзвлечьВсеИзАрхиваВоВременныйКаталог(Знач ИмяАрхива) - Архив = Новый ЧтениеZipФайла(ИмяАрхива); - Распаковка = юТест.ИмяВременногоФайла(); - СоздатьКаталог(Распаковка); - - Архив.ИзвлечьВсе(Распаковка); - Архив.Закрыть(); - - Возврат Распаковка; -КонецФункции - -Процедура ПроверитьНаличиеФайла(Знач ИскомыйПутьФайла, Знач СообщениеОшибки) - Файл = Новый Файл(ИскомыйПутьФайла); - юТест.ПроверитьИстину(Файл.Существует(), СообщениеОшибки + ИскомыйПутьФайла); -КонецПроцедуры - -Процедура ПроверитьОтсутствиеФайла(Знач НеверныйПутьФайла, Знач СообщениеОшибки) - Файл = Новый Файл(НеверныйПутьФайла); - юТест.ПроверитьЛожь(Файл.Существует(), СообщениеОшибки + НеверныйПутьФайла); -КонецПроцедуры diff --git a/tests/zip/fixtures/testUTF8.zip b/tests/zip/fixtures/testUTF8.zip new file mode 100644 index 000000000..75889de50 Binary files /dev/null and b/tests/zip/fixtures/testUTF8.zip differ diff --git a/tests/zip/test-zip.os b/tests/zip/test-zip.os new file mode 100644 index 000000000..7475ce6ac --- /dev/null +++ b/tests/zip/test-zip.os @@ -0,0 +1,727 @@ +Перем юТест; +Перем ТекущийКаталогСохр; +Перем Чтение; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + + ВсеТесты.Добавить("ТестДолжен_СоздатьАрхивЧерезКонструкторИмениФайла"); + ВсеТесты.Добавить("ТестДолжен_СоздатьАрхивЧерезКонструкторСНеобязательнымиПараметрами"); + ВсеТесты.Добавить("ТестДолжен_СоздатьАрхивЧерезМетодОткрыть"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивОдиночныйФайлБезПутей"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивОдиночныйСПолнымПутем"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивКаталогТестов"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивКаталогТестовПоМаске"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивКаталогСОтносительнымиПутями"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивВложенныйКаталогСОтносительнымиПутями"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивНесколькоВложенныхКаталоговСОтносительнымиПутями"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивФайлСОтносительнымиПутями"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменем"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменем"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменемНеИзТекущегоКаталога"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиВложенныйФайлСПолнымИменемНеИзТекущегоКаталога"); + ВсеТесты.Добавить("ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменемНеИзТекущегоКаталога"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвлечениеБезПутей"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьИзвлечениеБезПутейДляОдиночногоЭлемента"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьРазмерИзвлеченногоОдиночногоЭлемента"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПарольАрхива"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьПарольРусскиеБуквыФайловАрхива"); + ВсеТесты.Добавить("ТестДолжен_ПроверитьШифрованиеЗипАрхива"); + + //Отключен до исправления библиотеки + // https://github.com/EvilBeaver/OneScript/issues/870 + //ВсеТесты.Добавить("ТестДолжен_ПроверитьСимволыЮникодаВИменахФайлов"); + + Возврат ВсеТесты; + +КонецФункции + +Функция СоздатьВременныйФайл(Знач Расширение = "tmp") + Возврат юТест.ИмяВременногоФайла(Расширение); +КонецФункции + +Процедура ПередЗапускомТеста() Экспорт + ТекущийКаталогСохр = ТекущийКаталог(); +КонецПроцедуры + +Процедура ПослеЗапускаТеста() Экспорт + УстановитьТекущийКаталог(ТекущийКаталогСохр); + + Если ЗначениеЗаполнено(Чтение) Тогда + Чтение.Закрыть(); + КонецЕсли; + + юТест.УдалитьВременныеФайлы(); +КонецПроцедуры + +Процедура ТестДолжен_СоздатьАрхивЧерезКонструкторИмениФайла() Экспорт + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(ИмяАрхива); + Архив.Записать(); + + Файл = Новый Файл(ИмяАрхива); + юТест.ПроверитьИстину(Файл.Существует()); + +КонецПроцедуры + +Процедура ТестДолжен_СоздатьАрхивЧерезКонструкторСНеобязательнымиПараметрами() Экспорт + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(ИмяАрхива, "123", 456); + Архив.Записать(); + + Файл = Новый Файл(ИмяАрхива); + юТест.ПроверитьИстину(Файл.Существует()); + +КонецПроцедуры + +Процедура ТестДолжен_СоздатьАрхивЧерезМетодОткрыть() Экспорт + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(); + Архив.Открыть(ИмяАрхива,,"Это комментарий",,УровеньСжатияZip.Максимальный); + Архив.Записать(); + + Файл = Новый Файл(ИмяАрхива); + юТест.ПроверитьИстину(Файл.Существует()); + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивОдиночныйФайлБезПутей() Экспорт + + ФайлСкрипта = ТекущийСценарий().Источник; + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(); + Архив.Открыть(ИмяАрхива,,"Это комментарий",,УровеньСжатияZip.Максимальный); + Архив.Добавить(ФайлСкрипта, РежимСохраненияПутейZip.НеСохранятьПути); + Архив.Записать(); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + + Попытка + юТест.ПроверитьРавенство("", Чтение.Элементы[0].Путь); + юТест.ПроверитьРавенство("test-zip.os", Чтение.Элементы[0].Имя); + юТест.ПроверитьРавенство("test-zip", Чтение.Элементы[0].ИмяБезРасширения); + Исключение + Чтение.Закрыть(); + ВызватьИсключение; + КонецПопытки; + + Чтение.Закрыть(); + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивОдиночныйСПолнымПутем() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(); + Архив.Открыть(ИмяАрхива); + Архив.Добавить(ФайлСкрипта.ПолноеИмя, РежимСохраненияПутейZip.СохранятьПолныеПути); + Архив.Записать(); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + + СИ = Новый СистемнаяИнформация; + Если Найти(СИ.ВерсияОС, "Windows") > 0 Тогда + ИмяБезДиска = Сред(ФайлСкрипта.Путь, Найти(ФайлСкрипта.Путь, "\")+1); + Иначе + ИмяБезДиска = Сред(ФайлСкрипта.Путь,2); + КонецЕсли; + + Попытка + юТест.ПроверитьРавенство(ИмяБезДиска, Чтение.Элементы[0].Путь); + Исключение + Чтение.Закрыть(); + УдалитьФайлы(ИмяАрхива); + ВызватьИсключение; + КонецПопытки; + + Чтение.Закрыть(); + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивКаталогТестов() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); + + ВременныйКаталог = СоздатьВременныйФайл(); + КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); + СоздатьКаталог(КаталогКопииТестов); + + ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.os"); + Для Каждого Файл Из ВсеФайлы Цикл + Если Файл.ЭтоФайл() Тогда + КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); + КонецЕсли; + КонецЦикла; + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(); + Архив.Открыть(ИмяАрхива); + + Архив.Добавить(КаталогКопииТестов + ПолучитьРазделительПути(), РежимСохраненияПутейZip.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); + Архив.Записать(); + + ОжидаемоеИмя = КаталогСкрипта.Имя + ПолучитьРазделительПути(); + Попытка + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + Для Каждого Элемент Из Чтение.Элементы Цикл + юТест.ПроверитьРавенство(ОжидаемоеИмя, Элемент.Путь, "Проверка элемента zip: " + Элемент.ПолноеИмя); + КонецЦикла; + Исключение + Чтение.Закрыть(); + ВызватьИсключение; + КонецПопытки; + юТест.ПроверитьРавенство(ВсеФайлы.Количество(), Чтение.Элементы.Количество()); + Чтение.Закрыть(); +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивКаталогТестовПоМаске() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); + + ВременныйКаталог = СоздатьВременныйФайл(); + КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); + СоздатьКаталог(КаталогКопииТестов); + ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.*"); + РасширениеТестов = ".os"; + КоличествоТестов = 0; + + ДопК = ОбъединитьПути(КаталогКопииТестов, "add"); + СоздатьКаталог(ДопК); + + Для Каждого Файл Из ВсеФайлы Цикл + Если Файл.ЭтоКаталог() Тогда + Продолжить; + КонецЕсли; + + Если Файл.Расширение = РасширениеТестов Тогда + КоличествоТестов = КоличествоТестов + 2; + КонецЕсли; + КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); + КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(ДопК, Файл.Имя)); + КонецЦикла; + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(ИмяАрхива); + Архив.Добавить(ВременныйКаталог + ПолучитьРазделительПути() + "*.os", РежимСохраненияПутейZip.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); + Архив.Записать(); + + ОжидаемоеИмяКорень = КаталогСкрипта.Имя + ПолучитьРазделительПути(); + ОжидаемоеИмяДоп = ОбъединитьПути(КаталогСкрипта.Имя, "add") + ПолучитьРазделительПути(); + Попытка + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + Для Каждого Элемент Из Чтение.Элементы Цикл + юТест.Проверить(Элемент.Путь = ОжидаемоеИмяКорень или Элемент.Путь = ОжидаемоеИмяДоп, "Проверка для пути: " + Элемент.Путь); + юТест.ПроверитьРавенство(РасширениеТестов, Элемент.Расширение); + КонецЦикла; + Исключение + Чтение.Закрыть(); + ВызватьИсключение; + КонецПопытки; + + юТест.ПроверитьИстину(КоличествоТестов > 0); + юТест.ПроверитьРавенство(КоличествоТестов, Чтение.Элементы.Количество()); + + Чтение.Закрыть(); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвлечениеБезПутей() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); + + ВременныйКаталог = СоздатьВременныйФайл(); + КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); + СоздатьКаталог(КаталогКопииТестов); + ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.os"); + Для Каждого Файл Из ВсеФайлы Цикл + Если Файл.ЭтоФайл() Тогда + КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); + КонецЕсли; + КонецЦикла; + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(); + Архив.Открыть(ИмяАрхива); + Архив.Добавить(ВременныйКаталог,РежимСохраненияПутейZip.СохранятьОтносительныеПути,РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); + Архив.Записать(); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + КаталогИзвлечения = СоздатьВременныйФайл(); + СоздатьКаталог(КаталогИзвлечения); + Чтение.ИзвлечьВсе(КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); + Чтение.Закрыть(); + ИзвлеченныеФайлы = НайтиФайлы(КаталогИзвлечения, "*.*"); + + юТест.ПроверитьНеравенство(0, ИзвлеченныеФайлы.Количество()); + юТест.ПроверитьРавенство(ВсеФайлы.Количество(), ИзвлеченныеФайлы.Количество()); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьИзвлечениеБезПутейДляОдиночногоЭлемента() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + КаталогСкрипта = Новый Файл(ФайлСкрипта.Путь); + + ВременныйКаталог = СоздатьВременныйФайл(); + КаталогКопииТестов = ОбъединитьПути(ВременныйКаталог, КаталогСкрипта.Имя); + СоздатьКаталог(КаталогКопииТестов); + ВсеФайлы = НайтиФайлы(КаталогСкрипта.ПолноеИмя, "*.os"); + Для Каждого Файл Из ВсеФайлы Цикл + Если Файл.ЭтоФайл() Тогда + КопироватьФайл(Файл.ПолноеИмя, ОбъединитьПути(КаталогКопииТестов, Файл.Имя)); + КонецЕсли; + КонецЦикла; + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(); + Архив.Открыть(ИмяАрхива); + Архив.Добавить(ВременныйКаталог,РежимСохраненияПутейZip.СохранятьОтносительныеПути,РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); + Архив.Записать(); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + КаталогИзвлечения = СоздатьВременныйФайл(); + СоздатьКаталог(КаталогИзвлечения); + Элемент = Чтение.Элементы.Найти(ФайлСкрипта.Имя); + + Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); + Чтение.Закрыть(); + ИзвлеченныеФайлы = НайтиФайлы(КаталогИзвлечения, "*.os"); + + юТест.ПроверитьНеравенство(0, ИзвлеченныеФайлы.Количество()); + юТест.ПроверитьРавенство(ФайлСкрипта.Имя, ИзвлеченныеФайлы[0].Имя); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьРазмерИзвлеченногоОдиночногоЭлемента() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(); + Архив.Открыть(ИмяАрхива); + Архив.Добавить(ФайлСкрипта.ПолноеИмя); + Архив.Записать(); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + КаталогИзвлечения = СоздатьВременныйФайл(); + СоздатьКаталог(КаталогИзвлечения); + Элемент = Чтение.Элементы.Найти(ФайлСкрипта.Имя); + Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); + Чтение.Закрыть(); + + ИзвлеченныйФайл = Новый Файл(ОбъединитьПути(КаталогИзвлечения, ФайлСкрипта.Имя)); + + юТест.ПроверитьРавенство(ФайлСкрипта.Размер(), ИзвлеченныйФайл.Размер()); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПарольАрхива() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + + Пароль = "password"; + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(ИмяАрхива, Пароль); + Архив.Добавить(ФайлСкрипта.ПолноеИмя); + Архив.Записать(); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива, Пароль); + КаталогИзвлечения = СоздатьВременныйФайл(); + СоздатьКаталог(КаталогИзвлечения); + Элемент = Чтение.Элементы.Найти(ФайлСкрипта.Имя); + Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); + Чтение.Закрыть(); + + ИзвлеченныйФайл = Новый Файл(ОбъединитьПути(КаталогИзвлечения, ФайлСкрипта.Имя)); + + юТест.ПроверитьРавенство(ФайлСкрипта.Размер(), ИзвлеченныйФайл.Размер()); + +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьПарольРусскиеБуквыФайловАрхива() Экспорт + + ФайлСкрипта = Новый Файл(ТекущийСценарий().Источник); + + ИмяРусскогоФайла = "ОбщиеФункции.os"; + + ПутьРусскогоФайла = ОбъединитьПути(ФайлСкрипта.Путь, "../testlib", ИмяРусскогоФайла); + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(ИмяАрхива); + Архив.Добавить(ПутьРусскогоФайла); + Архив.Записать(); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + КаталогИзвлечения = СоздатьВременныйФайл(); + СоздатьКаталог(КаталогИзвлечения); + Элемент = Чтение.Элементы.Найти(ИмяРусскогоФайла); + Чтение.Извлечь(Элемент, КаталогИзвлечения, РежимВосстановленияПутейФайловZIP.НеВосстанавливать); + Чтение.Закрыть(); + + ИзвлеченныйФайл = Новый Файл(ОбъединитьПути(КаталогИзвлечения, ИмяРусскогоФайла)); + + юТест.ПроверитьИстину(ИзвлеченныйФайл.Существует()); + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивКаталогСОтносительнымиПутями() Экспорт + ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Истина, Истина); +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивФайлСОтносительнымиПутями() Экспорт + ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Ложь, Истина); +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменем() Экспорт + ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Истина, Ложь); +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменем() Экспорт + ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Ложь, Ложь); +КонецПроцедуры + +Процедура ДобавитьВАрхивСОтносительнымиПутями_КаталогИлиФайл(Знач ПередатьКаталог, Знач ПередатьОтносительныйПуть) Экспорт + УстановитьВременныйКаталогКакТекущий(); + + ИМЯ_КАТАЛОГА = "РодительскийКаталог"; + ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; + + ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ИМЯ_КАТАЛОГА, ИМЯ_ФАЙЛА); + ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; + ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; + ПолныйПутьКаталога = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьКаталога); + ПолныйПутьФайла = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьФайла); + + Если ПередатьКаталог Тогда + Если ПередатьОтносительныйПуть Тогда + Путь = ОтносительныйПутьКаталога; + Иначе + Путь = ПолныйПутьКаталога; + КонецЕсли; + Иначе + Если ПередатьОтносительныйПуть Тогда + Путь = ОтносительныйПутьФайла; + Иначе + Путь = ПолныйПутьФайла; + КонецЕсли; + КонецЕсли; + + ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(Путь); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + + юТест.ПроверитьРавенство(Чтение.Элементы.Количество(), 1, "Количество элементов zip: "); + Элемент = Чтение.Элементы[0]; + юТест.ПроверитьРавенство(СтрЗаменить(ОтносительныйПутьФайла, "\", "/"), + Элемент.ПолноеИмя, "Проверка элемента zip: " + Элемент.ПолноеИмя); + + Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); + + НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); + ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); + + ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); + ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Каталог должен был существовать в корне, а его нет."); + + ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); + ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивВложенныйКаталогСОтносительнымиПутями() Экспорт + УстановитьВременныйКаталогКакТекущий(); + + ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА = "РодительскийКаталог"; + ИМЯ_КАТАЛОГА = "ВложенныйКаталог"; + ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; + + СоздатьКаталог(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); + + ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ОбъединитьПути(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА, ИМЯ_КАТАЛОГА), ИМЯ_ФАЙЛА); + ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; + ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; + + ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(ОтносительныйПутьКаталога); + Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + + НеверныйПутьКаталогаВКорне = ОбъединитьПути(Распаковка, ИМЯ_КАТАЛОГА); + ПроверитьОтсутствиеФайла(НеверныйПутьКаталогаВКорне, "Вложенный каталог не должен был существовать в корне, а он есть."); + + НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); + ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); + + ИскомыйПутьРодительскогоКаталога = ОбъединитьПути(Распаковка, ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); + ПроверитьНаличиеФайла(ИскомыйПутьРодительскогоКаталога, "Родительский каталог должен был существовать в корне, а его нет."); + + ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); + ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Вложенный каталог должен был существовать в каталоге родителя, а его нет."); + + ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); + ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивНесколькоВложенныхКаталоговСОтносительнымиПутями() Экспорт + УстановитьВременныйКаталогКакТекущий(); + + ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА = "РодительскийКаталог"; + ИМЯ_КАТАЛОГА = "ВложенныйКаталог"; + ИМЯ_КАТАЛОГА2 = "ВложенныйКаталог2"; + ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; + ИМЯ_ФАЙЛА2 = "ВложенныйФайл2.txt"; + + СоздатьКаталог(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); + + ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ОбъединитьПути(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА, ИМЯ_КАТАЛОГА), ИМЯ_ФАЙЛА); + ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; + ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; + + ОписаниеКаталога2 = ПодготовитьФайлВоВложенномКаталоге(ОбъединитьПути(ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА, ИМЯ_КАТАЛОГА2), ИМЯ_ФАЙЛА2); + ОтносительныйПутьКаталога2 = ОписаниеКаталога2.ПутьКаталога; + ОтносительныйПутьФайла2 = ОписаниеКаталога2.ПутьФайла; + + МассивПутей = Новый Массив; + МассивПутей.Добавить(ОтносительныйПутьКаталога); + МассивПутей.Добавить(ОтносительныйПутьКаталога2); + + ИмяАрхива = ДобавитьКоллекциюФайловИлиКаталоговВАрхивСОтносительнымиПутями(МассивПутей); + Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + + НеверныйПутьКаталогаВКорне = ОбъединитьПути(Распаковка, ИМЯ_КАТАЛОГА); + ПроверитьОтсутствиеФайла(НеверныйПутьКаталогаВКорне, "Вложенный каталог не должен был существовать в корне, а он есть."); + + НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); + ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); + + НеверныйПутьКаталогаВКорне2 = ОбъединитьПути(Распаковка, ИМЯ_КАТАЛОГА2); + ПроверитьОтсутствиеФайла(НеверныйПутьКаталогаВКорне2, "Второй вложенный каталог не должен был существовать в корне, а он есть."); + + НеверныйПутьФайлаВКорне2 = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА2); + ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне2, "Второй файл не должен был существовать в корне распаковки, а он есть."); + + ИскомыйПутьРодительскогоКаталога = ОбъединитьПути(Распаковка, ИМЯ_РОДИТЕЛЬСКОГО_КАТАЛОГА); + ПроверитьНаличиеФайла(ИскомыйПутьРодительскогоКаталога, "Родительский каталог должен был существовать в корне, а его нет."); + + ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); + ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Вложенный каталог должен был существовать в каталоге родителя, а его нет."); + + ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); + ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); + + ИскомыйПутьКаталога2 = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога2); + ПроверитьНаличиеФайла(ИскомыйПутьКаталога2, "Второй вложенный каталог должен был существовать в каталоге родителя, а его нет."); + + ИскомыйПутьФайла2 = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла2); + ПроверитьНаличиеФайла(ИскомыйПутьФайла2, "Второй файл должен был существовать во втором вложенном каталоге, а его нет."); + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиКаталогСПолнымИменемНеИзТекущегоКаталога() Экспорт + ДобавитьВАрхивСОтносительнымиПутямиФайлИлиКаталогСПолнымИменемНеИзТекущегоКаталога(Истина); +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиВложенныйФайлСПолнымИменемНеИзТекущегоКаталога() Экспорт + ДобавитьВАрхивСОтносительнымиПутямиФайлИлиКаталогСПолнымИменемНеИзТекущегоКаталога(Ложь); +КонецПроцедуры + +Процедура ДобавитьВАрхивСОтносительнымиПутямиФайлИлиКаталогСПолнымИменемНеИзТекущегоКаталога(Знач ПередатьКаталог) Экспорт + УстановитьВременныйКаталогКакТекущий(); + + ИМЯ_КАТАЛОГА = "ДругойРодительскийКаталог"; + ИМЯ_ФАЙЛА = "ДругойВложенныйФайл.txt"; + + ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге(ИМЯ_КАТАЛОГА, ИМЯ_ФАЙЛА); + ОтносительныйПутьКаталога = ОписаниеКаталога.ПутьКаталога; + ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; + ПолныйПутьКаталога = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьКаталога); + ПолныйПутьФайла = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьФайла); + + УстановитьТекущийКаталог(ТекущийКаталогСохр); + + Если ПередатьКаталог Тогда + Путь = ПолныйПутьКаталога; + Иначе + Путь = ПолныйПутьФайла; + КонецЕсли; + + ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(Путь); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + + юТест.ПроверитьРавенство(Чтение.Элементы.Количество(), 1, "Количество элементов zip: "); + Элемент = Чтение.Элементы[0]; + Если ПередатьКаталог Тогда + юТест.ПроверитьРавенство(ОтносительныйПутьКаталога + ПолучитьРазделительПути(), Элемент.Путь, "Проверка элемента zip: " + Элемент.ПолноеИмя); + Иначе + юТест.ПроверитьРавенство(ИМЯ_ФАЙЛА, Элемент.ПолноеИмя, "Проверка элемента zip: " + Элемент.ПолноеИмя); + юТест.ПроверитьРавенство("", Элемент.Путь, "Проверка пути элемента zip: " + Элемент.ПолноеИмя); + КонецЕсли; + + Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); + + Если ПередатьКаталог Тогда + НеверныйПутьФайлаВКорне = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); + ПроверитьОтсутствиеФайла(НеверныйПутьФайлаВКорне, "Файл не должен был существовать в корне распаковки, а он есть."); + + ИскомыйПутьКаталога = ОбъединитьПути(Распаковка, ОтносительныйПутьКаталога); + ПроверитьНаличиеФайла(ИскомыйПутьКаталога, "Каталог должен был существовать в корне, а его нет."); + + ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ОтносительныйПутьФайла); + ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать во вложенном каталоге, а его нет."); + Иначе + ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); + ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать в корне распаковки, а его нет."); + КонецЕсли; + +КонецПроцедуры + +Процедура ТестДолжен_ДобавитьВАрхивСОтносительнымиПутямиФайлСПолнымИменемНеИзТекущегоКаталога() Экспорт + УстановитьВременныйКаталогКакТекущий(); + + ИМЯ_ФАЙЛА = "ВложенныйФайл.txt"; + + ОписаниеКаталога = ПодготовитьФайлВоВложенномКаталоге("", ИМЯ_ФАЙЛА); + ОтносительныйПутьФайла = ОписаниеКаталога.ПутьФайла; + ПолныйПутьФайла = ОбъединитьПути(ТекущийКаталог(), ОписаниеКаталога.ПутьФайла); + УстановитьТекущийКаталог(ТекущийКаталогСохр); + + ИмяАрхива = ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(ПолныйПутьФайла); + + Чтение = Новый ЧтениеZipФайла(ИмяАрхива); + + юТест.ПроверитьРавенство(Чтение.Элементы.Количество(), 1, "Количество элементов zip: "); + Элемент = Чтение.Элементы[0]; + юТест.ПроверитьРавенство(ИМЯ_ФАЙЛА, Элемент.ПолноеИмя, "Проверка элемента zip: " + Элемент.ПолноеИмя); + + Распаковка = ИзвлечьВсеИзАрхиваВоВременныйКаталог(ИмяАрхива); + + ИскомыйПутьФайла = ОбъединитьПути(Распаковка, ИМЯ_ФАЙЛА); + ПроверитьНаличиеФайла(ИскомыйПутьФайла, "Файл должен был существовать в корне распаковки, а его нет."); +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьШифрованиеЗипАрхива() Экспорт + + Архивируемый = СоздатьФайл(); + Архив = ПолучитьИмяВременногоФайла("zip"); + ЗаписьZipФайла = Новый ЗаписьZipФайла(Архив, "123", , , , МетодШифрованияZIP.AES256); + ЗаписьZipФайла.Добавить(Архивируемый); + ЗаписьZipФайла.Записать(); + + УдалитьФайлы(Архивируемый); + + ЧтениеZipФайла = Новый ЧтениеZipФайла(Архив, "123"); + ЭлементЗашифрован = ЧтениеZipФайла.Элементы[0].Зашифрован; + ЧтениеZipФайла.Закрыть(); + УдалитьФайлы(Архив); + + юТест.ПроверитьИстину(ЭлементЗашифрован); + +КонецПроцедуры + +Функция УстановитьВременныйКаталогКакТекущий() + ИмяКаталогаКорня = юТест.ИмяВременногоФайла(); + СоздатьКаталог(ИмяКаталогаКорня); + УстановитьТекущийКаталог(ИмяКаталогаКорня); + Возврат ТекущийКаталог(); +КонецФункции + +Функция ПодготовитьФайлВоВложенномКаталоге(Знач ИмяКаталога, Знач ИмяФайла) + ПутьКаталога = ИмяКаталога; + Если Не ПустаяСтрока(ИмяКаталога) Тогда + СоздатьКаталог(ПутьКаталога); + ПутьФайла = ОбъединитьПути(ПутьКаталога, ИмяФайла); + Иначе + ПутьФайла = ИмяФайла; + КонецЕсли; + + СоздатьФайл(ПутьФайла); + + Возврат Новый Структура( + "ПутьКаталога, ПутьФайла", ПутьКаталога, ПутьФайла); +КонецФункции + +Функция СоздатьФайл(Знач ПутьФайла = "") + Если ПутьФайла = "" Тогда + ПутьФайла = СоздатьВременныйФайл(); + КонецЕсли; + + ЗТ = Новый ЗаписьТекста(ПутьФайла); + ЗТ.Записать("Привет"); + ЗТ.Закрыть(); + Возврат ПутьФайла; +КонецФункции + +Функция ДобавитьКоллекциюФайловИлиКаталоговВАрхивСОтносительнымиПутями(Знач МассивПутей) + ИмяАрхива = юТест.ИмяВременногоФайла("zip"); + Архив = Новый ЗаписьZIPФайла(ИмяАрхива); + + Для каждого ОтносительныйПуть Из МассивПутей Цикл + Архив.Добавить(ОтносительныйПуть, РежимСохраненияПутейZIP.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно); + КонецЦикла; + + Архив.Записать(); + Возврат ИмяАрхива; +КонецФункции + +Функция ДобавитьФайлИлиКаталогВАрхивСОтносительнымиПутями(Знач ОтносительныйПуть) + МассивПутей = Новый Массив; + МассивПутей.Добавить(ОтносительныйПуть); + Возврат ДобавитьКоллекциюФайловИлиКаталоговВАрхивСОтносительнымиПутями(МассивПутей); +КонецФункции + +Функция ИзвлечьВсеИзАрхиваВоВременныйКаталог(Знач ИмяАрхива) + Архив = Новый ЧтениеZipФайла(ИмяАрхива); + Распаковка = юТест.ИмяВременногоФайла(); + СоздатьКаталог(Распаковка); + + Архив.ИзвлечьВсе(Распаковка); + Архив.Закрыть(); + + Возврат Распаковка; +КонецФункции + +Процедура ПроверитьНаличиеФайла(Знач ИскомыйПутьФайла, Знач СообщениеОшибки) + Файл = Новый Файл(ИскомыйПутьФайла); + юТест.ПроверитьИстину(Файл.Существует(), СообщениеОшибки + ИскомыйПутьФайла); +КонецПроцедуры + +Процедура ПроверитьОтсутствиеФайла(Знач НеверныйПутьФайла, Знач СообщениеОшибки) + Файл = Новый Файл(НеверныйПутьФайла); + юТест.ПроверитьЛожь(Файл.Существует(), СообщениеОшибки + НеверныйПутьФайла); +КонецПроцедуры + +// https://github.com/EvilBeaver/OneScript/issues/870 +Процедура ТестДолжен_ПроверитьСимволыЮникодаВИменахФайлов() Экспорт + ФайлАрхива = ОбъединитьПути(ТекущийСценарий().Каталог, "fixtures", "testUTF8.zip"); + + ВременныйКаталог = СоздатьВременныйФайл(); + СоздатьКаталог(ВременныйКаталог); + + ФайлZip = Новый ЧтениеZipФайла(ФайлАрхива); + Попытка + ФайлZip.ИзвлечьВсе(ВременныйКаталог); + Исключение + ФайлZip.Закрыть(); + ВызватьИсключение; + КонецПопытки; + + ФайлZip.Закрыть(); + +КонецПроцедуры diff --git a/tests/zip/test-zipfilereader-from-stream.os b/tests/zip/test-zipfilereader-from-stream.os new file mode 100644 index 000000000..507992b7f --- /dev/null +++ b/tests/zip/test-zipfilereader-from-stream.os @@ -0,0 +1,56 @@ +Перем юТест; +Перем ИмяФайла; +Перем ИмяАрхива; + +Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт + + юТест = ЮнитТестирование; + + ВсеТесты = Новый Массив; + ВсеТесты.Добавить("ТестДолжен_ПроверитьЧтениеZIPИзПотока"); + + Возврат ВсеТесты; + +КонецФункции + +Процедура ПередЗапускомТеста() Экспорт + ИнициализироватьТестовыеДанные(); +КонецПроцедуры + +Функция СоздатьВременныйФайл(Знач Расширение = "tmp") + Возврат юТест.ИмяВременногоФайла(Расширение); +КонецФункции + +Функция ИнициализироватьТестовыеДанные() Экспорт + + ИмяФайла = СоздатьВременныйФайл("txt"); + Сообщить(ИмяФайла); + Текст = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.ANSI); + Текст.ЗаписатьСтроку("Содержимое файла"); + Текст.Закрыть(); + + ИмяАрхива = СоздатьВременныйФайл("zip"); + Архив = Новый ЗаписьZipФайла(ИмяАрхива); + Архив.Добавить(ИмяФайла); + Архив.Записать(); + + УдалитьФайлы(ИмяФайла); + + Возврат ИмяАрхива; + +КонецФункции + +Функция ТестДолжен_ПроверитьЧтениеZIPИзПотока() Экспорт + + ДвоичныеДанные = Новый ДвоичныеДанные(ИмяАрхива); + Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения(); + ЧтениеZipФайла = Новый ЧтениеZipФайла(Поток); + + Файл = Новый Файл(ИмяФайла); + юТест.ПроверитьЛожь(Файл.Существует(), ИмяФайла + ": должен быть удаленным после архивирования"); + ЧтениеZipФайла.ИзвлечьВсе(КаталогВременныхФайлов(), РежимВосстановленияПутейФайловZIP.НеВосстанавливать); + + Файл = Новый Файл(ИмяФайла); + юТест.ПроверитьИстину(Файл.Существует(), ИмяФайла + ": ожидаемый файл не существует."); + +КонецФункции \ No newline at end of file