Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbbc6cb7

Browse files
committed
Added the ability to implement delegates withref andout parameters in Python, by returning the modified parameter values in a tuple.
BREAKING: MethodBinder omits a void return type when returning a tuple of out parameters.DelegateManager unpacks a tuple of out parameters from Python (reversing the logic in MethodBinder) and sets the out parameters of the delegate.
1 parent92932fd commitbbc6cb7

File tree

8 files changed

+471
-57
lines changed

8 files changed

+471
-57
lines changed

‎CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1212
- Ability to instantiate new .NET arrays using`Array[T](dim1, dim2, ...)` syntax
1313
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
1414
- Add GetPythonThreadID and Interrupt methods in PythonEngine
15+
- Ability to implement delegates with`ref` and`out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355])
1516

1617
###Changed
1718
- Drop support for Python 2, 3.4, and 3.5
@@ -32,6 +33,7 @@ details about the cause of the failure
3233
- floating point values passed from Python are no longer silently truncated
3334
when .NET expects an integer[#1342][i1342]
3435
- More specific error messages for method argument mismatch
36+
- BREAKING: Methods with`ref` or`out` parameters and void return type return a tuple of only the`ref` and`out` parameters.
3537

3638
###Fixed
3739

@@ -48,8 +50,8 @@ when .NET expects an integer [#1342][i1342]
4850
- Fixed issue when calling PythonException.Format where another exception would be raise for unnormalized exceptions
4951
- Made it possible to call`ToString`,`GetHashCode`, and`GetType` on inteface objects
5052
- Fixed objects returned by enumerating`PyObject` being disposed too soon
51-
- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException
52-
-`import` may now raise errors with more detail than "No module named X"
53+
- Incorrectly using a non-generic type with type parameters now produces a helpful Python error instead of throwing NullReferenceException ([#1325][i1325])
54+
-`import` may now raise errors with more detail than "No module named X"
5355
- Providing an invalid type parameter to a generic type or method produces a helpful Python error
5456

5557
###Removed

‎src/runtime/converter.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,23 +338,23 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
338338

339339
if(mt!=null)
340340
{
341-
if(mtisCLRObject)
341+
if(mtisCLRObjectco)
342342
{
343-
objecttmp=((CLRObject)mt).inst;
343+
objecttmp=co.inst;
344344
if(obType.IsInstanceOfType(tmp))
345345
{
346346
result=tmp;
347347
returntrue;
348348
}
349349
if(setError)
350350
{
351-
Exceptions.SetError(Exceptions.TypeError,$"value cannot be converted to{obType}");
351+
stringtypeString=tmpisnull?"null":tmp.GetType().ToString();
352+
Exceptions.SetError(Exceptions.TypeError,$"{typeString} value cannot be converted to{obType}");
352353
}
353354
returnfalse;
354355
}
355-
if(mtisClassBase)
356+
if(mtisClassBasecb)
356357
{
357-
varcb=(ClassBase)mt;
358358
if(!cb.type.Valid)
359359
{
360360
Exceptions.SetError(Exceptions.TypeError,cb.type.DeletedMessage);

‎src/runtime/delegatemanager.cs

Lines changed: 166 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
usingSystem;
2-
usingSystem.Collections;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Linq;
34
usingSystem.Reflection;
45
usingSystem.Reflection.Emit;
6+
usingSystem.Text;
57

68
namespacePython.Runtime
79
{
@@ -11,23 +13,20 @@ namespace Python.Runtime
1113
/// </summary>
1214
internalclassDelegateManager
1315
{
14-
privateHashtablecache;
15-
privateTypebasetype;
16-
privateTypelisttype;
17-
privateTypevoidtype;
18-
privateTypetypetype;
19-
privateTypeptrtype;
20-
privateCodeGeneratorcodeGenerator;
16+
privatereadonlyDictionary<Type,Type>cache=newDictionary<Type,Type>();
17+
privatereadonlyTypebasetype=typeof(Dispatcher);
18+
privatereadonlyTypearrayType=typeof(object[]);
19+
privatereadonlyTypevoidtype=typeof(void);
20+
privatereadonlyTypetypetype=typeof(Type);
21+
privatereadonlyTypeptrtype=typeof(IntPtr);
22+
privatereadonlyCodeGeneratorcodeGenerator=newCodeGenerator();
23+
privatereadonlyConstructorInfoarrayCtor;
24+
privatereadonlyMethodInfodispatch;
2125

2226
publicDelegateManager()
2327
{
24-
basetype=typeof(Dispatcher);
25-
listtype=typeof(ArrayList);
26-
voidtype=typeof(void);
27-
typetype=typeof(Type);
28-
ptrtype=typeof(IntPtr);
29-
cache=newHashtable();
30-
codeGenerator=newCodeGenerator();
28+
arrayCtor=arrayType.GetConstructor(new[]{typeof(int)});
29+
dispatch=basetype.GetMethod("Dispatch");
3130
}
3231

3332
/// <summary>
@@ -58,10 +57,9 @@ private Type GetDispatcher(Type dtype)
5857
// unique signatures rather than delegate types, since multiple
5958
// delegate types with the same sig could use the same dispatcher.
6059

61-
objectitem=cache[dtype];
62-
if(item!=null)
60+
if(cache.TryGetValue(dtype,outTypeitem))
6361
{
64-
return(Type)item;
62+
returnitem;
6563
}
6664

6765
stringname=$"__{dtype.FullName}Dispatcher";
@@ -103,34 +101,77 @@ private Type GetDispatcher(Type dtype)
103101

104102
MethodBuildermb=tb.DefineMethod("Invoke",MethodAttributes.Public,method.ReturnType,signature);
105103

106-
ConstructorInfoctor=listtype.GetConstructor(Type.EmptyTypes);
107-
MethodInfodispatch=basetype.GetMethod("Dispatch");
108-
MethodInfoadd=listtype.GetMethod("Add");
109-
110104
il=mb.GetILGenerator();
111-
il.DeclareLocal(listtype);
112-
il.Emit(OpCodes.Newobj,ctor);
105+
// loc_0 = new object[pi.Length]
106+
il.DeclareLocal(arrayType);
107+
il.Emit(OpCodes.Ldc_I4,pi.Length);
108+
il.Emit(OpCodes.Newobj,arrayCtor);
113109
il.Emit(OpCodes.Stloc_0);
114110

111+
boolanyByRef=false;
112+
115113
for(varc=0;c<signature.Length;c++)
116114
{
117115
Typet=signature[c];
118116
il.Emit(OpCodes.Ldloc_0);
117+
il.Emit(OpCodes.Ldc_I4,c);
119118
il.Emit(OpCodes.Ldarg_S,(byte)(c+1));
120119

120+
if(t.IsByRef)
121+
{
122+
// The argument is a pointer. We must dereference the pointer to get the value or object it points to.
123+
t=t.GetElementType();
124+
if(t.IsValueType)
125+
{
126+
il.Emit(OpCodes.Ldobj,t);
127+
}
128+
else
129+
{
130+
il.Emit(OpCodes.Ldind_Ref);
131+
}
132+
anyByRef=true;
133+
}
134+
121135
if(t.IsValueType)
122136
{
123137
il.Emit(OpCodes.Box,t);
124138
}
125139

126-
il.Emit(OpCodes.Callvirt,add);
127-
il.Emit(OpCodes.Pop);
140+
// args[c] = arg
141+
il.Emit(OpCodes.Stelem_Ref);
128142
}
129143

130144
il.Emit(OpCodes.Ldarg_0);
131145
il.Emit(OpCodes.Ldloc_0);
132146
il.Emit(OpCodes.Call,dispatch);
133147

148+
if(anyByRef)
149+
{
150+
// Dispatch() will have modified elements of the args list that correspond to out parameters.
151+
for(varc=0;c<signature.Length;c++)
152+
{
153+
Typet=signature[c];
154+
if(t.IsByRef)
155+
{
156+
t=t.GetElementType();
157+
// *arg = args[c]
158+
il.Emit(OpCodes.Ldarg_S,(byte)(c+1));
159+
il.Emit(OpCodes.Ldloc_0);
160+
il.Emit(OpCodes.Ldc_I4,c);
161+
il.Emit(OpCodes.Ldelem_Ref);
162+
if(t.IsValueType)
163+
{
164+
il.Emit(OpCodes.Unbox_Any,t);
165+
il.Emit(OpCodes.Stobj,t);
166+
}
167+
else
168+
{
169+
il.Emit(OpCodes.Stind_Ref);
170+
}
171+
}
172+
}
173+
}
174+
134175
if(method.ReturnType==voidtype)
135176
{
136177
il.Emit(OpCodes.Pop);
@@ -218,7 +259,7 @@ public void Dispose()
218259
GC.SuppressFinalize(this);
219260
}
220261

221-
publicobjectDispatch(ArrayListargs)
262+
publicobjectDispatch(object[]args)
222263
{
223264
IntPtrgs=PythonEngine.AcquireLock();
224265
objectob;
@@ -235,7 +276,7 @@ public object Dispatch(ArrayList args)
235276
returnob;
236277
}
237278

238-
publicobjectTrueDispatch(ArrayListargs)
279+
privateobjectTrueDispatch(object[]args)
239280
{
240281
MethodInfomethod=dtype.GetMethod("Invoke");
241282
ParameterInfo[]pi=method.GetParameters();
@@ -259,20 +300,108 @@ public object TrueDispatch(ArrayList args)
259300
throwe;
260301
}
261302

262-
if(rtype==typeof(void))
303+
try
263304
{
264-
returnnull;
265-
}
305+
intbyRefCount=pi.Count(parameterInfo=>parameterInfo.ParameterType.IsByRef);
306+
if(byRefCount>0)
307+
{
308+
// By symmetry with MethodBinder.Invoke, when there are out
309+
// parameters we expect to receive a tuple containing
310+
// the result, if any, followed by the out parameters. If there is only
311+
// one out parameter and the return type of the method is void,
312+
// we instead receive the out parameter as the result from Python.
313+
314+
boolisVoid=rtype==typeof(void);
315+
inttupleSize=byRefCount+(isVoid?0:1);
316+
if(isVoid&&byRefCount==1)
317+
{
318+
// The return type is void and there is a single out parameter.
319+
for(inti=0;i<pi.Length;i++)
320+
{
321+
Typet=pi[i].ParameterType;
322+
if(t.IsByRef)
323+
{
324+
if(!Converter.ToManaged(op,t,outobjectnewArg,true))
325+
{
326+
Exceptions.RaiseTypeError($"The Python function did not return{t.GetElementType()} (the out parameter type)");
327+
thrownewPythonException();
328+
}
329+
args[i]=newArg;
330+
break;
331+
}
332+
}
333+
returnnull;
334+
}
335+
elseif(Runtime.PyTuple_Check(op)&&Runtime.PyTuple_Size(op)==tupleSize)
336+
{
337+
intindex=isVoid?0:1;
338+
for(inti=0;i<pi.Length;i++)
339+
{
340+
Typet=pi[i].ParameterType;
341+
if(t.IsByRef)
342+
{
343+
IntPtritem=Runtime.PyTuple_GetItem(op,index++);
344+
if(!Converter.ToManaged(item,t,outobjectnewArg,true))
345+
{
346+
Exceptions.RaiseTypeError($"The Python function returned a tuple where element{i} was not{t.GetElementType()} (the out parameter type)");
347+
thrownewPythonException();
348+
}
349+
args[i]=newArg;
350+
}
351+
}
352+
if(isVoid)
353+
{
354+
returnnull;
355+
}
356+
IntPtritem0=Runtime.PyTuple_GetItem(op,0);
357+
if(!Converter.ToManaged(item0,rtype,outobjectresult0,true))
358+
{
359+
Exceptions.RaiseTypeError($"The Python function returned a tuple where element 0 was not{rtype} (the return type)");
360+
thrownewPythonException();
361+
}
362+
returnresult0;
363+
}
364+
else
365+
{
366+
stringtpName=Runtime.PyObject_GetTypeName(op);
367+
if(Runtime.PyTuple_Check(op))
368+
{
369+
tpName+=$" of size{Runtime.PyTuple_Size(op)}";
370+
}
371+
StringBuildersb=newStringBuilder();
372+
if(!isVoid)sb.Append(rtype.FullName);
373+
for(inti=0;i<pi.Length;i++)
374+
{
375+
Typet=pi[i].ParameterType;
376+
if(t.IsByRef)
377+
{
378+
if(sb.Length>0)sb.Append(",");
379+
sb.Append(t.GetElementType().FullName);
380+
}
381+
}
382+
stringreturnValueString=isVoid?"":"the return value and ";
383+
Exceptions.RaiseTypeError($"Expected a tuple ({sb}) of{returnValueString}the values for out and ref parameters, got{tpName}.");
384+
thrownewPythonException();
385+
}
386+
}
387+
388+
if(rtype==typeof(void))
389+
{
390+
returnnull;
391+
}
266392

267-
objectresult;
268-
if(!Converter.ToManaged(op,rtype,outresult,true))
393+
objectresult;
394+
if(!Converter.ToManaged(op,rtype,outresult,true))
395+
{
396+
thrownewPythonException();
397+
}
398+
399+
returnresult;
400+
}
401+
finally
269402
{
270403
Runtime.XDecref(op);
271-
thrownewPythonException();
272404
}
273-
274-
Runtime.XDecref(op);
275-
returnresult;
276405
}
277406
}
278407
}

‎src/runtime/methodbinder.cs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -960,42 +960,43 @@ internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw, MethodBase i
960960
}
961961

962962
// If there are out parameters, we return a tuple containing
963-
// the result followed by the out parameters. If there is only
963+
// the result, if any, followed by the out parameters. If there is only
964964
// one out parameter and the return type of the method is void,
965965
// we return the out parameter as the result to Python (for
966966
// code compatibility with ironpython).
967967

968968
varmi=(MethodInfo)binding.info;
969969

970-
if(binding.outs==1&&mi.ReturnType==typeof(void))
971-
{
972-
}
973-
974970
if(binding.outs>0)
975971
{
976972
ParameterInfo[]pi=mi.GetParameters();
977973
intc=pi.Length;
978974
varn=0;
979975

980-
IntPtrt=Runtime.PyTuple_New(binding.outs+1);
981-
IntPtrv=Converter.ToPython(result,mi.ReturnType);
982-
Runtime.PyTuple_SetItem(t,n,v);
983-
n++;
976+
boolisVoid=mi.ReturnType==typeof(void);
977+
inttupleSize=binding.outs+(isVoid?0:1);
978+
IntPtrt=Runtime.PyTuple_New(tupleSize);
979+
if(!isVoid)
980+
{
981+
IntPtrv=Converter.ToPython(result,mi.ReturnType);
982+
Runtime.PyTuple_SetItem(t,n,v);
983+
n++;
984+
}
984985

985986
for(vari=0;i<c;i++)
986987
{
987988
Typept=pi[i].ParameterType;
988-
if(pi[i].IsOut||pt.IsByRef)
989+
if(pt.IsByRef)
989990
{
990-
v=Converter.ToPython(binding.args[i],pt.GetElementType());
991+
IntPtrv=Converter.ToPython(binding.args[i],pt.GetElementType());
991992
Runtime.PyTuple_SetItem(t,n,v);
992993
n++;
993994
}
994995
}
995996

996997
if(binding.outs==1&&mi.ReturnType==typeof(void))
997998
{
998-
v=Runtime.PyTuple_GetItem(t,1);
999+
IntPtrv=Runtime.PyTuple_GetItem(t,0);
9991000
Runtime.XIncref(v);
10001001
Runtime.XDecref(t);
10011002
returnv;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp