* CodeGenerator.cs: Added LoopBegin and LoopEnd to EmitContext, they are used by
[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 using System;
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Threading;
14 using Microsoft.JScript.Vsa;
15 using System.Runtime.CompilerServices;
16
17 namespace Microsoft.JScript {
18
19         internal class EmitContext {
20
21                 internal TypeBuilder type_builder;
22                 internal ILGenerator ig;
23                 internal ModuleBuilder mod_builder;
24
25                 internal Label LoopBegin, LoopEnd;
26
27                 internal EmitContext (TypeBuilder type)
28                 {
29                         type_builder = type;
30
31                         if (type_builder != null) {
32                                 MethodBuilder global_code =  type_builder.DefineMethod (
33                                                                         "Global Code",
34                                                                         MethodAttributes.Public,
35                                                                         typeof (System.Object),
36                                                                         new Type [] {});
37                                 ig = global_code.GetILGenerator ();
38                         }
39                 }
40
41                 internal EmitContext (TypeBuilder type_builder, ModuleBuilder mod_builder, ILGenerator ig)
42                 {
43                         this.type_builder = type_builder;
44                         this.mod_builder = mod_builder;
45                         this.ig = ig;
46                 }
47         }
48
49         public class CodeGenerator {
50
51                 private static string MODULE = "JScript Module";
52
53                 internal static string mod_name;
54                 internal static AppDomain app_domain;
55                 internal static AssemblyName assembly_name;
56                 internal static AssemblyBuilder assembly_builder;
57                 internal static ModuleBuilder module_builder;
58
59                 internal static void Init (string file_name)
60                 {
61                         app_domain = Thread.GetDomain ();
62
63                         assembly_name = new AssemblyName ();
64                         assembly_name.Name =  trim_extension (file_name);
65
66                         mod_name = MODULE;
67
68                         assembly_builder = app_domain.DefineDynamicAssembly (
69                                              assembly_name,
70                                              AssemblyBuilderAccess.RunAndSave);
71
72                         ConstructorInfo ctr_info = typeof (Microsoft.JScript.ReferenceAttribute).GetConstructor (new Type [] { typeof (string) });
73                         // FIXME: find out which is the blob.
74                         byte [] blob  = new byte [] {};
75                         assembly_builder.SetCustomAttribute (ctr_info, blob); 
76
77                         module_builder = assembly_builder.DefineDynamicModule (
78                                                 mod_name,
79                                                 assembly_name.Name + ".exe", 
80                                                 false);
81                 }
82
83                 internal static string trim_extension (string file_name)
84                 {
85                         int index = file_name.LastIndexOf ('.');
86
87                         if (index < 0)
88                                 return file_name;
89                         else
90                                 return file_name.Substring (0, index);
91                 }
92
93                 internal static void Save (string target_name)
94                 {
95                         assembly_builder.Save (target_name);
96                 }
97
98                 internal static void Emit (AST prog)
99                 {
100                         if (prog == null)
101                                 return;
102
103                         TypeBuilder type_builder;
104                         type_builder = module_builder.DefineType ("JScript 0", TypeAttributes.Public);
105
106                         type_builder.SetParent (typeof (GlobalScope));
107                         type_builder.SetCustomAttribute (new CustomAttributeBuilder
108                                                          (typeof (CompilerGlobalScopeAttribute).GetConstructor (new Type [] {}), new object [] {}));
109
110                         EmitContext ec = new EmitContext (type_builder);
111                         ec.mod_builder = module_builder;
112                         ILGenerator global_code = ec.ig;
113
114                         emit_default_script_constructor (ec);
115                         emit_default_init_global_code (global_code);
116                         prog.Emit (ec);
117                         emit_default_end_global_code (global_code);
118                         ec.type_builder.CreateType ();
119
120                         //
121                         // Build the default 'JScript Main' class
122                         //
123                         ec.type_builder = module_builder.DefineType ("JScript Main");
124                         emit_jscript_main (ec.type_builder);
125                         ec.type_builder.CreateType ();
126                 }
127
128                 internal static void emit_default_init_global_code (ILGenerator ig)
129                 {
130                         ig.Emit (OpCodes.Ldarg_0);
131                         ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine"));
132                         ig.Emit (OpCodes.Ldarg_0);
133                         ig.Emit (OpCodes.Call,
134                                 typeof (VsaEngine).GetMethod ("PushScriptObject",
135                                                               new Type [] { typeof (ScriptObject)}));
136                 }
137
138                 internal static void emit_default_end_global_code (ILGenerator ig)
139                 {
140                         ig.Emit (OpCodes.Ldnull);
141                         ig.Emit (OpCodes.Ldarg_0);
142                         ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine"));
143                         ig.Emit (OpCodes.Call, typeof (VsaEngine).GetMethod ("PopScriptObject"));
144                         ig.Emit (OpCodes.Pop);
145                         ig.Emit (OpCodes.Ret);          
146                 }
147
148                 internal static void emit_default_script_constructor (EmitContext ec)
149                 {
150                         ConstructorBuilder cons_builder;
151                         TypeBuilder tb = ec.type_builder;
152                         cons_builder = tb.DefineConstructor (MethodAttributes.Public,
153                                                              CallingConventions.Standard,
154                                                              new Type [] { typeof (GlobalScope) });
155
156                         ILGenerator ig = cons_builder.GetILGenerator ();
157                         ig.Emit (OpCodes.Ldarg_0);
158                         ig.Emit (OpCodes.Ldarg_1);
159                         ig.Emit (OpCodes.Dup);
160                         ig.Emit (OpCodes.Ldfld,
161                                  typeof (ScriptObject).GetField ("engine"));
162                         
163                         ig.Emit (OpCodes.Call, 
164                                  typeof (GlobalScope).GetConstructor (new Type [] {typeof (GlobalScope), 
165                                                                                    typeof (VsaEngine)}));
166                         ig.Emit (OpCodes.Ret);
167                 }
168
169                 internal static void emit_jscript_main (TypeBuilder tb)
170                 {
171                         emit_jscript_main_constructor (tb);
172                         emit_jscript_main_entry_point (tb);
173                 }
174
175                 internal static void emit_jscript_main_constructor (TypeBuilder tb)
176                 {
177                         ConstructorBuilder cons = tb.DefineConstructor (MethodAttributes.Public, 
178                                                                         CallingConventions.Standard,
179                                                                         new Type [] {});
180                         ILGenerator ig = cons.GetILGenerator ();
181                         ig.Emit (OpCodes.Ldarg_0);
182                         ig.Emit (OpCodes.Call, typeof (Object).GetConstructor (new Type [] {}));
183                         ig.Emit (OpCodes.Ret);
184                 }
185
186                 internal static void emit_jscript_main_entry_point (TypeBuilder tb)
187                 {
188                         MethodBuilder method;
189                         method = tb.DefineMethod ("Main", 
190                                                   MethodAttributes.Public | MethodAttributes.Static,
191                                                   typeof (void), new Type [] {typeof (String [])});
192
193                         method.SetCustomAttribute (new CustomAttributeBuilder 
194                                                    (typeof (STAThreadAttribute).GetConstructor (
195                                                                                         new Type [] {}),
196                                                      new object [] {}));
197
198                         ILGenerator ig = method.GetILGenerator ();
199
200                         ig.DeclareLocal (typeof (GlobalScope));
201
202                         ig.Emit (OpCodes.Ldc_I4_1);
203                         ig.Emit (OpCodes.Ldc_I4_1);
204                         ig.Emit (OpCodes.Newarr, typeof (string));
205                         ig.Emit (OpCodes.Dup);
206                         ig.Emit (OpCodes.Ldc_I4_0);
207
208                         ig.Emit (OpCodes.Ldstr,
209                                  "mscorlib, Version=1.0.3300.0, Culture=neutral, Pub" + 
210                                  "licKeyToken=b77a5c561934e089");
211
212                         ig.Emit (OpCodes.Stelem_Ref);
213
214                         ig.Emit (OpCodes.Call,
215                                  typeof (VsaEngine).GetMethod ("CreateEngineAndGetGlobalScope", 
216                                                                new Type [] {typeof (bool), 
217                                                                             typeof (string [])}));      
218                         ig.Emit (OpCodes.Stloc_0);
219                         ig.Emit (OpCodes.Ldloc_0);
220                         
221                         ig.Emit (OpCodes.Newobj,
222                                  assembly_builder.GetType ("JScript 0").GetConstructor (
223                                                                         new Type [] {typeof (GlobalScope)})); 
224                         ig.Emit (OpCodes.Call, 
225                                  assembly_builder.GetType ("JScript 0").GetMethod (
226                                                                            "Global Code", new Type [] {}));
227                         ig.Emit (OpCodes.Pop);
228                         ig.Emit (OpCodes.Ret);
229
230                         assembly_builder.SetEntryPoint (method);
231                 }
232
233                 public static void Run (string file_name, AST prog)
234                 {
235                         CodeGenerator.Init (file_name);
236                         CodeGenerator.Emit (prog);
237                         CodeGenerator.Save (trim_extension (file_name) + ".exe");
238                 }
239
240                 internal static void fall_true (EmitContext ec, AST ast, Label lbl)
241                 {
242                         ILGenerator ig = ec.ig;
243                         if (ast is Binary) {
244                                 Binary b = ast as Binary;
245                                 switch (b.current_op) {
246                                 case JSToken.LogicalOr:
247                                         Label ftLb = ig.DefineLabel ();
248                                         fall_false (ec, b.left, ftLb);
249                                         fall_true (ec, b.right, lbl);
250                                         ig.MarkLabel (ftLb);
251                                         break;
252                                 case JSToken.LogicalAnd:
253                                         fall_true (ec, b.left, lbl);
254                                         fall_true (ec, b.right, lbl);
255                                         break;
256                                 }
257                         } else {
258                                 ast.Emit (ec);
259                                 if (need_convert_to_boolean (ast))
260                                         emit_to_boolean (ast, ig, 0);
261                                 ig.Emit (OpCodes.Brfalse, lbl);
262                         }
263                 }
264
265                 internal static void fall_false (EmitContext ec, AST ast, Label lbl)
266                 {
267                         ILGenerator ig = ec.ig;
268                         if (ast is Binary) {
269                                 Binary b = ast as Binary;
270                                 switch (b.current_op) {
271                                 case JSToken.LogicalOr:
272                                         fall_false (ec, b.left, lbl);
273                                         fall_false (ec, b.right, lbl);
274                                         break;
275                                 case JSToken.LogicalAnd:
276                                         Label ftLb = ig.DefineLabel ();
277                                         fall_true (ec, b.left, ftLb);
278                                         fall_false (ec, b.right, lbl);
279                                         ig.MarkLabel (ftLb);
280                                         break;
281                                 }
282                         } else {                                
283                                 ast.Emit (ec);
284                                 if (need_convert_to_boolean (ast))
285                                         emit_to_boolean (ast, ig, 0);
286                                 ig.Emit (OpCodes.Brtrue, lbl);
287                         }
288                 }
289
290                 internal static void emit_to_boolean (AST ast, ILGenerator ig, int i)                   
291                 {
292                         ig.Emit (OpCodes.Ldc_I4, i);
293                         ig.Emit (OpCodes.Call, typeof (Convert).GetMethod ("ToBoolean", 
294                                                                            new Type [] { typeof (object), typeof (Boolean)}));
295                 }
296
297                 internal static bool need_convert_to_boolean (AST ast)
298                 {
299                         if (ast == null)
300                                 return false;
301
302                         if (ast is Identifier)
303                                 return true;
304                         else if (ast is Expression) {
305                                 Expression exp = ast as Expression;
306                                 int n = exp.exprs.Count - 1;
307                                 AST tmp = (AST) exp.exprs [n];
308                                 if (tmp is Equality || tmp is Relational || tmp is BooleanLiteral)
309                                         return false;
310                                 else
311                                         return true;
312                         } else 
313                                 return false;
314                 }
315         }
316 }