""" BackEnd Type wrappers for dotNet/CLR """ class ClrNativeType inherits NativeType """ Acts as a holder for a Native Type, typically from scanning a DLL. """ var _type as System.Type cue init(type as System.Type) base.init _type = type def equals(other as Object?) as bool is override if other is this return true if other inherits ClrNativeType return .clrType == other.clrType else return false def getHashCode as int is override return .clrType.getHashCode get backEndType as Type # was clrType changed to backEndType return _type # Not part of NativeType - used by a cast to ClrNativeType then deref (:( # should only be used/called from within ClrBackEnd code get clrType as Type return _type get name as String is override return _type.name get fullName as String is override return _type.fullName to ! get isValueType as bool is override return _type.isValueType get baseType as NativeType? is override return if(_type.baseType, ClrNativeType(_type.baseType), nil) def customAttributes as IList is override return .clrType.getCustomAttributes(true) to IList get isSystemObjectClass as bool is override return /#_type and #/ .fullName == 'System.Object' get isSystemTypeClass as bool is override return /#_type and #/ .fullName == 'System.Type' class ClrTypeProxy inherits NativeTypeProxy """ Acts as an ITypeProxy where the CLR type is known. Typically used when scanning DLLs. """ shared var _cobraNameForSharpGenericNameCache = Dictionary() def cobraNameForSharpBoxName(name as String) as String """ Returns 'Foo' for 'Foo' and 'Foo' for 'Foo`2' In other words, works for generic and non-generic classes, structs and interfaces. Does *not* work for arrays, pointers, etc. """ if '`' not in name return name if _cobraNameForSharpGenericNameCache.containsKey(name) return _cobraNameForSharpGenericNameCache[name] else parts = name.split(c'`') count = int.parse(parts[1]) cobraName = parts[0] + ' 9 # should have bool, char, all the ints, etc. # Must return the Cobra primitive types in place of System.Boolean, System.Char, System.Int16, etc. # because it's the primitives that are used all over the compiler. clrPrimitiveToIType = .compiler.primitiveToITypeCache if clrPrimitiveToIType is nil or clrPrimitiveToIType.count == 0 typeToIType = _makePrimitiveTypesDict clrPrimitiveToIType = Dictionary() for key in typeToIType.keys, clrPrimitiveToIType[key] = typeToIType[key] (.compiler to Compiler).primitiveCache = clrPrimitiveToIType to ! basicType as IType? if clrPrimitiveToIType.tryGetValue(clrType, out basicType) return basicType to ! # handle wrapped types, like arrays and pointers, with recursive calls if clrType.isArray # assert clrType.name.endsWith(r'[]') # could be [,] so TODO: handle multidim arrays return .typeProvider.arrayType(_realTypeWithCache(clrType.getElementType to !)) else if clrType.isNested and not clrType.isGenericParameter declaringType = _realTypeWithCache(clrType.declaringType to !) potential = declaringType.memberForName(clrType.name) if potential is nil # comes up on MS Windows .NET 2.0 for multiple types when using System.Windows.Forms # error: Cannot locate nested CLR type "System.Windows.Forms.UnsafeNativeMethods+IOleControl" (simple name is "IOleControl"). if 'NativeMethods' in clrType.toString return _hack(clrType) .throwError('Cannot locate nested CLR type "[clrType]" (simple name is "[clrType.name]").') else if potential inherits IType return potential else .throwError('Located CLR type spec "[clrType]" but got a [potential.englishName] instead of a type.') else if clrType.isPointer assert clrType.name.endsWith('*') # TODO: handle pointer types return _realTypeWithCache(clrType.getElementType to !) else if clrType.isByRef assert clrType.name.endsWith('&') # TODO: handle ref types return _realTypeWithCache(clrType.getElementType to !) # generic parameters if clrType.isGenericParameter return GenericParam(ClrNativeType(clrType)) typeName = _computeTypeName(clrType) missing = false curNS = _computeNameSpace(clrType, level, out missing) if missing # since the CLR type exists, but cannot be located in our namespaces, # it must be pulled from a dependent DLL that was not directly referenced # but maybe it was already attempted if level > 0, .throwError('Cannot read CLR type "[clrType.fullName]" or its assembly "[clrType.assembly]". Please report to the Cobra discussion forums (http://cobra-language.com/).') (.compiler to dynamic).clrReadAssembly(clrType.assembly) return _realTypeWithoutCache(clrType, level+1) # recurse. guard is just above. # return namespace member member as IMember? = curNS.declForName(typeName) if member inherits IType if member inherits Box if clrType.isGenericType and not clrType.isGenericTypeDefinition # So we have something like ICollection> which is all CLR types. # We need the Cobra types of those args so we can construct the Cobra type from the generic cobra type # otherwise, we would just end up returning the generic definition. member = _typeForArgsOfGeneric(clrType, member) return member else msg = 'Cannot locate CLR type "[clrType]".' if clrType.namespace and clrType.namespace.startsWith('System.') # TODO: On .NET 2.0 only \Tests\110-basics-two\550-collection-equality.cobra # fails with: Cannot locate CLR type "System.Collections.KeyValuePairs[]". # Figure out why. if .compiler and .compiler.verbosity > 1 print msg # TODO: .compiler.warning(msg) return .compiler.objectType else .throwError(msg) return .compiler.intType # CC: to make C# code gen happy. def _makePrimitiveTypesDict as IDictionary clrPrimitiveToIType = Dictionary() #def _makePrimitiveTypesDict as IDictionary # clrPrimitiveToIType = Dictionary() for bt in .compiler.basicTypes if bt.systemAliasProxy key = (.compiler.nativeType((bt.systemAliasProxy to LibraryTypeProxy).qualifiedName) to ClrNativeType).clrType clrPrimitiveToIType[key] = bt assert clrPrimitiveToIType.count == 0 or clrPrimitiveToIType.count > 9 return clrPrimitiveToIType def _computeTypeName(clrType as Type) as String typeName = clrType.name if '`' in typeName # generic like IComparable`1 assert r'[' not in typeName typeName = .cobraNameForSharpBoxName(typeName) else if typeName[typeName.length-1].isLetterOrDigit pass else .throwError('Cannot locate CLR type "[clrType]".') return typeName def _computeNameSpace(clrType as Type, level as int, missing as out bool) as NameSpace missing = false if clrType.namespace is nil, return .compiler.globalNS nameParts = clrType.namespace.split(c'.') member = .compiler.globalNS.symbolForName(nameParts[0]) if member inherits NameSpace, curNS = member else, missing = true if not missing i = 1 while i < nameParts.length namePart = nameParts[i] possible = curNS.declForName(namePart) if possible is nil missing = true break else if possible inherits NameSpace curNS = possible else .throwError('Found "[namePart]" at component [i+1] of CLR type "[clrType.fullName]", but it is a [possible.englishName].') i += 1 return curNS def _typeForArgsOfGeneric(clrType as Type, member as IType) as IType args = List() for genArg in clrType.getGenericArguments args.add(_realTypeWithCache(genArg)) boxMember = member to Box if boxMember.qualifiedName == 'System.Nullable' assert args.count == 1 member = .typeProvider.nilableType(args[0]) else member = boxMember.constructedTypeFor(args) return member def _hack(clrType as Type) as IType if clrType.isInterface return ClrTypeProxy(sharp'typeof(System.ICloneable)').realType else return ClrTypeProxy(Object).realType