2005-01-05 Cesar Lopez Nataren <cesar@ciencias.unam.mx>
[mono.git] / mcs / class / Microsoft.JScript / Microsoft.JScript / CodeGenerator.cs
1 //
2 // CodeGenerator.cs
3 //
4 // Author:
5 //      Cesar Lopez Nataren (cesar@ciencias.unam.mx)
6 //
7 // (C) 2003, 2004 Cesar Lopez Nataren
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.IO;
33 using System.Reflection;
34 using System.Reflection.Emit;
35 using System.Threading;
36 using Microsoft.JScript.Vsa;
37 using System.Runtime.CompilerServices;
38
39 namespace Microsoft.JScript {
40
41         internal class EmitContext {
42
43                 internal TypeBuilder type_builder;
44                 internal ILGenerator ig;
45                 internal ModuleBuilder mod_builder;
46
47                 internal Label LoopBegin, LoopEnd;
48
49                 internal EmitContext (TypeBuilder type)
50                 {
51                         type_builder = type;
52
53                         if (type_builder != null) {
54                                 MethodBuilder global_code =  type_builder.DefineMethod (
55                                                                         "Global Code",
56                                                                         MethodAttributes.Public,
57                                                                         typeof (System.Object),
58                                                                         new Type [] {});
59                                 ig = global_code.GetILGenerator ();
60                         }
61                 }
62
63                 internal EmitContext (TypeBuilder type_builder, ModuleBuilder mod_builder, ILGenerator ig)
64                 {
65                         this.type_builder = type_builder;
66                         this.mod_builder = mod_builder;
67                         this.ig = ig;
68                 }
69         }
70
71         public class CodeGenerator {
72
73                 private static string MODULE = "JScript Module";
74
75                 internal static string mod_name;
76                 internal static AppDomain app_domain;
77                 internal static AssemblyName assembly_name;
78                 internal static AssemblyBuilder assembly_builder;
79                 internal static ModuleBuilder module_builder;
80
81                 internal static string Basename (string name)
82                 {
83                         int pos = name.LastIndexOf ('/');
84
85                         if (pos != -1)
86                                 return name.Substring (pos + 1);
87
88                         pos = name.LastIndexOf ('\\');
89                         if (pos != -1)
90                                 return name.Substring (pos + 1);
91
92                         return name;
93                 }
94
95                 internal static string Dirname (string name)
96                 {
97                         int pos = name.LastIndexOf ('/');
98
99                         if (pos != -1)
100                                 return name.Substring (0, pos);
101
102                         pos = name.LastIndexOf ('\\');
103                         if (pos != -1)
104                                 return name.Substring (0, pos);
105
106                         return ".";
107                 }
108
109                 internal static void Init (string file_name)
110                 {
111                         app_domain = Thread.GetDomain ();
112
113                         assembly_name = new AssemblyName ();
114                         assembly_name.Name = Path.GetFileNameWithoutExtension (file_name);
115                         mod_name = MODULE;
116
117                         assembly_builder = app_domain.DefineDynamicAssembly (
118                                              assembly_name,
119                                              AssemblyBuilderAccess.RunAndSave,
120                                              Dirname (file_name));
121
122                         ConstructorInfo ctr_info = typeof (Microsoft.JScript.ReferenceAttribute).GetConstructor (new Type [] { typeof (string) });
123                         // FIXME: find out which is the blob.
124                         byte [] blob  = new byte [] {};
125                         assembly_builder.SetCustomAttribute (ctr_info, blob); 
126
127                         module_builder = assembly_builder.DefineDynamicModule (
128                                                mod_name,
129                                                Basename (assembly_name.Name + ".exe"),
130                                                false);
131                 }
132
133                 internal static string trim_extension (string file_name)
134                 {
135                         int index = file_name.LastIndexOf ('.');
136
137                         if (index < 0)
138                                 return file_name;
139                         else
140                                 return file_name.Substring (0, index);
141                 }
142
143                 internal static void Save (string target_name)
144                 {
145                         assembly_builder.Save (CodeGenerator.Basename (target_name));
146                 }
147
148                 internal static void Emit (AST prog)
149                 {
150                         if (prog == null)
151                                 return;
152
153                         TypeBuilder type_builder;
154                         type_builder = module_builder.DefineType ("JScript 0", TypeAttributes.Public);
155
156                         type_builder.SetParent (typeof (GlobalScope));
157                         type_builder.SetCustomAttribute (new CustomAttributeBuilder
158                                                          (typeof (CompilerGlobalScopeAttribute).GetConstructor (new Type [] {}), new object [] {}));
159
160                         EmitContext ec = new EmitContext (type_builder);
161                         ec.mod_builder = module_builder;
162                         ILGenerator global_code = ec.ig;
163
164                         emit_default_script_constructor (ec);
165                         emit_default_init_global_code (global_code);
166                         prog.Emit (ec);
167                         emit_default_end_global_code (global_code);
168                         ec.type_builder.CreateType ();
169
170                         //
171                         // Build the default 'JScript Main' class
172                         //
173                         ec.type_builder = module_builder.DefineType ("JScript Main");
174                         emit_jscript_main (ec.type_builder);
175                         ec.type_builder.CreateType ();
176                 }
177
178                 internal static void emit_default_init_global_code (ILGenerator ig)
179                 {
180                         ig.Emit (OpCodes.Ldarg_0);
181                         ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine"));
182                         ig.Emit (OpCodes.Ldarg_0);
183                         ig.Emit (OpCodes.Call,
184                                 typeof (VsaEngine).GetMethod ("PushScriptObject",
185                                                               new Type [] { typeof (ScriptObject)}));
186                 }
187
188                 internal static void emit_default_end_global_code (ILGenerator ig)
189                 {
190                         ig.Emit (OpCodes.Ldnull);
191                         ig.Emit (OpCodes.Ldarg_0);
192                         ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine"));
193                         ig.Emit (OpCodes.Call, typeof (VsaEngine).GetMethod ("PopScriptObject"));
194                         ig.Emit (OpCodes.Pop);
195                         ig.Emit (OpCodes.Ret);          
196                 }
197
198                 internal static void emit_default_script_constructor (EmitContext ec)
199                 {
200                         ConstructorBuilder cons_builder;
201                         TypeBuilder tb = ec.type_builder;
202                         cons_builder = tb.DefineConstructor (MethodAttributes.Public,
203                                                              CallingConventions.Standard,
204                                                              new Type [] { typeof (GlobalScope) });
205
206                         ILGenerator ig = cons_builder.GetILGenerator ();
207                         ig.Emit (OpCodes.Ldarg_0);
208                         ig.Emit (OpCodes.Ldarg_1);
209                         ig.Emit (OpCodes.Dup);
210                         ig.Emit (OpCodes.Ldfld,
211                                  typeof (ScriptObject).GetField ("engine"));
212                         
213                         ig.Emit (OpCodes.Call, 
214                                  typeof (GlobalScope).GetConstructor (new Type [] {typeof (GlobalScope), 
215                                                                                    typeof (VsaEngine)}));
216                         ig.Emit (OpCodes.Ret);
217                 }
218
219                 internal static void emit_jscript_main (TypeBuilder tb)
220                 {
221                         emit_jscript_main_constructor (tb);
222                         emit_jscript_main_entry_point (tb);
223                 }
224
225                 internal static void emit_jscript_main_constructor (TypeBuilder tb)
226                 {
227                         ConstructorBuilder cons = tb.DefineConstructor (MethodAttributes.Public, 
228                                                                         CallingConventions.Standard,
229                                                                         new Type [] {});
230                         ILGenerator ig = cons.GetILGenerator ();
231                         ig.Emit (OpCodes.Ldarg_0);
232                         ig.Emit (OpCodes.Call, typeof (Object).GetConstructor (new Type [] {}));
233                         ig.Emit (OpCodes.Ret);
234                 }
235
236                 internal static void emit_jscript_main_entry_point (TypeBuilder tb)
237                 {
238                         MethodBuilder method;
239                         method = tb.DefineMethod ("Main", 
240                                                   MethodAttributes.Public | MethodAttributes.Static,
241                                                   typeof (void), new Type [] {typeof (String [])});
242
243                         method.SetCustomAttribute (new CustomAttributeBuilder 
244                                                    (typeof (STAThreadAttribute).GetConstructor (
245                                                                                         new Type [] {}),
246                                                      new object [] {}));
247
248                         ILGenerator ig = method.GetILGenerator ();
249
250                         ig.DeclareLocal (typeof (GlobalScope));
251
252                         ig.Emit (OpCodes.Ldc_I4_1);
253                         ig.Emit (OpCodes.Ldc_I4_1);
254                         ig.Emit (OpCodes.Newarr, typeof (string));
255                         ig.Emit (OpCodes.Dup);
256                         ig.Emit (OpCodes.Ldc_I4_0);
257
258                         ig.Emit (OpCodes.Ldstr,
259                                  "mscorlib, Version=1.0.3300.0, Culture=neutral, Pub" + 
260                                  "licKeyToken=b77a5c561934e089");
261
262                         ig.Emit (OpCodes.Stelem_Ref);
263
264                         ig.Emit (OpCodes.Call,
265                                  typeof (VsaEngine).GetMethod ("CreateEngineAndGetGlobalScope", 
266                                                                new Type [] {typeof (bool), 
267                                                                             typeof (string [])}));      
268                         ig.Emit (OpCodes.Stloc_0);
269                         ig.Emit (OpCodes.Ldloc_0);
270                         
271                         ig.Emit (OpCodes.Newobj,
272                                  assembly_builder.GetType ("JScript 0").GetConstructor (
273                                                                         new Type [] {typeof (GlobalScope)})); 
274                         ig.Emit (OpCodes.Call, 
275                                  assembly_builder.GetType ("JScript 0").GetMethod (
276                                                                            "Global Code", new Type [] {}));
277                         ig.Emit (OpCodes.Pop);
278                         ig.Emit (OpCodes.Ret);
279
280                         assembly_builder.SetEntryPoint (method);
281                 }
282
283                 public static void Run (string file_name, AST prog)
284                 {
285                         CodeGenerator.Init (file_name);
286                         CodeGenerator.Emit (prog);
287                         CodeGenerator.Save (trim_extension (file_name) + ".exe");
288                 }
289
290                 static void emit_default_case (EmitContext ec, AST ast, OpCode op, Label lbl)
291                 {
292                         ast.Emit (ec);
293                         if (need_convert_to_boolean (ast))
294                                 emit_to_boolean (ast, ec.ig, 0);
295                         ec.ig.Emit (op, lbl);
296                 }
297
298                 static void ft_binary_recursion (EmitContext ec, AST ast, Label lbl)
299                 {
300                         ILGenerator ig = ec.ig;
301                         if (ast is Binary) {
302                                 Binary b = ast as Binary;
303                                 switch (b.op) {
304                                 case JSToken.LogicalOr:
305                                         Label ftLb = ig.DefineLabel ();
306                                         fall_false (ec, b.left, ftLb);
307                                         fall_true (ec, b.right, lbl);
308                                         ig.MarkLabel (ftLb);
309                                         break;
310                                 case JSToken.LogicalAnd:
311                                         fall_true (ec, b.left, lbl);
312                                         fall_true (ec, b.right, lbl);
313                                         break;
314
315                                 case JSToken.LessThan:
316                                         ig.Emit (OpCodes.Ldc_I4_0);
317                                         ig.Emit (OpCodes.Conv_R8);
318                                         ig.Emit (OpCodes.Blt, lbl);
319                                         break;
320                                 }
321                         }
322                 }
323
324                 static void ft_emit_equality (EmitContext ec, AST ast, Label lbl)
325                 {
326                         ILGenerator ig = ec.ig;
327                         Equality eq = ast as Equality;
328
329                         switch (eq.op) {
330                         case JSToken.NotEqual:
331                                 ig.Emit (OpCodes.Brtrue, lbl);
332                                 break;
333                         case JSToken.Equal:
334                                 ig.Emit (OpCodes.Brfalse, lbl);
335                                 break;
336                         }
337                 }
338
339                 internal static void fall_true (EmitContext ec, AST ast, Label lbl)
340                 {
341                         ILGenerator ig = ec.ig;
342                         Type type = ast.GetType ();
343
344                         if (type == typeof (Expression)) {  
345                                 Expression exp = ast as Expression;
346                                 exp.Emit (ec);
347                                 AST last_exp = (AST) exp.exprs [exp.exprs.Count - 1];
348
349                                 if (last_exp is Binary)
350                                         ft_binary_recursion (ec, last_exp, lbl);
351                                 else if (last_exp is Equality)
352                                         ft_emit_equality (ec, last_exp, lbl);
353                         } else if (type == typeof (Binary))
354                                 ft_binary_recursion (ec, ast, lbl);
355                         else
356                                 emit_default_case (ec, ast, OpCodes.Brfalse, lbl);
357                 }
358
359                 static void ff_emit_relational (EmitContext ec, AST ast, Label lbl)
360                 {
361                         ILGenerator ig = ec.ig;
362                         Relational r = ast as Relational;
363                         r.Emit (ec);
364
365                         switch (r.op) {
366                         case JSToken.LessThan:
367                                 ig.Emit (OpCodes.Ldc_I4_0);
368                                 ig.Emit (OpCodes.Conv_R8);
369                                 ig.Emit (OpCodes.Blt, lbl);
370                                 break;
371                         }
372                 }
373
374                 static void ff_binary_recursion (EmitContext ec, AST ast, Label lbl)
375                 {
376                         ILGenerator ig = ec.ig;
377                         Binary b = ast as Binary;
378
379                         switch (b.op) {
380                         case JSToken.LogicalOr:
381                                 fall_false (ec, b.left, lbl);
382                                 fall_false (ec, b.right, lbl);
383                                 break;
384
385                         case JSToken.LogicalAnd:
386                                 Label ftLb = ig.DefineLabel ();
387                                 fall_true (ec, b.left, ftLb);
388                                 fall_false (ec, b.right, lbl);
389                                 ig.MarkLabel (ftLb);
390                                 break;
391                         }
392                 }
393
394                 static void ff_emit_equality_cond (EmitContext ec, AST ast, Label lbl)
395                 {
396                         ILGenerator ig = ec.ig;
397                         Equality eq = ast as Equality;
398                         eq.Emit (ec);
399
400                         switch (eq.op) {
401                         case JSToken.NotEqual:
402                         case JSToken.Equal:
403                                 ig.Emit (OpCodes.Brfalse, lbl);
404                                 break;
405                         }
406                 }
407                         
408                 internal static void fall_false (EmitContext ec, AST ast, Label lbl)
409                 {
410                         Type type = ast.GetType ();
411
412                         if (type == typeof (Expression)) {  
413                                 Expression exp = ast as Expression;
414
415                                 if (exp.Size > 1)
416                                         exp.Emit (ec);
417
418                                 AST last_exp = (AST) exp.exprs [exp.exprs.Count - 1];
419
420                                 if (last_exp is Relational)
421                                         ff_emit_relational (ec, last_exp, lbl);
422                                 else if (last_exp is Binary)
423                                         ff_binary_recursion (ec, last_exp, lbl);
424                                 else if (last_exp is Identifier || last_exp is BooleanLiteral)
425                                         emit_default_case (ec, last_exp, OpCodes.Brtrue, lbl);
426                                 else if (last_exp is Equality) 
427                                         ff_emit_equality_cond (ec, last_exp, lbl);
428                                 else {
429                                         Console.WriteLine ("fall_false, last_exp.GetType () == {0}", last_exp);
430                                         throw new Exception ("uknown type: " + last_exp.GetType ().ToString ());
431                                 }
432                         } else if (type == typeof (Binary))
433                                 ff_binary_recursion (ec, ast, lbl);
434                         else 
435                                 emit_default_case (ec, ast, OpCodes.Brtrue, lbl);
436                 }
437
438                 internal static void emit_to_boolean (AST ast, ILGenerator ig, int i)
439                 {
440                         ig.Emit (OpCodes.Ldc_I4, i);
441                         ig.Emit (OpCodes.Call, typeof (Convert).GetMethod ("ToBoolean", 
442                                                                            new Type [] { typeof (object), typeof (Boolean)}));
443                 }
444
445                 internal static bool need_convert_to_boolean (AST ast)
446                 {
447                         if (ast == null)
448                                 return false;
449
450                         if (ast is Identifier)
451                                 return true;
452                         else if (ast is Expression) {
453                                 Expression exp = ast as Expression;
454                                 int n = exp.exprs.Count - 1;
455                                 AST tmp = (AST) exp.exprs [n];
456                                 if (tmp is Equality || tmp is Relational || tmp is BooleanLiteral)
457                                         return false;
458                                 else
459                                         return true;
460                         } else 
461                                 return false;
462                 }
463         }
464 }