Skip to content

Commit b0d32e0

Browse files
committed
fixes #1546 маршаллинг обязательных параметров при передаче Неопределено
1 parent d147a30 commit b0d32e0

5 files changed

Lines changed: 99 additions & 56 deletions

File tree

src/OneScript.Core/Exceptions/RuntimeException.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public static RuntimeException InvalidArgumentType(string argName)
8383
$"Invalid type of argument '{argName}'");
8484
}
8585

86-
public static RuntimeException InvalidArgumentType(int argNum, string argName )
86+
public static RuntimeException InvalidArgumentType(int argNum, string argName)
8787
{
8888
return new RuntimeException(
8989
$"Неверный тип аргумента номер {argNum} '{argName}'",

src/ScriptEngine/Machine/Contexts/ContextMethodMapper.cs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -234,21 +234,26 @@ private static InvocationExpression MethodCallExpression(
234234

235235
for (int bslIndex = 0,clrIndex = clrIndexStart; bslIndex < argsLen; bslIndex++, clrIndex++)
236236
{
237-
var convertMethod = _genConvertParamMethod.MakeGenericMethod(parameters[clrIndex].ParameterType);
238-
237+
var targetType = parameters[clrIndex].ParameterType;
238+
var convertMethod = ContextValuesMarshaller.BslGenericParameterConverter.MakeGenericMethod(targetType);
239+
239240
Expression defaultArg;
240241
if (parameters[clrIndex].HasDefaultValue)
241242
{
242-
defaultArg = Expression.Constant(parameters[clrIndex].DefaultValue, parameters[clrIndex].ParameterType);
243+
defaultArg = Expression.Constant(parameters[clrIndex].DefaultValue, targetType);
243244
}
244245
else
245246
{
246-
defaultArg = Expression.Default(parameters[clrIndex].ParameterType);
247+
defaultArg = ContextValuesMarshaller.GetDefaultBslValueConstant(targetType);
247248
}
248249

249250
var indexedArg = Expression.ArrayIndex(argsParam, Expression.Constant(bslIndex));
250-
var conversionCall = Expression.Call(convertMethod, indexedArg, defaultArg, processParam);
251-
argsPass.Add(conversionCall);
251+
var conversionCall = Expression.Call(convertMethod,
252+
indexedArg,
253+
defaultArg,
254+
processParam);
255+
256+
argsPass.Add(Expression.Convert(conversionCall, targetType));
252257
}
253258

254259
var methodCall = Expression.Invoke(methodClojure, argsPass);
@@ -281,19 +286,6 @@ private static Expression CreateDelegateExpr(MethodInfo target)
281286

282287
return methodClojure;
283288
}
284-
285-
private static readonly MethodInfo _genConvertParamMethod =
286-
typeof(InternalMethInfo).GetMethod(nameof(ConvertParam),
287-
BindingFlags.Static | BindingFlags.NonPublic);
288-
289-
private static T ConvertParam<T>(IValue value, T def, IBslProcess process)
290-
{
291-
if (value == null || value.IsSkippedArgument())
292-
return def;
293-
294-
return ContextValuesMarshaller.ConvertParam<T>(value, process, def);
295-
}
296289
}
297-
298290
}
299291
}

