diff --git a/src/OneScript.StandardLibrary/Reflector.cs b/src/OneScript.StandardLibrary/Reflector.cs index 6c3e8ff65..25b270c93 100644 --- a/src/OneScript.StandardLibrary/Reflector.cs +++ b/src/OneScript.StandardLibrary/Reflector.cs @@ -51,72 +51,63 @@ public IValue CallMethod(IBslProcess process, IRuntimeContextInstance target, st 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.CallParameters); - + var argsToPass = target.DynamicMethodSignatures ? GetArgsToPass(arguments) : GetArgsToPass(arguments, methInfo.CallParameters); + IValue retValue = BslUndefinedValue.Instance; - try - { - if (methInfo.IsFunction()) - { - target.CallAsFunction(methodIdx, argsToPass, out retValue, process); - } - else - { - target.CallAsProcedure(methodIdx, argsToPass, process); - } - } - catch (Exception ex) + if (methInfo.IsFunction()) { - CopyArgsBack(argsToPass, arguments); - throw ex; + target.CallAsFunction(methodIdx, argsToPass, out retValue, process); } + else + { + target.CallAsProcedure(methodIdx, argsToPass, process); + } - CopyArgsBack(argsToPass, arguments); return retValue; } - private static void CopyArgsBack(IValue[] argsToPass, ArrayImpl arguments) + // в динамические методы все параметры будут переданы как переменные-ссылки на элементы массива аргументов + // это дает возможность возвращать значения через параметры + // ответственность за неизменность параметров, передаваемых по значению, - на вызываемом методе + private static IValue[] GetArgsToPass(ArrayImpl arguments) { - if (arguments == null) - return; + var argsToPass = new IValue[arguments?.Count() ?? 0]; - for (int i = 0; i < argsToPass.Length; i++) + for (int i = 0; i < argsToPass.Length; ++i) { - if (i < arguments.Count()) - { - arguments.Set(i, argsToPass[i] is IValueReference r ? r.Value : argsToPass[i]); - } - } - } - + argsToPass[i] = Variable.CreateIndexedPropertyReference(arguments, ValueFactory.Create(i), "$argref"); + } + + return argsToPass; + } + private static IValue[] GetArgsToPass(ArrayImpl arguments, ReadOnlySpan parameters) { - var argValues = arguments?.ToArray() ?? Array.Empty(); + var argValues = arguments?.ToArray() ?? []; // ArrayImpl не может (не должен!) содержать null или NotAValidValue - - if (argValues.Length > parameters.Length) - throw RuntimeException.TooManyArgumentsPassed(); - var argsToPass = new IValue[parameters.Length]; + int parCount = parameters.Length; + int argCount = argValues.Length; + + if (argCount > parCount) + throw RuntimeException.TooManyArgumentsPassed(); - int i = 0; - for (; i < argValues.Length; i++) + var argsToPass = argValues; + if (argCount < parCount) + { + Array.Resize(ref argsToPass, parCount); + while (--parCount >= argCount) + { + if (!parameters[parCount].HasDefaultValue) + throw RuntimeException.TooFewArgumentsPassed(); + // else keep null as a default value + } + } + + for (int i = 0; i < argCount; 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 + argsToPass[i] = Variable.CreateIndexedPropertyReference(arguments, ValueFactory.Create(i), ""); } return argsToPass; diff --git a/src/ScriptEngine/Machine/MachineInstance.cs b/src/ScriptEngine/Machine/MachineInstance.cs index 5471c9487..649b88997 100644 --- a/src/ScriptEngine/Machine/MachineInstance.cs +++ b/src/ScriptEngine/Machine/MachineInstance.cs @@ -904,7 +904,8 @@ private IValue[] PopArguments() for (--argCount; argCount >= 0; --argCount) { - args[argCount] = _operationStack.Pop(); + var arg = _operationStack.Pop(); + args[argCount] = arg.IsSkippedArgument() ? null : arg; } return args; } @@ -961,26 +962,18 @@ private bool MethodCallImpl(int arg, bool asFunc) private void CallContext(IRuntimeContextInstance instance, int index, BslMethodInfo methodInfo, IValue[] argValues, bool asFunc) { - IValue[] realArgs; - if (instance.DynamicMethodSignatures) - { - realArgs = argValues; - } - else - { - realArgs = new IValue[methodInfo.CallParameters.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; - } - } - + IValue[] realArgs = argValues; + if (!instance.DynamicMethodSignatures) + { + int parCount = methodInfo.CallParameters.Length; + int argCount = argValues.Length; + if (argCount < parCount) + { + Array.Resize(ref realArgs, parCount); + // для неуказанных параметров останется null + } + } + if (asFunc) { instance.CallAsFunction(index, realArgs, out IValue retVal, _process); @@ -1043,56 +1036,53 @@ private void ResolveMethodFunc(int arg) private void PrepareContextCallArguments(int arg, out IRuntimeContextInstance context, out int methodId, out IValue[] argValues) { var factArgs = PopArguments(); - var argCount = factArgs.Length; - + var argCount = factArgs.Length; + argValues = factArgs; + var objIValue = _operationStack.Pop(); context = objIValue.AsObject(); var methodName = _module.Identifiers[arg]; - methodId = context.GetMethodNumber(methodName); - + 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.CallParameters; - - 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(); - } - } + return; + } + + var methodInfo = context.GetMethodInfo(methodId); + var methodParams = methodInfo.CallParameters; + int parCount = methodParams.Length; + + if (argCount > parCount) + throw RuntimeException.TooManyArgumentsPassed(); + + if (argCount < parCount) + { + Array.Resize(ref argValues, parCount); + while (--parCount >= argCount) + { + if (!methodParams[parCount].HasDefaultValue) + throw RuntimeException.TooFewArgumentsPassed(); + // иначе останется null как неуказанный параметр + } + } + + for (int i = 0; i < argCount; i++) + { + var argValue = factArgs[i]; + if (argValue is IValueReference r) + { + if (!methodParams[i].IsByRef) + argValues[i] = r.Value; + } + else if (argValue is not null) + { + if (methodParams[i].IsByRef) + argValues[i] = Variable.Create(argValue, ""); + } + else if (!methodParams[i].HasDefaultValue) + throw RuntimeException.MissedArgument(); + } } private void Jmp(int arg) diff --git a/tests/reflector.os b/tests/reflector.os index c143bbf00..5c92ac878 100644 --- a/tests/reflector.os +++ b/tests/reflector.os @@ -27,6 +27,8 @@ ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаСПараметрамиПоУмолчанию"); ВсеТесты.Добавить("ТестДолжен_ПроверитьВызовМетодаБезПараметровПоУмолчанию"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПередачуПараметровПоУмолчанию"); + + ВсеТесты.Добавить("ТестДолжен_ПроверитьВозвратПараметраДляДинамическогоМетода"); ВсеТесты.Добавить("ТестДолжен_ПроверитьМетодСуществуетДляТипа"); ВсеТесты.Добавить("ТестДолжен_ПроверитьПолучитьТаблицуМетодовДляТипа"); @@ -971,4 +973,30 @@ Таблица = Рефлектор.ПолучитьТаблицуСвойств(ФиксСтруктура); юТест.ПроверитьРавенство(2, Таблица.Количество()); юТест.ПроверитьНеРавенство(Неопределено, Таблица.Найти("Поле1")); -КонецПроцедуры \ No newline at end of file +КонецПроцедуры + + +Процедура ТестВозвратаПараметров(Пар1, Знач Пар2) Экспорт + Пар1 = -11; + Пар2 = -22; +КонецПроцедуры + +Процедура ТестДолжен_ПроверитьВозвратПараметраДляДинамическогоМетода() Экспорт + + Действие = Новый Действие(ЭтотОбъект, "ТестВозвратаПараметров"); + + Рефлектор = Новый Рефлектор; + П1 = 11; + П2 = 22; + + Аргументы = Новый Массив; + Аргументы.Добавить(П1); + Аргументы.Добавить(П2); + Рефлектор.ВызватьМетод(Действие, "Выполнить", Аргументы); + + юТест.ПроверитьРавенство(-11, Аргументы[0], "Изменение параметра"); + юТест.ПроверитьРавенство(22, Аргументы[1], "Сохранение параметра по значению"); + юТест.ПроверитьРавенство(11, П1, "Сохранение переменной"); + +КонецПроцедуры +