""" Translate between PyPy ootypesystem and .NET Common Type System """ import exceptions from py.builtin import set from pypy.rpython.lltypesystem import lltype from pypy.rpython.lltypesystem import rffi from pypy.rpython.ootypesystem import ootype from pypy.translator.cli.option import getoption from pypy.translator.cli import oopspec from pypy.tool.ansi_print import ansi_log import py log = py.log.Producer("cli") py.log.setconsumer("cli", ansi_log) class CliType(object): def typename(self): raise NotImplementedError def __str__(self): return self.typename() def __hash__(self): return hash(self.typename()) def __eq__(self, other): return self.typename() == other.typename() def __ne__(self, other): return self.typename() != other.typename() class CliPrimitiveType(CliType): def __init__(self, name): self.name = name def typename(self): return self.name class CliReferenceType(CliType): prefix = 'class ' def typename(self): return self.prefix + self.classname() def classname(self): raise NotImplementedError class CliClassType(CliReferenceType): def __init__(self, assembly, name): self.assembly = assembly self.name = name def classname(self): if self.assembly: return '[%s]%s' % (self.assembly, self.name) else: return self.name class CliValueType(CliClassType): prefix = 'valuetype ' class CliGenericType(CliReferenceType): def __init__(self, assembly, name, numparam): self.assembly = assembly self.name = name self.numparam = numparam def classname(self): paramtypes = [self.paramtype(i) for i in range(self.numparam)] thistype = self.specialize(*paramtypes) return thistype.classname() def specialize(self, *types): assert len(types) == self.numparam return CliSpecializedType(self, types) def paramtype(self, num): assert 0 <= num < self.numparam return CliPrimitiveType('!%d' % num) class CliSpecializedType(CliReferenceType): def __init__(self, generic_type, arg_types): self.generic_type = generic_type self.arg_types = arg_types def classname(self): assembly = self.generic_type.assembly name = self.generic_type.name numparam = self.generic_type.numparam arglist = ', '.join([arg.typename() for arg in self.arg_types]) return '[%s]%s`%d<%s>' % (assembly, name, numparam, arglist) class CliArrayType(CliType): def __init__(self, itemtype): self.itemtype = itemtype def typename(self): return '%s[]' % self.itemtype.typename() T = CliPrimitiveType class types: void = T('void') int16 = T('int16') int32 = T('int32') uint32 = T('unsigned int32') int64 = T('int64') uint64 = T('unsigned int64') bool = T('bool') float64 = T('float64') char = T('char') string = T('string') weakref = CliClassType('pypylib', 'pypy.runtime.WeakReference') type = CliClassType('mscorlib', 'System.Type') object = CliClassType('mscorlib', 'System.Object') list = CliGenericType('pypylib', 'pypy.runtime.List', 1) list_of_void = CliClassType('pypylib', 'pypy.runtime.ListOfVoid') dict = CliGenericType('pypylib', 'pypy.runtime.Dict', 2) dict_void_void = CliClassType('pypylib', 'pypy.runtime.DictVoidVoid') dict_items_iterator = CliGenericType('pypylib', 'pypy.runtime.DictItemsIterator', 2) string_builder = CliClassType('pypylib', 'pypy.runtime.StringBuilder') del T WEAKREF = types.weakref.classname() PYPY_DICT_OF_VOID = '[pypylib]pypy.runtime.DictOfVoid`2<%s, int32>' PYPY_DICT_OF_VOID_KEY = '[pypylib]pypy.runtime.DictOfVoidKey`2' _lltype_to_cts = { ootype.Void: types.void, rffi.SHORT: types.int16, ootype.Signed: types.int32, ootype.Unsigned: types.uint32, ootype.SignedLongLong: types.int64, ootype.UnsignedLongLong: types.uint64, ootype.Bool: types.bool, ootype.Float: types.float64, ootype.Char: types.char, ootype.UniChar: types.char, ootype.Class: types.type, ootype.String: types.string, ootype.StringBuilder: types.string_builder, ootype.Unicode: types.string, ootype.UnicodeBuilder: types.string_builder, ootype.WeakReference: types.weakref, ootype.Object: types.object, # maps generic types to their ordinal ootype.List.SELFTYPE_T: types.list, ootype.List.ITEMTYPE_T: types.list.paramtype(0), ootype.Dict.SELFTYPE_T: types.dict, ootype.Dict.KEYTYPE_T: types.dict.paramtype(0), ootype.Dict.VALUETYPE_T: types.dict.paramtype(1), ootype.DictItemsIterator.SELFTYPE_T: types.dict_items_iterator, ootype.DictItemsIterator.KEYTYPE_T: types.dict_items_iterator.paramtype(0), ootype.DictItemsIterator.VALUETYPE_T: types.dict_items_iterator.paramtype(1), } def _get_from_dict(d, key, error): try: return d[key] except KeyError: if getoption('nostop'): log.WARNING(error) return key else: assert False, error class CTS(object): ILASM_KEYWORDS = set(["at", "as", "implicitcom", "implicitres", "noappdomain", "noprocess", "nomachine", "extern", "instance", "explicit", "default", "vararg", "unmanaged", "cdecl", "stdcall", "thiscall", "fastcall", "marshal", "in", "out", "opt", "retval", "static", "public", "private", "family", "initonly", "rtspecialname", "specialname", "assembly", "famandassem", "famorassem", "privatescope", "literal", "notserialized", "value", "not_in_gc_heap", "interface", "sealed", "abstract", "auto", "sequential", "ansi", "unicode", "autochar", "bestfit", "charmaperror", "import", "serializable", "nested", "lateinit", "extends", "implements", "final", "virtual", "hidebysig", "newslot", "unmanagedexp", "pinvokeimpl", "nomangle", "ole", "lasterr", "winapi", "native", "il", "cil", "optil", "managed", "forwardref", "runtime", "internalcall", "synchronized", "noinlining", "custom", "fixed", "sysstring", "array", "variant", "currency", "syschar", "void", "bool", "int8", "int16", "int32", "int64", "float32", "float64", "error", "unsigned", "uint", "uint8", "uint16", "uint32", "uint64", "decimal", "date", "bstr", "lpstr", "lpwstr", "lptstr", "objectref", "iunknown", "idispatch", "struct", "safearray", "int", "byvalstr", "tbstr", "lpvoid", "any", "float", "lpstruct", "null", "ptr", "vector", "hresult", "carray", "userdefined", "record", "filetime", "blob", "stream", "storage", "streamed_object", "stored_object", "blob_object", "cf", "clsid", "method", "class", "pinned", "modreq", "modopt", "typedref", "type","refany", "wchar", "char", "fromunmanaged", "callmostderived", "bytearray", "with", "init", "to", "catch", "filter", "finally", "fault", "handler", "tls", "field", "request", "demand", "assert", "deny", "permitonly", "linkcheck", "inheritcheck", "reqmin", "reqopt", "reqrefuse", "prejitgrant", "prejitdeny", "noncasdemand", "noncaslinkdemand", "noncasinheritance", "readonly", "nometadata", "algorithm", "fullorigin", "nan", "inf", "publickey", "enablejittracking", "disablejitoptimizer", "preservesig", "beforefieldinit", "alignment", "nullref", "valuetype", "compilercontrolled", "reqsecobj", "enum", "object", "string", "true", "false", "is", "on", "off", "add", "and", "arglist", "beq", "bge", "bgt", "ble", "blt", "bne", "box", "br", "break", "brfalse", "brnull", "brtrue", "call", "calli", "callvirt", "castclass", "ceq", "cgt", "ckfinite", "clt", "conf", "constrained", "conv", "cpblk", "cpobj", "div", "dup", "endfault", "endfilter", "endfinally", "initblk", "initobj", "isinst", "jmp", "ldarg", "ldarga", "ldc", "ldelem", "ldelema", "ldfld", "ldflda", "ldftn", "ldind", "ldlen", "ldloc", "ldloca", "ldnull", "ldobj", "ldsfld", "ldsflda", "ldstr", "ldtoken", "ldvirtftn", "leave", "localloc", "mkrefany", "mul", "neg", "newarr", "newobj", "nop", "not", "or", "pop", "readonly", "refanytype", "refanyval", "rem", "ret", "rethrow", "shl", "shr", "sizeof", "starg", "stelem", "stfld", "stind", "stloc", "stobj", "stsfld", "sub", "switch", "tail", "throw", "unaligned", "unbox", "volatile", "xor", "ole"]) # ole is not a keyword, but mono ilasm fails if you use it as a field/method name types = types # for convenience def __init__(self, db): self.db = db def escape_name(self, name): """Mangle then name if it's a ilasm reserved word""" if name in self.ILASM_KEYWORDS: return "'%s'" % name else: return name def lltype_to_cts(self, t): if t is ootype.ROOT: return types.object elif isinstance(t, lltype.Ptr) and isinstance(t.TO, lltype.OpaqueType): return types.object elif isinstance(t, ootype.Instance): if getattr(t, '_is_value_type', False): cls = CliValueType else: cls = CliClassType NATIVE_INSTANCE = t._hints.get('NATIVE_INSTANCE', None) if NATIVE_INSTANCE: return cls(None, NATIVE_INSTANCE._name) else: name = self.db.pending_class(t) return cls(None, name) elif isinstance(t, ootype.Record): name = self.db.pending_record(t) return CliClassType(None, name) elif isinstance(t, ootype.StaticMethod): delegate = self.db.record_delegate(t) return CliClassType(None, delegate) elif isinstance(t, ootype.Array): item_type = self.lltype_to_cts(t.ITEM) if item_type == types.void: # special case: Array of Void return types.list_of_void return CliArrayType(item_type) elif isinstance(t, ootype.List): item_type = self.lltype_to_cts(t.ITEM) if item_type == types.void: # special case: List of Void return types.list_of_void return types.list.specialize(item_type) elif isinstance(t, ootype.Dict): key_type = self.lltype_to_cts(t._KEYTYPE) value_type = self.lltype_to_cts(t._VALUETYPE) if value_type == types.void: # special cases: Dict with voids if key_type == types.void: return types.dict_void_void else: # XXX return CliClassType(None, PYPY_DICT_OF_VOID % key_type) elif key_type == types.void: assert value_type != types.void return CliClassType(None, PYPY_DICT_OF_VOID_KEY % value_type) return types.dict.specialize(key_type, value_type) elif isinstance(t, ootype.DictItemsIterator): key_type = self.lltype_to_cts(t._KEYTYPE) value_type = self.lltype_to_cts(t._VALUETYPE) if key_type == types.void: key_type = types.int32 # placeholder if value_type == types.void: value_type = types.int32 # placeholder return types.dict_items_iterator.specialize(key_type, value_type) return _get_from_dict(_lltype_to_cts, t, 'Unknown type %s' % t) def llvar_to_cts(self, var): return self.lltype_to_cts(var.concretetype), var.name def llconst_to_cts(self, const): return self.lltype_to_cts(const.concretetype), const.value def ctor_name(self, t): return 'instance void %s::.ctor()' % self.lltype_to_cts(t) def static_meth_to_signature(self, sm): from pypy.translator.oosupport import metavm graph = getattr(sm, 'graph', None) if graph: return self.graph_to_signature(graph) module, name = metavm.get_primitive_name(sm) func_name = '[pypylib]pypy.builtin.%s::%s' % (module, name) T = ootype.typeOf(sm) return self.format_signatue(func_name, T.ARGS, T.RESULT) def graph_to_signature(self, graph, is_method = False, func_name = None): func_name = func_name or graph.name func_name = self.escape_name(func_name) namespace = getattr(graph.func, '_namespace_', None) if namespace: func_name = '%s::%s' % (namespace, func_name) ARGS = [arg.concretetype for arg in graph.getargs() if arg.concretetype is not ootype.Void] if is_method: ARGS = ARGS[1:] RESULT = graph.getreturnvar().concretetype return self.format_signatue(func_name, ARGS, RESULT) def format_signatue(self, func_name, ARGS, RESULT): arg_types = [self.lltype_to_cts(ARG).typename() for ARG in ARGS] arg_list = ', '.join(arg_types) ret_type = self.lltype_to_cts(RESULT) return '%s %s(%s)' % (ret_type, func_name, arg_list) def op_to_signature(self, op, func_name): ret_type, ret_var = self.llvar_to_cts(op.result) func_name = self.escape_name(func_name) args = [arg for arg in op.args[1:] if arg.concretetype is not ootype.Void] arg_types = [self.lltype_to_cts(arg.concretetype).typename() for arg in args] arg_list = ', '.join(arg_types) return '%s %s(%s)' % (ret_type, func_name, arg_list) def method_signature(self, TYPE, name_or_desc): # TODO: use callvirt only when strictly necessary if isinstance(TYPE, ootype.Instance): if isinstance(name_or_desc, ootype._overloaded_meth_desc): name = name_or_desc.name METH = name_or_desc.TYPE virtual = True else: name = name_or_desc owner, meth = TYPE._lookup(name) METH = meth._TYPE virtual = getattr(meth, '_virtual', True) class_name = self.db.class_name(TYPE) full_name = 'class %s::%s' % (class_name, self.escape_name(name)) returntype = self.lltype_to_cts(METH.RESULT) arg_types = [self.lltype_to_cts(ARG).typename() for ARG in METH.ARGS if ARG is not ootype.Void] arg_list = ', '.join(arg_types) return '%s %s(%s)' % (returntype, full_name, arg_list), virtual elif isinstance(TYPE, (ootype.BuiltinType, ootype.StaticMethod)): assert isinstance(name_or_desc, str) name = name_or_desc if isinstance(TYPE, ootype.StaticMethod): METH = TYPE else: METH = oopspec.get_method(TYPE, name) class_name = self.lltype_to_cts(TYPE) if isinstance(TYPE, ootype.Dict): KEY = TYPE._KEYTYPE VALUE = TYPE._VALUETYPE name = name_or_desc if KEY is ootype.Void and VALUE is ootype.Void and name == 'll_get_items_iterator': # ugly, ugly special case ret_type = types.dict_items_iterator.specialize(types.int32, types.int32) elif VALUE is ootype.Void and METH.RESULT is ootype.Dict.VALUETYPE_T: ret_type = types.void else: ret_type = self.lltype_to_cts(METH.RESULT) ret_type = dict_of_void_ll_copy_hack(TYPE, ret_type) else: ret_type = self.lltype_to_cts(METH.RESULT) generic_types = getattr(TYPE, '_generic_types', {}) arg_types = [self.lltype_to_cts(arg).typename() for arg in METH.ARGS if arg is not ootype.Void and \ generic_types.get(arg, arg) is not ootype.Void] arg_list = ', '.join(arg_types) return '%s %s::%s(%s)' % (ret_type, class_name, name, arg_list), False else: assert False def dict_of_void_ll_copy_hack(TYPE, ret_type): # XXX: ugly hack to make the ll_copy signature correct when # CustomDict is special-cased to DictOfVoid. if isinstance(TYPE, ootype.CustomDict) and TYPE._VALUETYPE is ootype.Void: return ret_type.typename().replace('Dict`2', 'DictOfVoid`2') else: return ret_type