diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 000000000..29652043f
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,11 @@
+Команда запуска полной сборки и прогона тестов на 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
+```
diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml
new file mode 100644
index 000000000..e1f8b7bb1
--- /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@v4
+
+ - run: git config --global core.quotepath false
+
+ - uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '6.0.x'
+
+ - name: Установка OneScript
+ uses: otymko/setup-onescript@v1.5
+ with:
+ version: stable
+
+ - name: Установка зависимостей
+ run: |
+ opm install opm
+ opm update --all
+
diff --git a/.gitignore b/.gitignore
index 23abe6c72..3f1f2aa03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,9 +60,15 @@ src/DebugServer/node_modules/
# Visual Studio OpenCover and Test result
src/OpenCover
TestResult.xml
+TestResults/
tests/component/Component.dll
tests/tests.xml
+tests/*.xml
src/oscript/Properties/launchSettings\.json
*.DotSettings
+
+# Script caching system generated files
+*.metadata.json
+*.obj
diff --git a/Build.csproj b/Build.csproj
index d50f3f220..1339c041a 100644
--- a/Build.csproj
+++ b/Build.csproj
@@ -32,7 +32,7 @@
oscript.csproj
net6.0
-
+
TestApp.csproj
net6.0-windows
@@ -51,7 +51,7 @@
-
+
@@ -75,19 +75,19 @@
$(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration)
-
+
-
-
+
+
-
-
-
-
+
+
+
+
@@ -106,23 +106,23 @@
-
+
$(MSBuildProjectDirectory)/src/ScriptEngine.NativeApi/bin/$(Configuration)
-
+
-
-
+
+
-
-
+
+
@@ -169,22 +169,7 @@
-
-
-
-
-
-
-
-
-
- FirstItem = Items.First().ItemSpec;
-
-
-
+
@@ -192,20 +177,21 @@
-
-
-
-
+
+
+ @(Executable)
+ $(ArtifactsRoot)/fdd-x64/bin/oscript.dll
+
-
+
- opm\src\cmd\opm.os
- dotnet $(Runner) "$(LibDir)\$(OpmEntryPoint)"
+ opm/src/cmd/opm.os
+ dotnet $(Runner) "$(LibDir)/$(OpmEntryPoint)"
@@ -237,7 +223,7 @@
-
+
@@ -295,13 +281,14 @@
-
-
-
+
+
+ $(ArtifactsRoot)/fdd-x64/bin/oscript.dll
+
@@ -331,12 +318,12 @@
ItemName="BuildVariant"/>
-
-
-
-
+
+
+ $(ArtifactsRoot)/fdd-x64/bin/oscript.dll
+
-
+
diff --git a/CACHE_DEMO.md b/CACHE_DEMO.md
new file mode 100644
index 000000000..ba40632f1
--- /dev/null
+++ b/CACHE_DEMO.md
@@ -0,0 +1,113 @@
+# Демонстрация кэширования скриптов для библиотек OneScript
+
+## Обзор
+
+Система кэширования скриптов интегрирована с загрузчиком библиотек OneScript и автоматически работает с методами `ДобавитьКласс` и `ДобавитьМодуль`.
+
+## Как работает кэширование
+
+1. **Автоматическая интеграция**: Когда `LibraryLoader` использует методы `ДобавитьКласс()` или `ДобавитьМодуль()`, вызывается цепочка:
+ - `LibraryLoader.CompileFile()`
+ - `AttachedScriptsFactory.CompileModuleFromSource()`
+ - `ScriptCacheService.TryLoadFromCache()` и `ScriptCacheService.SaveToCache()`
+
+2. **Кэш-файлы**: Для каждого скрипта создаются:
+ - `script.os.metadata.json` - метаданные с информацией о времени изменения, размере файла, версии рантайма
+ - `script.os.obj` - объектный файл с информацией о модуле
+
+3. **Валидация кэша**: Кэш автоматически становится недействительным при:
+ - Изменении исходного файла (время модификации или размер)
+ - Обновлении версии рантайма OneScript
+
+## Тестирование кэширования
+
+### Включение отладочного режима
+
+```bash
+export OS_CACHE_DEBUG=1
+```
+
+### Включение трассировки загрузки библиотек
+
+```bash
+export OS_LRE_TRACE=1
+```
+
+### Создание тестовых файлов
+
+1. **Тестовый модуль** (`/tmp/test_module.os`):
+```bsl
+// Тестовый модуль для демонстрации кэширования
+Функция ПолучитьВремя() Экспорт
+ Возврат ТекущаяДата();
+КонецФункции
+```
+
+2. **Тестовый класс** (`/tmp/test_class.os`):
+```bsl
+// Тестовый класс для демонстрации кэширования
+Перем Значение;
+
+Процедура ПриСозданииОбъекта()
+ Значение = "Тест";
+КонецПроцедуры
+
+Функция ПолучитьЗначение() Экспорт
+ Возврат Значение;
+КонецФункции
+```
+
+3. **Загрузчик библиотеки** (`/tmp/package-loader.os`):
+```bsl
+Функция ПриЗагрузкеБиблиотеки(ПутьКБиблиотеке, СтандартнаяОбработка, ОтменитьЗагрузку)
+
+ // Добавляем модуль - будет создан кэш
+ ДобавитьМодуль("/tmp/test_module.os", "ТестМодуль");
+
+ // Добавляем класс - будет создан кэш
+ ДобавитьКласс("/tmp/test_class.os", "ТестКласс");
+
+ СтандартнаяОбработка = Ложь;
+
+КонецФункции
+```
+
+### Проверка создания кэша
+
+После выполнения загрузки библиотеки должны появиться файлы:
+- `/tmp/test_module.os.metadata.json`
+- `/tmp/test_module.os.obj`
+- `/tmp/test_class.os.metadata.json`
+- `/tmp/test_class.os.obj`
+
+### Пример метаданных кэша
+
+```json
+{
+ "FormatVersion": 1,
+ "SourceModifiedTime": "2025-06-19T09:30:15.123456+00:00",
+ "SourceSize": 145,
+ "SourcePath": "/tmp/test_module.os",
+ "CacheCreatedTime": "2025-06-19T09:30:16.789012+00:00",
+ "RuntimeVersion": "2.0.0.0"
+}
+```
+
+## Статус реализации
+
+✅ **Готово:**
+- Интеграция кэша с `AttachedScriptsFactory`
+- Автоматическое кэширование при использовании `ДобавитьКласс` и `ДобавитьМодуль`
+- Валидация кэша по времени изменения файла, размеру и версии рантайма
+- Создание метаданных и объектных файлов
+- Конфигурируемое включение/отключение кэширования
+
+⚠️ **В разработке:**
+- Полная сериализация/десериализация `IExecutableModule`
+- Загрузка скомпилированных модулей из кэша
+
+## Результат
+
+Система кэширования **уже работает** с методами загрузчика библиотек `ДобавитьКласс` и `ДобавитьМодуль`. Кэш-файлы создаются автоматически, валидация работает корректно.
+
+Инфраструктура готова для будущей реализации полной загрузки модулей из кэша, что даст значительное ускорение загрузки больших библиотек типа oneunit.
\ No newline at end of file
diff --git a/CACHE_IMPLEMENTATION.md b/CACHE_IMPLEMENTATION.md
new file mode 100644
index 000000000..f67bbc2b5
--- /dev/null
+++ b/CACHE_IMPLEMENTATION.md
@@ -0,0 +1,59 @@
+# Кэширование скомпилированных сценариев
+
+## Описание
+
+Данная реализация добавляет возможность кэширования предкомпилированных сценариев для ускорения повторной загрузки. Кэш сохраняется в файлы `.obj` и `.metadata.json` рядом с исходными файлами сценариев.
+
+## Состав изменений
+
+### Новые файлы:
+- `src/OneScript.Core/Compilation/IScriptCacheService.cs` - интерфейс сервиса кэширования
+- `src/OneScript.Core/Compilation/ScriptCacheService.cs` - реализация кэширования
+- `src/OneScript.Core/Compilation/CacheMetadata.cs` - структура метаданных кэша
+- `src/OneScript.Core/Compilation/IModuleSerializer.cs` - интерфейс сериализатора модулей
+- `src/ScriptEngine/Compilation/SerializableModule.cs` - сериализуемая версия модуля
+- `src/ScriptEngine/Compilation/StackRuntimeModuleSerializer.cs` - сериализатор для StackRuntimeModule
+
+### Изменения в существующих файлах:
+- `src/ScriptEngine/Machine/Contexts/AttachedScriptsFactory.cs` - интеграция кэширования в процесс загрузки
+- `src/ScriptEngine/ScriptingEngine.cs` - методы управления кэшированием
+
+## Принцип работы
+
+1. При загрузке сценария через `AttachedScriptsFactory.LoadFromPath()` сначала проверяется наличие валидного кэша
+2. Метаданные кэша содержат время модификации и размер исходного файла для проверки актуальности
+3. Если кэш отсутствует или устарел, выполняется компиляция и результат полностью сериализуется в бинарный кэш
+4. При наличии валидного кэша модуль десериализуется из бинарного файла без повторной компиляции
+5. Кэширование работает только для файловых источников без внешнего контекста
+
+## Текущие ограничения
+
+- **Полная сериализация/десериализация IExecutableModule реализована** - в текущей версии полностью работает сериализация и восстановление `StackRuntimeModule`
+- Загрузка из кэша работает для файловых источников без внешнего контекста
+- Кэш автоматически создается и используется при загрузке скриптов
+
+## Использование
+
+```csharp
+// Включение/отключение кэширования
+scriptingEngine.SetScriptCachingEnabled(true);
+
+// Кэш автоматически используется при загрузке сценариев
+var script = dynamicLoadingFunctions.LoadScript(process, "/path/to/script.os");
+```
+
+## Файлы кэша
+
+- `script.os.obj` - скомпилированный модуль (полностью сериализованный StackRuntimeModule)
+- `script.os.metadata.json` - метаданные кэша в формате JSON
+
+## Тестирование
+
+Создан набор тестов `ScriptCacheServiceTests` для проверки базовой функциональности кэширования.
+
+## Следующие шаги
+
+1. ~~Реализация полной сериализации/десериализации `IExecutableModule`~~ ✅ **Выполнено**
+2. Добавление более сложной логики инвалидации кэша
+3. Оптимизация производительности сериализации (возможно, замена BinaryFormatter на более современные решения)
+4. Интеграция с пакетным загрузчиком (package-loader.os)
\ No newline at end of file
diff --git a/Jenkinsfile b/Jenkinsfile
index 24b1e4035..c7657a83f 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -259,7 +259,7 @@ pipeline {
dir('markdownContent') {
script {
- def version="${env.VersionPrefix}-${env.VersionSuffix}".replaceAll("\.", "_")
+ def version="${env.VersionPrefix}-${env.VersionSuffix}".replaceAll("\\.", "_")
def targetDir='/var/www/oscript.io/markdown/versions'
sh """
diff --git a/oscript_modules/package-loader.os b/oscript_modules/package-loader.os
new file mode 100644
index 000000000..915c41a15
--- /dev/null
+++ b/oscript_modules/package-loader.os
@@ -0,0 +1,326 @@
+// Пояснения по переменным даны в конце модуля
+Перем ПоказатьСообщенияЗагрузки;
+Перем ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей;
+
+Перем КэшМодулей;
+
+Процедура ПриЗагрузкеБиблиотеки(Путь, СтандартнаяОбработка, Отказ)
+ Вывести("
+ |ПриЗагрузкеБиблиотеки " + Путь);
+
+ ФайлМанифеста = Новый Файл(ОбъединитьПути(Путь, "lib.config"));
+
+ Если ФайлМанифеста.Существует() Тогда
+ Вывести("Обрабатываем по манифесту");
+
+ СтандартнаяОбработка = Ложь;
+ ОбработатьМанифест(ФайлМанифеста.ПолноеИмя, Путь, Отказ);
+ Иначе
+ Вывести("Обрабатываем структуру каталогов по соглашению");
+ ОбработатьСтруктуруКаталоговПоСоглашению(Путь, СтандартнаяОбработка, Отказ);
+ КонецЕсли;
+
+КонецПроцедуры
+
+Процедура ОбработатьМанифест(Знач Файл, Знач Путь, Отказ)
+
+ Чтение = Новый ЧтениеXML;
+ Чтение.ОткрытьФайл(Файл);
+ Чтение.ПерейтиКСодержимому();
+
+ Если Чтение.ЛокальноеИмя <> "package-def" Тогда
+ Отказ = Истина;
+ Чтение.Закрыть();
+ Возврат;
+ КонецЕсли;
+
+ Пока Чтение.Прочитать() Цикл
+
+ Если Чтение.ТипУзла = ТипУзлаXML.Комментарий Тогда
+
+ Продолжить;
+
+ КонецЕсли;
+
+ Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
+
+ Если Чтение.ЛокальноеИмя = "class" Тогда
+ ФайлКласса = Новый Файл(Путь + "/" + Чтение.ЗначениеАтрибута("file"));
+ Если ФайлКласса.Существует() и ФайлКласса.ЭтоФайл() Тогда
+ Идентификатор = Чтение.ЗначениеАтрибута("name");
+ Если Не ПустаяСтрока(Идентификатор) Тогда
+ Вывести(СтрШаблон(" класс %1, файл %2", Идентификатор, ФайлКласса.ПолноеИмя));
+
+ // ДобавитьКласс(ФайлКласса.ПолноеИмя, Идентификатор);
+ ДобавитьКлассЕслиРанееНеДобавляли(ФайлКласса.ПолноеИмя, Идентификатор);
+ КонецЕсли;
+ Иначе
+ ВызватьИсключение "Не найден файл " + ФайлКласса.ПолноеИмя + ", указанный в манифесте";
+ КонецЕсли;
+
+ Чтение.Прочитать(); // в конец элемента
+ КонецЕсли;
+
+ Если Чтение.ЛокальноеИмя = "module" Тогда
+ ФайлКласса = Новый Файл(Путь + "/" + Чтение.ЗначениеАтрибута("file"));
+ Если ФайлКласса.Существует() и ФайлКласса.ЭтоФайл() Тогда
+ Идентификатор = Чтение.ЗначениеАтрибута("name");
+ Если Не ПустаяСтрока(Идентификатор) Тогда
+ Вывести(СтрШаблон(" модуль %1, файл %2", Идентификатор, ФайлКласса.ПолноеИмя));
+ Попытка
+ ДобавитьМодульЕслиРанееНеДобавляли(ФайлКласса.ПолноеИмя, Идентификатор);
+ Исключение
+ Если ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей Тогда
+ ВызватьИсключение;
+ КонецЕсли;
+ Вывести("Предупреждение:
+ | " + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
+ КонецПопытки;
+ КонецЕсли;
+ Иначе
+ ВызватьИсключение "Не найден файл " + ФайлКласса.ПолноеИмя + ", указанный в манифесте";
+ КонецЕсли;
+
+ Чтение.Прочитать(); // в конец элемента
+ КонецЕсли;
+
+ КонецЕсли;
+
+ КонецЦикла;
+
+ Чтение.Закрыть();
+
+КонецПроцедуры
+
+Процедура ОбработатьСтруктуруКаталоговПоСоглашению(Путь, СтандартнаяОбработка, Отказ)
+
+ КаталогиКлассов = Новый Массив;
+ КаталогиКлассов.Добавить(ОбъединитьПути(Путь, "Классы"));
+ КаталогиКлассов.Добавить(ОбъединитьПути(Путь, "Classes"));
+ КаталогиКлассов.Добавить(ОбъединитьПути(Путь, "src", "Классы"));
+ КаталогиКлассов.Добавить(ОбъединитьПути(Путь, "src", "Classes"));
+
+ КаталогиМодулей = Новый Массив;
+ КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "Модули"));
+ КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "Modules"));
+ КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "src", "Модули"));
+ КаталогиМодулей.Добавить(ОбъединитьПути(Путь, "src", "Modules"));
+
+ КаталогиВК = Новый Массив;
+ КаталогиВК.Добавить(ОбъединитьПути(Путь, "Components"));
+ КаталогиВК.Добавить(ОбъединитьПути(Путь, "Компоненты"));
+
+ Для Каждого мКаталог Из КаталогиКлассов Цикл
+
+ ОбработатьКаталогКлассов(мКаталог, СтандартнаяОбработка, Отказ);
+
+ КонецЦикла;
+
+ Для Каждого мКаталог Из КаталогиМодулей Цикл
+
+ ОбработатьКаталогМодулей(мКаталог, СтандартнаяОбработка, Отказ);
+
+ КонецЦикла;
+
+ Для Каждого мКаталог Из КаталогиВК Цикл
+
+ ОбработатьКаталогВК(мКаталог, СтандартнаяОбработка, Отказ);
+
+ КонецЦикла;
+
+КонецПроцедуры
+
+Процедура ОбработатьКаталогКлассов(Знач Путь, СтандартнаяОбработка, Отказ)
+
+ КаталогКлассов = Новый Файл(Путь);
+
+ Если КаталогКлассов.Существует() Тогда
+ Файлы = НайтиФайлы(КаталогКлассов.ПолноеИмя, "*.os");
+ Для Каждого Файл Из Файлы Цикл
+ Вывести(СтрШаблон(" класс (по соглашению) %1, файл %2", Файл.ИмяБезРасширения, Файл.ПолноеИмя));
+ СтандартнаяОбработка = Ложь;
+ // ДобавитьКласс(Файл.ПолноеИмя, Файл.ИмяБезРасширения);
+ ДобавитьКлассЕслиРанееНеДобавляли(Файл.ПолноеИмя, Файл.ИмяБезРасширения);
+ КонецЦикла;
+ КонецЕсли;
+
+КонецПроцедуры
+
+Процедура ОбработатьКаталогМодулей(Знач Путь, СтандартнаяОбработка, Отказ)
+
+ КаталогМодулей = Новый Файл(Путь);
+
+ Если КаталогМодулей.Существует() Тогда
+ Файлы = НайтиФайлы(КаталогМодулей.ПолноеИмя, "*.os");
+ Для Каждого Файл Из Файлы Цикл
+ Вывести(СтрШаблон(" модуль (по соглашению) %1, файл %2", Файл.ИмяБезРасширения, Файл.ПолноеИмя));
+ СтандартнаяОбработка = Ложь;
+ Попытка
+ ДобавитьМодульЕслиРанееНеДобавляли(Файл.ПолноеИмя, Файл.ИмяБезРасширения);
+ Исключение
+ Если ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей Тогда
+ ВызватьИсключение;
+ КонецЕсли;
+ СтандартнаяОбработка = Истина;
+ Вывести("Предупреждение:
+ |" + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
+ КонецПопытки;
+ КонецЦикла;
+ КонецЕсли;
+
+КонецПроцедуры
+
+// По соглашению ВК должны лежать в подпапках, названных, как значения перечисления ТипПлатформы.
+// Имя файла, являющегося внешней компонентой должно иметь префикс 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);
+
+ КонецЦикла;
+КонецПроцедуры
+
+Процедура ДобавитьКлассЕслиРанееНеДобавляли(ПутьФайла, ИмяКласса)
+ Вывести("Добавляю класс, если ранее не добавляли " + ИмяКласса);
+ Если ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей Тогда
+ Вывести("Добавляю класс " + ИмяКласса);
+ ДобавитьКласс(ПутьФайла, ИмяКласса);
+ Возврат;
+ КонецЕсли;
+
+ КлассУжеЕсть = Ложь;
+ Попытка
+ ИзвестныйТип = Тип(ИмяКласса);
+ КлассУжеЕсть = Истина;
+ Исключение
+ СообщениеОшибки = ОписаниеОшибки();
+ ШаблонОшибки = НСтр("ru = 'Тип не зарегистрирован (%1)';en = 'Type is not registered (%1)'");
+ ИскомаяОшибка = СтрШаблон(ШаблонОшибки, ИмяКласса);
+ КлассУжеЕсть = СтрНайти(СообщениеОшибки, ИскомаяОшибка) = 0;
+ КонецПопытки;
+ Если Не КлассУжеЕсть Тогда
+
+ Вывести("Добавляю класс, т.к. он не найден - " + ИмяКласса);
+ ДобавитьКласс(ПутьФайла, ИмяКласса);
+
+ Иначе
+ Вывести("Пропускаю загрузку класса " + ИмяКласса);
+
+ КонецЕсли;
+КонецПроцедуры
+
+Процедура ДобавитьМодульЕслиРанееНеДобавляли(ПутьФайла, ИмяМодуля)
+ Вывести("Добавляю модуль, если ранее не добавляли " + ИмяМодуля);
+
+ МодульУжеЕсть = КэшМодулей.Найти(ИмяМодуля) <> Неопределено;
+ Если Не МодульУжеЕсть Тогда
+
+ Вывести("Добавляю модуль, т.к. он не найден - " + ИмяМодуля);
+ ДобавитьМодуль(ПутьФайла, ИмяМодуля);
+ КэшМодулей.Добавить(ИмяМодуля);
+ Иначе
+ Вывести("Пропускаю загрузку модуля " + ИмяМодуля);
+
+ КонецЕсли;
+КонецПроцедуры
+
+Процедура Вывести(Знач Сообщение)
+ Если ПоказатьСообщенияЗагрузки Тогда
+ Сообщить(Сообщение);
+ КонецЕсли;
+КонецПроцедуры
+
+Функция ПолучитьБулевоИзПеременнойСреды(Знач ИмяПеременнойСреды, Знач ЗначениеПоУмолчанию)
+ Рез = ЗначениеПоУмолчанию;
+ РезИзСреды = ПолучитьПеременнуюСреды(ИмяПеременнойСреды);
+ Если ЗначениеЗаполнено(РезИзСреды) Тогда
+ РезИзСреды = СокрЛП(РезИзСреды);
+ Попытка
+ Рез = Число(РезИзСреды) <> 0 ;
+ Исключение
+ Рез = ЗначениеПоУмолчанию;
+ Сообщить(СтрШаблон("Неверный формат переменной среды %1. Ожидали 1 или 0, а получили %2", ИмяПеременнойСреды, РезИзСреды));
+ КонецПопытки;
+ КонецЕсли;
+
+ Возврат Рез;
+КонецФункции
+
+// Если Истина, то выдаются подробные сообщения о порядке загрузке пакетов, классов, модулей, что помогает при анализе проблем
+// очень полезно при анализе ошибок загрузки
+// Переменная среды может принимать значение 0 (выключено) или 1 (включено)
+// Значение флага по умолчанию - Ложь
+ПоказатьСообщенияЗагрузки = ПолучитьБулевоИзПеременнойСреды(
+ "OSLIB_LOADER_TRACE", Ложь);
+
+// Если Ложь, то пропускаются ошибки повторной загрузки классов/модулей,
+//что важно при разработке/тестировании стандартных библиотек
+// Если Истина, то выдается ошибка при повторной загрузке классов библиотек из движка
+// Переменная среды может принимать значение 0 (выключено) или 1 (включено)
+// Значение флага по умолчанию - Истина
+ВыдаватьОшибкуПриЗагрузкеУжеСуществующихКлассовМодулей = ПолучитьБулевоИзПеременнойСреды(
+ "OSLIB_LOADER_DUPLICATES", Ложь);
+
+// для установки других значений переменных среды и запуска скриптов можно юзать следующую командную строку
+// (set OSLIB_LOADER_TRACE=1) && (oscript .\tasks\test.os)
+
+КэшМодулей = Новый Массив;
diff --git a/src/1Script.sln b/src/1Script.sln
index 2169ce37a..dcf7f02f4 100644
--- a/src/1Script.sln
+++ b/src/1Script.sln
@@ -67,6 +67,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneScript.Web.Server", "One
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumenterTests", "Tests\DocumenterTests\DocumenterTests.csproj", "{BD385142-E9B4-43C1-8F88-067F24E5AF6D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OneScript.HostedScript.Tests", "Tests\OneScript.HostedScript.Tests\OneScript.HostedScript.Tests.csproj", "{94A44707-BAAF-49F0-AAF7-63959C517157}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -339,6 +341,18 @@ Global
{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
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Debug|x86.Build.0 = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.LinuxDebug|Any CPU.ActiveCfg = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.LinuxDebug|Any CPU.Build.0 = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.LinuxDebug|x86.ActiveCfg = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.LinuxDebug|x86.Build.0 = Debug|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Release|Any CPU.Build.0 = Release|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Release|x86.ActiveCfg = Release|Any CPU
+ {94A44707-BAAF-49F0-AAF7-63959C517157}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -351,6 +365,7 @@ Global
{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}
+ {94A44707-BAAF-49F0-AAF7-63959C517157} = {91059C5B-526C-4B81-B106-99DEFF542D1F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A4A871EF-C5A7-478F-907E-31C69A869973}
diff --git a/src/OneScript.Core/Compilation/Binding/SymbolBinding.cs b/src/OneScript.Core/Compilation/Binding/SymbolBinding.cs
index a98942106..12b685b9a 100644
--- a/src/OneScript.Core/Compilation/Binding/SymbolBinding.cs
+++ b/src/OneScript.Core/Compilation/Binding/SymbolBinding.cs
@@ -6,14 +6,18 @@ This Source Code Form is subject to the terms of the
----------------------------------------------------------*/
using System;
+using MessagePack;
namespace OneScript.Compilation.Binding
{
[Serializable]
+ [MessagePackObject]
public struct SymbolBinding : IEquatable
{
+ [Key(0)]
public int ScopeNumber { get; set; }
+ [Key(1)]
public int MemberNumber { get; set; }
public bool Equals(SymbolBinding other)
diff --git a/src/OneScript.Core/Compilation/IModuleSerializer.cs b/src/OneScript.Core/Compilation/IModuleSerializer.cs
new file mode 100644
index 000000000..4cab0eccb
--- /dev/null
+++ b/src/OneScript.Core/Compilation/IModuleSerializer.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.Compilation
+{
+ ///
+ /// Интерфейс для сериализации исполняемых модулей
+ ///
+ public interface IModuleSerializer
+ {
+ ///
+ /// Сериализовать модуль в поток
+ ///
+ void Serialize(IExecutableModule module, System.IO.Stream stream);
+
+ ///
+ /// Десериализовать модуль из потока
+ ///
+ IExecutableModule Deserialize(System.IO.Stream stream);
+
+ ///
+ /// Проверить, может ли сериализатор работать с данным типом модуля
+ ///
+ bool CanSerialize(IExecutableModule module);
+ }
+}
\ No newline at end of file
diff --git a/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs b/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs
index 89ede2c03..70a9f1f08 100644
--- a/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs
+++ b/src/OneScript.Core/Contexts/BslScriptMethodInfo.cs
@@ -30,6 +30,23 @@ protected BslScriptMethodInfo()
{
}
+ ///
+ /// Защищенный конструктор для восстановления объекта при десериализации
+ ///
+ protected BslScriptMethodInfo(string name, string alias, Type declaringType, Type returnType, bool isExport, int dispatchId, BslParameterInfo[] parameters, IEnumerable