Пример #1
0
 public override void VisitFunctionDecl(FunctionDecl decl, VisitKind visitKind)
 {
     if (visitKind == VisitKind.Enter && AttributeHelpers.FindObjcDeprecated(decl.Attrs, out VersionTuple version))
     {
         PlainCDeprecatedFunctions [decl.QualifiedName] = version;
     }
 }
Пример #2
0
 public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
 {
     if (visitKind == VisitKind.Enter && AttributeHelpers.FindObjcDeprecated(decl.Attrs, out VersionTuple version))
     {
         ObjCDeprecatedSelectors[decl.QualifiedName] = version;
     }
 }
Пример #3
0
 void VisitItem(NamedDecl decl, VisitKind visitKind)
 {
     if (visitKind == VisitKind.Enter && AttributeHelpers.FindObjcDeprecated(decl.Attrs, out VersionTuple version))
     {
         ObjCDeprecatedItems[decl.Name] = version;
     }
 }
Пример #4
0
        public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            // protocol members are checked in ObjCProtocolCheck
            if (decl.DeclContext is ObjCProtocolDecl)
            {
                return;
            }

            // don't process methods (or types) that are unavailable for the current platform
            if (!decl.IsAvailable() || !(decl.DeclContext as Decl).IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            string selector = decl.GetSelector();

            if (String.IsNullOrEmpty(selector))
            {
                return;
            }

            var  name  = (decl.IsClassMethod ? "+" : String.Empty) + decl.QualifiedName;
            bool found = qualified_selectors.Contains(name);

            if (!found)
            {
                // a category could be inlined into the type it extend
                var category = decl.DeclContext as ObjCCategoryDecl;
                if (category != null)
                {
                    var cname = category.Name;
                    if (cname == null)
                    {
                        name = GetCategoryBase(category) + name;
                    }
                    else
                    {
                        name = name.ReplaceFirstInstance(cname, GetCategoryBase(category));
                    }
                    found = qualified_selectors.Contains(name);
                }
            }
            if (!found)
            {
                Log.On(framework).Add($"!missing-selector! {name} not bound");
            }
        }
Пример #5
0
 public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
 {
     if (visitKind != VisitKind.Enter)
     {
         return;
     }
     Visit(decl);
 }
        public override void VisitObjCInterfaceDecl(ObjCInterfaceDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            var name = decl.Name;

            if (name.EndsWith("Internal", StringComparison.Ordinal))
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            TypeDefinition td;

            if (!type_map.TryGetValue(name, out td))
            {
                Console.WriteLine("!missing-type! {0} not bound", name);
                // other checks can't be done without an actual type to inspect
                return;
            }

            // check base type
            var nbt = decl.SuperClass?.Name;
            var mbt = td.BaseType?.Resolve().GetName();

            if (nbt != mbt)
            {
                Console.WriteLine("!wrong-base-type! {0} expected {1} actual {2}", name, nbt, mbt);
            }

            // check protocols
            foreach (var protocol in decl.Protocols)
            {
                var pname = protocol.Name;
                if (!ImplementProtocol(pname, td))
                {
                    Console.WriteLine("!missing-protocol-conformance! {0} should conform to {1}", name, pname);
                }
            }

            // TODO : check for extraneous protocols

            type_map.Remove(name);
        }
Пример #7
0
        public override void VisitObjCInterfaceDecl(ObjCInterfaceDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            var name = decl.Name;

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            if (!type_map.TryGetValue(name, out var td))
            {
                Log.On(framework).Add($"!missing-type! {name} not bound");
                // other checks can't be done without an actual type to inspect
                return;
            }

            // check base type
            var nbt = decl.SuperClass?.Name;
            var mbt = td.BaseType?.Resolve().GetName();

            if (nbt != mbt)
            {
                Log.On(framework).Add($"!wrong-base-type! {name} expected {nbt} actual {mbt}");
            }

            // check protocols
            foreach (var protocol in decl.Protocols)
            {
                var pname = protocol.Name;
                if (!ImplementProtocol(pname, td))
                {
                    Log.On(framework).Add($"!missing-protocol-conformance! {name} should conform to {pname}");
                }
            }

            // TODO : check for extraneous protocols

            type_map.Remove(name);
        }
Пример #8
0
 public override void VisitObjCCategoryDecl(ObjCCategoryDecl decl, VisitKind visitKind)
 {
     if (visitKind != VisitKind.Enter)
     {
         return;
     }
     foreach (var d in decl.Methods)
     {
         Visit(d);
     }
 }
Пример #9
0
 public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
 {
     if (visitKind == VisitKind.Enter && AttributeHelpers.FindObjcDeprecated(decl.Attrs, out VersionTuple version))
     {
         var qn = decl.QualifiedName;
         if (decl.IsClassMethod)
         {
             qn = "+" + qn;
         }
         ObjCDeprecatedSelectors [qn] = version;
     }
 }
Пример #10
0
 void VisitItem(NamedDecl decl, VisitKind visitKind)
 {
     if (visitKind == VisitKind.Enter && AttributeHelpers.FindObjcDeprecated(decl.Attrs, out VersionTuple version))
     {
         // `(anonymous)` has a null name
         var name = decl.Name;
         if (name is not null)
         {
             ObjCDeprecatedItems[name] = version;
         }
     }
 }
        public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            // don't process methods (or types) that are unavailable for the current platform
            if (!decl.IsAvailable() || !(decl.DeclContext as Decl).IsAvailable())
            {
                return;
            }

            var method = GetMethod(decl);

            // don't report missing [DesignatedInitializer] for types that are not bound - that's a different problem
            if (method == null)
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var designated_initializer = method.IsDesignatedInitializer();

            if (!method.IsConstructor)
            {
                if (designated_initializer)
                {
                    Log.On(framework).Add($"!incorrect-designated-initializer! {method.GetName ()} is not a constructor");
                }
            }
            else if (decl.IsDesignatedInitializer)
            {
                if (!designated_initializer)
                {
                    Log.On(framework).Add($"!missing-designated-initializer! {method.GetName ()} is missing an [DesignatedInitializer] attribute");
                }
            }
            else
            {
                if (designated_initializer)
                {
                    Log.On(framework).Add($"!extra-designated-initializer! {method.GetName ()} is incorrectly decorated with an [DesignatedInitializer] attribute");
                }
            }
        }
Пример #12
0
        public override void VisitFunctionDecl(FunctionDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            // skip macros : we generally implement them but their name is lost (so no matching is possible)
            if (!decl.IsExternC)
            {
                return;
            }

            var name = decl.Name;

            // do not consider _* or __* as public API that should be bound
            if (name [0] == '_')
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            if (!dllimports.ContainsKey(name))
            {
                // if we find functions without matching DllImport then we report them
                // but don't report deprecated functions
                if (!decl.IsDeprecated())
                {
                    Log.On(framework).Add($"!missing-pinvoke! {name} is not bound");
                }
                return;
            }

            dllimports.Remove(name);
        }
Пример #13
0
        public override void VisitObjCCategoryDecl(ObjCCategoryDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            var categoryName = decl.Name;

            if (categoryName == null)
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var ciName = decl.ClassInterface.Name;

            if (!type_map_copy.TryGetValue(ciName, out var td))
            {
                // other checks can't be done without an actual type to inspect
                return;
            }

            // check protocols
            foreach (var protocol in decl.Protocols)
            {
                var pname = protocol.Name;
                if (!ImplementProtocol(pname, td))
                {
                    Log.On(framework).Add($"!missing-protocol-conformance! {ciName} should conform to {pname} (defined in '{categoryName}' category)");
                }
            }
        }
Пример #14
0
		public override void VisitCXXRecordDecl(CXXRecordDecl decl, VisitKind visitKind)
		{
			if (visitKind != VisitKind.Enter || !decl.IsCompleteDefinition || decl.Name == null)
				return;

			nameToDecl[decl.QualifiedName] = decl;
			switch (decl.QualifiedName) {
				case "Urho3D::RefCounted":
					UrhoRefCounted = decl;
					break;
				case "Urho3D::Object":
					UrhoObjectType = decl;
					break;
				case "Urho3D::EventHandler":
					EventHandlerType = decl;
					break;
			}
		}
Пример #15
0
        public override void VisitObjCInterfaceDecl(ObjCInterfaceDecl decl, VisitKind visitKind)
        {
            if (visitKind == VisitKind.Enter && !IsPrivate(decl.Name))
            {
                currentInterfaceDecl = decl;
                currentType          = APITestTool.XamarinMacAssembly.GetTypes().FirstOrDefault(x => x.Name == Extrospection.Helpers.GetManagedName(decl.Name));          // Technically SingleOrDefault, but significantly slower

                // If we couldn't match up a type, we're going to skip testing for now.
                if (currentType == null)
                {
                    currentInterfaceDecl = null;
                }
            }
            else
            {
                currentInterfaceDecl = null;
                currentType          = null;
            }
        }
