5 // Cesar Lopez Nataren (cesar@ciencias.unam.mx)
7 // (C) 2003, 2004 Cesar Lopez Nataren
8 // (C) 2005, Novell, Inc. (http://novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Reflection;
35 using System.Reflection.Emit;
36 using System.Threading;
37 using Microsoft.JScript.Vsa;
38 using System.Collections;
40 namespace Microsoft.JScript {
42 internal class EmitContext {
44 internal TypeBuilder type_builder;
45 internal ILGenerator ig;
46 internal ModuleBuilder mod_builder;
47 internal MethodBuilder global_code;
49 internal Label LoopBegin, LoopEnd;
51 internal EmitContext (TypeBuilder type_builder, ModuleBuilder mod_builder, ILGenerator ig)
53 this.type_builder = type_builder;
54 this.mod_builder = mod_builder;
59 internal class CodeGenerator {
61 private static string MODULE = "JScript Module";
62 private static string CORLIB = typeof (object).Assembly.FullName;
64 internal static string mod_name;
65 internal static AppDomain app_domain;
66 internal static AssemblyName assembly_name;
67 internal static AssemblyBuilder assembly_builder;
68 internal static ModuleBuilder module_builder;
69 private static int next_type = 0;
70 private static ArrayList global_types = new ArrayList ();
71 private static Hashtable global_methods = new Hashtable ();
72 private static Hashtable source_file_to_type = new Hashtable ();
74 private static string NextType {
75 get { return "JScript " + next_type++; }
78 internal static string Basename (string name)
80 int pos = name.LastIndexOf ('/');
83 return name.Substring (pos + 1);
85 pos = name.LastIndexOf ('\\');
87 return name.Substring (pos + 1);
92 internal static string Dirname (string name)
94 int pos = name.LastIndexOf ('/');
97 return name.Substring (0, pos);
99 pos = name.LastIndexOf ('\\');
101 return name.Substring (0, pos);
106 internal static void Init (string file_name)
108 app_domain = Thread.GetDomain ();
110 assembly_name = new AssemblyName ();
111 assembly_name.Name = Path.GetFileNameWithoutExtension (file_name);
114 assembly_builder = app_domain.DefineDynamicAssembly (
116 AssemblyBuilderAccess.RunAndSave,
117 Dirname (file_name));
119 ConstructorInfo ctr_info = typeof (Microsoft.JScript.ReferenceAttribute).GetConstructor (new Type [] { typeof (string) });
120 assembly_builder.SetCustomAttribute (new CustomAttributeBuilder (ctr_info, new object [] {CORLIB}));
122 module_builder = assembly_builder.DefineDynamicModule (
124 Basename (assembly_name.Name + ".exe"),
128 internal static string trim_extension (string file_name)
130 int index = file_name.LastIndexOf ('.');
135 return file_name.Substring (0, index);
138 internal static void Save (string target_name)
140 assembly_builder.Save (CodeGenerator.Basename (target_name));
143 internal static void EmitDecls (ScriptBlock prog)
148 string next_type = CodeGenerator.NextType;
150 prog.InitTypeBuilder (module_builder, next_type);
151 prog.InitGlobalCode ();
153 global_types.Add (next_type);
154 global_methods.Add (next_type, prog.GlobalCode);
155 source_file_to_type.Add (prog.Location.SourceName, next_type);
157 prog.EmitDecls (module_builder);
160 internal static void emit_jscript_main (TypeBuilder tb)
162 emit_jscript_main_constructor (tb);
163 emit_jscript_main_entry_point (tb);
166 internal static void emit_jscript_main_constructor (TypeBuilder tb)
168 ConstructorBuilder cons = tb.DefineConstructor (MethodAttributes.Public,
169 CallingConventions.Standard,
171 ILGenerator ig = cons.GetILGenerator ();
172 ig.Emit (OpCodes.Ldarg_0);
173 ig.Emit (OpCodes.Call, typeof (Object).GetConstructor (new Type [] {}));
174 ig.Emit (OpCodes.Ret);
177 internal static void emit_jscript_main_entry_point (TypeBuilder tb)
179 MethodBuilder method;
180 method = tb.DefineMethod ("Main",
181 MethodAttributes.Public | MethodAttributes.Static,
182 typeof (void), new Type [] {typeof (String [])});
184 method.SetCustomAttribute (new CustomAttributeBuilder
185 (typeof (STAThreadAttribute).GetConstructor (
189 ILGenerator ig = method.GetILGenerator ();
191 ig.DeclareLocal (typeof (GlobalScope));
193 ig.Emit (OpCodes.Ldc_I4_1);
194 ig.Emit (OpCodes.Ldc_I4_1);
195 ig.Emit (OpCodes.Newarr, typeof (string));
196 ig.Emit (OpCodes.Dup);
197 ig.Emit (OpCodes.Ldc_I4_0);
199 ig.Emit (OpCodes.Ldstr, CORLIB);
201 ig.Emit (OpCodes.Stelem_Ref);
203 ig.Emit (OpCodes.Call,
204 typeof (VsaEngine).GetMethod ("CreateEngineAndGetGlobalScope",
205 new Type [] {typeof (bool),
206 typeof (string [])}));
207 ig.Emit (OpCodes.Stloc_0);
209 foreach (string type_name in global_types) {
210 ig.Emit (OpCodes.Ldloc_0);
211 ig.Emit (OpCodes.Newobj, assembly_builder.GetType (type_name).GetConstructor (
212 new Type [] {typeof (GlobalScope)}));
213 ig.Emit (OpCodes.Call, (MethodInfo) global_methods [type_name]);
214 ig.Emit (OpCodes.Pop);
216 ig.Emit (OpCodes.Ret);
218 assembly_builder.SetEntryPoint (method);
221 public static void Run (string file_name, ScriptBlock [] blocks)
223 CodeGenerator.Init (file_name);
226 // Emit first all the declarations (function and variables)
228 foreach (ScriptBlock script_block in blocks)
229 CodeGenerator.EmitDecls (script_block);
232 // emit everything that's not a declaration
234 foreach (ScriptBlock script_block in blocks)
235 script_block.Emit ();
238 // Create the types ('JScript N')
240 foreach (ScriptBlock script_block in blocks)
241 script_block.CreateType ();
244 // Build the default 'JScript Main' class
246 TypeBuilder main_type_builder = module_builder.DefineType ("JScript Main");
247 emit_jscript_main (main_type_builder);
248 main_type_builder.CreateType ();
250 CodeGenerator.Save (trim_extension (file_name) + ".exe");
253 static void emit_default_case (EmitContext ec, AST ast, OpCode op, Label lbl)
256 if (need_convert_to_boolean (ast))
257 emit_to_boolean (ast, ec.ig, 0);
258 ec.ig.Emit (op, lbl);
261 static void ft_binary_recursion (EmitContext ec, AST ast, Label lbl)
263 ILGenerator ig = ec.ig;
265 Binary b = ast as Binary;
267 case JSToken.LogicalOr:
268 Label ftLb = ig.DefineLabel ();
269 fall_false (ec, b.left, ftLb);
270 fall_true (ec, b.right, lbl);
273 case JSToken.LogicalAnd:
274 fall_true (ec, b.left, lbl);
275 fall_true (ec, b.right, lbl);
278 case JSToken.LessThan:
279 ig.Emit (OpCodes.Ldc_I4_0);
280 ig.Emit (OpCodes.Conv_R8);
281 ig.Emit (OpCodes.Blt, lbl);
286 ig.Emit (OpCodes.Ldc_I4_1);
287 ig.Emit (OpCodes.Call, typeof (Convert).GetMethod ("ToBoolean", new Type [] {typeof (object), typeof (bool)}));
288 ig.Emit (OpCodes.Brfalse, lbl);
294 static void ft_emit_equality (EmitContext ec, AST ast, Label lbl)
296 ILGenerator ig = ec.ig;
301 else if (ast is StrictEquality)
302 eq = (StrictEquality) ast;
306 case JSToken.NotEqual:
307 case JSToken.StrictNotEqual:
308 ig.Emit (OpCodes.Brtrue, lbl);
311 case JSToken.StrictEqual:
312 ig.Emit (OpCodes.Brfalse, lbl);
317 internal static void fall_true (EmitContext ec, AST ast, Label lbl)
319 Type type = ast.GetType ();
321 if (type == typeof (Expression)) {
322 Expression exp = ast as Expression;
323 AST last_exp = (AST) exp.exprs [exp.exprs.Count - 1];
324 if (exp.exprs.Count >= 2)
326 fall_true (ec, last_exp, lbl);
327 } else if (type == typeof (Binary))
328 ft_binary_recursion (ec, ast, lbl);
329 else if (type == typeof (Equality) || type == typeof (StrictEquality))
330 ft_emit_equality (ec, ast, lbl);
331 else if (type == typeof (Relational))
332 ft_emit_relational (ec, (Relational) ast, lbl);
334 emit_default_case (ec, ast, OpCodes.Brfalse, lbl);
337 static void ff_emit_relational (EmitContext ec, AST ast, Label lbl)
339 ILGenerator ig = ec.ig;
340 Relational r = ast as Relational;
346 case JSToken.LessThan:
347 opcode = OpCodes.Blt;
350 case JSToken.GreaterThan:
351 opcode = OpCodes.Bgt;
354 case JSToken.LessThanEqual:
355 opcode = OpCodes.Ble;
358 case JSToken.GreaterThanEqual:
359 opcode = OpCodes.Bge;
363 throw new Exception ("unexpected token");
366 ig.Emit (OpCodes.Ldc_I4_0);
367 ig.Emit (OpCodes.Conv_R8);
368 ig.Emit (opcode, lbl);
371 static void ft_emit_relational (EmitContext ec, Relational re, Label lbl)
373 ILGenerator ig = ec.ig;
381 case JSToken.LessThan:
382 opcode = OpCodes.Bge_Un;
385 case JSToken.GreaterThan:
386 opcode = OpCodes.Ble_Un;
389 case JSToken.LessThanEqual:
390 opcode = OpCodes.Bgt_Un;
393 case JSToken.GreaterThanEqual:
394 opcode = OpCodes.Blt_Un;
398 Console.WriteLine (re.Location.LineNumber);
399 throw new NotImplementedException ();
402 ig.Emit (OpCodes.Ldc_I4_0);
403 ig.Emit (OpCodes.Conv_R8);
404 ig.Emit (opcode, lbl);
407 static void ff_binary_recursion (EmitContext ec, AST ast, Label lbl)
409 ILGenerator ig = ec.ig;
410 Binary b = ast as Binary;
413 case JSToken.LogicalOr:
414 fall_false (ec, b.left, lbl);
415 fall_false (ec, b.right, lbl);
418 case JSToken.LogicalAnd:
419 Label ftLb = ig.DefineLabel ();
420 fall_true (ec, b.left, ftLb);
421 fall_false (ec, b.right, lbl);
427 static void ff_emit_equality_cond (EmitContext ec, AST ast, Label lbl)
429 ILGenerator ig = ec.ig;
430 Equality eq = ast as Equality;
434 case JSToken.NotEqual:
436 ig.Emit (OpCodes.Brfalse, lbl);
441 internal static void fall_false (EmitContext ec, AST ast, Label lbl)
443 Type type = ast.GetType ();
445 if (type == typeof (Expression)) {
446 Expression exp = ast as Expression;
451 AST last_exp = (AST) exp.exprs [exp.exprs.Count - 1];
453 if (last_exp is Relational)
454 ff_emit_relational (ec, last_exp, lbl);
455 else if (last_exp is Binary)
456 ff_binary_recursion (ec, last_exp, lbl);
457 else if (last_exp is Identifier || last_exp is BooleanConstant)
458 emit_default_case (ec, last_exp, OpCodes.Brtrue, lbl);
459 else if (last_exp is Equality)
460 ff_emit_equality_cond (ec, last_exp, lbl);
462 Console.WriteLine ("WARNING: fall_false, last_exp.GetType () == {0}, {1}", last_exp, ast.Location.LineNumber);
464 } else if (type == typeof (Binary))
465 ff_binary_recursion (ec, ast, lbl);
466 else if (type == typeof (Relational))
467 ff_emit_relational (ec, ast, lbl);
469 emit_default_case (ec, ast, OpCodes.Brtrue, lbl);
472 internal static void emit_to_boolean (AST ast, ILGenerator ig, int i)
474 ig.Emit (OpCodes.Ldc_I4, i);
475 ig.Emit (OpCodes.Call, typeof (Convert).GetMethod ("ToBoolean",
476 new Type [] { typeof (object), typeof (Boolean)}));
479 internal static bool need_convert_to_boolean (AST ast)
484 if (ast is Identifier)
486 else if (ast is Expression) {
487 Expression exp = ast as Expression;
488 int n = exp.exprs.Count - 1;
489 AST tmp = (AST) exp.exprs [n];
490 if (tmp is Equality || tmp is Relational || tmp is BooleanConstant)
499 // Loads a current VsaEngine
501 internal static void load_engine (bool in_function, ILGenerator ig)
504 // If we are in a function declaration at global level,
505 // we must load the engine associated to the current 'JScript N' instance,
506 // otherwise pick up the engine at second place in method's signature.
509 ig.Emit (OpCodes.Ldarg_1);
511 ig.Emit (OpCodes.Ldarg_0);
512 ig.Emit (OpCodes.Ldfld, typeof (ScriptObject).GetField ("engine"));
516 internal static void emit_get_default_this (ILGenerator ig, bool inFunction)
518 CodeGenerator.load_engine (inFunction, ig);
519 ig.Emit (OpCodes.Call, typeof (VsaEngine).GetMethod ("ScriptObjectStackTop"));
520 Type iact_obj = typeof (IActivationObject);
521 ig.Emit (OpCodes.Castclass, iact_obj);
522 ig.Emit (OpCodes.Callvirt, iact_obj.GetMethod ("GetDefaultThisObject"));
525 internal static object variable_defined_in_current_scope (string id)
527 return TypeManager.defined_in_current_scope (id);
530 internal static void load_local_vars (ILGenerator ig, bool inFunction)
533 Type stack_frame = typeof (StackFrame);
535 CodeGenerator.load_engine (inFunction, ig);
537 ig.Emit (OpCodes.Call, typeof (VsaEngine).GetMethod ("ScriptObjectStackTop"));
538 ig.Emit (OpCodes.Castclass, stack_frame);
539 ig.Emit (OpCodes.Ldfld, stack_frame.GetField ("localVars"));
541 object [] locals = TypeManager.CurrentLocals;
542 n = locals != null ? locals.Length : 0;
545 for (int i = 0; i < n; i++) {
547 if (local is LocalBuilder) {
548 ig.Emit (OpCodes.Dup);
549 ig.Emit (OpCodes.Ldc_I4, i);
550 ig.Emit (OpCodes.Ldloc, (LocalBuilder) local);
551 ig.Emit (OpCodes.Stelem_Ref);
554 ig.Emit (OpCodes.Pop);
557 internal static void locals_to_stack_frame (ILGenerator ig, int lexical_depth, int lexical_difference, bool inFunction)
559 CodeGenerator.emit_parents (inFunction, lexical_difference, ig);
560 ig.Emit (OpCodes.Dup);
562 Type stack_frame = typeof (StackFrame);
563 ig.Emit (OpCodes.Castclass, stack_frame);
564 ig.Emit (OpCodes.Ldfld, stack_frame.GetField ("localVars"));
566 DictionaryEntry [] locals = TypeManager.LocalsAtDepth (lexical_depth);
569 foreach (DictionaryEntry entry in locals) {
570 if (entry.Value is LocalBuilder) {
571 ig.Emit (OpCodes.Dup);
572 ig.Emit (OpCodes.Ldc_I4, i);
573 ig.Emit (OpCodes.Ldloc, (short) i++);
574 ig.Emit (OpCodes.Stelem_Ref);
577 ig.Emit (OpCodes.Pop);
579 // FIXME: what determine this?
581 ig.Emit (OpCodes.Call, typeof (ScriptObject).GetMethod ("GetParent"));
582 ig.Emit (OpCodes.Pop);
585 internal static void emit_parents (bool inFunction, int lexical_difference, ILGenerator ig)
587 CodeGenerator.load_engine (inFunction, ig);
588 ig.Emit (OpCodes.Call, typeof (VsaEngine).GetMethod ("ScriptObjectStackTop"));
589 for (int i = 0; i < lexical_difference; i++)
590 ig.Emit (OpCodes.Call, typeof (ScriptObject).GetMethod ("GetParent"));
593 internal static void EmitBox (ILGenerator ig, object obj)
598 Type box_type = GetBoxType (obj);
600 if (box_type == null)
603 ig.Emit (OpCodes.Box, box_type);
606 internal static void EmitConv (ILGenerator ig, Type type)
608 TypeCode tc = Type.GetTypeCode (type);
611 case TypeCode.Double:
612 ig.Emit (OpCodes.Conv_R8);
616 throw new NotImplementedException ();
620 private static Type GetBoxType (object obj)
622 if (obj is ByteConstant || obj is ShortConstant || obj is IntConstant)
624 else if (obj is LongConstant)
625 return typeof (long);
626 else if (obj is FloatConstant || obj is DoubleConstant)
627 return typeof (double);
628 else if (obj is BooleanConstant || obj is StrictEquality || obj is Equality)
629 return typeof (bool);
630 else if (obj is Unary) {
631 Unary unary = (Unary) obj;
632 JSToken oper = unary.oper;
633 AST operand = unary.operand;
635 if (oper == JSToken.Minus || oper == JSToken.Plus ||
636 oper == JSToken.Increment || oper == JSToken.Decrement ||
637 oper == JSToken.BitwiseNot)
638 return GetBoxType (operand);
639 else if (oper == JSToken.LogicalNot || oper == JSToken.Delete)
640 return typeof (bool);
641 } else if (obj is Identifier) {
642 Identifier id = (Identifier) obj;
643 string name = id.name.Value;
644 if (name == "NaN" || name == "Infinity")
645 return typeof (double);
646 } else if (obj is Binary) {
647 Binary bin = obj as Binary;
648 if (bin.AccessField && !bin.LateBinding) {
649 MemberInfo binding = bin.Binding;
650 MemberTypes member_type = binding.MemberType;
651 if (member_type == MemberTypes.Property)
652 return ((PropertyInfo) binding).PropertyType;
654 } else if (obj is Relational) {
655 Relational re = (Relational) obj;
656 if (re.op == JSToken.In)
657 return typeof (bool);
662 internal static void emit_default_value (ILGenerator ig, ParameterInfo param)
664 Type param_type = param.ParameterType;
666 if (param_type == typeof (Double))
667 ig.Emit (OpCodes.Ldc_R8, GlobalObject.NaN);
668 else if (param_type == typeof (object))
669 ig.Emit (OpCodes.Ldsfld, typeof (Missing).GetField ("Value"));
671 throw new NotImplementedException ();
674 internal static void EmitRelationalComp (ILGenerator ig, Relational re)
678 if (op == JSToken.Instanceof)
680 else if (op == JSToken.In) {
681 ig.Emit (OpCodes.Box, typeof (bool));
685 Label true_case = ig.DefineLabel ();
686 Label box_to_bool = ig.DefineLabel ();
688 ig.Emit (OpCodes.Ldc_I4_0);
689 ig.Emit (OpCodes.Conv_R8);
694 case JSToken.LessThan:
695 opcode = OpCodes.Blt;
698 case JSToken.LessThanEqual:
699 opcode = OpCodes.Ble;
702 case JSToken.GreaterThan:
703 opcode = OpCodes.Bgt;
706 case JSToken.GreaterThanEqual:
707 opcode = OpCodes.Bge;
711 Console.WriteLine (re.Location.LineNumber);
712 throw new NotImplementedException ();
715 ig.Emit (opcode, true_case);
716 ig.Emit (OpCodes.Ldc_I4_0);
717 ig.Emit (OpCodes.Br, box_to_bool);
718 ig.MarkLabel (true_case);
719 ig.Emit (OpCodes.Ldc_I4_1);
720 ig.MarkLabel (box_to_bool);
721 ig.Emit (OpCodes.Box, typeof (bool));
724 internal static string GetTypeName (string srcName)
726 return (string) source_file_to_type [srcName];
729 internal static void EmitAssignAsExp (EmitContext ec, AST ast)
731 Assign assign = (Assign) ast;
732 LocalBuilder builder = assign.EmitAndReturnBuilder (ec);
733 ec.ig.Emit (OpCodes.Ldloc, builder);