Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/OneScript.Native/Runtime/CallableMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This Source Code Form is subject to the terms of the
using System.Collections.Generic;
using System.Linq.Expressions;
using OneScript.Contexts;
using OneScript.Exceptions;
using OneScript.Execution;
using OneScript.Native.Compiler;
using OneScript.Values;
Expand Down Expand Up @@ -42,7 +43,16 @@ public BslValue Invoke(IBslProcess process, object target, BslValue[] args)
{
throw new InvalidOperationException($"Method {_method} was not compiled");
}

if (_method.GetBslParameters().Length < args.Length)
{
throw RuntimeException.TooManyArgumentsPassed();
}
if (_method.GetBslParameters().Length > args.Length)
{
// TODO: значения параметров по-умолчанию?
throw RuntimeException.TooFewArgumentsPassed();
}
Comment on lines +46 to +54
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing default-parameter handling causes incorrect TooFewArgumentsPassed exception.

Lines 50-53 throw TooFewArgumentsPassed() whenever args.Length < parameters.Length, but this ignores parameters with default values. A caller passing fewer arguments than the total parameter count is valid when the omitted parameters have defaults.

MachineInstance.SetExecutionFrame (lines 175-184 in the other file) correctly checks paramDef.HasDefaultValue before throwing TooFewArgumentsPassed(). This code must do the same, otherwise delegate invocations of methods with optional parameters will fail.

🔧 Proposed fix
 if (_method.GetBslParameters().Length < args.Length)
 {
     throw RuntimeException.TooManyArgumentsPassed();
 }