src/ScriptEngine/Machine/Contexts/ContextValuesMarshaller.cs

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ This Source Code Form is subject to the terms of the
55
at http://mozilla.org/MPL/2.0/.
66
----------------------------------------------------------*/
77
using System;
8+
using System.Diagnostics;
89
using System.Linq;
10+
using System.Linq.Expressions;
911
using System.Reflection;
1012
using OneScript.Commons;
1113
using OneScript.Contexts;
@@ -19,37 +21,46 @@ namespace ScriptEngine.Machine.Contexts
1921
public static class ContextValuesMarshaller
2022
{
2123
public static MethodInfo BslParameterConverter { get; private set; }
22-
public static MethodInfo BslParameterGenericConverter { get; private set; }
24+
public static MethodInfo BslGenericParameterConverter { get; private set; }
2325
public static MethodInfo BslReturnValueGenericConverter { get; private set; }
2426

2527
static ContextValuesMarshaller()
2628
{
2729
BslParameterConverter = typeof(ContextValuesMarshaller).GetMethods()
28-
.First(x => x.Name == nameof(ConvertParam) && x.GetGenericArguments().Length == 0 && x.GetParameters().Length == 3);
30+
.First(x => x.Name == nameof(ConvertParam) && x.GetGenericArguments().Length == 0 &&
31+
x.GetParameters().Length == 4);
2932

30-
BslParameterGenericConverter = typeof(ContextValuesMarshaller).GetMethods()
31-
.First(x => x.Name == nameof(ConvertParam) && x.GetGenericArguments().Length == 1 && x.GetParameters().Length == 3);
33+
BslGenericParameterConverter = typeof(ContextValuesMarshaller).GetMethods()
34+
.First(x => x.Name == nameof(ConvertParam) && x.GetGenericArguments().Length == 1 &&
35+
x.GetParameters().Length == 3);
3236

3337
BslReturnValueGenericConverter = typeof(ContextValuesMarshaller).GetMethods()
3438
.First(x => x.Name == nameof(ConvertReturnValue) && x.GetGenericArguments().Length == 1);
3539
}
3640

37-
public static T ConvertParam<T>(IValue value, T defaultValue = default)
38-
{
39-
return ConvertParam<T>(value, ForbiddenBslProcess.Instance, defaultValue);
40-
}
41-
42-
public static T ConvertParam<T>(IValue value, IBslProcess process, T defaultValue = default)
41+
/// <summary>
42+
/// Выполняет конвертацию значения из Bsl в значение параметра метода C#
43+
/// </summary>
44+
/// <param name="value">Универсальное значение из Bsl</param>
45+
/// <param name="type">Целевой тип в который надо сконвертировать значение.</param>
46+
/// <param name="defaultValue">Значение по умолчанию, которое будет возвращено, если <paramref name="value"/> не заполнен.</param>
47+
/// <param name="process">Текущий BSL-процесс, в рамках которого вызывается метод</param>
48+
/// <returns>Значение целевого типа</returns>
49+
public static object ConvertParam(IValue value, Type type, object defaultValue, IBslProcess process)
4350
{
44-
object valueObj = ConvertParam(value, typeof(T), process);
45-
return valueObj != null ? (T)valueObj : defaultValue;
51+
Debug.Assert(defaultValue == null || type.IsInstanceOfType(defaultValue));
52+
53+
var converted = ConvertParam(value, type, process);
54+
return converted ?? defaultValue;
4655
}
4756

48-
public static object ConvertParam(IValue value, Type type)
49-
{
50-
return ConvertParam(value, type, ForbiddenBslProcess.Instance);
51-
}
52-
57+
/// <summary>
58+
/// Выполняет конвертацию значения из Bsl в значение параметра метода C#
59+
/// </summary>
60+
/// <param name="value">Универсальное значение из Bsl</param>
61+
/// <param name="type">Целевой тип в который надо сконвертировать значение.</param>
62+
/// <param name="process">Текущий BSL-процесс, в рамках которого вызывается метод</param>
63+
/// <returns>Значение целевого типа</returns>
5364
public static object ConvertParam(IValue value, Type type, IBslProcess process)
5465
{
5566
try
@@ -65,12 +76,40 @@ public static object ConvertParam(IValue value, Type type, IBslProcess process)
6576
throw RuntimeException.InvalidArgumentValue();
6677
}
6778
}
68-
79+
80+
/// <summary>
81+
/// Выполняет конвертацию значения из Bsl в значение параметра метода C#.
82+
/// В данный метод нельзя передавать значения, конвертация которых в целевой тип (напр. строку)
83+
/// может приводить к вызову другого Bsl-кода. Метод выбросит исключение, если конвертация захочет выполнить bsl-код.
84+
/// </summary>
85+
/// <param name="value">Универсальное значение из Bsl</param>
86+
/// <param name="defaultValue">Значение по умолчанию, которое будет возвращено, если <paramref name="value"/> не заполнен.</param>
87+
/// <typeparam name="T">Целевой тип в который надо сконвертировать значение</typeparam>
88+
/// <returns>Значение целевого типа</returns>
89+
public static T ConvertParam<T>(IValue value, T defaultValue = default)
90+
{
91+
return ConvertParam<T>(value, defaultValue, ForbiddenBslProcess.Instance);
92+
}
93+
6994
/// <summary>
70-
/// Выполняет строгую конвертацию парамтера в запрошенный тип.
95+
/// Выполняет конвертацию значения из Bsl в значение параметра метода C#
96+
/// </summary>
97+
/// <param name="value">Универсальное значение из Bsl</param>
98+
/// <param name="process">Текущий BSL-процесс, в рамках которого вызывается метод</param>
99+
/// <param name="defaultValue">Значение по умолчанию, которое будет возвращено, если <paramref name="value"/> не заполнен.</param>
100+
/// <typeparam name="T">Целевой тип в который надо сконвертировать значение</typeparam>
101+
/// <returns>Значение целевого типа</returns>
102+
public static T ConvertParam<T>(IValue value, T defaultValue, IBslProcess process)
103+
{
104+
object valueObj = ConvertParam(value, typeof(T), process);
105+
return valueObj != null ? (T)valueObj : defaultValue;
106+
}
107+
108+
/// <summary>
109+
/// Выполняет строгую конвертацию параметра в запрошенный тип.
71110
/// Не выполняет приведение объектов к строке, в отличие от ConvertParam.
72111
/// Это значит, что нельзя скормить объект в C# параметр с типом string через конверсию в AsString.
73-
/// Выдает исключение о неверном типе парамтера.
112+
/// Выдает исключение о неверном типе параметра.
74113
/// </summary>
75114
public static T ConvertValueStrict<T>(IValue value)
76115
{
@@ -102,6 +141,21 @@ public static T ConvertValueStrict<T>(IValue value)
102141
}
103142
}
104143

