ClassDeclarationSyntax WithEnums(ClassDeclarationSyntax cl, WaylandProtocol protocol, WaylandProtocolInterface iface) { if (iface.Enums == null) { return(cl); } foreach (var en in iface.Enums) { cl = cl.AddMembers(CreateEnum(en)); } return(cl); }
ClassDeclarationSyntax WithFactory(ClassDeclarationSyntax cl, WaylandProtocolInterface iface) { if (iface.Name == "wl_display" || iface.Name == "wl_registry") { return(cl); } var factoryInterfaceType = ParseTypeName("IBindFactory<" + cl.Identifier.Text + ">"); var fac = ClassDeclaration("ProxyFactory") .AddBaseListTypes(SimpleBaseType(factoryInterfaceType)) .AddMembers(MethodDeclaration( ParseTypeName("WlInterface*"), "GetInterface") .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) .WithBody(Block(ReturnStatement(GetWlInterfaceAddressFor(iface.Name)))) ) .AddMembers(MethodDeclaration( ParseTypeName(cl.Identifier.Text), "Create") .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) .WithParameterList(ParameterList(SeparatedList(new[] { Parameter(Identifier("handle")).WithType(ParseTypeName("IntPtr")), Parameter(Identifier("version")).WithType(ParseTypeName("int")), Parameter(Identifier("display")).WithType(ParseTypeName("WlDisplay")), }))) .WithBody(Block(ReturnStatement( ObjectCreationExpression(ParseTypeName(cl.Identifier.Text)) .WithArgumentList(ArgumentList(SeparatedList(new[] { Argument(IdentifierName("handle")), Argument(IdentifierName("version")), Argument(IdentifierName("display")) }))) ))) ); cl = cl .AddMembers(fac) .AddMembers(PropertyDeclaration(factoryInterfaceType, "BindFactory") .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword))) .WithAccessorList(AccessorList(SingletonList( AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(Semicolon())))) .WithInitializer(EqualsValueClause( ObjectCreationExpression(ParseTypeName("ProxyFactory")) .WithArgumentList(ArgumentList()) )).WithSemicolonToken(Semicolon()) ); return(cl); }
ClassDeclarationSyntax WithRequests(ClassDeclarationSyntax cl, WaylandProtocol protocol, WaylandProtocolInterface iface) { if (iface.Requests == null) { return(cl); } for (var idx = 0; idx < iface.Requests.Length; idx++) { var method = CreateMethod(protocol, iface, iface.Requests[idx], idx); if (method != null) { cl = cl.AddMembers(method); } } return(cl); }
ClassDeclarationSyntax WithEvents(ClassDeclarationSyntax cl, WaylandProtocol protocol, WaylandProtocolInterface iface) { var evs = iface.Events ?? Array.Empty <WaylandProtocolMessage>(); var eventInterface = InterfaceDeclaration("IEvents") .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))); var dispatcherBody = Block(); for (var eventIndex = 0; eventIndex < evs.Length; eventIndex++) { var ev = evs[eventIndex]; var eventName = "On" + Pascalize(ev.Name); var handlerParameters = new SeparatedSyntaxList <ParameterSyntax>(); var arguments = new SeparatedSyntaxList <ArgumentSyntax>(); handlerParameters = handlerParameters.Add(Parameter(Identifier("eventSender")) .WithType(ParseTypeName(GetWlInterfaceTypeName(iface.Name)))); arguments = arguments.Add(Argument(IdentifierName("this"))); var eargs = ev.Arguments ?? Array.Empty <WaylandProtocolArgument>(); for (var argIndex = 0; argIndex < eargs.Length; argIndex++) { var arg = eargs[argIndex]; TypeSyntax parameterType = null; ExpressionSyntax argument = ElementAccessExpression(IdentifierName("arguments"), BracketedArgumentList(SingletonSeparatedList(Argument(MakeLiteralExpression(argIndex))))); var argName = "@" + Pascalize(arg.Name, true); if (arg.Type == WaylandArgumentTypes.Int32 || arg.Type == WaylandArgumentTypes.Fixed || arg.Type == WaylandArgumentTypes.FileDescriptor || arg.Type == WaylandArgumentTypes.Uint32) { var nativeType = arg.Type == WaylandArgumentTypes.Uint32 ? "uint" : "int"; var managedType = TryGetEnumTypeReference(protocol.Name, iface.Name, ev.Name, arg.Name, arg.Enum) ?? nativeType; parameterType = ParseTypeName(managedType); argument = MemberAccess(argument, arg.Type == WaylandArgumentTypes.Uint32 ? "UInt32" : "Int32"); if (managedType != nativeType) { argument = CastExpression(ParseTypeName(managedType), argument); } } else if (arg.Type == WaylandArgumentTypes.NewId) { parameterType = ParseTypeName(Pascalize(arg.Interface)); argument = ObjectCreationExpression(parameterType) .WithArgumentList( ArgumentList(SeparatedList(new[] { Argument(MemberAccess(argument, "IntPtr")), Argument(IdentifierName("Version")), Argument(IdentifierName("Display")) } ))); } else if (arg.Type == WaylandArgumentTypes.String) { parameterType = ParseTypeName("System.String"); argument = InvocationExpression( MemberAccess(ParseTypeName("System.Runtime.InteropServices.Marshal"), "PtrToStringAnsi"), ArgumentList(SingletonSeparatedList(Argument(MemberAccess(argument, "IntPtr"))))); } else if (arg.Type == WaylandArgumentTypes.Object) { var parameterTypeString = arg.Interface == null ? "WlProxy" : GetWlInterfaceTypeName(arg.Interface); parameterType = ParseTypeName(parameterTypeString); argument = InvocationExpression(MemberAccess(ParseTypeName("WlProxy"), "FromNative<" + parameterTypeString + ">"), ArgumentList(SingletonSeparatedList(Argument(MemberAccess(argument, "IntPtr"))))); } else if (arg.Type == WaylandArgumentTypes.Array) { var arrayElementType = _hints.GetTypeNameForArray(protocol.Name, iface.Name, ev.Name, arg.Name); if (arg.AllowNull) { throw new NotSupportedException( "Wrapping nullable arrays is currently not supported"); } parameterType = ParseTypeName("ReadOnlySpan<" + arrayElementType + ">"); argument = InvocationExpression( MemberAccess(ParseTypeName("NWayland.Interop.WlArray"), "SpanFromWlArrayPtr<" + arrayElementType + ">"), ArgumentList(SingletonSeparatedList(Argument(MemberAccess(argument, "IntPtr"))))); } handlerParameters = handlerParameters.Add(Parameter(Identifier(argName)).WithType(parameterType)); arguments = arguments.Add(Argument(argument)); } eventInterface = eventInterface.AddMembers( MethodDeclaration(ParseTypeName("void"), eventName) .WithParameterList(ParameterList(handlerParameters)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); dispatcherBody = dispatcherBody.AddStatements( IfStatement(BinaryExpression( SyntaxKind.EqualsExpression, IdentifierName("opcode"), MakeLiteralExpression(eventIndex)), ExpressionStatement(ConditionalAccessExpression(IdentifierName("Events"), InvocationExpression(MemberBindingExpression(IdentifierName(eventName))) .WithArgumentList(ArgumentList(arguments)))) )); } cl = cl.AddMembers(eventInterface); cl = cl.AddMembers(PropertyDeclaration(ParseTypeName("IEvents"), "Events") .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))) .WithAccessorList(AccessorList(List(new[] { AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithSemicolonToken(Semicolon()), AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) .WithSemicolonToken(Semicolon()) }))) ); cl = cl.AddMembers(MethodDeclaration(ParseTypeName("void"), "DispatchEvent") .WithModifiers(TokenList(Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.OverrideKeyword))) .WithParameterList(ParameterList(SeparatedList(new[] { Parameter(Identifier("opcode")).WithType(ParseTypeName("uint")), Parameter(Identifier("arguments")).WithType(ParseTypeName("WlArgument*")) } ))) .WithBody(dispatcherBody) ); return(cl); }
MethodDeclarationSyntax CreateMethod(WaylandProtocol protocol, WaylandProtocolInterface iface, WaylandProtocolRequest request, int index) { var newIdArgument = request.Arguments?.FirstOrDefault(a => a.Type == WaylandArgumentTypes.NewId); if (newIdArgument != null && newIdArgument.Interface == null) { return(null); } var ctorType = newIdArgument?.Interface; var dotNetCtorType = ctorType == null ? "void" : GetWlInterfaceTypeName(ctorType); var method = MethodDeclaration( ParseTypeName(dotNetCtorType), Pascalize(request.Name)); var plist = new SeparatedSyntaxList <ParameterSyntax>(); var arglist = new SeparatedSyntaxList <ExpressionSyntax>(); var statements = new SeparatedSyntaxList <StatementSyntax>(); var callStatements = new SeparatedSyntaxList <StatementSyntax>(); var fixedDeclarations = new List <VariableDeclarationSyntax>(); if (request.Since > 0) { statements = statements.Add(IfStatement( BinaryExpression(SyntaxKind.LessThanExpression, IdentifierName("Version"), MakeLiteralExpression(request.Since)) , request.Type == "destructor" ? (StatementSyntax)ReturnStatement() : ThrowStatement(ObjectCreationExpression(ParseTypeName("System.InvalidOperationException")) .WithArgumentList( ArgumentList(SingletonSeparatedList(Argument(MakeLiteralExpression( $"Request {request.Name} is only supported since version {request.Since}")))))))); } if (request.Arguments != null) { foreach (var arg in request.Arguments ?? Array.Empty <WaylandProtocolArgument>()) { TypeSyntax parameterType = null; var nullCheck = false; var argName = "@" + Pascalize(arg.Name, true); if (arg.Type == WaylandArgumentTypes.Int32 || arg.Type == WaylandArgumentTypes.Fixed || arg.Type == WaylandArgumentTypes.FileDescriptor || arg.Type == WaylandArgumentTypes.Uint32) { var nativeType = arg.Type == WaylandArgumentTypes.Uint32 ? "uint" : "int"; var managedType = TryGetEnumTypeReference(protocol.Name, iface.Name, request.Name, arg.Name, arg.Enum) ?? nativeType; parameterType = ParseTypeName(managedType); if (nativeType != managedType) { arglist = arglist.Add(CastExpression(ParseTypeName(nativeType), IdentifierName(argName))); } else { arglist = arglist.Add(IdentifierName(argName)); } } else if (arg.Type == WaylandArgumentTypes.NewId) { arglist = arglist.Add(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName("WlArgument"), IdentifierName("NewId"))); } else if (arg.Type == WaylandArgumentTypes.String) { nullCheck = true; parameterType = ParseTypeName("System.String"); var tempName = "__marshalled__" + argName.TrimStart('@'); var bufferType = ParseTypeName("NWayland.Interop.NWaylandMarshalledString"); statements = statements.Add(LocalDeclarationStatement( new SyntaxTokenList(Token(SyntaxKind.UsingKeyword)), VariableDeclaration(ParseTypeName("var")) .WithVariables(SingletonSeparatedList( VariableDeclarator(tempName) .WithInitializer(EqualsValueClause(ObjectCreationExpression(bufferType) .WithArgumentList( ArgumentList( SingletonSeparatedList(Argument(IdentifierName(argName))))))) )))); arglist = arglist.Add(IdentifierName(tempName)); } else if (arg.Type == WaylandArgumentTypes.Object) { nullCheck = true; parameterType = ParseTypeName(GetWlInterfaceTypeName(arg.Interface)); arglist = arglist.Add(IdentifierName(argName)); } else if (arg.Type == WaylandArgumentTypes.Array) { if (arg.AllowNull) { throw new NotSupportedException( "Wrapping nullable arrays is currently not supported"); } var arrayElementType = _hints.GetTypeNameForArray(protocol.Name, iface.Name, request.Name, arg.Name); parameterType = ParseTypeName("ReadOnlySpan<" + arrayElementType + ">"); var pointerName = "__pointer__" + argName.TrimStart('@'); var tempName = "__marshalled__" + argName.TrimStart('@'); fixedDeclarations.Add(VariableDeclaration(ParseTypeName(arrayElementType + "*"), SingletonSeparatedList(VariableDeclarator(pointerName) .WithInitializer(EqualsValueClause(IdentifierName(argName)))))); callStatements = callStatements.Add(LocalDeclarationStatement(VariableDeclaration(ParseTypeName("var")) .WithVariables(SingletonSeparatedList(VariableDeclarator(tempName) .WithInitializer(EqualsValueClause( InvocationExpression(MemberAccess(ParseTypeName("NWayland.Interop.WlArray"), "FromPointer"), ArgumentList(SeparatedList(new[] { Argument(IdentifierName(pointerName)), Argument(MemberAccess(IdentifierName(argName), "Length")) } )))) ))))); arglist = arglist.Add(PrefixUnaryExpression(SyntaxKind.AddressOfExpression, IdentifierName(tempName))); } if (parameterType != null) { plist = plist.Add(Parameter(Identifier(argName)).WithType(parameterType)); } if (nullCheck) { statements = statements.Insert(0, IfStatement( BinaryExpression(SyntaxKind.EqualsExpression, IdentifierName(argName), MakeNullLiteralExpression()), ThrowStatement(ObjectCreationExpression(ParseTypeName("System.ArgumentNullException")) .WithArgumentList( ArgumentList( SingletonSeparatedList( Argument(MakeLiteralExpression(argName.TrimStart('@'))))))))); } } } callStatements = callStatements.Add(LocalDeclarationStatement(VariableDeclaration(ParseTypeName("WlArgument*")) .WithVariables(SingletonSeparatedList(VariableDeclarator("__args") .WithInitializer(EqualsValueClause(StackAllocArrayCreationExpression( ArrayType(ParseTypeName("WlArgument[]")), InitializerExpression(SyntaxKind.ArrayInitializerExpression, arglist)))))))); var marshalArgs = SeparatedList(new[] { Argument(MemberAccess(IdentifierName("this"), "Handle")), Argument(MakeLiteralExpression(index)), Argument(IdentifierName("__args")) }); if (ctorType != null) { marshalArgs = marshalArgs.Add(Argument(GetWlInterfaceRefFor(ctorType))); } var callExpr = InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName("LibWayland"), IdentifierName(ctorType == null ? "wl_proxy_marshal_array" : "wl_proxy_marshal_array_constructor")), ArgumentList(marshalArgs)); if (ctorType == null) { callStatements = callStatements.Add(ExpressionStatement(callExpr)); } else { callStatements = callStatements.Add(LocalDeclarationStatement(VariableDeclaration(ParseTypeName("var")) .WithVariables(SingletonSeparatedList( VariableDeclarator("__ret").WithInitializer(EqualsValueClause(callExpr)))))); callStatements = callStatements.Add(ReturnStatement(ConditionalExpression(BinaryExpression( SyntaxKind.EqualsExpression, IdentifierName("__ret"), MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName("IntPtr"), IdentifierName("Zero"))), MakeNullLiteralExpression(), ObjectCreationExpression(ParseTypeName(dotNetCtorType)).WithArgumentList( ArgumentList(SeparatedList(new[] { Argument(IdentifierName("__ret")), Argument(IdentifierName("Version")), Argument(IdentifierName("Display")) })))))); } if (fixedDeclarations.Count == 0) { statements = statements.AddRange(callStatements); } else { var callBlock = (StatementSyntax)Block(callStatements); fixedDeclarations.Reverse(); foreach (var fd in fixedDeclarations) { callBlock = FixedStatement(fd, callBlock); } statements = statements.Add(callBlock); } method = WithSummary(method.WithParameterList(ParameterList(plist)) .WithBody(Block(statements)) .WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword))), request.Description); if (request.Type == "destructor") { method = method .WithIdentifier(Identifier("CallWaylandDestructor")) .WithModifiers(TokenList( Token(SyntaxKind.ProtectedKeyword), Token(SyntaxKind.SealedKeyword), Token(SyntaxKind.OverrideKeyword) )); } return(method); }