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 GeneratorData(Smoke *smoke, string defaultNamespace, List <string> imports, List <Assembly> references, CodeCompileUnit unit, string destination, string docs) { Destination = destination; Docs = docs; Smoke = smoke; string argNamesFile = GetArgNamesFile(Smoke); if (File.Exists(argNamesFile)) { foreach (string[] strings in File.ReadAllLines(argNamesFile).Select(line => line.Split(';'))) { ArgumentNames[strings[0]] = strings[1].Split(','); } } CompileUnit = unit; Imports = imports; References = references; DefaultNamespace = new CodeNamespace(defaultNamespace); this.AddUsings(DefaultNamespace); foreach (Assembly assembly in References) { smokeClassAttribute = assembly.GetType("QtCore.SmokeClass", false); if (smokeClassAttribute != null) { smokeClassGetSignature = smokeClassAttribute.GetProperty("Signature").GetGetMethod(); break; } } foreach (Assembly assembly in References) { foreach (Type type in assembly.GetTypes()) { object[] attributes = type.GetCustomAttributes(smokeClassAttribute, false); if (attributes.Length != 0) { string smokeClassName = (string)smokeClassGetSignature.Invoke(attributes[0], null); Type t; if (ReferencedTypeMap.TryGetValue(smokeClassName, out t) && t.IsInterface) { continue; } ReferencedTypeMap[smokeClassName] = type; } else { ReferencedTypeMap[type.Name] = type; } } } CompileUnit.Namespaces.Add(DefaultNamespace); NamespaceMap[defaultNamespace] = DefaultNamespace; }
public GeneratorData(Smoke* smoke, string defaultNamespace, List<string> imports, List<Assembly> references, CodeCompileUnit unit, string destination, string docs) { Destination = destination; Docs = docs; Smoke = smoke; string argNamesFile = GetArgNamesFile(Smoke); if (File.Exists(argNamesFile)) { foreach (string[] strings in File.ReadAllLines(argNamesFile).Select(line => line.Split(';'))) { ArgumentNames[strings[0]] = strings[1].Split(','); } } CompileUnit = unit; Imports = imports; References = references; DefaultNamespace = new CodeNamespace(defaultNamespace); this.AddUsings(DefaultNamespace); foreach (Assembly assembly in References) { smokeClassAttribute = assembly.GetType("QtCore.SmokeClass", false); if (smokeClassAttribute != null) { smokeClassGetSignature = smokeClassAttribute.GetProperty("Signature").GetGetMethod(); break; } } foreach (Assembly assembly in References) { foreach (Type type in assembly.GetTypes()) { object[] attributes = type.GetCustomAttributes(smokeClassAttribute, false); if (attributes.Length != 0) { string smokeClassName = (string) smokeClassGetSignature.Invoke(attributes[0], null); Type t; if (ReferencedTypeMap.TryGetValue(smokeClassName, out t) && t.IsInterface) { continue; } ReferencedTypeMap[smokeClassName] = type; } else { ReferencedTypeMap[type.Name] = type; } } } CompileUnit.Namespaces.Add(DefaultNamespace); NamespaceMap[defaultNamespace] = DefaultNamespace; }
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; } }
public void PreMembersHook(Smoke *smoke, Smoke.Class *klass, CodeTypeDeclaration type) { if (type.Name == "QObject") { // Add 'Qt' base class type.BaseTypes.Add(new CodeTypeReference("Qt")); // add the Q_EMIT field CodeMemberField Q_EMIT = new CodeMemberField(typeof(object), "Q_EMIT"); Q_EMIT.Attributes = MemberAttributes.Family; Q_EMIT.InitExpression = new CodePrimitiveExpression(null); type.Members.Add(Q_EMIT); } }
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; } } } }
private CodeParameterDeclarationExpression GetArgument(Smoke *smoke, short *typeIndex, IList <string> methodArgs, IEnumerable args, ref int count) { bool isRef; CodeTypeReference argType = translator.CppToCSharp(smoke->types + *typeIndex, out isRef); string argName = this.GetArgName(smoke, typeIndex, methodArgs, ref count, isRef, argType); if (!argName.Contains(" = ")) { RemoveDefaultValuesFromPreviousArgs(args); } CodeParameterDeclarationExpression arg = new CodeParameterDeclarationExpression(argType, argName); if (isRef) { arg.Direction = FieldDirection.Ref; } return(arg); }
public string GetArgNamesFile(Smoke *smoke) { string dest = Destination; if (string.IsNullOrEmpty(dest)) { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { dest = Path.GetDirectoryName(Path.GetDirectoryName(Environment.GetFolderPath(Environment.SpecialFolder.System))); } else { dest = Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture); } } dest = Path.Combine(dest, "share"); dest = Path.Combine(dest, "smoke"); return(Path.Combine(dest, ByteArrayManager.GetString(smoke->module_name) + ".argnames.txt")); }
public void PostMembersHook(Smoke *smoke, Smoke.Class *klass, CodeTypeDeclaration type) { if (Util.IsQObject(klass)) { CodeMemberProperty emit = new CodeMemberProperty(); emit.Name = "Emit"; emit.Attributes = MemberAttributes.Family | MemberAttributes.New | MemberAttributes.Final; emit.HasGet = true; emit.HasSet = false; string signalsIfaceName = "I" + type.Name + "Signals"; CodeTypeReference returnType = new CodeTypeReference(signalsIfaceName); emit.Type = returnType; emit.GetStatements.Add(new CodeMethodReturnStatement(new CodeCastExpression( returnType, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "Q_EMIT") ))); type.Members.Add(emit); string className = ByteArrayManager.GetString(klass->className); int colon = className.LastIndexOf("::", StringComparison.Ordinal); string prefix = (colon != -1) ? className.Substring(0, colon) : string.Empty; IList typeCollection = Data.GetTypeCollection(prefix); CodeTypeDeclaration ifaceDecl = new CodeTypeDeclaration(signalsIfaceName); ifaceDecl.IsInterface = true; if (className != "QObject") { string parentClassName = ByteArrayManager.GetString(smoke->classes[smoke->inheritanceList[klass->parents]].className); colon = parentClassName.LastIndexOf("::", StringComparison.Ordinal); prefix = (colon != -1) ? parentClassName.Substring(0, colon) : string.Empty; if (colon != -1) { parentClassName = parentClassName.Substring(colon + 2); } string parentInterface = (prefix != string.Empty) ? prefix.Replace("::", ".") + "." : string.Empty; parentInterface += "I" + parentClassName + "Signals"; ifaceDecl.BaseTypes.Add(new CodeTypeReference(parentInterface)); } Dictionary <CodeSnippetTypeMember, CodeMemberMethod> signalEvents = new Dictionary <CodeSnippetTypeMember, CodeMemberMethod>(); GetSignals(smoke, klass, delegate(string signature, string name, string typeName, IntPtr metaMethod) { CodeMemberMethod signal = new CodeMemberMethod(); signal.Attributes = MemberAttributes.Abstract; // capitalize the first letter StringBuilder builder = new StringBuilder(name); builder[0] = char.ToUpper(builder[0]); string tmp = builder.ToString(); signal.Name = tmp; bool isRef; try { if (typeName == string.Empty) { signal.ReturnType = new CodeTypeReference(typeof(void)); } else { signal.ReturnType = Translator.CppToCSharp(typeName, out isRef); } } catch (NotSupportedException) { Debug.Print(" |--Won't wrap signal {0}::{1}", className, signature); return; } CodeAttributeDeclaration attr = new CodeAttributeDeclaration("Q_SIGNAL", new CodeAttributeArgument(new CodePrimitiveExpression(signature))); signal.CustomAttributes.Add(attr); int argNum = 1; StringBuilder fullNameBuilder = new StringBuilder("Slot"); GetMetaMethodParameters(metaMethod, delegate(string paramType, string paramName) { if (paramName == string.Empty) { paramName = "arg" + argNum.ToString(); } argNum++; CodeParameterDeclarationExpression param; try { short id = smoke->IDType(paramType); CodeTypeReference paramTypeRef; if (id > 0) { paramTypeRef = Translator.CppToCSharp(smoke->types + id, out isRef); } else { if (!paramType.Contains("::")) { id = smoke->IDType(className + "::" + paramType); if (id > 0) { paramTypeRef = Translator.CppToCSharp(smoke->types + id, out isRef); } else { paramTypeRef = Translator.CppToCSharp(paramType, out isRef); } } else { paramTypeRef = Translator.CppToCSharp(paramType, out isRef); } } param = new CodeParameterDeclarationExpression(paramTypeRef, paramName); } catch (NotSupportedException) { Debug.Print(" |--Won't wrap signal {0}::{1}", className, signature); return; } if (isRef) { param.Direction = FieldDirection.Ref; } signal.Parameters.Add(param); if (argNum == 2) { fullNameBuilder.Append('<'); } fullNameBuilder.Append(param.Type.BaseType); fullNameBuilder.Append(','); }); if (fullNameBuilder[fullNameBuilder.Length - 1] == ',') { fullNameBuilder[fullNameBuilder.Length - 1] = '>'; } ifaceDecl.Members.Add(signal); CodeSnippetTypeMember signalEvent = new CodeSnippetTypeMember(); signalEvent.Name = signal.Name; CodeSnippetTypeMember existing = signalEvents.Keys.FirstOrDefault(m => m.Name == signal.Name); if (existing != null) { CodeSnippetTypeMember signalEventToUse; CodeMemberMethod signalToUse; if (signal.Parameters.Count == 0) { signalEventToUse = existing; signalToUse = signalEvents[existing]; } else { signalEventToUse = signalEvent; signalToUse = signal; } string suffix = signalToUse.Parameters.Cast <CodeParameterDeclarationExpression>().Last().Name; if (suffix.StartsWith("arg") && suffix.Length > 3 && char.IsDigit(suffix[3])) { string lastType = signalToUse.Parameters.Cast <CodeParameterDeclarationExpression>().Last().Type.BaseType; suffix = lastType.Substring(lastType.LastIndexOf('.') + 1); } else { StringBuilder lastParamBuilder = new StringBuilder(suffix); lastParamBuilder[0] = char.ToUpper(lastParamBuilder[0]); suffix = lastParamBuilder.ToString(); } signalEventToUse.Text = signalEventToUse.Text.Replace(signalEventToUse.Name, signalEventToUse.Name += suffix); } signalEvent.Text = string.Format(@" public event {0} {1} {{ add {{ QObject.Connect(this, Qt.SIGNAL(""{2}""), (QObject) value.Target, Qt.SLOT(value.Method.Name + ""{3}"")); }} remove {{ QObject.Disconnect(this, Qt.SIGNAL(""{2}""), (QObject) value.Target, Qt.SLOT(value.Method.Name + ""{3}"")); }} }}" , fullNameBuilder, signalEvent.Name, signature, signature.Substring(signature.IndexOf('('))); signalEvents.Add(signalEvent, signal); }); typeCollection.Add(ifaceDecl); foreach (KeyValuePair <CodeSnippetTypeMember, CodeMemberMethod> signalEvent in signalEvents) { CodeSnippetTypeMember implementation = signalEvent.Key; CodeCommentStatementCollection comments = new CodeCommentStatementCollection(); foreach (CodeTypeMember current in from CodeTypeMember member in type.Members where member.Name == implementation.Name select member) { if (comments.Count == 0) { comments.AddRange(current.Comments); } current.Name = "On" + current.Name; } signalEvent.Value.Comments.AddRange(comments); signalEvent.Key.Comments.AddRange(comments); type.Members.Add(signalEvent.Key); } } }
public GeneratorData(Smoke *smoke, string defaultNamespace, List <string> imports, List <Assembly> references, string destination, string docs) : this(smoke, defaultNamespace, imports, references, new CodeCompileUnit(), destination, docs) { }
private static extern bool GetProperties(Smoke *smoke, short classId, AddProperty addProp);
private void PostMethodBodyHooks(Smoke *smoke, Smoke.Method *smokeMethod, CodeMemberMethod cmm, CodeTypeDeclaration type) { this.CommentMember(smoke, smokeMethod, cmm, type); GenerateEvent(cmm, cmm.Name, type, true); }
// adapted from QtRuby's findAllMethods() public void FindAllMethods(short c, IDictionary <ModuleIndex, string> ret, bool searchSuperClasses) { Class *klass = classes + c; if (klass->external) { Smoke *smoke = (Smoke *)0; short index = 0; if (!Util.GetModuleIndexFromClassName(klass->className, ref smoke, ref index)) { Console.Error.WriteLine(" |--Failed resolving external class {0}", ByteArrayManager.GetString(klass->className)); return; } smoke->FindAllMethods(index, ret, true); return; } Smoke *thisPtr; fixed(Smoke *smoke = &this) { thisPtr = smoke; // hackish, but this is the cleanest way that I know to get the 'this' pointer } short imax = numMethodMaps; short imin = 0; short methmin = -1, methmax = -1; int icmp = -1; while (imax >= imin) { short icur = (short)((imin + imax) / 2); icmp = methodMaps[icur].classId - c; if (icmp == 0) { short pos = icur; while (icur > 0 && methodMaps[icur - 1].classId == c) { icur--; } methmin = icur; icur = pos; while (icur < imax && methodMaps[icur + 1].classId == c) { icur++; } methmax = icur; break; } if (icmp > 0) { imax = (short)(icur - 1); } else { imin = (short)(icur + 1); } } if (icmp != 0) { Console.Error.WriteLine("WARNING: FindAllMethods failed for class {0}", ByteArrayManager.GetString(classes[c].className)); return; } for (short i = methmin; i <= methmax; i++) { string mungedName = ByteArrayManager.GetString(methodNames[methodMaps[i].name]); short methId = methodMaps[i].method; if (methId > 0) { ret[new ModuleIndex(thisPtr, methId)] = mungedName; } else { for (short *overload = ambiguousMethodList + (-methId); *overload > 0; overload++) { ret[new ModuleIndex(thisPtr, *overload)] = mungedName; } } } if (searchSuperClasses) { for (short *parent = inheritanceList + classes[c].parents; *parent > 0; parent++) { FindAllMethods(*parent, ret, true); } } }
static extern void GetSignals(Smoke *smoke, void *klass, AddSignal addSignalFn);
public static unsafe int Main(string[] args) { string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; string pluginDirectory; if (baseDirectory.EndsWith("bin")) { // Windows pluginDirectory = Path.GetFullPath(Path.Combine(baseDirectory, "..\\lib\\plugins")); } else { // *nix pluginDirectory = Path.Combine(baseDirectory, "plugins"); } List <string> codeFiles = new List <string>(); List <CodeCompileUnit> codeSnippets = new List <CodeCompileUnit>(); List <Assembly> references = new List <Assembly>(); List <string> imports = new List <string>(); StringBuilder compilerOptions = new StringBuilder(); bool codeOnly = false; string codeFile = string.Empty; string assemblyFile = "out.dll"; int warnLevel = 0; string smokeLib = null; string defaultNamespace = "Qyoto"; string globalClass = "Global"; string destination = string.Empty; string docs = string.Empty; List <Assembly> plugins = new List <Assembly>(); foreach (string arg in args) { if (arg == "-help" || arg == "--help" || arg == "-h") { PrintHelp(); return(0); } if (arg == "-verbose") { Debug.Listeners.Add(new ConsoleTraceListener(true)); continue; } if (arg == "-code-only") { codeOnly = true; continue; } if (arg.StartsWith("-code-file:")) { codeFile = arg.Substring(11); continue; } if (arg.StartsWith("-out:")) { assemblyFile = arg.Substring(5); continue; } if (arg.StartsWith("-warn:")) { warnLevel = int.Parse(arg.Substring(6)); continue; } if (arg.StartsWith("-r:")) { references.Add(Assembly.LoadFrom(arg.Substring(3))); continue; } if (arg.StartsWith("-reference:")) { references.Add(Assembly.LoadFrom(arg.Substring(11))); continue; } if (arg.StartsWith("-global-class:")) { globalClass = arg.Substring(14); continue; } if (arg.StartsWith("-namespace:")) { defaultNamespace = arg.Substring(11); continue; } if (arg.StartsWith("-dest:")) { destination = arg.Substring("-dest:".Length); continue; } if (arg.StartsWith("-import:")) { imports.AddRange(arg.Substring(8).Split(',')); continue; } if (arg.StartsWith("-docs:")) { docs = arg.Substring("-docs:".Length); continue; } if (arg.StartsWith("-plugins:")) { foreach (string str in arg.Substring(9).Split(',')) { Assembly a; try { a = Assembly.LoadFrom(str); } catch (FileNotFoundException) { a = Assembly.LoadFrom(Path.Combine(pluginDirectory, str)); } plugins.Add(a); } continue; } if (arg.StartsWith("-")) { compilerOptions.Append(" /"); compilerOptions.Append(arg.Substring(1)); continue; } if (smokeLib == null) { smokeLib = arg; continue; } codeFiles.Add(arg); FileStream fs = new FileStream(arg, FileMode.Open); StreamReader sr = new StreamReader(fs); codeSnippets.Add(new CodeSnippetCompileUnit(sr.ReadToEnd())); sr.Close(); fs.Close(); } if (!string.IsNullOrEmpty(docs)) { compilerOptions.Append(" /doc:" + Path.ChangeExtension(Path.GetFileName(assemblyFile), ".xml")); } compilerOptions.Append(" -debug"); if (smokeLib == null) { PrintHelp(); return(1); } Smoke *smoke = InitSmoke(smokeLib); if (smoke == (Smoke *)0) { return(SmokeLoadingFailure); } List <ICustomTranslator> customTranslators = (from plugin in plugins from type in plugin.GetTypes() from iface in type.GetInterfaces() where iface == typeof(ICustomTranslator) select(ICustomTranslator) Activator.CreateInstance(type)).ToList(); GeneratorData data = new GeneratorData(smoke, defaultNamespace, imports, references, destination, docs); data.GlobalSpaceClassName = globalClass; Translator translator = new Translator(data, customTranslators); foreach (IHookProvider provider in from type in plugins.SelectMany(plugin => plugin.GetTypes()) where type.GetInterfaces().Any(iface => iface == typeof(IHookProvider)) select(IHookProvider) Activator.CreateInstance(type)) { provider.Translator = translator; provider.Data = data; provider.RegisterHooks(); } ClassesGenerator classgen = new ClassesGenerator(data, translator); Console.Error.WriteLine("Generating CodeCompileUnit..."); classgen.Run(); DestroySmoke((IntPtr)smoke); Dictionary <string, string> providerOptions = new Dictionary <string, string>(); providerOptions.Add("CompilerVersion", "v4.0"); CodeDomProvider csharp = new CSharpCodeProvider(providerOptions); if (codeFile != string.Empty) { FileStream fs = new FileStream(codeFile, FileMode.Create); StreamWriter sw = new StreamWriter(fs); Console.Error.WriteLine("Generating code..."); CodeGeneratorOptions cgo = new CodeGeneratorOptions(); csharp.GenerateCodeFromCompileUnit(data.CompileUnit, sw, cgo); sw.Close(); fs.Close(); codeFiles.Add(codeFile); } if (codeOnly) { if (codeFile == string.Empty) { Console.Error.WriteLine("Missing output filename. Use the -code-file:<file> option."); return(MissingOptionError); } return(NoError); } codeSnippets.Add(data.CompileUnit); Console.Error.WriteLine("Compiling assembly..."); CompilerParameters cp = new CompilerParameters(); cp.GenerateExecutable = false; cp.TreatWarningsAsErrors = false; cp.OutputAssembly = assemblyFile; cp.GenerateInMemory = false; cp.WarningLevel = warnLevel; cp.CompilerOptions = compilerOptions.ToString(); cp.ReferencedAssemblies.Add(typeof(Regex).Assembly.Location); cp.ReferencedAssemblies.Add(typeof(ExtensionAttribute).Assembly.Location); foreach (Assembly assembly in references) { cp.ReferencedAssemblies.Add(assembly.Location); } CompilerResults cr; if (codeFile == null) { cr = csharp.CompileAssemblyFromDom(cp, codeSnippets.ToArray()); } else { cr = csharp.CompileAssemblyFromFile(cp, codeFiles.ToArray()); } bool errorsOccured = false; foreach (CompilerError error in cr.Errors) { if (!error.IsWarning) { errorsOccured = true; } Console.Error.WriteLine(error); } if (errorsOccured) { Console.Error.WriteLine("Errors occured. No assembly was generated."); return(CompilationError); } Console.Error.WriteLine("Done."); return(NoError); }
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); }
private static extern long GetEnumValue(Smoke *smoke, Smoke.Method *meth);
public static unsafe bool IsClassAbstract(Smoke *smoke, short classId) { return(GetAbstractMethods(smoke, classId).Count > 0); }
public CodeMemberMethod GenerateBasicMethodDefinition(Smoke *smoke, Smoke.Method *method) { return(GenerateBasicMethodDefinition(smoke, method, null)); }
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); }
public CodeMemberMethod GenerateBasicMethodDefinition(Smoke *smoke, Smoke.Method *method, CodeTypeReference iface) { string cppSignature = smoke->GetMethodSignature(method); return(GenerateBasicMethodDefinition(smoke, method, cppSignature, iface)); }
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 string GetArgName(Smoke *smoke, short *typeIndex, IList <string> methodArgs, ref int count, bool isRef, CodeTypeReference argType) { if (methodArgs == null) { return("arg" + count++); } string arg = methodArgs[count++ - 1]; int nameEnd = arg.IndexOf(' '); if (nameEnd > 0) { string defaultValue = arg.Substring(nameEnd + 3); arg = arg.Substring(0, nameEnd); if (isRef) { return(arg); } // HACK if (argType.BaseType.EndsWith("NativeLong") || argType.BaseType.EndsWith("NativeULong")) { return(arg); } arg = Provider.CreateEscapedIdentifier(arg); Smoke.TypeId typeId = (Smoke.TypeId)((smoke->types + *typeIndex)->flags & (ushort)Smoke.TypeFlags.tf_elem); switch (typeId) { case Smoke.TypeId.t_voidp: case Smoke.TypeId.t_class: switch (argType.BaseType) { case "System.Int64": return(arg + " = 0"); case "System.IntPtr": return(arg + " = new IntPtr()"); } if (argType.BaseType.Contains("QObjectWrapOption")) { return(arg + " = 0"); } switch (defaultValue) { case "0": return(arg + " = null"); case "QString()": return(arg + " = \"\""); } break; case Smoke.TypeId.t_int: if (char.IsLetter(defaultValue[0])) { int indexOfColon = defaultValue.IndexOf("::", StringComparison.Ordinal); string containingType; string enumMember; string additional; if (indexOfColon > 0) { containingType = defaultValue.Substring(0, indexOfColon); Match match = Regex.Match(defaultValue, @"::(\w+)(.*)"); enumMember = match.Groups[1].Value; additional = match.Groups[2].Value; } else { containingType = this.type.Name; enumMember = defaultValue; additional = string.Empty; } string enumType = (from keyValue in this.data.EnumTypeMap where keyValue.Value.IsEnum && keyValue.Key.StartsWith(containingType + "::") from CodeTypeMember member in keyValue.Value.Members where member.Name == enumMember select keyValue.Value.Name).FirstOrDefault() ?? (from referencedType in this.data.ReferencedTypeMap.Values where referencedType.IsEnum && referencedType.FullName.Contains("." + containingType + "+") from FieldInfo member in referencedType.GetFields(BindingFlags.Public | BindingFlags.Static) where member.Name == enumMember select referencedType.Name).FirstOrDefault(); if (string.IsNullOrEmpty(enumType)) { var result = (from CodeTypeReference baseType in this.type.BaseTypes where this.data.CSharpTypeMap.ContainsKey(baseType.BaseType) let baseTypeDeclaration = this.data.CSharpTypeMap[baseType.BaseType] from CodeTypeDeclaration member in baseTypeDeclaration.Members.OfType <CodeTypeDeclaration>() where member.IsEnum from CodeTypeMember enumValue in member.Members where enumValue.Name == enumMember select new KeyValuePair <string, string>(baseType.BaseType, member.Name)).FirstOrDefault(); containingType = result.Key; enumType = result.Value; } return(arg + " = (int) " + GetEnumMembers(string.Format("{0}.{1}", containingType, enumType), enumMember) + additional); } goto default; case Smoke.TypeId.t_uint: case Smoke.TypeId.t_enum: if (defaultValue == "0" || defaultValue.EndsWith("()")) { return(arg + " = 0"); } if (char.IsLetter(defaultValue[0])) { return(arg + " = " + GetEnumMembers(argType.BaseType, defaultValue)); } goto default; case Smoke.TypeId.t_char: break; default: return(arg + " = " + defaultValue); } } return(arg); }
public static extern unsafe bool GetModuleIndexFromClassName(byte *name, ref Smoke *smoke, ref short index);
public CodeMemberMethod GenerateMethod(Smoke *smoke, short idx, string mungedName) { return(GenerateMethod(smoke, smoke->methods + idx, mungedName, null)); }
public ModuleIndex(Smoke *smoke, short index) { this.smoke = smoke; this.index = index; }
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); }