Пример #16
0
        public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            // don't process methods (or types) that are unavailable for the current platform
            if (!decl.IsAvailable() || !(decl.DeclContext as Decl).IsAvailable())
            {
                return;
            }

            // does not look exposed, but part of the dump
            if (decl.DumpToString().IndexOf("UI_APPEARANCE_SELECTOR", StringComparison.OrdinalIgnoreCase) < 0)
            {
                return;
            }

            VisitObjCMethodDecl(decl);
        }
Пример #17
0
        public override void VisitCXXRecordDecl(CXXRecordDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter || !decl.IsCompleteDefinition || decl.Name == null)
            {
                return;
            }

            nameToDecl[decl.QualifiedName] = decl;
            switch (decl.QualifiedName)
            {
            case "Urho3D::RefCounted":
                UrhoRefCounted = decl;
                break;

            case "Urho3D::Object":
                UrhoObjectType = decl;
                break;

            case "Urho3D::EventHandler":
                EventHandlerType = decl;
                break;
            }
        }
Пример #18
0
            protected override bool DumpNode(SyntaxNode node, VisitKind visitKind)
            {
                string GetIndent(int depth, bool lastChild)
                => new string(' ', depth * 2);

                if (visitKind == VisitKind.Enter)
                {
                    writer.Write(GetIndent(NodeStack.Count - 1, false));
                    writer.Write(node.GetType().Name);
                    writer.WriteLine();
                }
                else if (visitKind == VisitKind.Leave)
                {
                    var tokens = NodeStack[0].Tokens;
                    for (int i = 0; i < tokens.Count; i++)
                    {
                        var token = tokens[i];
                        writer.Write(GetIndent(NodeStack.Count, i == tokens.Count - 1));
                        writer.Write(token.Kind);

                        switch (token.Kind)
                        {
                        case SyntaxKind.WhiteSpaceTriviaToken:
                            writer.WriteLine();
                            break;

                        default:
                            writer.Write(" → ");
                            writer.WriteLine(token.SourceText);
                            break;
                        }
                    }
                }

                return(true);
            }
Пример #19
0
		public override void VisitCXXRecordDecl(CXXRecordDecl decl, VisitKind visitKind)
		{
			//Console.WriteLine($"{decl.QualifiedName} {visitKind} ");
			if (decl.QualifiedName == ("Urho3D::String")) {
				//Console.WriteLine($"{decl.QualifiedName} {visitKind} {decl.IsCompleteDefinition}");
			}

			if (visitKind != VisitKind.Enter || !decl.IsCompleteDefinition || decl.Name == null) {
				return;
			}
			if (visitKind == VisitKind.Leave)
				currentType = null;


			BaseNodeType baseNodeType;
			if (baseNodeTypes.TryGetValue(decl.QualifiedName, out baseNodeType)) {
				baseNodeType.Decl = decl;
				baseNodeType.Bind();
				return;
			}

			foreach (var bnt in baseNodeTypes.Values) {
				if (bnt.Decl != null && decl.IsDerivedFrom(bnt.Decl)) {
					bnt.Bind(decl);
					return;
				}
			}
		}
Пример #20
0
		public override void VisitEnumDecl(EnumDecl decl, VisitKind visitKind)
		{
			if (visitKind != VisitKind.Enter || !decl.IsCompleteDefinition || decl.QualifiedName == null)
				return;

			if (!decl.QualifiedName.StartsWith ("Urho")) {
				if (!decl.QualifiedName.ToLower ().Contains ("loopmode2d"))
			
				return;
			}
			
			//Console.WriteLine($"VisitingType: {decl.QualifiedName}");
			string typeName = RemapTypeName(decl.Name);

			PushType(new TypeDeclaration
			{
				Name = typeName,
				ClassType = ClassType.Enum,
				Modifiers = Modifiers.Public
			}, StringUtil.GetTypeComments(decl));

			foreach (var constantDecl in decl.Decls<EnumConstantDecl>()) {
				var valueName = RemapEnumName (typeName, constantDecl.Name);

				switch (valueName) {
				// LIST HERE ANY Values we want to skip
				case "foo":
				case null:
					continue;

				}
				var x = new EnumMemberDeclaration();
				var enumValue = new EnumMemberDeclaration { Name = valueName };
				if (constantDecl.InitExpr != null) {
					APSInt v;

					constantDecl.InitExpr.EvaluateAsInt (decl.AstContext, out v);
					var ul = v.GetLimitedValue ();
					PrimitiveExpression value;
					if ((ul & 0xffffffff) == ul)
						value = new PrimitiveExpression ((int)ul);
					else
						throw new NotImplementedException ($"Got a {ul} value which will not fit on an int, you must manually handle this case in the generatorg");
					enumValue.Initializer = value;
				}
				currentType.Members.Add(enumValue);
			}
		}
Пример #21
0
 public override void VisitObjCCategoryDecl(ObjCCategoryDecl decl, VisitKind visitKind) => VisitItem(decl, visitKind);
Пример #22
0
 public virtual bool VisitAttributeStatementSyntax(
     AttributeStatementSyntax attributeStatement,
     VisitKind visitKind)
 => VisitStatementSyntax(attributeStatement, visitKind);
Пример #23
0
        public override void VisitEnumDecl(EnumDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            string name = decl.Name;

            if (name == null)
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var mname = Helpers.GetManagedName(name);

            if (!enums.TryGetValue(mname, out var type))
            {
                Log.On(framework).Add($"!missing-enum! {name} not bound");
                return;
            }
            else
            {
                enums.Remove(mname);
            }

            int  native_size = 4;
            bool native      = false;

            // FIXME: this can be simplified
            switch (decl.IntegerQualType.ToString())
            {
            case "NSInteger":
            case "NSUInteger":
            case "CFIndex":
            case "CFOptionFlags":
            case "AVAudioInteger":
                native_size = 8;                 // in managed code it's always the largest size
                native      = true;
                break;

            case "unsigned long":
            case "unsigned int":
            case "int32_t":
            case "uint32_t":
            case "int":
            case "GLint":
            case "GLuint":
            case "GLenum":
            case "SInt32":
            case "UInt32":
            case "OptionBits":             // UInt32
            case "long":
            case "FourCharCode":
            case "OSStatus":
                break;

            case "int64_t":
            case "uint64_t":
            case "unsigned long long":
            case "CVOptionFlags":             // uint64_t
                native_size = 8;
                break;

            case "UInt16":
            case "int16_t":
            case "uint16_t":
            case "short":
                native_size = 2;
                break;

            case "UInt8":
            case "int8_t":
            case "uint8_t":
                native_size = 1;
                break;

            default:
                throw new NotImplementedException(decl.IntegerQualType.ToString());
            }

            // check correct [Native] decoration
            if (native)
            {
                if (!IsNative(type))
                {
                    Log.On(framework).Add($"!missing-enum-native! {name}");
                }
            }
            else
            {
                if (IsNative(type))
                {
                    Log.On(framework).Add($"!extra-enum-native! {name}");
                }
            }

            int managed_size = 4;

            switch (GetEnumUnderlyingType(type).Name)
            {
            case "Byte":
            case "SByte":
                managed_size = 1;
                break;

            case "Int16":
            case "UInt16":
                managed_size = 2;
                break;

            case "Int32":
            case "UInt32":
                break;

            case "Int64":
            case "UInt64":
                managed_size = 8;
                break;

            default:
                throw new NotImplementedException();
            }
            if (native_size != managed_size)
            {
                Log.On(framework).Add($"!wrong-enum-size! {name} managed {managed_size} vs native {native_size}");
            }
        }
