コード例 #1
0
ファイル: CxxBinder.cs プロジェクト: corefan/urho
		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));
				}
			}

		}
コード例 #2
0
ファイル: CsToTs.cs プロジェクト: RReverser/Netjs
			public override void VisitMethodDeclaration (MethodDeclaration methodDeclaration)
			{
				base.VisitMethodDeclaration (methodDeclaration);

				//
				// Try to force the method into a form we can handle
				//
				var m = methodDeclaration;

				var gotos = m.Body.Descendants.OfType<GotoStatement> ().ToList ();
				if (gotos.Count == 0)
					return;

				if (HasBadLabels (m)) {
					foreach (var ts in transforms) {
						m = (MethodDeclaration)methodDeclaration.Clone ();

						m.AcceptVisitor (new LabelLoops ());
						m.AcceptVisitor (new SwitchSectionBlocksToStatements ());
						foreach (var t in ts) {
							m.AcceptVisitor (t);
							m.AcceptVisitor (new RemoveRedundantGotos ());
							m.AcceptVisitor (new RemoveUnreachableStatements ());
						}
						m.AcceptVisitor (new RemoveLabelsWithoutGotos ());

						if (!HasBadLabels (m)) {
							break;
						}
					}

					if (HasBadLabels (m)) {
						App.Warning ("! GOTO labels at different levels in {0}.{1}(). This is not supported.", 
							methodDeclaration.GetParent<TypeDeclaration> ().Name, 
							methodDeclaration.Name);
						return;
					} else {
						methodDeclaration.ReplaceWith (m);
					}
				}

				//
				// Handle it
				//
				m.AcceptVisitor (new CreateGotoLoop ());
				m.AcceptVisitor (new RewriteGotos ());
			}