Skip to content

Commit 134a8c2

Browse files
committed
feat(mono-rt2): allow default parameters for dynfunc
1 parent 675069c commit 134a8c2

File tree

1 file changed

+43
-17
lines changed

1 file changed

+43
-17
lines changed

code/client/clrcore-v2/DynFunc.cs

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ private static DynFunc Construct(object target, MethodInfo method)
126126
hasThis ? new[] { typeof(object), typeof(Remote), typeof(object[]) } : new[] { typeof(Remote), typeof(object[]) });
127127

128128
ILGenerator g = lambda.GetILGenerator();
129+
g.DeclareLocal(typeof(uint));
129130

130131
OpCode ldarg_remote, ldarg_args;
131132
if (hasThis)
@@ -141,8 +142,13 @@ private static DynFunc Construct(object target, MethodInfo method)
141142
ldarg_args = OpCodes.Ldarg_1;
142143
}
143144

145+
g.Emit(ldarg_args);
146+
g.Emit(OpCodes.Ldlen);
147+
g.Emit(OpCodes.Stloc_0);
144148
for (int i = 0, p = 0; i < parameters.Length; ++i)
145149
{
150+
Label lblOutOfRange = g.DefineLabel();
151+
Label lblNextArgument = g.DefineLabel();
146152
var parameter = parameters[i];
147153
var t = parameter.ParameterType;
148154

@@ -176,27 +182,15 @@ private static DynFunc Construct(object target, MethodInfo method)
176182
throw new ArgumentException($"{nameof(SourceAttribute)} used on type {t}, this type can't be constructed with parameter Remote.");
177183
}
178184

185+
g.Emit(OpCodes.Ldc_I4, p);
186+
g.Emit(OpCodes.Ldloc_0);
187+
g.Emit(OpCodes.Bge_Un, lblOutOfRange);
179188
g.Emit(ldarg_args);
180189
g.Emit(OpCodes.Ldc_I4_S, (byte)p++);
181190
g.Emit(OpCodes.Ldelem_Ref);
182191

183192
if (t.IsValueType)
184193
{
185-
Label diff = g.DefineLabel();
186-
Label done = g.DefineLabel();
187-
188-
g.Emit(OpCodes.Dup);
189-
190-
// check if given type is already of the target type
191-
g.Emit(OpCodes.Isinst, t);
192-
g.Emit(OpCodes.Brfalse_S, diff);
193-
194-
// same type, unbox and pass it as an argument
195-
g.Emit(OpCodes.Unbox_Any, t);
196-
g.Emit(OpCodes.Br, done);
197-
198-
// not the same type, try and convert it
199-
g.MarkLabel(diff);
200194
if (t.IsPrimitive)
201195
{
202196
g.Emit(OpCodes.Call, convertMethods[t]); // already handles null
@@ -206,14 +200,46 @@ private static DynFunc Construct(object target, MethodInfo method)
206200
g.Emit(OpCodes.Pop);
207201
g.Emit(OpCodes.Ldloc_S, g.DeclareLocal(t));
208202
}
209-
210-
g.MarkLabel(done);
211203
}
212204
else if (t != typeof(object))
213205
{
214206
g.Emit(OpCodes.Castclass, t); // throwing cast, exception on fail
215207
//g.Emit(OpCodes.Isinst, t); // non throwing cast, null on fail
216208
}
209+
g.Emit(OpCodes.Br, lblNextArgument);
210+
211+
g.MarkLabel(lblOutOfRange);
212+
if (parameter.IsOptional)
213+
{
214+
switch (parameter.DefaultValue)
215+
{
216+
case null: g.Emit(OpCodes.Ldnull); break;
217+
case string v: g.Emit(OpCodes.Ldstr, v); break;
218+
case byte v: g.Emit(OpCodes.Ldc_I4, v); break;
219+
case ushort v: g.Emit(OpCodes.Ldc_I4, v); break;
220+
case uint v: g.Emit(OpCodes.Ldc_I4, v); break;
221+
case ulong v: g.Emit(OpCodes.Ldc_I4, v); break;
222+
case sbyte v: g.Emit(OpCodes.Ldc_I4, v); break;
223+
case short v: g.Emit(OpCodes.Ldc_I4, v); break;
224+
case int v: g.Emit(OpCodes.Ldc_I4, v); break;
225+
}
226+
227+
// Generic way to also support Nullable<> types
228+
if (t.IsGenericType)
229+
{
230+
Type[] genericArguments = t.GetGenericArguments();
231+
if (genericArguments.Length == 1)
232+
g.Emit(OpCodes.Newobj, t.GetConstructor(genericArguments));
233+
else
234+
throw new ArgumentException($"Default value for {t} is unsupported.");
235+
}
236+
}
237+
else
238+
{
239+
g.Emit(OpCodes.Newobj, typeof(IndexOutOfRangeException).GetConstructor(Type.EmptyTypes));
240+
g.Emit(OpCodes.Throw);
241+
}
242+
g.MarkLabel(lblNextArgument);
217243
}
218244

219245
#if DYN_FUNC_CALLI

0 commit comments

Comments
 (0)