144+
public static Expression GetDefaultBslValueConstant(Type targetType)
145+
{
146+
if (targetType == typeof(string))
147+
{
148+
return Expression.Constant("");
149+
}
150+
151+
return Expression.Default(targetType);
152+
}
153+
154+
public static object ConvertParam(IValue value, Type type)
155+
{
156+
return ConvertParam(value, type, ForbiddenBslProcess.Instance);
157+
}
158+
105159
private static object ConvertValueType(IValue value, Type type, IBslProcess process)
106160
{
107161
object valueObj;

src/ScriptEngine/Machine/TypeFactory.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,33 +115,30 @@ private InstanceConstructor CreateConstructor(IValue[] arguments)
115115
++paramIndex;
116116
break;
117117
}
118-
119118

120119
if (parameters[paramIndex].ParameterType == typeof(IValue))
121120
argsToPass.Add(Expression.ArrayIndex(argsParam, Expression.Constant(i)));
122121
else
123122
{
124123
var conversionArg = Expression.ArrayIndex(argsParam, Expression.Constant(i));
125-
if (parameters[i].HasDefaultValue)
126-
{
127-
var convertMethod = ContextValuesMarshaller.BslParameterGenericConverter.MakeGenericMethod(parameters[i].ParameterType);
128-
var defaultArg = Expression.Constant(parameters[i].DefaultValue, parameters[i].ParameterType);
124+
var targetType = parameters[paramIndex].ParameterType;
125+
var convertMethod = ContextValuesMarshaller.BslGenericParameterConverter.MakeGenericMethod(targetType);
129126

130-
var marshalledArg = Expression.Call(convertMethod, conversionArg, bslProcessParameter, defaultArg);
131-
argsToPass.Add(marshalledArg);
127+
Expression marshalledArg;
128+
if (parameters[paramIndex].HasDefaultValue)
129+
{
130+
var defaultArg = Expression.Constant(parameters[paramIndex].DefaultValue, targetType);
131+
marshalledArg = Expression.Call(convertMethod, conversionArg, defaultArg, bslProcessParameter);
132132
}
133133
else
134134
{
135-
// FIXME: Сомнительно, что тут надо использовать non-generic вариант вызова
136-
// а потом делать cast в тип параметра. Кажется, что можно использовать BslParameterGenericConverter
137-
var marshalledArg = Expression.Call(
138-
ContextValuesMarshaller.BslParameterConverter,
135+
marshalledArg = Expression.Call(
136+
convertMethod,
139137
conversionArg,
140-
Expression.Constant(parameters[paramIndex].ParameterType),
138+
ContextValuesMarshaller.GetDefaultBslValueConstant(targetType),
141139
bslProcessParameter);
142-
143-
argsToPass.Add(Expression.Convert(marshalledArg, parameters[paramIndex].ParameterType));
144140
}
141+
argsToPass.Add(Expression.Convert(marshalledArg, targetType));
145142
}
146143

147144
++paramIndex;

src/ScriptEngine/Machine/ValueBinder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] m
3434

3535
public override object ChangeType(object value, Type type, CultureInfo culture)
3636
{
37-
if (value is IValue)
37+
if (value is IValue iValue)
3838
{
39-
return ContextValuesMarshaller.ConvertParam((IValue) value, type);
39+
return ContextValuesMarshaller.ConvertParam(iValue, type);
4040
}
4141
return Type.DefaultBinder.ChangeType(value, type, culture);
4242
}

0 commit comments

Comments
 (0)