Пример #24
0
        public override void VisitObjCProtocolDecl(ObjCProtocolDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var            name = decl.Name;
            TypeDefinition td;

            if (!protocol_map.TryGetValue(name, out td))
            {
                if (!decl.IsDeprecated())
                {
                    Log.On(framework).Add($"!missing-protocol! {name} not bound");
                }
                // other checks can't be done without an actual protocol to inspect
                return;
            }

            // build type selector-required map
            var map = new Dictionary <string, bool> ();

            foreach (var ca in td.CustomAttributes)
            {
                string export      = null;
                string g_export    = null;
                string s_export    = null;
                bool   is_required = false;
                bool   is_property = false;
                bool   is_static   = false;
                switch (ca.Constructor.DeclaringType.Name)
                {
                case "ProtocolMemberAttribute":
                    foreach (var p in ca.Properties)
                    {
                        switch (p.Name)
                        {
                        case "Selector":
                            export = p.Argument.Value as string;
                            break;

                        case "GetterSelector":
                            g_export = p.Argument.Value as string;
                            break;

                        case "SetterSelector":
                            s_export = p.Argument.Value as string;
                            break;

                        case "IsRequired":
                            is_required = (bool)p.Argument.Value;
                            break;

                        case "IsProperty":
                            is_property = (bool)p.Argument.Value;
                            break;

                        case "IsStatic":
                            is_static = (bool)p.Argument.Value;
                            break;
                        }
                    }
                    break;
                }
                if (is_property)
                {
                    if (g_export != null)
                    {
                        if (is_static)
                        {
                            g_export = "+" + g_export;
                        }
                        map.Add(g_export, is_required);
                    }
                    if (s_export != null)
                    {
                        if (is_static)
                        {
                            s_export = "+" + s_export;
                        }
                        map.Add(s_export, is_required);
                    }
                }
                else if (export != null)
                {
                    if (is_static)
                    {
                        export = "+" + export;
                    }
                    map.Add(export, is_required);
                }
            }

            var deprecatedProtocol = (decl.DeclContext as Decl).IsDeprecated();

            // don't report anything for deprecated protocols
            // (we still report some errors for deprecated members of non-deprecated protocols - because abstract/non-abstract can
            // change the managed API and we want to get things right, even if for deprecated members).
            if (!deprecatedProtocol)
            {
                var remaining = new Dictionary <string, bool> (map);

                // check that required members match the [Abstract] members
                foreach (ObjCMethodDecl method in decl.Methods)
                {
                    // some members might not be part of the current platform
                    if (!method.IsAvailable())
                    {
                        continue;
                    }

                    var selector = GetSelector(method);
                    if (selector == null)
                    {
                        continue;
                    }

                    // a .NET interface cannot have constructors - so we cannot enforce that on the interface
                    if (IsInit(selector))
                    {
                        continue;
                    }

                    if (method.IsClassMethod)
                    {
                        selector = "+" + selector;
                    }

                    bool is_abstract;
                    if (map.TryGetValue(selector, out is_abstract))
                    {
                        bool required = method.ImplementationControl == ObjCImplementationControl.Required;
                        if (required)
                        {
                            if (!is_abstract)
                            {
                                Log.On(framework).Add($"!incorrect-protocol-member! {GetName (decl, method)} is REQUIRED and should be abstract");
                            }
                        }
                        else
                        {
                            if (is_abstract)
                            {
                                Log.On(framework).Add($"!incorrect-protocol-member! {GetName (decl, method)} is OPTIONAL and should NOT be abstract");
                            }
                        }
                        remaining.Remove(selector);
                    }
                    else if (!method.IsClassMethod)
                    {
                        // a .NET interface cannot have static methods - so we can only report missing instance methods
                        if (!decl.IsDeprecated())
                        {
                            Log.On(framework).Add($"!missing-protocol-member! {GetName (decl, method)} not found");
                        }
                        remaining.Remove(selector);
                    }
                }

                foreach (var selector in remaining.Keys)
                {
                    Log.On(framework).Add($"!extra-protocol-member! unexpected selector {decl.Name}::{selector} found");
                }
                remaining.Clear();
            }
            map.Clear();

            protocol_map.Remove(name);
        }
Пример #25
0
 public virtual bool VisitEdgeStatementSyntax(
     EdgeStatementSyntax edgeStatement,
     VisitKind visitKind)
 => VisitStatementSyntax(edgeStatement, visitKind);
Пример #26
0
        public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            // don't process methods (or types) that are unavailable for the current platform
            if (!decl.IsAvailable() || !(decl.DeclContext as Decl).IsAvailable())
            {
                return;
            }

            var method = GetMethod(decl);

            // don't report missing nullability on types that are not bound - that's a different problem
            if (method == null)
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var t = method.DeclaringType;
            // look for [NullableContext] for defaults
            var managed_default_nullability = GetNullableContext(method);

            if (managed_default_nullability == Null.Oblivious)
            {
                managed_default_nullability = GetNullableContext(t);
            }

            // check parameters
            // categories have an offset of 1 for the extension method type (spotted as static types)
            int i = t.IsSealed && t.IsAbstract ? 1 : 0;

            foreach (var p in decl.Parameters)
            {
                var mp = method.Parameters [i++];
                // a managed `out` value does not need to be inialized, won't be null (but can be ignored)
                if (mp.IsOut)
                {
                    continue;
                }

                var pt = mp.ParameterType;
                // if bound as `IntPtr` then nullability attributes won't be present
                if (pt.IsValueType)
                {
                    continue;
                }

                Null parameter_nullable;

                // if we used a type by reference (e.g. `ref float foo`); or a nullable type (e.g. `[BindAs]`)
                // then assume it's meant as a nullable type) without additional decorations
                if (pt.IsByReference || pt.FullName.StartsWith("System.Nullable`1<", StringComparison.Ordinal))
                {
                    parameter_nullable = Null.Annotated;
                }
                else
                {
                    // check C# 8 compiler attributes
                    var nullable = GetNullable(mp);
                    if (nullable.Length > 1)
                    {
                        // check the type itself, TODO check the generics (don't think we have such cases yet)
                        parameter_nullable = nullable [0];
                    }
                    else if (nullable.Length == 0)
                    {
                        parameter_nullable = managed_default_nullability;
                    }
                    else
                    {
                        parameter_nullable = nullable [0];
                    }
                }

                // match with native and, if needed, report discrepancies
                p.QualType.Type.GetNullability(p.AstContext, out var nullability);
                switch (nullability)
                {
                case NullabilityKind.NonNull:
                    if (parameter_nullable == Null.Annotated)
                    {
                        Log.On(framework).Add($"!extra-null-allowed! '{method.FullName}' has a extraneous [NullAllowed] on parameter #{i-1}");
                    }
                    break;

                case NullabilityKind.Nullable:
                    if (parameter_nullable != Null.Annotated)
                    {
                        Log.On(framework).Add($"!missing-null-allowed! '{method.FullName}' is missing an [NullAllowed] on parameter #{i-1}");
                    }
                    break;

                case NullabilityKind.Unspecified:
                    break;
                }
            }

            // with .net a constructor will always return something (or throw)
            // that's not the case in ObjC where `init*` can return `nil`
            if (method.IsConstructor)
            {
                return;
            }

            var mrt = method.ReturnType;

            // if bound as an `IntPtr` then the nullability will not be visible in the metadata
            if (mrt.IsValueType)
            {
                return;
            }

            Null return_nullable;

            // if we used a nullable type (e.g. [BindAs] then assume it's meant as a nullable type) without additional decorations
            if (mrt.FullName.StartsWith("System.Nullable`1<", StringComparison.Ordinal))
            {
                return_nullable = Null.Annotated;
            }
            else
            {
                ICustomAttributeProvider cap;
                // the managed attributes are on the property, not the special methods
                if (method.IsGetter)
                {
                    var property = method.FindProperty();
                    // also `null_resettable` will only show something (natively) on the setter (since it does not return null, but accept it)
                    // in this case we'll trust xtro checking the setter only (if it exists, if not then it can't be `null_resettable`)
                    if (property.SetMethod != null)
                    {
                        return;
                    }
                    cap = property;
                }
                else
                {
                    cap = method.MethodReturnType;
                }
                Null [] mrt_nullable = GetNullable(cap);

                if (mrt_nullable.Length > 1)
                {
                    // check the type itself, TODO check the generics (don't think we have such cases yet)
                    return_nullable = mrt_nullable [0];
                }
                else if (mrt_nullable.Length == 0)
                {
                    return_nullable = managed_default_nullability;
                }
                else
                {
                    return_nullable = mrt_nullable [0];
                }
            }

            var rt = decl.ReturnQualType;

            rt.Type.GetNullability(decl.AstContext, out var rnull);
            switch (rnull)
            {
            case NullabilityKind.NonNull:
                if (return_nullable == Null.Annotated)
                {
                    Log.On(framework).Add($"!extra-null-allowed! '{method}' has a extraneous [NullAllowed] on return type");
                }
                break;

            case NullabilityKind.Nullable:
                if (return_nullable != Null.Annotated)
                {
                    Log.On(framework).Add($"!missing-null-allowed! '{method}' is missing an [NullAllowed] on return type");
                }
                break;

            case NullabilityKind.Unspecified:
                break;
            }
        }
