2004-12-30 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / tools / monop / monop.cs
1 //
2 // monop -- a semi-clone of javap
3 //
4 // Authors:
5 //      Ben Maurer (bmaurer@users.sourceforge.net)
6 //      John Luke  (john.luke@gmail.com)
7 //
8 // (C) 2004 Ben Maurer
9 // (C) 2004 John Luke
10 //
11
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33
34 using System;
35 using System.CodeDom.Compiler;
36 using System.Collections;
37 using System.Diagnostics;
38 using System.IO;
39 using System.Reflection;
40 using System.Text;
41
42 class MonoP {
43         static string assembly;
44         static BindingFlags default_flags = 
45                 BindingFlags.Instance |
46                 BindingFlags.Static |
47                 BindingFlags.Public;
48         
49         // very common namespaces, all in corlib
50         static readonly string [] v_common_ns = {
51                 "System",
52                 "System.Collections",
53                 "System.Reflection",
54                 "System.Text",
55                 "System.IO"
56         };
57         
58         static readonly string [] common_assemblies = {
59                 "System.Xml.dll",
60                 "System.Web.dll",
61                 "gtk-sharp.dll",
62                 "glib-sharp.dll"
63         };
64         
65         static readonly string [] common_ns = {
66                 "System.Xml",
67                 "System.Web",
68                 "Gtk",
69                 "GLib"
70         };
71         
72         static Type GetType (string tname, bool ignoreCase)
73         {
74                 Type t;
75                 if (assembly != null) {
76                         Assembly a = GetAssembly (assembly, true);
77                         t = a.GetType (tname, false, ignoreCase);
78                 } else 
79                         t = Type.GetType (tname, false, ignoreCase);
80
81                 return t;
82         }
83         
84         static string SearchTypes (string name)
85         {
86                 StringBuilder sb = new StringBuilder ();
87
88                 foreach (string asm in GetKnownAssemblyNames ()) {
89                         Assembly a = GetAssembly (asm, false);
90
91                         if (a == null)
92                                 continue;
93
94                         foreach (Type t in a.GetTypes ()) {
95                                 if (t.Name == name)
96                                         sb.Append (t.FullName + " from " + a.Location + "\n");
97
98                                 if (t.Name.ToLower ().IndexOf (name.ToLower ()) > 0)
99                                         sb.Append (t.FullName + " from " + a.Location + "\n");
100                         }
101                 }
102
103                 if (sb.Length == 0) 
104                         return null;
105                 
106                 return sb.ToString ();
107         }
108
109         static string [] GetKnownAssemblyNames ()
110         {
111                 Process p = new Process ();
112                 p.StartInfo.UseShellExecute = false;
113                 p.StartInfo.RedirectStandardOutput = true;
114                 p.StartInfo.FileName = "gacutil";
115                 p.StartInfo.Arguments = "-l";
116                 p.Start ();
117
118                 string s;
119                 ArrayList names = new ArrayList ();
120                 StreamReader output = p.StandardOutput;
121
122                 while ((s = output.ReadLine ()) != null)
123                         names.Add (s);
124
125                 p.WaitForExit ();
126                 
127                 int length = names.Count - 1;
128                 string [] retval = new string [length];
129                 retval [0] = typeof (Object).Assembly.FullName;         
130                 names.CopyTo (1, retval, 1, length - 1); // skip the first and last line
131                 return retval;
132         }
133
134         static Assembly GetAssembly (string assembly, bool exit)
135         {
136                 Assembly a = null;
137
138                 // if -r:~/foo.dll syntax is used the shell misses it
139                 if (assembly.StartsWith ("~/"))
140                         assembly = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), assembly.Substring (2));
141
142                 try {
143                         // if it exists try to use LoadFrom
144                         if (File.Exists (assembly))
145                                 a = Assembly.LoadFrom (assembly);
146                         // if it looks like a fullname try that
147                         else if (assembly.Split (',').Length == 4)
148                                 a = Assembly.Load (assembly);
149                         // see if MONO_PATH has it
150                         else
151                                 a = LoadFromMonoPath (assembly);
152                 } catch {
153                         // ignore exception it gets handled below
154                 }
155
156                 // last try partial name
157                 // this (apparently) is exception safe
158                 if (a == null)
159                         a = Assembly.LoadWithPartialName (assembly);
160
161                 if (a == null && exit) {
162                         Console.WriteLine ("Could not load {0}", MonoP.assembly);
163                         Environment.Exit (1);
164                 }
165
166                 return a;
167         }
168
169         static Assembly LoadFromMonoPath (string assembly)
170         {
171                 // ; on win32, : everywhere else
172                 char sep = (Path.DirectorySeparatorChar == '/' ? ':' : ';');
173                 string[] paths = Environment.GetEnvironmentVariable ("MONO_PATH").Split (sep);
174                 foreach (string path in paths) {        
175                         string apath = Path.Combine (path, assembly);
176                         if (File.Exists (apath))
177                                 return Assembly.LoadFrom (apath);       
178                 }
179                 return null;
180         }
181
182         static Type GetType (string tname)
183         {
184                 return GetType (tname, false);
185         }
186         
187         static void PrintTypes (string assembly)
188         {
189                 Assembly a = GetAssembly (assembly, true);
190
191                 Console.WriteLine ();
192                 Console.WriteLine ("Assembly Information:");
193
194                 object[] cls = a.GetCustomAttributes (typeof (CLSCompliantAttribute), false);
195                 if (cls.Length > 0) {
196                         CLSCompliantAttribute cca = cls[0] as CLSCompliantAttribute;
197                         if (cca.IsCompliant)
198                                 Console.WriteLine ("[CLSCompliant]");
199                 }
200
201                 foreach (string ai in a.ToString ().Split (','))
202                         Console.WriteLine (ai.Trim ());
203                         
204                 Console.WriteLine ();
205                 Type [] types = a.GetExportedTypes ();
206                 Array.Sort (types, new TypeSorter ());
207
208                 foreach (Type t in types)
209                         Console.WriteLine (t.FullName);
210
211                 Console.WriteLine ("\nTotal: {0} types.", types.Length);
212         }
213         
214         static void Completion (string prefix)
215         {
216                 foreach (Type t in typeof (object).Assembly.GetExportedTypes ()) {
217                         if (t.Name.StartsWith (prefix)) {
218                                 if (Array.IndexOf (v_common_ns, t.Namespace) != -1) {
219                                         Console.WriteLine (t.Name);
220                                         return;
221                                 }
222                         }
223                         
224                         if (t.FullName.StartsWith (prefix)) {
225                                 Console.WriteLine (t.FullName);
226                         }
227                 }
228                 
229                 foreach (string assm in common_assemblies) {
230                         try {
231                                 
232                                 Assembly a = GetAssembly (assm, true);
233                                 foreach (Type t in a.GetExportedTypes ()) {
234                                         
235                                         if (t.Name.StartsWith (prefix)) {
236                                                 if (Array.IndexOf (common_ns, t.Namespace) != -1) {
237                                                         Console.WriteLine (t.Name);
238                                                         return;
239                                                 }
240                                         }
241                                         
242                                         if (t.FullName.StartsWith (prefix)) {
243                                                 Console.WriteLine (t.FullName);
244                                         }
245                                 }
246                                 
247                         } catch {
248                         }
249                 }
250                 
251         }
252
253         static void PrintUsage ()
254         {
255                 Console.WriteLine ("Usage is: monop [option] [-c] [-r:Assembly] [class-name]");
256         }
257
258         static void PrintHelp ()
259         {
260                 PrintUsage ();
261                 Console.WriteLine ("");
262                 Console.WriteLine ("options:");
263                 Console.WriteLine ("\t--declared-only,-d\tOnly show members declared in the Type");
264                 Console.WriteLine ("\t--help,-h\t\tShow this information");
265                 Console.WriteLine ("\t--private,-p\t\tShow private members");
266                 Console.WriteLine ("\t--search,-s,-k\t\tSearch through all known namespaces");
267         }
268         
269         static void Main (string [] args)
270         {
271                 if (args.Length < 1) {
272                         PrintUsage ();
273                         return;
274                 }
275
276                 if (args.Length == 1 && (args[0] == "--help" || args[0] == "-h"))
277                 {
278                         PrintHelp ();
279                         return;
280
281                 }
282                 
283                 int i = 0;
284                 if (args [0].StartsWith ("-r:") || args [0].StartsWith ("/r:")){
285                         i++;
286                         assembly = args [0].Substring (3);
287                         
288                         if (args.Length == 1) {
289                                 PrintTypes (assembly);
290                                 return;
291                         }
292                 }
293                 
294                 if (args [0] == "-c") {
295                         Completion (args [1]);
296                         return;
297                 }
298
299                 if (args [i] == "--private" || args [i] == "-p") {
300                         default_flags |= BindingFlags.NonPublic;
301                         i++;
302                 }
303
304                 if (args [i] == "--declared-only" || args [i] == "-d") {
305                         default_flags |= BindingFlags.DeclaredOnly;
306                         i++;
307                 }
308
309                 bool search = false;
310                 if (args [i] == "--search" || args [i] == "-s" || args [i] == "-k") {
311                         search = true;
312                         i++;
313                 }
314
315                 if (args.Length < i + 1) {
316                         PrintUsage ();
317                         return;
318                 }
319
320                 string message = null;
321                 string tname = args [i];
322                 Type t = null;
323
324                 if (search){
325                         string matches = SearchTypes (tname);
326
327                         if (matches != null) {
328                                 Console.WriteLine ("The follow types match:");
329                                 Console.WriteLine (matches);
330                                 return;
331                         } else
332                                 goto notfound;
333                 }
334                         
335                 t = GetType (tname);
336
337                 if (t == null) {
338                         // Try some very common ones, dont load anything
339                         foreach (string ns in v_common_ns) {
340                                 t = GetType (ns + "." + tname, true);
341                                 if (t != null)
342                                         goto found;
343                         }
344                 }
345
346                 if (t == null) {
347                         foreach (string assm in GetKnownAssemblyNames ()) {
348                                 try {
349                                         Assembly a = GetAssembly (assm, false);
350                                         t = a.GetType (tname, false, true);
351                                         if (t != null) {
352                                                 message = String.Format ("{0} is included in the {1} assembly.",
353                                                                 t.FullName, 
354                                                                 t.Assembly.GetName ().Name);
355                                                 goto found;
356                                         }
357                                         foreach (string ns in common_ns) {
358                                                 t = a.GetType (ns + "." + tname, false, true);
359                                                 if (t != null) {
360                                                         message = String.Format ("{0} is included in the {1} assembly.",
361                                                                         t.FullName, 
362                                                                         t.Assembly.GetName ().Name);
363                                                         goto found;
364                                                 }
365                                         }
366                                 } catch {
367                                 }
368                         }
369                 }
370
371         notfound:
372                 if (t == null) {
373                         Console.WriteLine ("Could not find {0}", tname);
374                         return;
375                 }
376         found:
377                 //
378                 // This gets us nice buffering
379                 //
380                 StreamWriter sw = new StreamWriter (Console.OpenStandardOutput (), Console.Out.Encoding);
381                 new Outline (t, sw).OutlineType (default_flags);
382                 sw.Flush ();
383
384                 if (message != null)
385                         Console.WriteLine (message);
386         }
387 }
388