Этот документ — практическая инструкция по добавлению в OneScript новых BSL-контекстов (классов), методов и свойств, а также глобальных методов. Здесь собраны готовые сниппеты, чек-лист и ссылки на ключевые места в исходниках.
См. также docs/developer_docs.md — карта компонентов и «куда лезть» при доработках.
- Что такое BSL-контекст
- Добавление нового BSL-класса (контекста)
- Добавление свойства
- Добавление метода
- Создание глобального контекста и глобальных методов
- Регистрация библиотек и package-loader.os
- i18n для API (двуязычные имена)
- Депрекейшен и предупреждения
- Тестирование (C# и BSL)
- Документация (OneScriptDocumenter)
- Безопасность
- Чек-лист готовности
- Контекст — это .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.
using OneScript.Contexts; // ContextClass, ContextMethod, ContextProperty, ScriptConstructor
using OneScript.Execution; // IBslProcess
using OneScript.Types; // TypeActivationContext
using ScriptEngine.Machine; // IValue, ValueFactory
using ScriptEngine.Machine.Contexts; // AutoContext<T>
[ContextClass("ПримерКласс", "SampleClass")]
public class SampleClass : AutoContext<SampleClass>
{
// Конструктор для 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<T>— это стандартная база для классов-контекстов. [ScriptConstructor]— статический фабричный метод, возможно принимающийTypeActivationContext. Можно объявить несколько перегрузок для разных сигнатур конструктора.IBslProcessможно внедрять первым параметром метода, чтобы получить доступ к сервисам/окружению выполнения.- Возвраты:
- Процедура — метод без возвращаемого значения (
void). - Функция — возвращает
IValueили конвертируемый тип C# (см.ContextValuesMarshaller).
- Процедура — метод без возвращаемого значения (
При старте ContextDiscoverer (src/ScriptEngine/Machine/Contexts/ContextDiscoverer.cs) сканирует подключённые сборки и автоматически регистрирует все классы, помеченные атрибутами ContextClass, GlobalContext и EnumerationType. Дополнительная ручная регистрация при этом не требуется.
[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у свойства.- Маршаллинг значения свойства автоматический через
ContextValuesMarshaller. - Для значений, которые часто читаются и не меняются, имеет смысл вычислять и кешировать
IValueв поле, а не пересоздавать его в геттере.
// Функция, возвращающая удвоенное значение
[ContextMethod("УдвоитьЧисло", "DoubleNumber")]
public int DoubleNumber(int number)
{
return number * 2;
}Значения параметров и результат метода будут автоматически сконвертированы из типов C# в типы BSL.
Заметки:
- Передача аргумента по ссылке — используйте тип
IVariableв сигнатуре метода. В него можно присвоить новое значение через.Value. - Передача по значению — используйте типы C# напрямую, если они поддерживаются маршаллером, или
IValue. - Необязательные параметры — задавайте через значения по умолчанию C# (
int count = 0,string mode = null). Эти значения будут видны вызывающему коду BSL. - Перегрузки — поддерживаются: можно объявить несколько методов с одним и тем же
ContextMethod-именем, но с разными сигнатурами.
using OneScript.Contexts; // GlobalContext, ContextMethod, IAttachableContext
using ScriptEngine.Machine; // IValue, ValueFactory
using ScriptEngine.Machine.Contexts; // GlobalContextBase<T>
[GlobalContext(Category = "Мои функции")]
public class MyGlobals : GlobalContextBase<MyGlobals>
{
// Фабрика экземпляра для внедрения в глобальную область
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(src/OneScript.StandardLibrary/StandardGlobalContext.cs): добавьте[ContextMethod]в соответствующий класс и реализуйте логику. - Внимание: изменение публичного API стандартной библиотеки требует обсуждения с мэйнтейнерами.
HostedScriptищет библиотеку и вызываетpackage-loader.os(дефолтный или кастомный из самой библиотеки).- Основные операции загрузчика (см.
src/ScriptEngine.HostedScript/LibraryLoader.cs):ДобавитьКласс/AddClass("path", "ИмяКласса")— регистрирует новый BSL-тип;ДобавитьМодуль/AddModule("path", "ИмяМодуля")— подключает модуль как глобальный;ДобавитьМакет/AddTemplate— регистрирует шаблон.
- Для отладки разрешения зависимостей полезно посмотреть
FileSystemDependencyResolver.cs— порядок поиска и защита от циклических зависимостей.
-
Каждый публичный элемент API (класс, метод, свойство, перечисление, элемент перечисления) должен иметь две формы имени — русскую и английскую — задаваемые в атрибутах:
[ContextClass("ИмяНаРусском", "EnglishName")] [ContextMethod("ВыполнитьДействие", "Perform")] [ContextProperty("Размер", "Size")]
-
Имена должны быть согласованы с уже существующим API: смотрите
OneScript.StandardLibraryи сгенерированный справочник (см. раздел Документация). -
Имена параметров и текст исключений переводить не нужно — они остаются на одном языке (как правило, на русском, в соответствии с привычным стилем 1С).
-
Категории глобальных контекстов (
GlobalContext(Category = "...")) тоже желательно делать осмысленными — они используются в подсказках и группировках в редакторах.
Для обозначения устаревших имён, классов, методов и свойств используется атрибут DeprecatedNameAttribute (src/OneScript.Core/Contexts/DeprecatedNameAttribute.cs):
[ContextMethod("НоваяФункция", "NewFunc")]
[DeprecatedName("СтараяФункция")]
[DeprecatedName("OldFunc")]
public IValue NewFunc() { /* ... */ }- Аргумент
name— устаревший псевдоним. При обращении к нему из BSL вызов всё ещё работает, но в системный лог пишется предупреждение. - Параметр
throwOnUse: trueпревращает использование устаревшего имени в ошибку выполнения. Применяется, когда нужно жёстко удалить старый псевдоним. - Атрибут можно ставить и на сам элемент (
[DeprecatedName("OldName")]рядом с[ContextMethod]), и на отдельные перечисления/типы. - Для классов, которые сами по себе устарели, реализуйте интерфейс
ISupportsDeprecation(src/OneScript.Core/Contexts/ISupportsDeprecation.cs) — возвращайтеIsDeprecated = true. Тогда система сможет логировать факт использования такого типа.
Изменение и удаление публичных имён без депрекейшена считается ломающим изменением — старайтесь сначала пройти этап с предупреждением.
В проекте принято покрывать новые контексты двумя слоями тестов.
-
Тесты лежат в
src/Tests/*(xUnit/NUnit). -
Для контекстов из
OneScript.StandardLibraryподходит проектsrc/Tests/OneScript.StandardLibrary.Tests. Там же есть пример проверки атрибутаDeprecatedName(ObsoleteEnumTest.cs). -
Для проверки кода, который должен компилироваться, используйте инфраструктуру из
src/Tests/OneScript.Dynamic.Tests(CompilerTestBase,CompileHelper). -
Запуск:
dotnet testв каталоге соответствующего проекта или одной командой:dotnet msbuild Build.csproj /t:UnitTests
-
Поведенческие сценарии лежат в каталоге
tests/*.osи совместимы с форматом xUnitFor1C. -
Для нового контекста создайте файл
tests/<ваш-контекст>.osсо списком тестов. Минимальный шаблон:Перем юТест; Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт юТест = ЮнитТестирование; ВсеТесты = Новый Массив; ВсеТесты.Добавить("ТестДолжен_СоздатьЭкземпляр"); Возврат ВсеТесты; КонецФункции Процедура ТестДолжен_СоздатьЭкземпляр() Экспорт Объект = Новый ПримерКласс(); юТест.ПроверитьРавенство("1.0", Объект.Версия); КонецПроцедуры
-
Запуск всего набора тестов через свежесобранный
oscript(см.README.md, раздел «Тестирование»):rem Windows dotnet build src/oscript/oscript.csproj tests\run-bsl-tests.cmd src\oscript\bin\Debug\net8.0\oscript.exe# Linux/macOS dotnet build src/oscript/oscript.csproj tests/run-bsl-tests.sh src/oscript/bin/Debug/net8.0/oscript -
Запуск одного теста:
<путь к oscript> tests/testrunner.os -run tests/<имя файла>.os.
- Утилита
OneScriptDocumenter(см.src/OneScriptDocumenter) формирует справку по платформе из XML-документации сборок и оглавленияdefault_toc.json. - Чтобы ваш контекст попал в справку:
- включите для проекта генерацию XML-документации (это сделано во всех публичных проектах через
oscommon.targets); - снабдите класс/методы/свойства XML-комментариями
<summary>,<param>,<returns>,<example>— они извлекаются документатором; - при необходимости — отредактируйте оглавление
src/OneScriptDocumenter/default_toc.json, чтобы новый раздел появился в нужном месте.
- включите для проекта генерацию XML-документации (это сделано во всех публичных проектах через
- Сгенерировать локально:
dotnet msbuild Build.csproj /t:BuildDocumentation. Результат окажется вbuilt/docs/(markdown + json).
- Не выполняйте произвольный код, поступивший «снаружи». Если контекст исполняет BSL, который пришёл от пользователя, делайте это только через явные точки расширения (
IBslProcess.Run/Eval), осознавая, какие глобальные имена и контексты доступны. - При работе с файловой системой и сетью валидируйте пути и URL, проверяйте кодировки и таймауты. Для долгих операций предусматривайте отмену.
- Не логируйте секреты (пароли, токены, заголовки
Authorization) в открытом виде. - Не доверяйте внешним десериализаторам: для JSON/XML используйте уже существующие в
OneScript.StandardLibraryобёртки и не добавляйте отключение защит «по умолчанию». - Если ваш контекст представляет собой обёртку поверх системного API, явно указывайте в XML-документации, какие побочные эффекты возможны (запись на диск, запуск процессов, сетевые вызовы).
- При добавлении опасных по умолчанию операций предусматривайте явный «согласный» флаг в API, а не включайте их «втихую».
Перед открытием Pull Request убедитесь, что выполнены пункты:
- У всех публичных элементов API заданы оба имени — русское и английское.
- Класс/метод/свойство снабжены XML-комментариями (
<summary>,<param>,<returns>). - Параметры с разумными значениями по умолчанию указаны через дефолты C#, а не через перегрузки-обёртки.
- Если переименовываете существующее имя — добавлен
DeprecatedNameсо старым псевдонимом. - Добавлены модульные тесты на C# и/или приёмочные тесты на BSL.
- Все тесты проходят локально (
dotnet msbuild Build.csproj /t:Test). - Соблюдены требования
CODESTYLE.md. - Проверено, что новый контекст корректно отображается в справке (
/t:BuildDocumentation), если он публичный. - Описание изменений в PR содержит мотивацию и ссылки на тесты/issue.