Пример #27
0
		public override void VisitCXXMethodDecl(CXXMethodDecl decl, VisitKind visitKind)
		{
			if (visitKind != VisitKind.Enter)
				return;

			var isConstructor = decl is CXXConstructorDecl;
			if (decl is CXXDestructorDecl || isConstructor)
				return;

			if (decl.IsCopyAssignmentOperator || decl.IsMoveAssignmentOperator)
				return;

			if (decl.Parent == null)
				return;
			if (!decl.Parent.QualifiedName.StartsWith("Urho3D::"))
				return;

			// Only get methods prefixed with Get with no parameters
			// and Set methods that return void and take a single parameter
			var name = decl.Name;

			// Handle Get methods that are not really getters
			// This is a get method that does not get anything

			QualType type;
			if (name.StartsWith("Get") || name.StartsWith("Is")) {
				if (decl.Parameters.Count() != 0)
					return;
				if (decl.ReturnQualType.ToString() == "void")
					return;
				if (name == "IsElementEventSender" ||
					name == "IsOpen" ||
					name == "IsPressed")
					return;

				type = decl.ReturnQualType;
			} else if (name.StartsWith("Set")) {
				if (decl.Parameters.Count() != 1)
					return;
				if (!(decl.ReturnQualType.Bind() is Sharpie.Bind.Types.VoidType))
					return;
				if ((name == "SetTypeName" || name == "SetType") && decl.Parent.Name == "UnknownComponent")
					return;
				if (decl.Access != AccessSpecifier.Public)
					return;
				type = decl.Parameters.FirstOrDefault().QualType;
			} else
				return;

			Dictionary<string, Dictionary<QualType, GetterSetter>> typeProperties;
			if (!allProperties.TryGetValue(decl.Parent.Name, out typeProperties)) {
				typeProperties = new Dictionary<string, Dictionary<QualType, GetterSetter>>();
				allProperties[decl.Parent.Name] = typeProperties;
			}

			var propName = name.Substring(name.StartsWith("Is") ? 2 : 3);

			Dictionary<QualType, GetterSetter> property;

			if (!typeProperties.TryGetValue(propName, out property)) {
				property = new Dictionary<QualType, GetterSetter>();
				typeProperties[propName] = property;
			}
			GetterSetter gs;
			if (!property.TryGetValue(type, out gs)) {
				gs = new GetterSetter() { Name = propName };
			}

			if (name.StartsWith("Get") || name.StartsWith("Is")) {
				Console.WriteLine($"Getter exists for {name}");
				gs.Getter = decl;
			} else {
				if (gs.Setter != null) {
					Console.WriteLine($"Setter exists for {name}");
				}
				gs.Setter = decl;
			}
			property[type] = gs;
		}
Пример #28
0
        public override void VisitCXXMethodDecl(CXXMethodDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            var isConstructor = decl is CXXConstructorDecl;

            if (decl is CXXDestructorDecl || isConstructor)
            {
                return;
            }

            if (decl.IsCopyAssignmentOperator || decl.IsMoveAssignmentOperator)
            {
                return;
            }

            if (decl.Parent == null)
            {
                return;
            }
            if (!decl.Parent.QualifiedName.StartsWith("Urho3D::"))
            {
                return;
            }

            // Only get methods prefixed with Get with no parameters
            // and Set methods that return void and take a single parameter
            var name = decl.Name;

            // Handle Get methods that are not really getters
            // This is a get method that does not get anything

            QualType type;

            if (name.StartsWith("Get") || name.StartsWith("Is"))
            {
                if (decl.Parameters.Count() != 0)
                {
                    return;
                }
                if (decl.ReturnQualType.ToString() == "void")
                {
                    return;
                }
                if (name == "IsElementEventSender" ||
                    name == "IsOpen" ||
                    name == "IsPressed")
                {
                    return;
                }

                type = decl.ReturnQualType;
            }
            else if (name.StartsWith("Set"))
            {
                if (decl.Parameters.Count() != 1)
                {
                    return;
                }
                if (!(decl.ReturnQualType.Bind() is Sharpie.Bind.Types.VoidType))
                {
                    return;
                }
                if ((name == "SetTypeName" || name == "SetType") && decl.Parent.Name == "UnknownComponent")
                {
                    return;
                }
                if (decl.Access != AccessSpecifier.Public)
                {
                    return;
                }
                type = decl.Parameters.FirstOrDefault().QualType;
            }
            else
            {
                return;
            }

            Dictionary <string, Dictionary <QualType, GetterSetter> > typeProperties;

            if (!allProperties.TryGetValue(decl.Parent.Name, out typeProperties))
            {
                typeProperties = new Dictionary <string, Dictionary <QualType, GetterSetter> >();
                allProperties[decl.Parent.Name] = typeProperties;
            }

            var propName = name.Substring(name.StartsWith("Is") ? 2 : 3);

            Dictionary <QualType, GetterSetter> property;

            if (!typeProperties.TryGetValue(propName, out property))
            {
                property = new Dictionary <QualType, GetterSetter>();
                typeProperties[propName] = property;
            }
            GetterSetter gs;

            if (!property.TryGetValue(type, out gs))
            {
                gs = new GetterSetter()
                {
                    Name = propName
                };
            }

            if (name.StartsWith("Get") || name.StartsWith("Is"))
            {
                Console.WriteLine($"Getter exists for {name}");
                if (propName != decl.Parent.Name || propName == "Text")
                {
                    // do not generate Getter if propertyName equals to typename (Text type already has a workaround for this case)
                    gs.Getter = decl;
                }
            }
            else
            {
                if (gs.Setter != null)
                {
                    Console.WriteLine($"Setter exists for {name}");
                }
                gs.Setter = decl;
            }
            property[type] = gs;
        }
Пример #29
0
        public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            // don't process methods (or types) that are unavailable for the current platform
            if (!decl.IsAvailable() || !(decl.DeclContext as Decl).IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var simd_type = string.Empty;
            var requires_marshal_directive = false;
            var native_simd = ContainsSimdTypes(decl, ref simd_type, ref requires_marshal_directive);

            ManagedSimdInfo info;

            managed_methods.TryGetValue(decl.GetName(), out info);
            var method = info?.Method;

            if (!native_simd)
            {
                if (method != null)
                {
                    // The managed method uses types that were incorrectly used in place of the correct Simd types,
                    // but the native method doesn't use the native Simd types. This means the binding is correct.
                }
                else
                {
                    // Neither the managed nor the native method have anything to do with Simd types.
                }
                return;
            }

            if (method == null)
            {
                // Could not map the native method to a managed method.
                // This needs investigation, to see why the native method couldn't be mapped.

                // Check if this is new API, in which case it probably couldn't be mapped because we haven't bound it.
                var is_new      = false;
                var attrs       = decl.Attrs.ToList();
                var parentClass = decl.DeclContext as Decl;
                if (parentClass != null)
                {
                    attrs.AddRange(parentClass.Attrs);
                }

                foreach (var attr in attrs)
                {
                    var av_attr = attr as AvailabilityAttr;
                    if (av_attr == null)
                    {
                        continue;
                    }
                    if (av_attr.Platform.Name != "ios")
                    {
                        continue;
                    }
                    if (av_attr.Introduced.Major >= 11)
                    {
                        is_new = true;
                        break;
                    }
                }
                if (is_new && !very_strict)
                {
                    return;
                }
                if (!strict)
                {
                    return;
                }
                Log.On(framework).Add($"!missing-simd-managed-method! {decl}: could not find a managed method for the native method {decl.GetName ()} (selector: {decl.Selector}). Found the simd type '{simd_type}' in the native signature.");
                return;
            }

            if (!info.ContainsInvalidMappingForSimd)
            {
                // The managed method does not have any types that are incorrect for Simd.
                if (requires_marshal_directive)
                {
                    CheckMarshalDirective(method, simd_type);
                }
                return;
            }

            if (method.IsObsolete())
            {
                // We have a potentially broken managed method, but it's obsolete. That's fine.
                return;
            }

            if (requires_marshal_directive)
            {
                CheckMarshalDirective(method, simd_type);
            }

            // We have a potentially broken managed method. This needs fixing/investigation.
            Log.On(framework).Add($"!unknown-simd-type-in-signature! {method}: the native signature has a simd type ({simd_type}), while the corresponding managed method is using an incorrect (non-simd) type.");
        }
