631 lines
27 KiB
Python
631 lines
27 KiB
Python
|
# Licensed to the .NET Foundation under one or more agreements.
|
||
|
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
|
||
|
# See the LICENSE file in the project root for more information.
|
||
|
|
||
|
|
||
|
__all__ = ["ClrClass", "ClrInterface", "accepts", "returns", "attribute", "propagate_attributes"]
|
||
|
|
||
|
import clr
|
||
|
clr.AddReference("Microsoft.Dynamic")
|
||
|
clr.AddReference("Microsoft.Scripting")
|
||
|
clr.AddReference("IronPython")
|
||
|
|
||
|
if clr.IsNetCoreApp:
|
||
|
clr.AddReference("System.Reflection.Emit")
|
||
|
|
||
|
import System
|
||
|
from System import Char, Void, Boolean, Array, Type, AppDomain
|
||
|
from System.Reflection import FieldAttributes, MethodAttributes, PropertyAttributes, ParameterAttributes
|
||
|
from System.Reflection import CallingConventions, TypeAttributes, AssemblyName
|
||
|
from System.Reflection.Emit import OpCodes, CustomAttributeBuilder, AssemblyBuilder, AssemblyBuilderAccess
|
||
|
from System.Runtime.InteropServices import DllImportAttribute, CallingConvention, CharSet
|
||
|
from Microsoft.Scripting.Generation import Snippets
|
||
|
from Microsoft.Scripting.Runtime import DynamicOperations
|
||
|
from Microsoft.Scripting.Utils import ReflectionUtils
|
||
|
from IronPython.Runtime import NameType, PythonContext
|
||
|
from IronPython.Runtime.Types import PythonType, ReflectedField, ReflectedProperty
|
||
|
|
||
|
def validate_clr_types(signature_types, var_signature = False):
|
||
|
if not isinstance(signature_types, tuple):
|
||
|
signature_types = (signature_types,)
|
||
|
for t in signature_types:
|
||
|
if type(t) is type(System.IComparable): # type overloaded on generic arity, eg IComparable and IComparable[T]
|
||
|
t = t[()] # select non-generic version
|
||
|
clr_type = clr.GetClrType(t)
|
||
|
if t == Void:
|
||
|
raise TypeError("Void cannot be used in signature")
|
||
|
is_typed = clr.GetPythonType(clr_type) == t
|
||
|
# is_typed needs to be weakened until the generated type
|
||
|
# gets explicitly published as the underlying CLR type
|
||
|
is_typed = is_typed or (hasattr(t, "__metaclass__") and t.__metaclass__ in [ClrInterface, ClrClass])
|
||
|
if not is_typed:
|
||
|
raise Exception, "Invalid CLR type %s" % str(t)
|
||
|
if not var_signature:
|
||
|
if clr_type.IsByRef:
|
||
|
raise TypeError("Byref can only be used as arguments and locals")
|
||
|
# ArgIterator is not present in Silverlight
|
||
|
if hasattr(System, "ArgIterator") and t == System.ArgIterator:
|
||
|
raise TypeError("Stack-referencing types can only be used as arguments and locals")
|
||
|
|
||
|
class TypedFunction(object):
|
||
|
"""
|
||
|
A strongly-typed function can get wrapped up as a staticmethod, a property, etc.
|
||
|
This class represents the raw function, but with the type information
|
||
|
it is decorated with.
|
||
|
Other information is stored as attributes on the function. See propagate_attributes
|
||
|
"""
|
||
|
def __init__(self, function, is_static = False, prop_name_if_prop_get = None, prop_name_if_prop_set = None):
|
||
|
self.function = function
|
||
|
self.is_static = is_static
|
||
|
self.prop_name_if_prop_get = prop_name_if_prop_get
|
||
|
self.prop_name_if_prop_set = prop_name_if_prop_set
|
||
|
|
||
|
class ClrType(type):
|
||
|
"""
|
||
|
Base metaclass for creating strongly-typed CLR types
|
||
|
"""
|
||
|
|
||
|
def is_typed_method(self, function):
|
||
|
if hasattr(function, "arg_types") != hasattr(function, "return_type"):
|
||
|
raise TypeError("One of @accepts and @returns is missing for %s" % function.func_name)
|
||
|
|
||
|
return hasattr(function, "arg_types")
|
||
|
|
||
|
def get_typed_properties(self):
|
||
|
for item_name, item in self.__dict__.items():
|
||
|
if isinstance(item, property):
|
||
|
if item.fget:
|
||
|
if not self.is_typed_method(item.fget): continue
|
||
|
prop_type = item.fget.return_type
|
||
|
else:
|
||
|
if not self.is_typed_method(item.fset): continue
|
||
|
prop_type = item.fset.arg_types[0]
|
||
|
validate_clr_types(prop_type)
|
||
|
clr_prop_type = clr.GetClrType(prop_type)
|
||
|
yield item, item_name, clr_prop_type
|
||
|
|
||
|
def emit_properties(self, typebld):
|
||
|
for prop, prop_name, clr_prop_type in self.get_typed_properties():
|
||
|
self.emit_property(typebld, prop, prop_name, clr_prop_type)
|
||
|
|
||
|
def emit_property(self, typebld, prop, name, clrtype):
|
||
|
prpbld = typebld.DefineProperty(name, PropertyAttributes.None, clrtype, None)
|
||
|
if prop.fget:
|
||
|
getter = self.emitted_methods[(prop.fget.func_name, prop.fget.arg_types)]
|
||
|
prpbld.SetGetMethod(getter)
|
||
|
if prop.fset:
|
||
|
setter = self.emitted_methods[(prop.fset.func_name, prop.fset.arg_types)]
|
||
|
prpbld.SetSetMethod(setter)
|
||
|
|
||
|
def dummy_function(self): raise RuntimeError("this should not get called")
|
||
|
|
||
|
def get_typed_methods(self):
|
||
|
"""
|
||
|
Get all the methods with @accepts (and @returns) decorators
|
||
|
Functions are assumed to be instance methods, unless decorated with @staticmethod
|
||
|
"""
|
||
|
|
||
|
# We avoid using the "types" library as it is not a builtin
|
||
|
FunctionType = type(ClrType.__dict__["dummy_function"])
|
||
|
|
||
|
for item_name, item in self.__dict__.items():
|
||
|
function = None
|
||
|
is_static = False
|
||
|
if isinstance(item, FunctionType):
|
||
|
function, is_static = item, False
|
||
|
elif isinstance(item, staticmethod):
|
||
|
function, is_static = getattr(self, item_name), True
|
||
|
elif isinstance(item, property):
|
||
|
if item.fget and self.is_typed_method(item.fget):
|
||
|
if item.fget.func_name == item_name:
|
||
|
# The property hides the getter. So yield the getter
|
||
|
yield TypedFunction(item.fget, False, item_name, None)
|
||
|
if item.fset and self.is_typed_method(item.fset):
|
||
|
if item.fset.func_name == item_name:
|
||
|
# The property hides the setter. So yield the setter
|
||
|
yield TypedFunction(item.fset, False, None, item_name)
|
||
|
continue
|
||
|
else:
|
||
|
continue
|
||
|
if self.is_typed_method(function):
|
||
|
yield TypedFunction(function, is_static)
|
||
|
|
||
|
def emit_methods(self, typebld):
|
||
|
# We need to track the generated methods so that we can emit properties
|
||
|
# referring these methods.
|
||
|
# Also, the hash is indexed by name *and signature*. Even though Python does
|
||
|
# not have method overloading, property getter and setter functions can have
|
||
|
# the same func_name attribute
|
||
|
self.emitted_methods = {}
|
||
|
for function_info in self.get_typed_methods():
|
||
|
method_builder = self.emit_method(typebld, function_info)
|
||
|
function = function_info.function
|
||
|
if self.emitted_methods.has_key((function.func_name, function.arg_types)):
|
||
|
raise TypeError("methods with clashing names")
|
||
|
self.emitted_methods[(function.func_name, function.arg_types)] = method_builder
|
||
|
|
||
|
def emit_classattribs(self, typebld):
|
||
|
if hasattr(self, '_clrclassattribs'):
|
||
|
for attrib_info in self._clrclassattribs:
|
||
|
if isinstance(attrib_info, type):
|
||
|
ci = clr.GetClrType(attrib_info).GetConstructor(())
|
||
|
cab = CustomAttributeBuilder(ci, ())
|
||
|
elif isinstance(attrib_info, CustomAttributeDecorator):
|
||
|
cab = attrib_info.GetBuilder()
|
||
|
else:
|
||
|
make_decorator = attrib_info()
|
||
|
cab = make_decorator.GetBuilder()
|
||
|
typebld.SetCustomAttribute(cab)
|
||
|
|
||
|
def get_clr_type_name(self):
|
||
|
if hasattr(self, "_clrnamespace"):
|
||
|
return self._clrnamespace + "." + self.__name__
|
||
|
else:
|
||
|
return self.__name__
|
||
|
|
||
|
def create_type(self, typebld):
|
||
|
self.emit_members(typebld)
|
||
|
new_type = typebld.CreateType()
|
||
|
self.map_members(new_type)
|
||
|
return new_type
|
||
|
|
||
|
class ClrInterface(ClrType):
|
||
|
"""
|
||
|
Set __metaclass__ in a Python class declaration to declare a
|
||
|
CLR interface type.
|
||
|
You need to specify object as the base-type if you do not specify any other
|
||
|
interfaces as the base interfaces
|
||
|
"""
|
||
|
|
||
|
def __init__(self, *args):
|
||
|
return super(ClrInterface, self).__init__(*args)
|
||
|
|
||
|
def emit_method(self, typebld, function_info):
|
||
|
assert(not function_info.is_static)
|
||
|
function = function_info.function
|
||
|
attributes = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract
|
||
|
method_builder = typebld.DefineMethod(
|
||
|
function.func_name,
|
||
|
attributes,
|
||
|
function.return_type,
|
||
|
function.arg_types)
|
||
|
|
||
|
instance_offset = 0 if function_info.is_static else 1
|
||
|
arg_names = function.func_code.co_varnames
|
||
|
for i in xrange(len(function.arg_types)):
|
||
|
# TODO - set non-trivial ParameterAttributes, default value and custom attributes
|
||
|
p = method_builder.DefineParameter(i + 1, ParameterAttributes.None, arg_names[i + instance_offset])
|
||
|
|
||
|
if hasattr(function, "CustomAttributeBuilders"):
|
||
|
for cab in function.CustomAttributeBuilders:
|
||
|
method_builder.SetCustomAttribute(cab)
|
||
|
|
||
|
return method_builder
|
||
|
|
||
|
def emit_members(self, typebld):
|
||
|
self.emit_methods(typebld)
|
||
|
self.emit_properties(typebld)
|
||
|
self.emit_classattribs(typebld)
|
||
|
|
||
|
def map_members(self, new_type): pass
|
||
|
|
||
|
interface_module_builder = None
|
||
|
|
||
|
@staticmethod
|
||
|
def define_interface(typename, bases):
|
||
|
for b in bases:
|
||
|
validate_clr_types(b)
|
||
|
if not ClrInterface.interface_module_builder:
|
||
|
name = AssemblyName("interfaces")
|
||
|
access = AssemblyBuilderAccess.Run
|
||
|
assembly_builder = ReflectionUtils.DefineDynamicAssembly(name, access)
|
||
|
ClrInterface.interface_module_builder = assembly_builder.DefineDynamicModule("interfaces")
|
||
|
attrs = TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract
|
||
|
return ClrInterface.interface_module_builder.DefineType(typename, attrs, None, bases)
|
||
|
|
||
|
def map_clr_type(self, clr_type):
|
||
|
"""
|
||
|
TODO - Currently "t = clr.GetPythonType(clr.GetClrType(C)); t == C" will be False
|
||
|
for C where C.__metaclass__ is ClrInterface, even though both t and C
|
||
|
represent the same CLR type. This can be fixed by publishing a mapping
|
||
|
between t and C in the IronPython runtime.
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
def __clrtype__(self):
|
||
|
# CFoo below will use ClrInterface as its metaclass, but the user will not expect CFoo
|
||
|
# to be an interface in this case:
|
||
|
#
|
||
|
# class IFoo(object):
|
||
|
# __metaclass__ = ClrInterface
|
||
|
# class CFoo(IFoo): pass
|
||
|
if not "__metaclass__" in self.__dict__:
|
||
|
return super(ClrInterface, self).__clrtype__()
|
||
|
|
||
|
bases = list(self.__bases__)
|
||
|
bases.remove(object)
|
||
|
bases = tuple(bases)
|
||
|
if False: # Snippets currently does not support creating interfaces
|
||
|
typegen = Snippets.Shared.DefineType(self.get_clr_type_name(), bases, True, False)
|
||
|
typebld = typegen.TypeBuilder
|
||
|
else:
|
||
|
typebld = ClrInterface.define_interface(self.get_clr_type_name(), bases)
|
||
|
clr_type = self.create_type(typebld)
|
||
|
self.map_clr_type(clr_type)
|
||
|
return clr_type
|
||
|
|
||
|
# Note that ClrClass inherits from ClrInterface to satisfy Python requirements of metaclasses.
|
||
|
# A metaclass of a subtype has to be subtype of the metaclass of a base type. As a result,
|
||
|
# if you define a type hierarchy as shown below, it requires ClrClass to be a subtype
|
||
|
# of ClrInterface:
|
||
|
#
|
||
|
# class IFoo(object):
|
||
|
# __metaclass__ = ClrInterface
|
||
|
# class CFoo(IFoo):
|
||
|
# __metaclass__ = ClrClass
|
||
|
class ClrClass(ClrInterface):
|
||
|
"""
|
||
|
Set __metaclass__ in a Python class declaration to specify strong-type
|
||
|
information for the class or its attributes. The Python class
|
||
|
retains its Python attributes, like being able to add or remove methods.
|
||
|
"""
|
||
|
|
||
|
# Holds the FieldInfo for a static CLR field which points to a
|
||
|
# Microsoft.Scripting.Runtime.DynamicOperations corresponding to the current ScriptEngine
|
||
|
dynamic_operations_field = None
|
||
|
|
||
|
def emit_fields(self, typebld):
|
||
|
if hasattr(self, "_clrfields"):
|
||
|
for fldname in self._clrfields:
|
||
|
field_type = self._clrfields[fldname]
|
||
|
validate_clr_types(field_type)
|
||
|
typebld.DefineField(
|
||
|
fldname,
|
||
|
clr.GetClrType(field_type),
|
||
|
FieldAttributes.Public)
|
||
|
|
||
|
def map_fields(self, new_type):
|
||
|
if hasattr(self, "_clrfields"):
|
||
|
for fldname in self._clrfields:
|
||
|
fldinfo = new_type.GetField(fldname)
|
||
|
setattr(self, fldname, ReflectedField(fldinfo))
|
||
|
|
||
|
@staticmethod
|
||
|
def get_dynamic_operations_field():
|
||
|
if ClrClass.dynamic_operations_field:
|
||
|
return ClrClass.dynamic_operations_field
|
||
|
python_context = clr.GetCurrentRuntime().GetLanguage(PythonContext)
|
||
|
dynamic_operations = DynamicOperations(python_context)
|
||
|
|
||
|
typegen = Snippets.Shared.DefineType(
|
||
|
"DynamicOperationsHolder" + str(hash(python_context)),
|
||
|
object,
|
||
|
True,
|
||
|
False)
|
||
|
typebld = typegen.TypeBuilder
|
||
|
typebld.DefineField(
|
||
|
"DynamicOperations",
|
||
|
DynamicOperations,
|
||
|
FieldAttributes.Public | FieldAttributes.Static)
|
||
|
new_type = typebld.CreateType()
|
||
|
ClrClass.dynamic_operations_field = new_type.GetField("DynamicOperations")
|
||
|
|
||
|
ClrClass.dynamic_operations_field.SetValue(None, dynamic_operations)
|
||
|
|
||
|
return ClrClass.dynamic_operations_field
|
||
|
|
||
|
def emit_typed_stub_to_python_method(self, typebld, function_info):
|
||
|
function = function_info.function
|
||
|
"""
|
||
|
Generate a stub method that repushes all the arguments and
|
||
|
dispatches to DynamicOperations.InvokeMember
|
||
|
"""
|
||
|
invoke_member = clr.GetClrType(DynamicOperations).GetMethod(
|
||
|
"InvokeMember",
|
||
|
Array[Type]((object, str, Array[object])))
|
||
|
|
||
|
# Type.GetMethod raises an AmbiguousMatchException if there is a generic and a non-generic method
|
||
|
# (like DynamicOperations.GetMember) with the same name and signature. So we have to do things
|
||
|
# the hard way
|
||
|
get_member_search = [m for m in clr.GetClrType(DynamicOperations).GetMethods() if m.Name == "GetMember" and not m.IsGenericMethod and m.GetParameters().Length == 2]
|
||
|
assert(len(get_member_search) == 1)
|
||
|
get_member = get_member_search[0]
|
||
|
|
||
|
set_member_search = [m for m in clr.GetClrType(DynamicOperations).GetMethods() if m.Name == "SetMember" and not m.IsGenericMethod and m.GetParameters().Length == 3]
|
||
|
assert(len(set_member_search) == 1)
|
||
|
set_member = set_member_search[0]
|
||
|
|
||
|
convert_to = clr.GetClrType(DynamicOperations).GetMethod(
|
||
|
"ConvertTo",
|
||
|
Array[Type]((object, Type)))
|
||
|
get_type_from_handle = clr.GetClrType(Type).GetMethod("GetTypeFromHandle")
|
||
|
|
||
|
attributes = MethodAttributes.Public
|
||
|
if function_info.is_static: attributes |= MethodAttributes.Static
|
||
|
if function.func_name == "__new__":
|
||
|
if function_info.is_static: raise TypeError
|
||
|
method_builder = typebld.DefineConstructor(
|
||
|
attributes,
|
||
|
CallingConventions.HasThis,
|
||
|
function.arg_types)
|
||
|
raise NotImplementedError("Need to call self.baseType ctor passing in self.get_python_type_field()")
|
||
|
else:
|
||
|
method_builder = typebld.DefineMethod(
|
||
|
function.func_name,
|
||
|
attributes,
|
||
|
function.return_type,
|
||
|
function.arg_types)
|
||
|
|
||
|
instance_offset = 0 if function_info.is_static else 1
|
||
|
arg_names = function.func_code.co_varnames
|
||
|
for i in xrange(len(function.arg_types)):
|
||
|
# TODO - set non-trivial ParameterAttributes, default value and custom attributes
|
||
|
p = method_builder.DefineParameter(i + 1, ParameterAttributes.None, arg_names[i + instance_offset])
|
||
|
|
||
|
ilgen = method_builder.GetILGenerator()
|
||
|
|
||
|
args_array = ilgen.DeclareLocal(Array[object])
|
||
|
args_count = len(function.arg_types)
|
||
|
ilgen.Emit(OpCodes.Ldc_I4, args_count)
|
||
|
ilgen.Emit(OpCodes.Newarr, object)
|
||
|
ilgen.Emit(OpCodes.Stloc, args_array)
|
||
|
for i in xrange(args_count):
|
||
|
arg_type = function.arg_types[i]
|
||
|
if clr.GetClrType(arg_type).IsByRef:
|
||
|
raise NotImplementedError("byref params not supported")
|
||
|
ilgen.Emit(OpCodes.Ldloc, args_array)
|
||
|
ilgen.Emit(OpCodes.Ldc_I4, i)
|
||
|
ilgen.Emit(OpCodes.Ldarg, i + int(not function_info.is_static))
|
||
|
ilgen.Emit(OpCodes.Box, arg_type)
|
||
|
ilgen.Emit(OpCodes.Stelem_Ref)
|
||
|
|
||
|
has_return_value = True
|
||
|
if function_info.prop_name_if_prop_get:
|
||
|
ilgen.Emit(OpCodes.Ldsfld, ClrClass.get_dynamic_operations_field())
|
||
|
ilgen.Emit(OpCodes.Ldarg, 0)
|
||
|
ilgen.Emit(OpCodes.Ldstr, function_info.prop_name_if_prop_get)
|
||
|
ilgen.Emit(OpCodes.Callvirt, get_member)
|
||
|
elif function_info.prop_name_if_prop_set:
|
||
|
ilgen.Emit(OpCodes.Ldsfld, ClrClass.get_dynamic_operations_field())
|
||
|
ilgen.Emit(OpCodes.Ldarg, 0)
|
||
|
ilgen.Emit(OpCodes.Ldstr, function_info.prop_name_if_prop_set)
|
||
|
ilgen.Emit(OpCodes.Ldarg, 1)
|
||
|
ilgen.Emit(OpCodes.Callvirt, set_member)
|
||
|
has_return_value = False
|
||
|
else:
|
||
|
ilgen.Emit(OpCodes.Ldsfld, ClrClass.get_dynamic_operations_field())
|
||
|
if function_info.is_static:
|
||
|
raise NotImplementedError("need to load Python class object from a CLR static field")
|
||
|
# ilgen.Emit(OpCodes.Ldsfld, class_object)
|
||
|
else:
|
||
|
ilgen.Emit(OpCodes.Ldarg, 0)
|
||
|
|
||
|
ilgen.Emit(OpCodes.Ldstr, function.func_name)
|
||
|
ilgen.Emit(OpCodes.Ldloc, args_array)
|
||
|
ilgen.Emit(OpCodes.Callvirt, invoke_member)
|
||
|
|
||
|
if has_return_value:
|
||
|
if function.return_type == Void:
|
||
|
ilgen.Emit(OpCodes.Pop)
|
||
|
else:
|
||
|
ret_val = ilgen.DeclareLocal(object)
|
||
|
ilgen.Emit(OpCodes.Stloc, ret_val)
|
||
|
ilgen.Emit(OpCodes.Ldsfld, ClrClass.get_dynamic_operations_field())
|
||
|
ilgen.Emit(OpCodes.Ldloc, ret_val)
|
||
|
ilgen.Emit(OpCodes.Ldtoken, clr.GetClrType(function.return_type))
|
||
|
ilgen.Emit(OpCodes.Call, get_type_from_handle)
|
||
|
ilgen.Emit(OpCodes.Callvirt, convert_to)
|
||
|
ilgen.Emit(OpCodes.Unbox_Any, function.return_type)
|
||
|
ilgen.Emit(OpCodes.Ret)
|
||
|
return method_builder
|
||
|
|
||
|
def emit_method(self, typebld, function_info):
|
||
|
function = function_info.function
|
||
|
if hasattr(function, "DllImportAttributeDecorator"):
|
||
|
dllImportAttributeDecorator = function.DllImportAttributeDecorator
|
||
|
name = function.func_name
|
||
|
dllName = dllImportAttributeDecorator.args[0]
|
||
|
entryName = function.func_name
|
||
|
attributes = MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl
|
||
|
callingConvention = CallingConventions.Standard
|
||
|
returnType = function.return_type
|
||
|
returnTypeRequiredCustomModifiers = ()
|
||
|
returnTypeOptionalCustomModifiers = ()
|
||
|
parameterTypes = function.arg_types
|
||
|
parameterTypeRequiredCustomModifiers = None
|
||
|
parameterTypeOptionalCustomModifiers = None
|
||
|
nativeCallConv = CallingConvention.Winapi
|
||
|
nativeCharSet = CharSet.Auto
|
||
|
method_builder = typebld.DefinePInvokeMethod(
|
||
|
name,
|
||
|
dllName,
|
||
|
entryName,
|
||
|
attributes,
|
||
|
callingConvention,
|
||
|
returnType,
|
||
|
returnTypeRequiredCustomModifiers,
|
||
|
returnTypeOptionalCustomModifiers,
|
||
|
parameterTypes,
|
||
|
parameterTypeRequiredCustomModifiers,
|
||
|
parameterTypeOptionalCustomModifiers,
|
||
|
nativeCallConv,
|
||
|
nativeCharSet)
|
||
|
else:
|
||
|
method_builder = self.emit_typed_stub_to_python_method(typebld, function_info)
|
||
|
|
||
|
if hasattr(function, "CustomAttributeBuilders"):
|
||
|
for cab in function.CustomAttributeBuilders:
|
||
|
method_builder.SetCustomAttribute(cab)
|
||
|
|
||
|
return method_builder
|
||
|
|
||
|
def map_pinvoke_methods(self, new_type):
|
||
|
pythonType = clr.GetPythonType(new_type)
|
||
|
for function_info in self.get_typed_methods():
|
||
|
function = function_info.function
|
||
|
if hasattr(function, "DllImportAttributeDecorator"):
|
||
|
# Overwrite the Python function with the pinvoke_method
|
||
|
pinvoke_method = getattr(pythonType, function.func_name)
|
||
|
setattr(self, function.func_name, pinvoke_method)
|
||
|
|
||
|
def emit_python_type_field(self, typebld):
|
||
|
return typebld.DefineField(
|
||
|
"PythonType",
|
||
|
PythonType,
|
||
|
FieldAttributes.Public | FieldAttributes.Static)
|
||
|
|
||
|
def set_python_type_field(self, new_type):
|
||
|
self.PythonType = new_type.GetField("PythonType")
|
||
|
self.PythonType.SetValue(None, self)
|
||
|
|
||
|
def add_wrapper_ctors(self, baseType, typebld):
|
||
|
python_type_field = self.emit_python_type_field(typebld)
|
||
|
for ctor in baseType.GetConstructors():
|
||
|
ctorparams = ctor.GetParameters()
|
||
|
|
||
|
# leave out the PythonType argument
|
||
|
assert(ctorparams[0].ParameterType == clr.GetClrType(PythonType))
|
||
|
ctorparams = ctorparams[1:]
|
||
|
|
||
|
ctorbld = typebld.DefineConstructor(
|
||
|
ctor.Attributes,
|
||
|
ctor.CallingConvention,
|
||
|
tuple([p.ParameterType for p in ctorparams]))
|
||
|
ilgen = ctorbld.GetILGenerator()
|
||
|
ilgen.Emit(OpCodes.Ldarg, 0)
|
||
|
ilgen.Emit(OpCodes.Ldsfld, python_type_field)
|
||
|
for index in xrange(len(ctorparams)):
|
||
|
ilgen.Emit(OpCodes.Ldarg, index + 1)
|
||
|
ilgen.Emit(OpCodes.Call, ctor)
|
||
|
ilgen.Emit(OpCodes.Ret)
|
||
|
|
||
|
def emit_members(self, typebld):
|
||
|
self.emit_fields(typebld)
|
||
|
self.add_wrapper_ctors(self.baseType, typebld)
|
||
|
super(ClrClass, self).emit_members(typebld)
|
||
|
|
||
|
def map_members(self, new_type):
|
||
|
self.map_fields(new_type)
|
||
|
self.map_pinvoke_methods(new_type)
|
||
|
self.set_python_type_field(new_type)
|
||
|
super(ClrClass, self).map_members(new_type)
|
||
|
|
||
|
def __clrtype__(self):
|
||
|
# CDerived below will use ClrClass as its metaclass, but the user may not expect CDerived
|
||
|
# to be a typed .NET class in this case:
|
||
|
#
|
||
|
# class CBase(object):
|
||
|
# __metaclass__ = ClrClass
|
||
|
# class CDerived(CBase): pass
|
||
|
if not "__metaclass__" in self.__dict__:
|
||
|
return super(ClrClass, self).__clrtype__()
|
||
|
|
||
|
# Create a simple Python type first.
|
||
|
self.baseType = super(ClrType, self).__clrtype__()
|
||
|
# We will now subtype it to create a customized class with the
|
||
|
# CLR attributes as defined by the user
|
||
|
typegen = Snippets.Shared.DefineType(self.get_clr_type_name(), self.baseType, True, False)
|
||
|
typebld = typegen.TypeBuilder
|
||
|
return self.create_type(typebld)
|
||
|
|
||
|
def make_cab(attrib_type, *args, **kwds):
|
||
|
clrtype = clr.GetClrType(attrib_type)
|
||
|
argtypes = tuple(map(lambda x:clr.GetClrType(type(x)), args))
|
||
|
ci = clrtype.GetConstructor(argtypes)
|
||
|
|
||
|
props = ([],[])
|
||
|
fields = ([],[])
|
||
|
|
||
|
for kwd in kwds:
|
||
|
pi = clrtype.GetProperty(kwd)
|
||
|
if pi is not None:
|
||
|
props[0].append(pi)
|
||
|
props[1].append(kwds[kwd])
|
||
|
else:
|
||
|
fi = clrtype.GetField(kwd)
|
||
|
if fi is not None:
|
||
|
fields[0].append(fi)
|
||
|
fields[1].append(kwds[kwd])
|
||
|
else:
|
||
|
raise TypeError("No %s Member found on %s" % (kwd, clrtype.Name))
|
||
|
|
||
|
return CustomAttributeBuilder(ci, args,
|
||
|
tuple(props[0]), tuple(props[1]),
|
||
|
tuple(fields[0]), tuple(fields[1]))
|
||
|
|
||
|
def accepts(*args):
|
||
|
"""
|
||
|
TODO - needs to be merged with clr.accepts
|
||
|
"""
|
||
|
validate_clr_types(args, True)
|
||
|
def decorator(function):
|
||
|
function.arg_types = args
|
||
|
return function
|
||
|
return decorator
|
||
|
|
||
|
def returns(return_type = Void):
|
||
|
"""
|
||
|
TODO - needs to be merged with clr.returns
|
||
|
"""
|
||
|
if return_type != Void:
|
||
|
validate_clr_types(return_type)
|
||
|
def decorator(function):
|
||
|
function.return_type = return_type
|
||
|
return function
|
||
|
return decorator
|
||
|
|
||
|
class CustomAttributeDecorator(object):
|
||
|
"""
|
||
|
This represents information about a custom-attribute applied to a type or a method
|
||
|
Note that we cannot use an instance of System.Attribute to capture this information
|
||
|
as it is not possible to go from an instance of System.Attribute to an instance
|
||
|
of System.Reflection.Emit.CustomAttributeBuilder as the latter needs to know
|
||
|
how to represent information in metadata to later *recreate* a similar instance of
|
||
|
System.Attribute.
|
||
|
|
||
|
Also note that once a CustomAttributeBuilder is created, it is not possible to
|
||
|
query it. Hence, we need to store the arguments required to store the
|
||
|
CustomAttributeBuilder so that pseudo-custom-attributes can get to the information.
|
||
|
"""
|
||
|
def __init__(self, attrib_type, *args, **kwargs):
|
||
|
self.attrib_type = attrib_type
|
||
|
self.args = args
|
||
|
self.kwargs = kwargs
|
||
|
|
||
|
def __call__(self, function):
|
||
|
if self.attrib_type == DllImportAttribute:
|
||
|
function.DllImportAttributeDecorator = self
|
||
|
else:
|
||
|
if not hasattr(function, "CustomAttributeBuilders"):
|
||
|
function.CustomAttributeBuilders = []
|
||
|
function.CustomAttributeBuilders.append(self.GetBuilder())
|
||
|
return function
|
||
|
|
||
|
def GetBuilder(self):
|
||
|
assert not self.attrib_type in [DllImportAttribute]
|
||
|
return make_cab(self.attrib_type, *self.args, **self.kwargs)
|
||
|
|
||
|
def attribute(attrib_type):
|
||
|
"""
|
||
|
This decorator is used to specify a CustomAttribute for a type or method.
|
||
|
"""
|
||
|
def make_decorator(*args, **kwargs):
|
||
|
return CustomAttributeDecorator(attrib_type, *args, **kwargs)
|
||
|
return make_decorator
|
||
|
|
||
|
def propagate_attributes(old_function, new_function):
|
||
|
"""
|
||
|
Use this if you replace a function in a type with ClrInterface or ClrClass as the metaclass.
|
||
|
This will typically be needed if you are defining a decorator which wraps functions with
|
||
|
new functions, and want it to work in conjunction with clrtype
|
||
|
"""
|
||
|
if hasattr(old_function, "return_type"):
|
||
|
new_function.func_name = old_function.func_name
|
||
|
new_function.return_type = old_function.return_type
|
||
|
new_function.arg_types = old_function.arg_types
|
||
|
if hasattr(old_function, "CustomAttributeBuilders"):
|
||
|
new_function.CustomAttributeBuilders = old_function.CustomAttributeBuilders
|
||
|
if hasattr(old_function, "CustomAttributeBuilders"):
|
||
|
new_function.DllImportAttributeDecorator = old_function.DllImportAttributeDecorator
|
||
|
|