2005-01-17 Cesar Lopez Nataren <cnataren@novell.com>
[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                         Type type = ast.GetType ();
342
343                         if (type == typeof (Expression)) {  
344                                 Expression exp = ast as Expression;
345                                 exp.Emit (ec);
346                                 AST last_exp = (AST) exp.exprs [exp.exprs.Count - 1];
347
348                                 if (last_exp is Binary)
349                                         ft_binary_recursion (ec, last_exp, lbl);
350                                 else if (last_exp is Equality)
351                                         ft_emit_equality (ec, last_exp, lbl);
352                         } else if (type == typeof (Binary))
353                                 ft_binary_recursion (ec, ast, lbl);
354                         else
355                                 emit_default_case (ec, ast, OpCodes.Brfalse, lbl);
356                 }
357
358                 static void ff_emit_relational (EmitContext ec, AST ast, Label lbl)
359                 {
360                         ILGenerator ig = ec.ig;
361                         Relational r = ast as Relational;
362                         r.Emit (ec);
363
364                         switch (r.op) {
365                         case JSToken.LessThan:
366                                 ig.Emit (OpCodes.Ldc_I4_0);
367                                 ig.Emit (OpCodes.Conv_R8);
368                                 ig.Emit (OpCodes.Blt, lbl);
369                                 break;
370                         }
371                 }
372
373                 static void ff_binary_recursion (EmitContext ec, AST ast, Label lbl)
374                 {
375                         ILGenerator ig = ec.ig;
376                         Binary b = ast as Binary;
377
378                         switch (b.op) {
379                         case JSToken.LogicalOr:
380                                 fall_false (ec, b.left, lbl);
381                                 fall_false (ec, b.right, lbl);
382                                 break;
383
384                         case JSToken.LogicalAnd:
385                                 Label ftLb = ig.DefineLabel ();
386                                 fall_true (ec, b.left, ftLb);
387                                 fall_false (ec, b.right, lbl);
388                                 ig.MarkLabel (ftLb);
389                                 break;
390                         }
391                 }
392
393                 static void ff_emit_equality_cond (EmitContext ec, AST ast, Label lbl)
394                 {
395                         ILGenerator ig = ec.ig;
396                         Equality eq = ast as Equality;
397                         eq.Emit (ec);
398
399                         switch (eq.op) {
400                         case JSToken.NotEqual:
401                         case JSToken.Equal:
402                                 ig.Emit (OpCodes.Brfalse, lbl);
403                                 break;
404                         }
405                 }
406                         
407                 internal static void fall_false (EmitContext ec, AST ast, Label lbl)
408                 {
409                         Type type = ast.GetType ();
410
411                         if (type == typeof (Expression)) {  
412                                 Expression exp = ast as Expression;
413
414                                 if (exp.Size > 1)
415                                         exp.Emit (ec);
416
417                                 AST last_exp = (AST) exp.exprs [exp.exprs.Count - 1];
418
419                                 if (last_exp is Relational)
420                                         ff_emit_relational (ec, last_exp, lbl);
421                                 else if (last_exp is Binary)
422                                         ff_binary_recursion (ec, last_exp, lbl);
423                                 else if (last_exp is Identifier || last_exp is BooleanLiteral)
424                                         emit_default_case (ec, last_exp, OpCodes.Brtrue, lbl);
425                                 else if (last_exp is Equality) 
426                                         ff_emit_equality_cond (ec, last_exp, lbl);
427                                 else {
428                                         Console.WriteLine ("fall_false, last_exp.GetType () == {0}", last_exp);
429                                         throw new Exception ("uknown type: " + last_exp.GetType ().ToString ());
430                                 }
431                         } else if (type == typeof (Binary))
432                                 ff_binary_recursion (ec, ast, lbl);
433                         else 
434                                 emit_default_case (ec, ast, OpCodes.Brtrue, lbl);
435                 }
436
437                 internal static void emit_to_boolean (AST ast, ILGenerator ig, int i)
438                 {
439                         ig.Emit (OpCodes.Ldc_I4, i);
440                         ig.Emit (OpCodes.Call, typeof (Convert).GetMethod ("ToBoolean", 
441                                                                            new Type [] { typeof (object), typeof (Boolean)}));
442                 }
443
444                 internal static bool need_convert_to_boolean (AST ast)
445                 {
446                         if (ast == null)
447                                 return false;
448
449                         if (ast is Identifier)
450                                 return true;
451                         else if (ast is Expression) {
452                                 Expression exp = ast as Expression;
453                                 int n = exp.exprs.Count - 1;
454                                 AST tmp = (AST) exp.exprs [n];
455                                 if (tmp is Equality || tmp is Relational || tmp is BooleanLiteral)
456                                         return false;
457                                 else
458                                         return true;
459                         } else 
460                                 return false;
461                 }
462                 
463                 //
464                 // Loads a current VsaEngine
465                 //
466                 internal static void load_engine (AST parent, ILGenerator ig)
467                 {
468                         //
469                         // If we are in a function declaration at global level,
470                         // we must load the engine associated to the current 'JScript N' instance,
471                         // otherwise pick up the engine at second place in method's signature.
472                         //
473                         if (parent == null ||  parent.GetType () == typeof (ScriptBlock)) {
474                                 ig.Emit (OpCodes.Ldarg_0);
475                                 ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine"));
476                         } else if (parent != null &&  parent.GetType () == typeof (FunctionDeclaration )) 
477                                 ig.Emit (OpCodes.Ldarg_1);
478                 }
479         }
480 }