Пример #30
0
        public override void VisitFunctionDecl(FunctionDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            // skip macros : we generally implement them but their name is lost (so no matching is possible)
            if (!decl.IsExternC)
            {
                return;
            }

            var name = decl.Name;

            // do not consider _* or __* as public API that should be bound
            if (name [0] == '_')
            {
                return;
            }

            // exclude non-framework code (e.g. all unix headers)
            var header_file = decl.PresumedLoc.FileName;
            var fxh         = header_file.IndexOf(".framework/Headers/", StringComparison.Ordinal);
            var framework   = String.Empty;

            if (fxh > 0)
            {
                var start = header_file.LastIndexOf('/', fxh) + 1;
                framework = header_file.Substring(start, fxh - start);
            }
            else
            {
                // FIXME: we only process the headers in Frameworks, not all the UNIX headers
                // that still miss a few things (like objc runtime) but nothing that *requires* binding
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            MethodDefinition md;

            if (!dllimports.TryGetValue(name, out md))
            {
                // FIXME: we ignore some frameworks we have not bound (too many entries for our data files)
                // we do this late because, in some cases like vImage, we do bind a few API
                switch (framework)
                {
                // Accelerate and friends
                case "vImage":
                case "vecLib":
                    return;

                // security
                case "GSS":
                    return;

                // exposed in OpenTK-1.dll
                case "OpenAL":
                case "OpenGLES":
                    return;
                }

                // if we find functions without matching DllImport then we report them
                Console.WriteLine("!missing-pinvoke! {0} is not bound", name);
                return;
            }

            dllimports.Remove(name);
        }
Пример #31
0
        public override void VisitEnumDecl(EnumDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            string name = decl.Name;

            if (name == null)
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var mname = Helpers.GetManagedName(name);

            // If our enum is obsoleted, then don't process it.
            if (obsoleted_enums.ContainsKey(mname))
            {
                return;
            }

            if (!enums.TryGetValue(mname, out var type))
            {
                Log.On(framework).Add($"!missing-enum! {name} not bound");
                return;
            }
            else
            {
                enums.Remove(mname);
            }

            int  native_size = 4;
            bool native      = false;

            // FIXME: this can be simplified
            switch (decl.IntegerQualType.ToString())
            {
            case "NSInteger":
            case "NSUInteger":
            case "CFIndex":
            case "CFOptionFlags":
            case "AVAudioInteger":
                native_size = 8;                 // in managed code it's always the largest size
                native      = true;
                break;

            case "unsigned long":
            case "unsigned int":
            case "int32_t":
            case "uint32_t":
            case "int":
            case "GLint":
            case "GLuint":
            case "GLenum":
            case "SInt32":
            case "UInt32":
            case "OptionBits":             // UInt32
            case "long":
            case "FourCharCode":
            case "OSStatus":
                break;

            case "int64_t":
            case "uint64_t":
            case "unsigned long long":
            case "CVOptionFlags":             // uint64_t
                native_size = 8;
                break;

            case "UInt16":
            case "int16_t":
            case "uint16_t":
            case "short":
                native_size = 2;
                break;

            case "UInt8":
            case "int8_t":
            case "uint8_t":
                native_size = 1;
                break;

            default:
                throw new NotImplementedException(decl.IntegerQualType.ToString());
            }

            // check correct [Native] decoration
            if (native)
            {
                if (!IsNative(type))
                {
                    Log.On(framework).Add($"!missing-enum-native! {name}");
                }
            }
            else
            {
                if (IsNative(type))
                {
                    Log.On(framework).Add($"!extra-enum-native! {name}");
                }
            }

            int  managed_size = 4;
            bool signed       = true;

            switch (GetEnumUnderlyingType(type).Name)
            {
            case "Byte":
                signed       = false;
                managed_size = 1;
                break;

            case "SByte":
                managed_size = 1;
                break;

            case "Int16":
                managed_size = 2;
                break;

            case "UInt16":
                signed       = false;
                managed_size = 2;
                break;

            case "Int32":
                break;

            case "UInt32":
                signed = false;
                break;

            case "Int64":
                managed_size = 8;
                break;

            case "UInt64":
                signed       = false;
                managed_size = 8;
                break;

            default:
                throw new NotImplementedException();
            }

            var fields = type.Fields;

            if (signed)
            {
                managed_signed_values.Clear();
                native_signed_values.Clear();
                foreach (var f in fields)
                {
                    // skip special `value__`
                    if (f.IsRuntimeSpecialName && !f.IsStatic)
                    {
                        continue;
                    }
                    if (!f.IsObsolete())
                    {
                        managed_signed_values [Convert.ToInt64(f.Constant)] = f;
                    }
                }

                long n = 0;
                foreach (var value in decl.Values)
                {
                    if ((value.InitExpr != null) && value.InitExpr.EvaluateAsInt(decl.AstContext, out var integer))
                    {
                        n = integer.SExtValue;
                    }

                    native_signed_values [n] = value.ToString();
                    // assume, sequentially assigned (in case next `value.InitExpr` is null)
                    n++;
                }

                foreach (var value in native_signed_values.Keys)
                {
                    if (!managed_signed_values.ContainsKey(value))
                    {
                        Log.On(framework).Add($"!missing-enum-value! {type.Name} native value {native_signed_values [value]} = {value} not bound");
                    }
                    else
                    {
                        managed_signed_values.Remove(value);
                    }
                }

                foreach (var value in managed_signed_values.Keys)
                {
                    if ((value == 0) && IsExtraZeroValid(type.Name, managed_signed_values [0].Name))
                    {
                        continue;
                    }
                    // value could be decorated with `[No*]` and those should not be reported
                    if (managed_signed_values [value].IsAvailable())
                    {
                        Log.On(framework).Add($"!extra-enum-value! Managed value {value} for {type.Name}.{managed_signed_values [value].Name} not found in native headers");
                    }
                }
            }
            else
            {
                managed_unsigned_values.Clear();
                native_unsigned_values.Clear();
                foreach (var f in fields)
                {
                    // skip special `value__`
                    if (f.IsRuntimeSpecialName && !f.IsStatic)
                    {
                        continue;
                    }
                    if (!f.IsObsolete())
                    {
                        managed_unsigned_values [Convert.ToUInt64(f.Constant)] = f;
                    }
                }

                ulong n = 0;
                foreach (var value in decl.Values)
                {
                    if ((value.InitExpr != null) && value.InitExpr.EvaluateAsInt(decl.AstContext, out var integer))
                    {
                        n = integer.ZExtValue;
                    }

                    native_unsigned_values [n] = value.ToString();
                    // assume, sequentially assigned (in case next `value.InitExpr` is null)
                    n++;
                }

                foreach (var value in native_unsigned_values.Keys)
                {
                    if (!managed_unsigned_values.ContainsKey(value))
                    {
                        // only for unsigned (flags) native enums we allow all bits set on 32 bits (UInt32.MaxValue)
                        // to be equal to all bit set on 64 bits (UInt64.MaxValue) since the MaxValue differs between
                        // 32bits (e.g. watchOS) and 64bits (all others) platforms
                        var log = true;
                        if (native && (value == UInt32.MaxValue))
                        {
                            log = !managed_unsigned_values.ContainsKey(UInt64.MaxValue);
                            managed_unsigned_values.Remove(UInt64.MaxValue);
                        }
                        if (log)
                        {
                            Log.On(framework).Add($"!missing-enum-value! {type.Name} native value {native_unsigned_values [value]} = {value} not bound");
                        }
                    }
                    else
                    {
                        managed_unsigned_values.Remove(value);
                    }
                }

                foreach (var value in managed_unsigned_values.Keys)
                {
                    if ((value == 0) && IsExtraZeroValid(type.Name, managed_unsigned_values [0].Name))
                    {
                        continue;
                    }
                    // value could be decorated with `[No*]` and those should not be reported
                    if (managed_unsigned_values [value].IsAvailable())
                    {
                        Log.On(framework).Add($"!extra-enum-value! Managed value {value} for {type.Name}.{managed_unsigned_values [value].Name} not found in native headers");
                    }
                }
            }

            if (native_size != managed_size)
            {
                Log.On(framework).Add($"!wrong-enum-size! {name} managed {managed_size} vs native {native_size}");
            }
        }
Пример #32
0
        public override void VisitObjCProtocolDecl(ObjCProtocolDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            var            name = decl.Name;
            TypeDefinition td;

            if (!protocol_map.TryGetValue(name, out td))
            {
                Console.WriteLine("!missing-protocol! {0} not bound", name);
                // other checks can't be done without an actual protocol to inspect
                return;
            }

            // build type selector-required map
            var map = new Dictionary <string, bool> ();

            foreach (var ca in td.CustomAttributes)
            {
                string export      = null;
                string g_export    = null;
                string s_export    = null;
                bool   is_required = false;
                bool   is_property = false;
                switch (ca.Constructor.DeclaringType.Name)
                {
                case "ProtocolMemberAttribute":
                    foreach (var p in ca.Properties)
                    {
                        switch (p.Name)
                        {
                        case "Selector":
                            export = p.Argument.Value as string;
                            break;

                        case "GetterSelector":
                            g_export = p.Argument.Value as string;
                            break;

                        case "SetterSelector":
                            s_export = p.Argument.Value as string;
                            break;

                        case "IsRequired":
                            is_required = (bool)p.Argument.Value;
                            break;

                        case "IsProperty":
                            is_property = (bool)p.Argument.Value;
                            break;
                        }
                    }
                    break;
                }
                if (is_property)
                {
                    if (g_export != null)
                    {
                        map.Add(g_export, is_required);
                    }
                    if (s_export != null)
                    {
                        map.Add(s_export, is_required);
                    }
                }
                else
                {
                    if (export != null)
                    {
                        map.Add(export, is_required);
                    }
                }
            }

            var remaining = new Dictionary <string, bool> (map);

            // check that required members match the [Abstract] members
            foreach (ObjCMethodDecl method in decl.Methods)
            {
                // some members might not be part of the current platform
                if (!method.IsAvailable())
                {
                    continue;
                }

                var selector = GetSelector(method);
                if (selector == null)
                {
                    continue;
                }

                // a .NET interface cannot have constructors - so we cannot enforce that on the interface
                if (IsInit(selector))
                {
                    continue;
                }

                bool is_abstract;
                if (map.TryGetValue(selector, out is_abstract))
                {
                    bool required = method.ImplementationControl == ObjCImplementationControl.Required;
                    if (required)
                    {
                        if (!is_abstract)
                        {
                            Console.WriteLine("!incorrect-protocol-member! {0} is REQUIRED and should be abstract", GetName(decl, method));
                        }
                    }
                    else
                    {
                        if (is_abstract)
                        {
                            Console.WriteLine("!incorrect-protocol-member! {0} is OPTIONAL and should NOT be abstract", GetName(decl, method));
                        }
                    }
                    remaining.Remove(selector);
                }
                else if (!method.IsClassMethod)
                {
                    // a .NET interface cannot have static methods - so we can only report missing instance methods
                    Console.WriteLine("!missing-protocol-member! {0} not found", GetName(decl, method));
                    remaining.Remove(selector);
                }
            }

            foreach (var selector in remaining.Keys)
            {
                Console.WriteLine("!extra-protocol-member! unexpected selector {0}::{1} found", decl.Name, selector);
            }
            remaining.Clear();
            map.Clear();

            protocol_map.Remove(name);
        }
Пример #33
0
 public virtual bool VisitNodeStatementSyntax(
     NodeStatementSyntax nodeStatement,
     VisitKind visitKind)
 => VisitStatementSyntax(nodeStatement, visitKind);
Пример #34
0
		// 
		// Determines if we should bind a method that the C++ API scanner found
		//
		bool MethodIsBindable (CXXMethodDecl decl, VisitKind visitKind)
		{
			// We only care about enter visits, not leave
			if (visitKind != VisitKind.Enter)
				return false;

			// Global definitions, not inside a class, skip
			if (currentType == null) 
				return false;

			// Do not bother with constructors in abstract classes
			var isConstructor = decl is CXXConstructorDecl;
			if (isConstructor && decl.Parent.IsAbstract)
				return false;

			// Do not wrap constructors
			if (decl is CXXDestructorDecl)
				return false;

			// Not supported in C#
			if (decl.IsCopyAssignmentOperator || decl.IsMoveAssignmentOperator)
				return false;

			// TODO: temporary, do not handle opreators
			if (!isConstructor && decl.Name.StartsWith ("operator", StringComparison.Ordinal))
				return false;

			// Sanity check
			if (RemapTypeName (decl.Parent.Name) != currentType.Name) {
				//Console.WriteLine("For some reason we go t amethod that does not belong here {0}.{1} on {2}", decl.Parent.Name, decl.Name, currentType.Name);
				return false;
			}

			// We only bind the public API
			if (decl.Access != AccessSpecifier.Public)
				return false;

			// Skip blacklisted methods
			if (SkipMethod (decl))
				return false;

			// Temporary: while we add support for other things, just to debug things
			// remove types we do not support
			int variantArgumentsCount = 0;
			foreach (var p in decl.Parameters)
			{
				bool isVariantArgument = IsVariantType(p.QualType);
				if (isVariantArgument)
					variantArgumentsCount++;

				if (IsUnsupportedType(p.QualType, returnType: false) && !isVariantArgument)
				{
					//Console.WriteLine($"Bailing out on {p.QualType} from {decl.QualifiedName}");
					return false;
				}
			}

			if (variantArgumentsCount > 1)
				return false; //it won't be easy to handle if it has more than one Variant argument

			if (IsUnsupportedType(decl.ReturnQualType, returnType: true)) {//variant return type is not support yet
				//Console.WriteLine($"RETURN Bailing out on {decl.ReturnQualType} from {decl.QualifiedName}");
				return false;
			}
			return true;
		}
Пример #35
0
		public override void VisitCXXMethodDecl(CXXMethodDecl decl, VisitKind visitKind)
		{
			if (!MethodIsBindable (decl, visitKind))
				return;

			var cmethodBuilder = new StringBuilder();

			AstType pinvokeReturn, methodReturn;
			WrapKind returnIsWrapped;
			ICSharpCode.NRefactory.CSharp.ParameterModifier pinvokeMod, methodMod;

			LookupMarshalTypes(decl.ReturnQualType, out pinvokeReturn, out pinvokeMod, out methodReturn, out methodMod, out returnIsWrapped, isReturn: true);
			var methodReturn2 = methodReturn.Clone();

			var propertyInfo = ScanBaseTypes.GetPropertyInfo(decl);
			if (propertyInfo != null) {
				propertyInfo.HostType = currentType;
				if (decl.Name.StartsWith("Get") || decl.Name.StartsWith("Is"))
					propertyInfo.MethodReturn = methodReturn.Clone();
			}

			var methodName = decl.Name;
			if (currentTypeNames.Contains(methodName))
				methodName += (uniqueMethodName++).ToString();
			currentTypeNames.Add(methodName);

			//
			// PInvoke declaration + C counterpart declaration
			//
			string pinvoke_name = currentType.Name + "_" + methodName;
			var isConstructor = decl is CXXConstructorDecl;

			if (isConstructor) {
				pinvokeReturn = new SimpleType ("IntPtr");

				// Do not bind a default constructor for Skeleton
				if (currentType.Name == "Skeleton")
					return;
			}

			var pinvoke = new MethodDeclaration
			{
				Name = pinvoke_name,
				ReturnType = pinvokeReturn,
				Modifiers = Modifiers.Extern | Modifiers.Static | Modifiers.Internal
			};
			if (!decl.IsStatic && !isConstructor)
				pinvoke.Parameters.Add(new ParameterDeclaration(new SimpleType("IntPtr"), "handle"));

			var dllImport = new Attribute { Type = new SimpleType("DllImport") };
			dllImport.Arguments.Add (new PrimitiveExpression ("mono-urho"));
			dllImport.Arguments.Add (new AssignmentExpression (new IdentifierExpression ("CallingConvention"), csParser.ParseExpression ("CallingConvention.Cdecl")));
			pinvoke.Attributes.Add(new AttributeSection(dllImport));

			// The C counterpart
			var cinvoke = new StringBuilder();
			string marshalReturn = "{0}";
			string creturnType = CleanTypeCplusplus(decl.ReturnQualType);

			switch (creturnType) {
			case "bool":
				creturnType = "int";
				break;
			case "Urho3D::StringHash":
				creturnType = "int";
				marshalReturn = "({0}).Value ()";
				break;
			case "Urho3D::String":
			case "const Urho3D::String &":
			case "const class Urho3D::String &":
				creturnType = "const char *";
				marshalReturn = "strdup(({0}).CString ())";
				break;
			case "const struct Urho3D::TileMapInfo2D &":
				creturnType = "Urho3D::TileMapInfo2D";
				break;
			case "const struct Urho3D::CrowdObstacleAvoidanceParams &":
				creturnType = "Urho3D::CrowdObstacleAvoidanceParams";
				break;
			case "const class Urho3D::Vector3 &":
			case "const class Urho3D::Vector2 &":
			case "const class Urho3D::Vector4 &":
			case "const class Urho3D::IntVector2 &":
			case "const class Urho3D::Quaternion &":
			case "const class Urho3D::Plane &":
			case "const class Urho3D::BoundingBox &": 
			case "const class Urho3D::Color &":
				
			case "Urho3D::Vector3":
			case "Urho3D::Vector2":
			case "Urho3D::Vector4":
			case "Urho3D::IntVector2":
			case "Urho3D::Quaternion":
			case "Urho3D::Plane":
			case "Urho3D::BoundingBox":
			case "Urho3D::Color":
				var nsIndex = creturnType.IndexOf ("Urho3D::") + "Urho3D".Length;
				creturnType = "Interop" + creturnType.Remove (0, nsIndex).Trim ('&', ' ') + " ";
				marshalReturn = "*((" + creturnType + " *) &({0}))";
				break;
			}

			if (creturnType.StartsWith("SharedPtr<"))
			{
				creturnType = creturnType.ExtractGenericParameter().DropClassOrStructPrefix() + " *";
				marshalReturn =
					"auto copy = {0};\n" +
					"\tauto plain = copy.Get();\n" +
					"\tcopy.Detach();\n" +
					"\tdelete copy;\n" +
					"\treturn plain;";
			}

			const string methodNameSuffix = "%MethodSuffix%";
			const string variantConverterMask = "%VariantConvertor%";

			if (isConstructor)
				cmethodBuilder.Append($"DllExport void *\n{pinvoke_name}{methodNameSuffix} (");
			else
				cmethodBuilder.Append($"DllExport {creturnType}\n{pinvoke_name}{methodNameSuffix} (");

			if (decl.IsStatic) {
				cinvoke.Append($"{decl.Parent.Name}::{decl.Name} (");

			} else if (isConstructor) {
				cinvoke.Append($"new {decl.Name}(");
			}

			else {
				cmethodBuilder.Append($"Urho3D::{decl.Parent.Name} *_target");
				if (decl.Parameters.Any())
					cmethodBuilder.Append(", ");
				cinvoke.Append($"_target->{decl.Name} (");
			}

			//
			// Method declaration
			//
			MethodDeclaration method = null;
			ConstructorDeclaration constructor = null;

			if (isConstructor) {
				constructor = new ConstructorDeclaration
				{
					Name = RemapMemberName(decl.Parent.Name, decl.Name),

					Modifiers = (decl.IsStatic ? Modifiers.Static : 0) |
						(propertyInfo != null ? Modifiers.Private : Modifiers.Public)  |
						(decl.Name == "ToString" ? Modifiers.Override : 0)
				};
				constructor.Body = new BlockStatement();
			} else {
				method = new MethodDeclaration
				{
					Name = RemapMemberName(decl.Parent.Name, decl.Name),
					ReturnType = methodReturn,
					Modifiers = (decl.IsStatic ? Modifiers.Static : 0) |
						(propertyInfo != null ? Modifiers.Private : Modifiers.Public)
				};
				method.Body = new BlockStatement();
			}

			// 
			// Marshal from C# to C and the C support to call into C++
			//
			var invoke = new InvocationExpression(new IdentifierExpression(pinvoke_name));
			if (!decl.IsStatic && !isConstructor)
				invoke.Arguments.Add(new IdentifierExpression("handle"));
			bool first = true;
			int anonymousParameterNameCount = 1;
			int currentParamCount = -1;
			foreach (var param in decl.Parameters) {
				currentParamCount++;
				AstType pinvokeParameter, parameter;
				WrapKind wrapKind;

				if (!first) {
					cinvoke.Append(", ");
					cmethodBuilder.Append(", ");
				} else
					first = false;

				LookupMarshalTypes(param.QualType, out pinvokeParameter, out pinvokeMod, out parameter, out methodMod, out wrapKind);

				string paramName = param.Name;
				if (string.IsNullOrEmpty(paramName))
					paramName = "param" + (anonymousParameterNameCount++);

				Expression parameterReference = new IdentifierExpression (paramName);
				switch (currentType.Name) {
				case "Input":
					switch (decl.Name) {
					case "GetMouseButtonDown":
					case "GetMouseButtonPress":
						parameter = new SimpleType ("MouseButton");
						parameterReference = new CastExpression (new PrimitiveType ("int"), parameterReference);
						break;
					case "GetKeyPress":
					case "GetKeyDown":
					case "GetScancodeFromKey":
					case "GetKeyName":
						if (currentParamCount == 0 && paramName == "key") {
							parameter = new SimpleType ("Key");
							parameterReference = new CastExpression (new PrimitiveType ("int"), parameterReference);
						}
						break;
					}
					break;
				case "VertexBuffer":
					switch (decl.Name) {
					case "SetSize":
						if (currentParamCount == 1 && paramName == "elementMask") {
							parameter = new SimpleType ("ElementMask");
							parameterReference = new CastExpression (new PrimitiveType ("uint"), parameterReference);
						}
						break;
					case "GetVertexSize":
						if (currentParamCount == 0 && paramName == "elementMask") {
							parameter = new SimpleType ("ElementMask");
							parameterReference = new CastExpression (new PrimitiveType ("uint"), parameterReference);
						}
						break;
					case "GetElementOffset":
						if (currentParamCount == 0 && paramName == "elementMask") {
							parameter = new SimpleType ("ElementMask");
							parameterReference = new CastExpression (new PrimitiveType ("uint"), parameterReference);
						}
						break;
					}
					break;
				case "Log":
					switch (decl.Name) {
					case "Write":
						if (currentParamCount == 0 && paramName == "level") {
							parameter = new SimpleType ("LogLevel");
							parameterReference = new CastExpression (new PrimitiveType ("int"), parameterReference);
						}
						break;
					}
					break;
				}

				if (constructor == null)
					method.Parameters.Add(new ParameterDeclaration(parameter, paramName, methodMod));
				else
					constructor.Parameters.Add(new ParameterDeclaration(parameter, paramName, methodMod));

				pinvoke.Parameters.Add(new ParameterDeclaration(pinvokeParameter, paramName, pinvokeMod));
				switch (wrapKind) {
				case WrapKind.None:
					invoke.Arguments.Add(parameterReference);
					break;
				case WrapKind.HandleMember:
				case WrapKind.UrhoObject:
					var cond = new ConditionalExpression (new BinaryOperatorExpression (new CastExpression (new PrimitiveType ("object"), parameterReference), BinaryOperatorType.Equality, new PrimitiveExpression (null)),
					csParser.ParseExpression ("IntPtr.Zero"), csParser.ParseExpression (paramName + ".Handle"));
					invoke.Arguments.Add (cond);
					break;
				case WrapKind.EventHandler:
					invoke.Arguments.Add(parameterReference);
					break;
				case WrapKind.StringHash:
					invoke.Arguments.Add (csParser.ParseExpression (paramName + ".Code"));
					break;
				case WrapKind.RefBlittable:
					invoke.Arguments.Add (new DirectionExpression (FieldDirection.Ref, parameterReference));
					break;
				case WrapKind.VectorSharedPtr:
					throw new NotImplementedException ("Vector marshaling not supported for parameters yet");
				}

				var ctype = CleanTypeCplusplus (param.QualType);
				string paramInvoke = paramName;
				switch (ctype) {
				case "bool":
					ctype = "int";
					break;
				case "Urho3D::Deserializer &":
				case "Urho3D::Serializer &":
					ctype = "File *";
					paramInvoke = $"*{paramInvoke}";
					break;
				case "const class Urho3D::String &":
					ctype = "const char *";
					paramInvoke = $"Urho3D::String({paramInvoke})";
					break;
				case "Urho3D::StringHash":
					ctype = "int";
					paramInvoke = $"Urho3D::StringHash({paramInvoke})";
					break;
				case "const class Urho3D::Variant &":
					paramInvoke = $"{variantConverterMask}({paramInvoke})";
					break;
				}

				cmethodBuilder.Append($"{ctype} {paramName}");
				cinvoke.Append($"{paramInvoke}");
			}
			cinvoke.Append(")");
			cmethodBuilder.Append(")\n{\n\t");

			// if the type has a ctor accepting Context - add a parameterless one that will use "this(Application.CurrentContext)"
			if (isConstructor &&
				decl.Parameters.Count() == 1 &&
				decl.Parameters.ElementAt(0).QualType.ToString() == "class Urho3D::Context *") {
				var ctor = new ConstructorDeclaration {
						Modifiers = Modifiers.Public,
						Body = new BlockStatement(),
						Initializer = new ConstructorInitializer { ConstructorInitializerType = ConstructorInitializerType.This }
					};
				ctor.Initializer.Arguments.Add(csParser.ParseExpression("Application.CurrentContext"));
				currentType.Members.Add(ctor);
			}

			if (method != null && methodReturn is Sharpie.Bind.Types.VoidType) {
				method.Body.Add(invoke);
				//	pn ($"fprintf (stderr,\"DEBUG {creturnType} {pinvoke_name} (...)\\n\");");
				cmethodBuilder.AppendLine($"{cinvoke.ToString()};");
			} else {
				ReturnStatement ret = null;
				Expression returnExpression;


				if (!isConstructor)
					ret = new ReturnStatement();

				switch (returnIsWrapped) {
				case WrapKind.HandleMember:
					returnExpression = new InvocationExpression (new MemberReferenceExpression (new IdentifierExpression ("Runtime"), "LookupRefCounted", methodReturn2), invoke);
					break;
				case WrapKind.UrhoObject:
					returnExpression = new InvocationExpression (new MemberReferenceExpression (new IdentifierExpression ("Runtime"), "LookupObject", methodReturn2), invoke);
					break;
				case WrapKind.EventHandler:
					returnExpression = invoke;
					break;
				case WrapKind.StringHash:
					returnExpression = new ObjectCreateExpression (new SimpleType ("StringHash"), invoke);
					break;
				case WrapKind.MarshalPtrToString:
					returnExpression = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression("Marshal"), "PtrToStringAnsi"), invoke);
					break;
				case WrapKind.MarshalPtrToStruct:
					returnExpression = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression("Marshal"), "PtrToStructure"), invoke, new TypeOfExpression(methodReturn2));
					returnExpression = new CastExpression(methodReturn2.Clone(), returnExpression);
					break;
				case WrapKind.VectorSharedPtr:
					var cacheName = "_" + method.Name + "_cache";
					var f = new FieldDeclaration () {
						ReturnType = method.ReturnType.Clone (),
						Modifiers = Modifiers.Private | (method.Modifiers & Modifiers.Static)
					};
					f.Variables.Add (new VariableInitializer (cacheName, null));
					currentType.Members.Add (f);
					
					var sharedPtrType = (methodReturn as SimpleType).TypeArguments.First ().Clone ();
					//TODO: check if UrhoObject
					var create = (sharedPtrType.ToString() == "AnimationState") ? "CreateVectorSharedPtrRefcountedProxy" : "CreateVectorSharedPtrProxy";

					returnExpression = new ConditionalExpression (
						new BinaryOperatorExpression (new IdentifierExpression (cacheName), BinaryOperatorType.InEquality, new PrimitiveExpression (null)),
						new IdentifierExpression (cacheName),
						new AssignmentExpression (
							new IdentifierExpression (cacheName), 
							new InvocationExpression (
								new MemberReferenceExpression (
									new IdentifierExpression ("Runtime"), create, sharedPtrType),
								invoke)));
						
						
					break;
				default:
					returnExpression = invoke;
					break;
				}
				if (ret != null) {
					ret.Expression = returnExpression;
					method.Body.Add(ret);
				} else {
					if (currentType.ClassType == ClassType.Class){
						//usually, the Context is the first object user creates so let's add additional check if engine is inited
						if (currentType.Name == "Context") {
							constructor.Body.Add (new InvocationExpression (new IdentifierExpression ("CheckEngine"), null));
						}
						bool hasBaseTypes = currentType.BaseTypes.Count != 0;
						if (hasBaseTypes) {
							constructor.Initializer = new ConstructorInitializer {
								ConstructorInitializerType = ConstructorInitializerType.Base,
							};
							constructor.Initializer.Arguments.Add(csParser.ParseExpression("UrhoObjectFlag.Empty"));
						}
						var ctorAssign = new AssignmentExpression(new IdentifierExpression("handle"), returnExpression);
						constructor.Body.Add(new ExpressionStatement(ctorAssign));
						if (hasBaseTypes) { 
							constructor.Body.Add (new InvocationExpression (new MemberReferenceExpression (new IdentifierExpression ("Runtime"), "RegisterObject"), new ThisReferenceExpression ()));
						}
					}
				}
				var rstr = String.Format(marshalReturn, cinvoke.ToString());
				CXXRecordDecl returnType;

				//Wrap with WeakPtr all RefCounted subclasses constructors
				if (isConstructor) {
					if (ScanBaseTypes.nameToDecl.TryGetValue(decl.Parent.QualifiedName, out returnType) && returnType.IsDerivedFrom(ScanBaseTypes.UrhoRefCounted))
						rstr = $"WeakPtr<{decl.Name}>({rstr})";
				}

				cmethodBuilder.AppendLine(!rstr.Contains("\treturn ") ? $"return {rstr};" : rstr);
			}
			cmethodBuilder.AppendLine("}\n");

			var code = cmethodBuilder.ToString();

			const string variantArgDef = "const class Urho3D::Variant &";

			//if methods contains Variant argument -- replace it with overloads
			if (code.Contains(variantArgDef))
			{
				var variantSupportedTypes = new Dictionary<string, string>
					{
						//C++ - C# types map
						{"const class Urho3D::Vector3 &", "Vector3"},
						{"const class Urho3D::IntRect &", "IntRect"},
						{"const class Urho3D::Color &", "Color"},
						{"const class Urho3D::Vector2 &", "Vector2"},
						{"const class Urho3D::Vector4 &", "Vector4"},
						{"const class Urho3D::IntVector2 &", "IntVector2"},
						{"const class Urho3D::Quaternion &", "Quaternion"},
						{"int", "int"},
						{"float", "float"},
						{"const char *", "string"},
						//TODO: Matrix, StringHash?
					};
				var primitiveTypes = new[] { "int", "float", "string" };

				pn("// Urho3D::Variant overloads begin:");
				int index = 0;
				foreach (var item in variantSupportedTypes)
				{
					//C:
					p(code
						.Replace(variantArgDef, item.Key)
						.Replace(methodNameSuffix, index.ToString())
						.Replace(variantConverterMask, item.Key == "const char *" ? "Urho3D::String" : string.Empty));
					//methodNameSuffix to avoid error:
					//  error C2733: second C linkage of overloaded function 'function name' not allowed.

					//C#:
					var isPrimitive = primitiveTypes.Contains(item.Value);

					AstType argumentType;
					var argumentModifier = ICSharpCode.NRefactory.CSharp.ParameterModifier.None;
					if (!isPrimitive)
					{
						argumentType = new SimpleType(item.Value);
						argumentModifier = ICSharpCode.NRefactory.CSharp.ParameterModifier.Ref;
					}
					else
					{
						argumentType = new PrimitiveType(item.Value);
					}

					var dllImportItem = (MethodDeclaration)pinvoke.Clone();
					var originalEntryPointName = dllImportItem.Name;
					dllImportItem.Name += index;
					var variantParam = dllImportItem.Parameters.First(p => p.ToString().Contains(variantArgDef));
					variantParam.Type = argumentType.Clone();
					variantParam.ParameterModifier = argumentModifier;
					currentType.Members.Add(dllImportItem);

					var clonedMethod = (MethodDeclaration)method.Clone();
					variantParam = clonedMethod.Parameters.First(p => p.ToString().Contains(variantArgDef));
					variantParam.Type = argumentType.Clone();

					//add 'index' to all EntryPoint invocations inside the method (no mater how complex method body is):
					//and 'ref' keyword for the argument
					clonedMethod.Body.Descendants
						.OfType<InvocationExpression>()
						.Where(ie => ie.Target is IdentifierExpression && ((IdentifierExpression)ie.Target).Identifier == originalEntryPointName)
						.ToList()
						.ForEach(ie =>
							{
								if (!isPrimitive)
								{
									//non-primitive types should be marked with 'ref' keyword
									var argument = ie.Arguments.OfType<IdentifierExpression>().First(arg => arg.Identifier == variantParam.Name);
									ie.Arguments.Remove(argument);
									ie.Arguments.Add(new DirectionExpression(FieldDirection.Ref, argument));
								}

								var exp = (IdentifierExpression)ie.Target;
								exp.Identifier += index;
							});

					currentType.Members.Add(clonedMethod);
					InsertSummaryComments(clonedMethod, StringUtil.GetMethodComments(decl));

					index++;
				}
				pn("// Urho3D::Variant overloads end.");
			}
			//method does not have "Variant" arguments
			else
			{
				//C:
				pn(code
					.Replace(methodNameSuffix, string.Empty)
					.Replace(variantConverterMask, string.Empty));

				//C#:
				currentType.Members.Add(pinvoke);

				if (method == null)
					currentType.Members.Add(constructor);
				else
				{
					currentType.Members.Add(method);
					InsertSummaryComments(method, StringUtil.GetMethodComments(decl));
				}
			}

		}
Пример #36
0
 public override void VisitObjCInterfaceDecl(ObjCInterfaceDecl decl, VisitKind visitKind) => VisitItem(decl, visitKind);