-if (_method.GetBslParameters().Length > args.Length)
+
+var parameters = _method.GetBslParameters();
+for (int i = args.Length; i < parameters.Length; i++)
 {
-    // TODO: значения параметров по-умолчанию?
-    throw RuntimeException.TooFewArgumentsPassed();
+    if (!parameters[i].HasDefaultValue)
+    {
+        throw RuntimeException.TooFewArgumentsPassed();
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (_method.GetBslParameters().Length < args.Length)
{
throw RuntimeException.TooManyArgumentsPassed();
}
if (_method.GetBslParameters().Length > args.Length)
{
// TODO: значения параметров по-умолчанию?
throw RuntimeException.TooFewArgumentsPassed();
}
if (_method.GetBslParameters().Length < args.Length)
{
throw RuntimeException.TooManyArgumentsPassed();
}
var parameters = _method.GetBslParameters();
for (int i = args.Length; i < parameters.Length; i++)
{
if (!parameters[i].HasDefaultValue)
{
throw RuntimeException.TooFewArgumentsPassed();
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/OneScript.Native/Runtime/CallableMethod.cs` around lines 46 - 54, The
check in CallableMethod.Invoke (or wherever CallableMethod.cs validates
arguments) incorrectly throws RuntimeException.TooFewArgumentsPassed when
args.Length < _method.GetBslParameters().Length without considering parameters
that have defaults; update the logic that compares _method.GetBslParameters() to
args to iterate the trailing/missing parameters and allow omitted ones that have
paramDef.HasDefaultValue (same approach as MachineInstance.SetExecutionFrame
uses) instead of immediately throwing, so only throw TooFewArgumentsPassed when
a missing parameter lacks HasDefaultValue.


var callableWrapper = GetCallableWrapper(target);
return _delegate.Invoke(callableWrapper, args, process);
}
Expand Down
10 changes: 10 additions & 0 deletions src/OneScript.StandardLibrary/DelegateAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ public override void CallAsProcedure(int methodNumber, IValue[] arguments, IBslP
_action(process, arguments);
}

[ContextMethod("Выполнить")]
public BslValue Execute(IBslProcess process, params BslValue[] p)
{
var retValue = _action(process, p);
return retValue is IValueReference r
? r.BslValue
: (BslValue)(retValue ?? ValueFactory.Create())
;
}

[ScriptConstructor]
public static DelegateAction Create(IRuntimeContextInstance target, string methodName)
{
Expand Down
10 changes: 9 additions & 1 deletion src/ScriptEngine/Machine/MachineInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,11 @@ private void SetExecutionFrame(ExecutionFrame frame, MachineMethodInfo methodInf
frame.MethodName = methodInfo.Name;
frame.InstructionPointer = methDescr.EntryPoint;

var parameters = methodInfo.GetBslParameters();
var parameters = methodInfo.GetBslParameters();
if (argValues.Length > parameters.Length)
{
throw RuntimeException.TooManyArgumentsPassed();
}
var variables = methDescr.LocalVariables;
var locals = new IVariable[variables.Length];
int i = 0;
Expand Down Expand Up @@ -171,6 +175,10 @@ private void SetExecutionFrame(ExecutionFrame frame, MachineMethodInfo methodInf
for (; i < parameters.Length; i++)
{
var paramDef = parameters[i];
if (!paramDef.HasDefaultValue)
{
throw RuntimeException.TooFewArgumentsPassed();
}
var value = paramDef.HasDefaultValue ? (IValue)paramDef.DefaultValue : ValueFactory.Create();
locals[i] = Variable.Create(value, variables[i]);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Tests/DocumenterTests/XmlDocConversionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public void TestConversionOfTextBlock()
var expected =
@"Для Каждого Переменная Из ПеременныеСреды() Цикл
Сообщить(Переменная.Ключ + "" = "" + Переменная.Значение);
КонецЦикла;";
КонецЦикла;"
.ReplaceLineEndings("\n");

Assert.Equal(expected, text);
}
Expand Down
99 changes: 99 additions & 0 deletions tests/cross-delegate-native.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#native

///////////////////////////////////////////////////////////////////////
// Тест класса Действие/Action
///////////////////////////////////////////////////////////////////////

Перем юТест;

////////////////////////////////////////////////////////////////////
// Программный интерфейс

Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт

юТест = ЮнитТестирование;

ВсеТесты = Новый Массив;

ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаТеста_Стэковый");
ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаТеста_Родной");

ВсеТесты.Добавить("ТестДолжен_ПроверитьКоличествоПараметров_Стэковый");
ВсеТесты.Добавить("ТестДолжен_ПроверитьКоличествоПараметров_Родной");

Возврат ВсеТесты;

КонецФункции

Процедура ТестДолжен_ВыполнитьДействиеОбъектаТеста(Знач ТипДелегата)

Делегат = ТестовыйДелегат(ТипДелегата);
Делегат.Выполнить("123", 456);

КонецПроцедуры

Процедура ТестДолжен_ПроверитьКоличествоПараметров(Знач ТипДелегата)

Делегат = ТестовыйДелегат(ТипДелегата);

ЕстьОшибка = Ложь;
Попытка
Делегат.Выполнить("123");
Исключение
ЕстьОшибка = Истина;
КонецПопытки;

юТест.ПроверитьИстину(ЕстьОшибка, "Вызов с недостаточным количеством параметров должен бросать исключение");

ЕстьОшибка = Ложь;
Попытка
Делегат.Выполнить("123", 456, '00010101');
Исключение
ЕстьОшибка = Истина;
КонецПопытки;

юТест.ПроверитьИстину(ЕстьОшибка, "Вызов с избыточным количеством параметров должен бросать исключение");

КонецПроцедуры

Процедура ТестДолжен_ВыполнитьДействиеОбъектаТеста_Стэковый() Экспорт
ТестДолжен_ВыполнитьДействиеОбъектаТеста("#stack");
КонецПроцедуры

Процедура ТестДолжен_ВыполнитьДействиеОбъектаТеста_Родной() Экспорт
ТестДолжен_ВыполнитьДействиеОбъектаТеста("#native");
КонецПроцедуры

Процедура ТестДолжен_ПроверитьКоличествоПараметров_Родной() Экспорт
ТестДолжен_ПроверитьКоличествоПараметров("#native")
КонецПроцедуры

Процедура ТестДолжен_ПроверитьКоличествоПараметров_Стэковый() Экспорт
ТестДолжен_ПроверитьКоличествоПараметров("#stack")
КонецПроцедуры

Функция ТестовыйДелегат(Знач ТипДелегата)

ПутьКСкрипту = юТест.ИмяВременногоФайла("os");
ИмяКласса = "Класс" + СтрЗаменить(Новый УникальныйИдентификатор, "-", "");

ТекстКласса = ТипДелегата +
"
|
|Процедура Тест(Парам, Парам2) Экспорт
| Сообщить(Парам + Парам2);
|КонецПроцедуры";

ЗаписьТекста = Новый ЗаписьТекста(ПутьКСкрипту);
ЗаписьТекста.Записать(ТекстКласса);
ЗаписьТекста.Закрыть();

ПодключитьСценарий(ПутьКСкрипту, ИмяКласса);

Класс = Новый(ИмяКласса);

Делегат = Новый Действие(Класс, "Тест");

Возврат Делегат;

КонецФункции
99 changes: 99 additions & 0 deletions tests/cross-delegate-stack.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#stack

///////////////////////////////////////////////////////////////////////
// Тест класса Действие/Action
///////////////////////////////////////////////////////////////////////

Перем юТест;

////////////////////////////////////////////////////////////////////
// Программный интерфейс

Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт

юТест = ЮнитТестирование;

ВсеТесты = Новый Массив;

ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаТеста_Стэковый");
ВсеТесты.Добавить("ТестДолжен_ВыполнитьДействиеОбъектаТеста_Родной");

ВсеТесты.Добавить("ТестДолжен_ПроверитьКоличествоПараметров_Стэковый");
ВсеТесты.Добавить("ТестДолжен_ПроверитьКоличествоПараметров_Родной");

Возврат ВсеТесты;

КонецФункции

Процедура ТестДолжен_ВыполнитьДействиеОбъектаТеста(Знач ТипДелегата)

Делегат = ТестовыйДелегат(ТипДелегата);
Делегат.Выполнить("123", 456);

КонецПроцедуры

Процедура ТестДолжен_ПроверитьКоличествоПараметров(Знач ТипДелегата)

Делегат = ТестовыйДелегат(ТипДелегата);

ЕстьОшибка = Ложь;
Попытка
Делегат.Выполнить("123");
Исключение
ЕстьОшибка = Истина;
КонецПопытки;

юТест.ПроверитьИстину(ЕстьОшибка, "Вызов с недостаточным количеством параметров должен бросать исключение");

ЕстьОшибка = Ложь;
Попытка
Делегат.Выполнить("123", 456, '00010101');
Исключение
ЕстьОшибка = Истина;
КонецПопытки;

юТест.ПроверитьИстину(ЕстьОшибка, "Вызов с избыточным количеством параметров должен бросать исключение");

КонецПроцедуры

Процедура ТестДолжен_ВыполнитьДействиеОбъектаТеста_Стэковый() Экспорт
ТестДолжен_ВыполнитьДействиеОбъектаТеста("#stack");
КонецПроцедуры

Процедура ТестДолжен_ВыполнитьДействиеОбъектаТеста_Родной() Экспорт
ТестДолжен_ВыполнитьДействиеОбъектаТеста("#native");
КонецПроцедуры

Процедура ТестДолжен_ПроверитьКоличествоПараметров_Родной() Экспорт
ТестДолжен_ПроверитьКоличествоПараметров("#native")
КонецПроцедуры

Процедура ТестДолжен_ПроверитьКоличествоПараметров_Стэковый() Экспорт
ТестДолжен_ПроверитьКоличествоПараметров("#stack")
КонецПроцедуры

Функция ТестовыйДелегат(Знач ТипДелегата)

ПутьКСкрипту = юТест.ИмяВременногоФайла("os");
ИмяКласса = "Класс" + СтрЗаменить(Новый УникальныйИдентификатор, "-", "");

ТекстКласса = ТипДелегата +
"
|
|Процедура Тест(Парам, Парам2) Экспорт
| Сообщить(Парам + Парам2);
|КонецПроцедуры";

ЗаписьТекста = Новый ЗаписьТекста(ПутьКСкрипту);
ЗаписьТекста.Записать(ТекстКласса);
ЗаписьТекста.Закрыть();

ПодключитьСценарий(ПутьКСкрипту, ИмяКласса);

Класс = Новый(ИмяКласса);

Делегат = Новый Действие(Класс, "Тест");

Возврат Делегат;

КонецФункции