private string[] GetMethodArgs(Smoke *smoke, Smoke.Method *method) { if (method->numArgs == 0) { return(new string[0]); } string className = ByteArrayManager.GetString(smoke->classes[method->classId].className); className = className.Substring(className.LastIndexOf(":") + 1); StringBuilder keyBuilder = new StringBuilder(className + "," + ByteArrayManager.GetString(smoke->methodNames[method->name])); for (short *typeIndex = smoke->argumentList + method->args; *typeIndex > 0; typeIndex++) { keyBuilder.Append(','); keyBuilder.Append(*typeIndex); } string key = keyBuilder.ToString(); if (data.ArgumentNames.ContainsKey(key)) { return(data.ArgumentNames[key]); } string argNamesFile = data.GetArgNamesFile(smoke); if (File.Exists(argNamesFile)) { foreach (string[] strings in File.ReadAllLines(argNamesFile).Select(line => line.Split(';'))) { data.ArgumentNames[strings[0]] = strings[1].Split(','); } return(data.ArgumentNames[key]); } return(null); }
public void SupportingMethodsHook(Smoke *smoke, Smoke.Method *method, CodeMemberMethod cmm, CodeTypeDeclaration type) { if (type.Name == "QObject" && cmm is CodeConstructor) { cmm.Statements.Add(new CodeSnippetStatement(QObjectDummyCtorCode)); } }
public int GetHashCode(Smoke.ModuleIndex mi) { Smoke.Method *method = mi.smoke->methods + mi.index; bool isConst = ((method->flags & (ushort)Smoke.MethodFlags.mf_const) > 0); bool isStatic = ((method->flags & (ushort)Smoke.MethodFlags.mf_static) > 0); int hash = method->name << 18 + method->args << 2 + (isConst? 1 : 0) << 1 + (isStatic? 1 : 0); return((hash ^ (long)mi.smoke).GetHashCode()); }
public bool Equals(Smoke.ModuleIndex first, Smoke.ModuleIndex second) { Smoke.Method *firstMeth = first.smoke->methods + first.index; Smoke.Method *secondMeth = second.smoke->methods + second.index; bool firstConst = ((firstMeth->flags & (ushort)Smoke.MethodFlags.mf_const) > 0); bool secondConst = ((secondMeth->flags & (ushort)Smoke.MethodFlags.mf_const) > 0); bool firstStatic = ((firstMeth->flags & (ushort)Smoke.MethodFlags.mf_static) > 0); bool secondStatic = ((secondMeth->flags & (ushort)Smoke.MethodFlags.mf_static) > 0); bool firstAbstract = ((firstMeth->flags & (ushort)Smoke.MethodFlags.mf_purevirtual) > 0); bool secondAbstract = ((secondMeth->flags & (ushort)Smoke.MethodFlags.mf_purevirtual) > 0); if (firstConst != secondConst || firstStatic != secondStatic || (m_abstractIsDifference && (firstAbstract != secondAbstract))) { return(false); } if (first.smoke == second.smoke) { // when the methods are in the same module, we can be rather quick if (firstMeth->name == secondMeth->name && firstMeth->args == secondMeth->args && firstMeth->ret == secondMeth->ret) { return(true); } return(false); } else { if (ByteArrayManager.strcmp(first.smoke->methodNames[firstMeth->name], second.smoke->methodNames[secondMeth->name]) == 0 && ByteArrayManager.strcmp(first.smoke->types[firstMeth->ret].name, second.smoke->types[secondMeth->ret].name) == 0 && firstMeth->numArgs == secondMeth->numArgs) { // name and number of arguments match, now compare the arguments individually short *firstMethodArgPtr = first.smoke->argumentList + firstMeth->args; short *secondMethodArgPtr = second.smoke->argumentList + secondMeth->args; while ((*firstMethodArgPtr) > 0) { if (ByteArrayManager.strcmp(first.smoke->types[*firstMethodArgPtr].name, second.smoke->types[*secondMethodArgPtr].name) != 0) { return(false); } firstMethodArgPtr++; secondMethodArgPtr++; } return(true); } return(false); } }
// this only has to work within the module boundaries public static bool EqualExceptConstness(Smoke.Method *firstMeth, Smoke.Method *secondMeth) { bool firstStatic = ((firstMeth->flags & (ushort)Smoke.MethodFlags.mf_static) > 0); bool secondStatic = ((secondMeth->flags & (ushort)Smoke.MethodFlags.mf_static) > 0); if (firstMeth->name == secondMeth->name && firstMeth->args == secondMeth->args && firstStatic == secondStatic) { return(true); } return(false); }
public void ScheduleAttributeAccessor(Smoke *smoke, Smoke.Method *meth) { string name = ByteArrayManager.GetString(smoke->methodNames[meth->name]); bool isSetMethod = false; if (name.StartsWith("set")) { name = name.Remove(0, 3); isSetMethod = true; } else { // capitalize the first letter StringBuilder builder = new StringBuilder(name); builder[0] = char.ToUpper(builder[0]); name = builder.ToString(); } // If the new name clashes with a name of a type declaration, keep the lower-case name. var typesWithSameName = from member in data.GetAccessibleMembers(smoke->classes + meth->classId) where (member.Type == MemberTypes.NestedType || member.Type == MemberTypes.Method) && member.Name == name select member; if (typesWithSameName.Any()) { string className = ByteArrayManager.GetString(smoke->classes[meth->classId].className); Debug.Print(" |--Conflicting names: property/type: {0} in class {1} - keeping original property name", name, className); name = char.ToLower(name[0]) + name.Substring(1); } Attribute attr; if (!attributes.TryGetValue(name, out attr)) { attr = new Attribute(); attr.Smoke = smoke; attributes.Add(name, attr); } if (isSetMethod) { attr.SetMethod = meth; } else { attr.GetMethod = meth; } }
private void GenerateInheritedMethods(Smoke.Class *klass, MethodsGenerator methgen, AttributeGenerator attrgen, List <Smoke.ModuleIndex> alreadyImplemented) { // Contains inherited methods that have to be implemented by the current class. // We use our custom comparer, so we don't end up with the same method multiple times. IDictionary <Smoke.ModuleIndex, string> implementMethods = new Dictionary <Smoke.ModuleIndex, string>(SmokeMethodEqualityComparer.DefaultEqualityComparer); bool firstParent = true; for (short *parent = data.Smoke->inheritanceList + klass->parents; *parent > 0; parent++) { if (firstParent) { // we're only interested in parents implemented as interfaces firstParent = false; continue; } // collect all methods (+ inherited ones) and add them to the implementMethods Dictionary data.Smoke->FindAllMethods(*parent, implementMethods, true); } foreach (KeyValuePair <Smoke.ModuleIndex, string> pair in implementMethods) { Smoke.Method *meth = pair.Key.smoke->methods + pair.Key.index; Smoke.Class * ifaceKlass = pair.Key.smoke->classes + meth->classId; if ((meth->flags & (ushort)Smoke.MethodFlags.mf_enum) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_ctor) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_copyctor) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_dtor) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_static) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_internal) > 0) { // no need to check for properties here - QObjects don't support multiple inheritance anyway continue; } if (alreadyImplemented.Contains(pair.Key, SmokeMethodEqualityComparer.DefaultEqualityComparer)) { continue; } if ((meth->flags & (ushort)Smoke.MethodFlags.mf_attribute) > 0) { attrgen.ScheduleAttributeAccessor(pair.Key.smoke, meth); continue; } CodeTypeReference type = translator.CppToCSharp(ByteArrayManager.GetString(ifaceKlass->className)); methgen.GenerateMethod(pair.Key.smoke, meth, pair.Value, type); } }
private void PostEnumMemberHook(Smoke *smoke, Smoke.Method *smokeMethod, CodeMemberField cmm, CodeTypeDeclaration type) { CodeTypeDeclaration parentType = this.memberDocumentation.Keys.FirstOrDefault(t => t.Name == (string)type.UserData["parent"]); if (parentType != null) { IList <string> docs = this.memberDocumentation[parentType]; string typeName = Regex.Escape(parentType.Name) + "::" + Regex.Escape(type.Name); if (type.Comments.Count == 0) { for (int i = 0; i < docs.Count; i++) { const string enumDoc = @"enum {0}(\s*flags {1}::\w+\s+)?(?<docsStart>.*?)(\n){{3}}"; Match matchEnum = Regex.Match(docs[i], string.Format(enumDoc, typeName, parentType.Name), RegexOptions.Singleline); if (matchEnum.Success) { string doc = (matchEnum.Groups["docsStart"].Value + matchEnum.Groups["docsEnd1"].Value).Trim(); doc = Regex.Replace(doc, @"(The \S+ type is a typedef for QFlags<\S+>\. It stores an OR combination of \S+ values\.)", string.Empty); doc = Regex.Replace(doc, @"ConstantValue(Description)?.*?(((\n){2})|$)", string.Empty, RegexOptions.Singleline).Trim(); if (!string.IsNullOrEmpty(doc)) { Util.FormatComment(doc, type, i > 0); break; } } } } string memberName = Regex.Escape(parentType.Name) + "::" + Regex.Escape(ByteArrayManager.GetString(smoke->methodNames[smokeMethod->name])); const string memberDoc = @"enum {0}.*{1}\t[^\t\n]+\t(?<docs>.*?)(&\w+;)?(\n)"; for (int i = 0; i < docs.Count; i++) { Match match = Regex.Match(docs[i], string.Format(memberDoc, typeName, memberName), RegexOptions.Singleline); if (match.Success) { string doc = match.Groups["docs"].Value.Trim(); if (!string.IsNullOrEmpty(doc)) { Util.FormatComment(char.ToUpper(doc[0]) + doc.Substring(1), cmm, i > 0); break; } } } } }
private void CommentMember(Smoke *smoke, Smoke.Method *smokeMethod, CodeTypeMember cmm, CodeTypeDeclaration type) { if (this.memberDocumentation.ContainsKey(type)) { IList <string> docs = this.memberDocumentation[type]; string typeName = Regex.Escape(type.Name); string signature = smoke->GetMethodSignature(smokeMethod); StringBuilder signatureRegex = new StringBuilder(); Match matchSignature = Regex.Match(signature, @"(?<name>[^\(]+)\((?<args>.*)\)"); string methodName = Regex.Escape(matchSignature.Groups["name"].Value); signatureRegex.Append(methodName); signatureRegex.Append(@"\s*\(\s*"); string[] argTypes = matchSignature.Groups["args"].Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); const string separator = @",\s*"; foreach (string argType in argTypes) { StringBuilder typeBuilder = new StringBuilder(Regex.Escape(argType)); typeBuilder.Replace(@"\*", @"\s*\*").Replace(@"&", @"\s*&").Replace(type.Name + "::", string.Empty); signatureRegex.Append(Translator.MatchFunctionPointer(typeBuilder.ToString()).Success ? @"[^,]+" : (typeBuilder + @"\s+\w+(\s*=\s*\w+)?")); signatureRegex.Append(separator); } if (argTypes.Length > 0) { signatureRegex.Remove(signatureRegex.Length - separator.Length, separator.Length); } signatureRegex.Append(@"\s*\)\s*"); string memberDoc = @"{0}( |(::)){1}( const)?( \[(\w+\s*)+\])?\n\W*(?<docs>.*?)(\n\s*){{1,2}}((&?\S* --)|((\n\s*){{2}}))"; for (int i = 0; i < docs.Count; i++) { Match match = Regex.Match(docs[i], string.Format(memberDoc, typeName, signatureRegex), RegexOptions.Singleline); if (match.Success) { Util.FormatComment(match.Groups["docs"].Value, cmm, i > 0); break; } memberDoc = @"{0}( |(::)){1}\s*\([^\n]*\)( const)?( \[(\w+\s*)+\])?\n\W*(?<docs>.*?)(\n\s*){{1,2}}((&?\S* --)|((\n\s*){{2}}))"; match = Regex.Match(docs[i], string.Format(memberDoc, typeName, methodName), RegexOptions.Singleline); if (match.Success) { Util.FormatComment(match.Groups["docs"].Value, cmm, i > 0); break; } } } }
public static unsafe Stack <KeyValuePair <Smoke.ModuleIndex, string> > GetAbstractMethods(Smoke *smoke, short classId) { Dictionary <Smoke.ModuleIndex, string> methods = new Dictionary <Smoke.ModuleIndex, string>(SmokeMethodEqualityComparer.AbstractRespectingComparer); SmokeMethodEqualityComparer defaultComparer = SmokeMethodEqualityComparer.DefaultEqualityComparer; smoke->FindAllMethods(classId, methods, true); var abstractMethods = new Stack <KeyValuePair <Smoke.ModuleIndex, string> >(); foreach (KeyValuePair <Smoke.ModuleIndex, string> pair in methods) { Smoke.Method *meth = pair.Key.smoke->methods + pair.Key.index; if ((meth->flags & (ushort)Smoke.MethodFlags.mf_purevirtual) == 0) { // only compare pure-virtuals continue; } abstractMethods.Push(pair); foreach (KeyValuePair <Smoke.ModuleIndex, string> other in methods) { // Break if we encounter our original Index. Anything after this one will be further up in the // hierarchy and thus can't override anything. if (pair.Key == other.Key) { break; } Smoke.Method *otherMeth = other.Key.smoke->methods + other.Key.index; if (defaultComparer.Equals(pair.Key, other.Key)) { if ((otherMeth->flags & (ushort)Smoke.MethodFlags.mf_purevirtual) == 0) { // overriden with implementation abstractMethods.Pop(); } break; } } } return(abstractMethods); }
private void FillEnums() { for (short i = 1; i < this.data.Smoke->numMethodMaps; i++) { Smoke.MethodMap *map = this.data.Smoke->methodMaps + i; if (map->method > 0) { Smoke.Method *meth = this.data.Smoke->methods + map->method; if ((meth->flags & (ushort)Smoke.MethodFlags.mf_enum) > 0) { this.eg.DefineMember(meth); } } else if (map->method < 0) { for (short *overload = this.data.Smoke->ambiguousMethodList + (-map->method); *overload > 0; overload++) { Smoke.Method *meth = this.data.Smoke->methods + *overload; if ((meth->flags & (ushort)Smoke.MethodFlags.mf_enum) > 0) { this.eg.DefineMember(meth); continue; } // if the methods differ only by constness, we will generate special code bool nextDiffersByConst = false; if (*(overload + 1) > 0) { if (SmokeMethodEqualityComparer.EqualExceptConstness(meth, this.data.Smoke->methods + *(overload + 1))) { nextDiffersByConst = true; } } if (nextDiffersByConst) { overload++; } } } } }
private static int CompareSmokeMethods(IntPtr i1, IntPtr i2) { Smoke.Method *m1 = (Smoke.Method *)i1; Smoke.Method *m2 = (Smoke.Method *)i2; if (m1->name > m2->name) { return(1); } if (m1->name < m2->name) { return(-1); } if (m1->numArgs > m2->numArgs) { return(1); } if (m1->numArgs < m2->numArgs) { return(-1); } return(0); }
/* * Generates an Enum member, creating the Enum if necessary. */ public void DefineMember(Smoke.Method *meth) { if ((meth->flags & (uint)Smoke.MethodFlags.mf_enum) == 0) { return; } string typeName = ByteArrayManager.GetString(data.Smoke->types[meth->ret].name); if (typeName == "long") // unnamed enum { return; } CodeTypeDeclaration enumType; if (!data.EnumTypeMap.TryGetValue(typeName, out enumType)) { enumType = DefineEnum(typeName); } CodeMemberField member = new CodeMemberField(); member.Name = ByteArrayManager.GetString(data.Smoke->methodNames[meth->name]); long value = GetEnumValue(data.Smoke, meth); if (value > int.MaxValue && enumType.BaseTypes.Count == 0) { // make the enum derive from 'long' if necessary enumType.BaseTypes.Add(new CodeTypeReference(typeof(long))); } member.InitExpression = new CodePrimitiveExpression(value); if (PostEnumMemberHook != null) { PostEnumMemberHook(data.Smoke, meth, member, enumType); } enumType.Members.Add(member); }
public CodeMemberMethod GenerateBasicMethodDefinition(Smoke *smoke, Smoke.Method *method, string cppSignature, CodeTypeReference iface) { // do we actually want that method? string className = ByteArrayManager.GetString(smokeClass->className); string completeSignature = className + "::" + cppSignature; if (translator.ExcludedMethods.Any(regex => regex.IsMatch(completeSignature))) { return(null); } CodeParameterDeclarationExpressionCollection args = new CodeParameterDeclarationExpressionCollection(); int count = 1; bool isRef; // make instance operators static and bring the arguments in the correct order string methName = ByteArrayManager.GetString(smoke->methodNames[method->name]); bool isOperator = false; string explicitConversionType = null; if (methName.StartsWith("operator")) { string op = methName.Substring(8); if (unsupportedOperators.Contains(op)) { // not supported Debug.Print(" |--Won't wrap method {0}::{1}", className, cppSignature); return(null); } if (op == "<<") { methName = "Write"; } else if (op == ">>") { methName = "Read"; } // binary/unary operator if (binaryOperators.Contains(op) || unaryOperators.Contains(op)) { // instance operator if (smoke->classes[method->classId].size > 0) { if (op == "*" && method->numArgs == 0 || (op == "++" || op == "--") && method->numArgs == 1) { // dereference operator and postfix in-/decrement operator are not supported Debug.Print(" |--Won't wrap method {0}::{1}", className, cppSignature); return(null); } try { CodeParameterDeclarationExpression exp = new CodeParameterDeclarationExpression(translator.CppToCSharp(className, out isRef), "arg" + count++); args.Add(exp); } catch (NotSupportedException) { Debug.Print(" |--Won't wrap method {0}::{1}", className, cppSignature); return(null); } } else { // global operator if (op == "*" && method->numArgs == 0 || (op == "++" || op == "--") && method->numArgs == 2) { // dereference operator and postfix in-/decrement operator are not supported Debug.Print(" |--Won't wrap method {0}::{1}", className, cppSignature); return(null); } } isOperator = true; } else if (op[0] == ' ') { // conversion operator explicitConversionType = op.Substring(1); if (explicitConversionType.Contains("QVariant")) { return(null); } try { explicitConversionType = translator.CppToCSharp(explicitConversionType, out isRef).GetStringRepresentation(); if (smoke->classes[method->classId].size > 0) { CodeParameterDeclarationExpression exp = new CodeParameterDeclarationExpression(translator.CppToCSharp(className, out isRef), "arg" + count++); args.Add(exp); } } catch (NotSupportedException) { Debug.Print(" |--Won't wrap method {0}::{1}", className, cppSignature); return(null); } isOperator = true; } } // translate arguments string[] methodArgs = isOperator ? null : GetMethodArgs(smoke, method); for (short *typeIndex = smoke->argumentList + method->args; *typeIndex > 0; typeIndex++) { try { args.Add(this.GetArgument(smoke, typeIndex, methodArgs, args, ref count)); } catch (NotSupportedException) { Debug.Print(" |--Won't wrap method {0}::{1}", className, cppSignature); return(null); } } this.RemovePreviousOverload(args, char.ToUpper(methName[0]) + methName.Substring(1)); // translate return type CodeTypeReference returnType; try { returnType = translator.CppToCSharp(smoke->types + method->ret, out isRef); } catch (NotSupportedException) { Debug.Print(" |--Won't wrap method {0}::{1}", className, cppSignature); return(null); } CodeMemberMethod cmm; if ((method->flags & (uint)Smoke.MethodFlags.mf_ctor) > 0) { cmm = new CodeConstructor(); cmm.Attributes = 0; // initialize to 0 so we can do |= ((CodeConstructor)cmm).ChainedConstructorArgs.Add(new CodeSnippetExpression("(System.Type) null")); } else { cmm = new CodeMemberMethod(); cmm.Attributes = 0; // initialize to 0 so we can do |= string csName = methName; if (!isOperator && methName != "finalize" && !qMethodExp.IsMatch(methName)) { // capitalize the first letter StringBuilder builder = new StringBuilder(csName); builder[0] = char.ToUpper(builder[0]); string tmp = builder.ToString(); // If the new name clashes with a name of a type declaration, keep the lower-case name. var typesWithSameName = from member in data.GetAccessibleMembers(smokeClass) where member.Type == MemberTypes.NestedType && member.Name == tmp select member; var propertiesWithSameName = (from member in data.GetAccessibleMembers(smokeClass) where member.Type == MemberTypes.Property && member.Name == tmp select member).ToList(); if (iface != null && propertiesWithSameName.Count() == 1 && (method->flags & (uint)Smoke.MethodFlags.mf_protected) == 0) { cmm.PrivateImplementationType = iface; csName = tmp; } else { if (propertiesWithSameName.Any()) { if ((method->flags & (uint)Smoke.MethodFlags.mf_virtual) == 0) { Debug.Print( " |--Conflicting names: method/(type or property): {0} in class {1} - keeping original method name", tmp, className); } else { csName = tmp; } } else if (typesWithSameName.Any()) { Debug.Print(" |--Conflicting names: method/classname: {0} in class {1} - keeping original method name", tmp, className); } else { csName = tmp; } } } if (explicitConversionType != null) { cmm.Name = "explicit operator " + explicitConversionType; cmm.ReturnType = new CodeTypeReference(" "); } else { cmm.Name = csName; cmm.ReturnType = returnType; } } // for destructors we already have this stuff set if ((method->flags & (uint)Smoke.MethodFlags.mf_dtor) == 0) { // set access if ((method->flags & (uint)Smoke.MethodFlags.mf_protected) > 0) { cmm.Attributes |= MemberAttributes.Family; } else { cmm.Attributes |= MemberAttributes.Public; } if (isOperator) { cmm.Attributes |= MemberAttributes.Final | MemberAttributes.Static; } else if (cmm.Name == "ToString" && args.Count == 0 && cmm.ReturnType.BaseType == "System.String") { cmm.Attributes = MemberAttributes.Public | MemberAttributes.Override; } else { if ((method->flags & (uint)Smoke.MethodFlags.mf_static) > 0) { cmm.Attributes |= MemberAttributes.Static; } else { // virtual/final MemberAttributes access; bool foundInInterface; bool isOverride = MethodOverrides(smoke, method, out access, out foundInInterface); // methods that have to be implemented from interfaces can't override anything if (iface == null && (isOverride = MethodOverrides(smoke, method, out access, out foundInInterface))) { cmm.Attributes = access | MemberAttributes.Override; } else if (foundInInterface) { cmm.Attributes = access; } if ((method->flags & (uint)Smoke.MethodFlags.mf_purevirtual) > 0) { if (!m_internalImplementation) { cmm.Attributes = (cmm.Attributes & ~MemberAttributes.ScopeMask) | MemberAttributes.Abstract; // The code generator doesn't like MemberAttributes.Abstract | MemberAttributes.Override being set. if (isOverride && !type.IsInterface) { cmm.ReturnType.BaseType = "override " + cmm.ReturnType.BaseType == "System.Void" ? "void" : cmm.ReturnType.BaseType; } } else { cmm.Attributes |= MemberAttributes.Override; } } if ((method->flags & (uint)Smoke.MethodFlags.mf_virtual) == 0 && (method->flags & (uint)Smoke.MethodFlags.mf_purevirtual) == 0 && !isOverride) { cmm.Attributes |= MemberAttributes.Final | MemberAttributes.New; } } } } else { // hack, so we don't have to use CodeSnippetTypeMember to generator the destructor cmm.ReturnType = new CodeTypeReference(" "); } // add the parameters foreach (CodeParameterDeclarationExpression exp in args) { cmm.Parameters.Add(exp); } this.DistributeMethod(cmm); return(cmm); }
private static extern long GetEnumValue(Smoke *smoke, Smoke.Method *meth);
public void Run() { for (short classId = 1; classId <= data.Smoke->numClasses; classId++) { Smoke.Class *klass = data.Smoke->classes + classId; if (klass->external) { continue; } List <Property> props = new List <Property>(); if (!GetProperties(data.Smoke, classId, (name, originalType, typeName, writable, isEnum) => props.Add(new Property(name, originalType, typeName, writable, isEnum)))) { continue; } CodeTypeDeclaration type = data.SmokeTypeMap[(IntPtr)klass]; string className = ByteArrayManager.GetString(klass->className); foreach (Property prop in props) { CodeMemberProperty cmp = new CodeMemberProperty(); try { bool isRef; short id = data.Smoke->IDType(prop.Type); if (id > 0) { cmp.Type = translator.CppToCSharp(data.Smoke->types + id, out isRef); } else { if (!prop.Type.Contains("::")) { id = data.Smoke->IDType(className + "::" + prop.Type); if (id > 0) { cmp.Type = translator.CppToCSharp(data.Smoke->types + id, out isRef); } else { cmp.Type = translator.CppToCSharp(prop.Type, out isRef); } } cmp.Type = translator.CppToCSharp(prop.Type, out isRef); } } catch (NotSupportedException) { Debug.Print(" |--Won't wrap Property {0}::{1}", className, prop.Name); continue; } if (documentation.ContainsKey(type)) { IList <string> docs = documentation[type]; for (int i = 0; i < docs.Count; i++) { Match match = Regex.Match(docs[i], prop.Name + " : (const )?" + prop.OriginalType + @"\n(?<docs>This.*?)\nAccess functions:", RegexOptions.Singleline); if (match.Success) { Util.FormatComment(match.Groups["docs"].Value, cmp, i > 0); break; } } } cmp.Name = prop.Name; // capitalize the first letter StringBuilder builder = new StringBuilder(cmp.Name); builder[0] = char.ToUpper(builder[0]); string capitalized = builder.ToString(); // If the new name clashes with a name of a type declaration, keep the lower-case name (or even make the name lower-case). var typesWithSameName = from member in data.GetAccessibleMembers(data.Smoke->classes + classId) where (member.Type == MemberTypes.NestedType || member.Type == MemberTypes.Method) && member.Name == capitalized select member; if (typesWithSameName.Any()) { Debug.Print( " |--Conflicting names: property/(type or method): {0} in class {1} - keeping original property name", capitalized, className); if (capitalized == cmp.Name) { builder[0] = char.ToLower(builder[0]); cmp.Name = builder.ToString(); // lower case the property if necessary } } else { cmp.Name = capitalized; } cmp.HasGet = true; cmp.HasSet = prop.IsWritable; cmp.Attributes = MemberAttributes.Public | MemberAttributes.New | MemberAttributes.Final; cmp.CustomAttributes.Add(new CodeAttributeDeclaration("Q_PROPERTY", new CodeAttributeArgument( new CodePrimitiveExpression(prop.OriginalType)), new CodeAttributeArgument( new CodePrimitiveExpression(prop.Name)))); // ===== get-method ===== short getterMapId = FindQPropertyGetAccessorMethodMapId(classId, prop, capitalized); if (getterMapId == 0) { Debug.Print(" |--Missing 'get' method for property {0}::{1} - using QObject.Property()", className, prop.Name); cmp.GetStatements.Add(new CodeMethodReturnStatement(new CodeCastExpression(cmp.Type, new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), "Property", new CodePrimitiveExpression(prop.Name))))); } else { Smoke.MethodMap *map = data.Smoke->methodMaps + getterMapId; short getterId = map->method; if (getterId < 0) { // simply choose the first (i.e. non-const) version if there are alternatives getterId = data.Smoke->ambiguousMethodList[-getterId]; } Smoke.Method *getter = data.Smoke->methods + getterId; if (getter->classId != classId) { // The actual get method is defined in a parent class - don't create a property for it. continue; } if ((getter->flags & (uint)Smoke.MethodFlags.mf_virtual) == 0 && (getter->flags & (uint)Smoke.MethodFlags.mf_purevirtual) == 0) { cmp.GetStatements.Add(new CodeMethodReturnStatement(new CodeCastExpression(cmp.Type, new CodeMethodInvokeExpression( SmokeSupport.interceptor_Invoke, new CodePrimitiveExpression( ByteArrayManager.GetString( data.Smoke->methodNames[getter->name ])), new CodePrimitiveExpression( data.Smoke->GetMethodSignature(getter)), new CodeTypeOfExpression(cmp.Type), new CodePrimitiveExpression(false))))); } else { cmp.HasGet = false; if (!cmp.HasSet) { // the get accessor is virtual and there's no set accessor => continue continue; } } } // ===== set-method ===== if (!prop.IsWritable) { // not writable? => continue type.Members.Add(cmp); continue; } char mungedSuffix; short setterMethId = FindQPropertySetAccessorMethodId(classId, prop, capitalized, out mungedSuffix); if (setterMethId == 0) { Debug.Print(" |--Missing 'set' method for property {0}::{1} - using QObject.SetProperty()", className, prop.Name); cmp.SetStatements.Add(new CodeExpressionStatement( new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "SetProperty", new CodePrimitiveExpression(prop.Name), new CodeArgumentReferenceExpression("value")))); } else { Smoke.Method *setter = data.Smoke->methods + setterMethId; if (setter->classId != classId) { // defined in parent class, continue type.Members.Add(cmp); continue; } string setterName = ByteArrayManager.GetString(data.Smoke->methodNames[setter->name]); if (!cmp.HasGet) { // so the 'get' method is virtual - generating a property for only the 'set' method is a bad idea MethodsGenerator mg = new MethodsGenerator(data, translator, type, klass); mg.GenerateMethod(setterMethId, setterName + mungedSuffix); continue; } cmp.SetStatements.Add(new CodeExpressionStatement( new CodeMethodInvokeExpression(SmokeSupport.interceptor_Invoke, new CodePrimitiveExpression(setterName + mungedSuffix), new CodePrimitiveExpression( this.data.Smoke->GetMethodSignature(setterMethId)), new CodeTypeOfExpression(typeof(void)), new CodePrimitiveExpression(false), new CodeTypeOfExpression(cmp.Type), new CodeArgumentReferenceExpression("value")))); } type.Members.Add(cmp); } } }
private void PostMethodBodyHooks(Smoke *smoke, Smoke.Method *smokeMethod, CodeMemberMethod cmm, CodeTypeDeclaration type) { this.CommentMember(smoke, smokeMethod, cmm, type); GenerateEvent(cmm, cmm.Name, type, true); }
public void Run() { HashSet <short> interfaceClasses = GetClassList(); // Make the interfaces known first, otherwise Translator won't work correctly. foreach (short idx in interfaceClasses) { Smoke.Class *klass = data.Smoke->classes + idx; string className = ByteArrayManager.GetString(klass->className); int colon = className.LastIndexOf("::", StringComparison.Ordinal); string prefix = (colon != -1) ? className.Substring(0, colon) : string.Empty; string name = (colon != -1) ? className.Substring(colon + 2) : className; CodeTypeDeclaration ifaceDecl = new CodeTypeDeclaration('I' + name); ifaceDecl.IsInterface = true; CodeAttributeDeclaration attr = new CodeAttributeDeclaration("SmokeClass", new CodeAttributeArgument( new CodePrimitiveExpression(className))); ifaceDecl.CustomAttributes.Add(attr); data.GetTypeCollection(prefix).Add(ifaceDecl); data.InterfaceTypeMap[className] = ifaceDecl; } // Now generate the methods. foreach (short idx in interfaceClasses) { Smoke.Class * klass = data.Smoke->classes + idx; string className = ByteArrayManager.GetString(klass->className); CodeTypeDeclaration ifaceDecl = data.InterfaceTypeMap[className]; short *parent = data.Smoke->inheritanceList + klass->parents; while (*parent > 0) { ifaceDecl.BaseTypes.Add(translator.CppToCSharp(data.Smoke->classes + *parent)); parent++; } MethodsGenerator mg = new MethodsGenerator(data, translator, ifaceDecl, klass); AttributeGenerator ag = new AttributeGenerator(data, translator, ifaceDecl); List <IntPtr> methods = new List <IntPtr>(); ///TODO: replace this algorithm, it's highly inefficient for (short i = 0; i <= data.Smoke->numMethods && data.Smoke->methods[i].classId <= idx; i++) { Smoke.Method *meth = data.Smoke->methods + i; if (meth->classId != idx) { continue; } string methName = ByteArrayManager.GetString(data.Smoke->methodNames[meth->name]); // we don't want anything except protected, const or empty flags if ((meth->flags & (ushort)Smoke.MethodFlags.mf_enum) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_ctor) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_copyctor) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_dtor) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_static) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_internal) > 0 || (meth->flags & (ushort)Smoke.MethodFlags.mf_protected) > 0 || methName.StartsWith("operator")) { continue; } if ((meth->flags & (ushort)Smoke.MethodFlags.mf_attribute) > 0) { ag.ScheduleAttributeAccessor(meth); continue; } methods.Add((IntPtr)meth); } methods.Sort(CompareSmokeMethods); foreach (Smoke.Method *method in methods) { CodeMemberMethod cmm = mg.GenerateBasicMethodDefinition(data.Smoke, method); if (cmm != null && !ifaceDecl.HasMethod(cmm)) { ifaceDecl.Members.Add(cmm); } } mg.GenerateProperties(); foreach (CodeMemberProperty prop in ag.GenerateBasicAttributeDefinitions()) { ifaceDecl.Members.Add(prop); } } }
private static bool MethodOverrides(Smoke *smoke, Smoke.Method *method, out MemberAttributes access, out bool foundInInterface) { access = MemberAttributes.Public; foundInInterface = false; if (smoke->inheritanceList[smoke->classes[method->classId].parents] == 0) { return(false); } long id = method - smoke->methods; Smoke.ModuleIndex methodModuleIndex = new Smoke.ModuleIndex(smoke, (short)id); Smoke.Method *firstMethod = (Smoke.Method *)IntPtr.Zero; short * firstParent = smoke->inheritanceList + smoke->classes[method->classId].parents; for (short *parent = firstParent; *parent > 0; parent++) { if (firstMethod != (Smoke.Method *)IntPtr.Zero && !foundInInterface) { // already found a method in the first parent class break; } // Do this with linq... there's probably room for optimization here. // Select virtual and pure virtual methods from superclasses. var inheritedVirtuals = from key in smoke->FindAllMethods(*parent, true).Keys where ((key.smoke->methods[key.index].flags & (ushort)Smoke.MethodFlags.mf_virtual) > 0 || (key.smoke->methods[key.index].flags & (ushort)Smoke.MethodFlags.mf_purevirtual) > 0) select key; foreach (Smoke.ModuleIndex mi in inheritedVirtuals) { Smoke.Method *meth = mi.smoke->methods + mi.index; if (SmokeMethodEqualityComparer.DefaultEqualityComparer.Equals(methodModuleIndex, mi)) { if ((meth->flags & (uint)Smoke.MethodFlags.mf_protected) > 0) { access = MemberAttributes.Family; } else { access = MemberAttributes.Public; } // don't return here - we need the access of the method in the topmost superclass firstMethod = meth; if (parent != firstParent) { foundInInterface = true; } } } } // we need to have a method that's not in a interface to mark it as overriden bool ret = firstMethod != (Smoke.Method *)IntPtr.Zero && !foundInInterface; // we need to have a public method in one of the interfaces for this to be set foundInInterface = firstMethod != (Smoke.Method *)IntPtr.Zero && foundInInterface && access == MemberAttributes.Public; return(ret); }
public CodeMemberMethod GenerateBasicMethodDefinition(Smoke *smoke, Smoke.Method *method) { return(GenerateBasicMethodDefinition(smoke, method, null)); }
public CodeMemberMethod GenerateBasicMethodDefinition(Smoke *smoke, Smoke.Method *method, CodeTypeReference iface) { string cppSignature = smoke->GetMethodSignature(method); return(GenerateBasicMethodDefinition(smoke, method, cppSignature, iface)); }
/* * Adds the methods to the classes created by Run() */ private void GenerateMethods() { short currentClassId = 0; Smoke.Class * klass = (Smoke.Class *)IntPtr.Zero; MethodsGenerator methgen = null; AttributeGenerator attrgen = null; CodeTypeDeclaration type = null; List <Smoke.ModuleIndex> alreadyImplemented = new List <Smoke.ModuleIndex>(); this.FillEnums(); for (short i = 1; i < data.Smoke->numMethodMaps; i++) { Smoke.MethodMap *map = data.Smoke->methodMaps + i; if (currentClassId != map->classId) { // we encountered a new class if (attrgen != null) { // generate inherited methods this.GenerateInheritedMethods(klass, methgen, attrgen, alreadyImplemented); // generate all scheduled attributes attrgen.Run(); if (PostMembersHooks != null) { PostMembersHooks(data.Smoke, klass, type); } methgen.GenerateProperties(); } currentClassId = map->classId; klass = data.Smoke->classes + currentClassId; type = data.SmokeTypeMap[(IntPtr)klass]; alreadyImplemented.Clear(); attrgen = new AttributeGenerator(data, translator, type); methgen = new MethodsGenerator(data, translator, type, klass); } string mungedName = ByteArrayManager.GetString(data.Smoke->methodNames[map->name]); if (map->method > 0) { Smoke.Method *meth = data.Smoke->methods + map->method; if ((meth->flags & (ushort)Smoke.MethodFlags.mf_enum) > 0) { continue; } if ((meth->flags & (ushort)Smoke.MethodFlags.mf_property) > 0 && // non-virtual properties are excluded (meth->flags & (ushort)Smoke.MethodFlags.mf_virtual) == 0 && (meth->flags & (ushort)Smoke.MethodFlags.mf_purevirtual) == 0) { continue; } if ((meth->flags & (ushort)Smoke.MethodFlags.mf_attribute) > 0) { attrgen.ScheduleAttributeAccessor(meth); continue; } methgen.GenerateMethod(map->method, mungedName); alreadyImplemented.Add(new Smoke.ModuleIndex(data.Smoke, map->method)); } else if (map->method < 0) { for (short *overload = data.Smoke->ambiguousMethodList + (-map->method); *overload > 0; overload++) { Smoke.Method *meth = data.Smoke->methods + *overload; if ((meth->flags & (ushort)Smoke.MethodFlags.mf_enum) > 0) { continue; } if ((meth->flags & (ushort)Smoke.MethodFlags.mf_property) > 0 && // non-virtual properties are excluded (meth->flags & (ushort)Smoke.MethodFlags.mf_virtual) == 0 && (meth->flags & (ushort)Smoke.MethodFlags.mf_purevirtual) == 0) { continue; } if ((meth->flags & (ushort)Smoke.MethodFlags.mf_attribute) > 0) { attrgen.ScheduleAttributeAccessor(meth); continue; } // if the methods differ only by constness, we will generate special code bool nextDiffersByConst = false; if (*(overload + 1) > 0) { if (SmokeMethodEqualityComparer.EqualExceptConstness(meth, data.Smoke->methods + *(overload + 1))) { nextDiffersByConst = true; } } methgen.GenerateMethod(*overload, mungedName); alreadyImplemented.Add(new Smoke.ModuleIndex(data.Smoke, *overload)); if (nextDiffersByConst) { overload++; } } } } // Generate the last scheduled attributes attrgen.Run(); // Generate remaining inherited methods this.GenerateInheritedMethods(klass, methgen, attrgen, alreadyImplemented); if (PostMembersHooks != null) { PostMembersHooks(data.Smoke, klass, type); } methgen.GenerateProperties(); AddMissingOperators(); }
public CodeMemberMethod GenerateMethod(Smoke *smoke, Smoke.Method *method, string mungedName, CodeTypeReference iface) { string cppSignature = smoke->GetMethodSignature(method); CodeMemberMethod cmm = GenerateBasicMethodDefinition(smoke, method, cppSignature, iface); if (cmm == null) { return(null); } // put the method into the correct type CodeTypeDeclaration containingType = type; if (cmm.Name.StartsWith("operator") || cmm.Name.StartsWith("explicit ")) { if (!data.CSharpTypeMap.TryGetValue(cmm.Parameters[0].Type.GetStringRepresentation(), out containingType)) { if (cmm.Parameters.Count < 2 || !data.CSharpTypeMap.TryGetValue(cmm.Parameters[1].Type.GetStringRepresentation(), out containingType)) { Debug.Print(" |--Can't find containing type for {0} - skipping", cppSignature); } return(null); } } // already implemented? if (containingType.HasMethod(cmm)) { if (iface == null || (method->flags & (uint)Smoke.MethodFlags.mf_protected) > 0) { // protected methods are not available in interfaces Debug.Print(" |--Skipping already implemented method {0}", cppSignature); return(null); } else { cmm.PrivateImplementationType = iface; } } if (PreMethodBodyHooks != null) { PreMethodBodyHooks(smoke, method, cmm, containingType); } // do we have pass-by-ref parameters? bool generateInvokeForRefParams = cmm.Parameters.Cast <CodeParameterDeclarationExpression>().Any(expr => expr.Direction == FieldDirection.Ref); // generate the SmokeMethod attribute CodeAttributeDeclaration attr = new CodeAttributeDeclaration("SmokeMethod", new CodeAttributeArgument( new CodePrimitiveExpression(cppSignature))); cmm.CustomAttributes.Add(attr); // choose the correct 'interceptor' CodeMethodInvokeExpression invoke; if ((cmm.Attributes & MemberAttributes.Static) == MemberAttributes.Static) { invoke = new CodeMethodInvokeExpression(SmokeSupport.staticInterceptor_Invoke); } else { invoke = new CodeMethodInvokeExpression(SmokeSupport.interceptor_Invoke); } // first pass the munged name, then the C++ signature invoke.Parameters.Add(new CodePrimitiveExpression(mungedName)); invoke.Parameters.Add(new CodePrimitiveExpression(cppSignature)); // retrieve the return type CodeTypeReference returnType; if ((method->flags & (uint)Smoke.MethodFlags.mf_dtor) > 0) { // destructor returnType = new CodeTypeReference(typeof(void)); } else if (cmm.Name.StartsWith("explicit operator ")) { // strip 'explicit operator' from the name to get the return type returnType = new CodeTypeReference(cmm.Name.Substring(18)); } else { returnType = cmm.ReturnType; } // add the return type invoke.Parameters.Add(new CodeTypeOfExpression(returnType)); invoke.Parameters.Add(new CodePrimitiveExpression(generateInvokeForRefParams)); invoke.Parameters.Add(new CodeVariableReferenceExpression("smokeArgs")); CodeArrayCreateExpression argsInitializer = new CodeArrayCreateExpression(typeof(object[])); // add the parameters foreach (CodeParameterDeclarationExpression param in cmm.Parameters) { argsInitializer.Initializers.Add(new CodeTypeOfExpression(param.Type)); string argReference = param.Name; int indexOfSpace = argReference.IndexOf(' '); if (indexOfSpace > 0) { argReference = argReference.Substring(0, indexOfSpace); } argsInitializer.Initializers.Add(new CodeArgumentReferenceExpression(argReference)); } CodeStatement argsStatement = new CodeVariableDeclarationStatement(typeof(object[]), "smokeArgs", argsInitializer); cmm.Statements.Add(argsStatement); // we have to call "CreateProxy()" in constructors if (cmm is CodeConstructor) { cmm.Statements.Add( new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "CreateProxy"))); } // add the method call statement CodeStatement statement; if (!generateInvokeForRefParams) { if (method->ret > 0 && (method->flags & (uint)Smoke.MethodFlags.mf_ctor) == 0) { statement = new CodeMethodReturnStatement(new CodeCastExpression(returnType, invoke)); } else { statement = new CodeExpressionStatement(invoke); } cmm.Statements.Add(statement); } else { if (method->ret > 0 && (method->flags & (uint)Smoke.MethodFlags.mf_ctor) == 0) { statement = new CodeVariableDeclarationStatement(returnType, "smokeRetval", new CodeCastExpression(returnType, invoke)); cmm.Statements.Add(statement); } else { statement = new CodeExpressionStatement(invoke); cmm.Statements.Add(statement); } int i = 0; foreach (CodeParameterDeclarationExpression param in cmm.Parameters) { ++i; if (param.Direction != FieldDirection.Ref) { continue; } cmm.Statements.Add(new CodeAssignStatement(new CodeVariableReferenceExpression(param.Name), new CodeCastExpression(param.Type.BaseType, new CodeArrayIndexerExpression( new CodeVariableReferenceExpression("smokeArgs"), new CodePrimitiveExpression(i * 2 - 1) ) ) )); } if (method->ret > 0 && (method->flags & (uint)Smoke.MethodFlags.mf_ctor) == 0) { cmm.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("smokeRetval"))); } } if (PostMethodBodyHooks != null) { PostMethodBodyHooks(smoke, method, cmm, containingType); } containingType.Members.Add(cmm); if ((method->flags & (uint)Smoke.MethodFlags.mf_dtor) != 0) { containingType.BaseTypes.Add(new CodeTypeReference(typeof(IDisposable))); CodeMemberMethod dispose = new CodeMemberMethod(); dispose.Name = "Dispose"; dispose.Attributes = MemberAttributes.Public | MemberAttributes.New | MemberAttributes.Final; dispose.Statements.AddRange(cmm.Statements); dispose.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression( new CodeTypeReferenceExpression("GC"), "SuppressFinalize", new CodeThisReferenceExpression() ))); containingType.Members.Add(dispose); } this.DistributeMethod(cmm); return(cmm); }
public void ScheduleAttributeAccessor(Smoke.Method *meth) { ScheduleAttributeAccessor(data.Smoke, meth); }