using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Security.Cryptography;
using System.Text;
using ElementTypes = Bartok.MSIL.ElementTypes;
using Bartok.MSIL;

public class ilgen: ITrackable {
    public static compilation visit(compilation ast, TextWriter w, string[] args, MessageWriter msg) {
        if (msg.Count == 0)
            new ilgen(w, msg).compilation(ast);
        return ast;
    }

    public Stack asts = new Stack();    // top element is current ast node
    public string mainTypeName = null;
    public StringDictionary moduleMap = new StringDictionary();
    public MessageWriter msg;
    public TextWriter wr;
    public BuiltinTypes T;
    public ilgen(TextWriter wr, compilation_unit ast) : this(wr, new MessageWriter()) {
        T = ((compilation)ast.parent).global.Types;
    }
    public ilgen(TextWriter wr, MessageWriter msg) {
        this.wr = wr;
        this.msg = msg;
    }

    // ITrackable
    public object subject {
        get { return asts.Count > 0 ? asts.Peek() : null; }
    }

    public virtual ILState NewILState(ilgen parent) {
        return new ILState(parent);
    }
    public class ILState: gen {
        protected ilgen parent;
        protected int depth = 0;
        public int maxstack = 0;
        protected int offset = 0;
        protected TextWriter w;

        public ILState(ilgen parent) : base(parent.T, parent.msg, parent.asts) {
            this.parent = parent;
            this.w = parent.wr;
        }
        override public object BeginTry() {
            Emit(".try {{");
            return base.BeginTry();
        }
        override public object BeginFinally(object handle) {
            base.BeginFinally(handle);
            Emit("}}");
            Emit("finally {{");
            return -(int)handle;
        }
        override public void boolean_value(binary_expression ast) {
            base.boolean_value(ast);
            if (!(ast.method != null && ast.method.Type == parent.T.Bool && ast.method.declSpace != null))
                Depth -= 1; // account for two loads in boolean_value
        }
        override public void catch_clause(Type ty, Local sym, statement block, object handle) {
            gotoLabel(OpCodes.Leave, (int)handle);
            Emit("}}");
            Emit("catch {0} {{", ClassRef(ty));
            Depth += 1;
            base.catch_clause(ty, sym, block, handle);
        }
        string ClassRef(Type t) { return parent.ClassRef(t); }
        override public void defLabel(int lab) {
            Emit("{0}:", getLabel(lab));
        }
        public int Depth {
            get { return depth; }
            set {
                //w.WriteLine("//depth={0}", value);
                depth = value;
                if (depth < 0) {
                    Debug.Assert(depth >= 0);
                    depth = 0;
                }
                maxstack = Math.Max(depth, maxstack);
            }
        }
        public void Emit(string fmt, params object[] operands) {
            w.WriteLine(fmt, operands);
        }
        public void Emit(OpCode op, params string[] operands) {
            w.Write(op);
            foreach (string s in operands)
                w.Write(" {0}", s);
            w.WriteLine();
            switch (op.StackBehaviourPop) {
            case StackBehaviour.Pop0: break;
            case StackBehaviour.Varpop: break;
            case StackBehaviour.Popref:
            case StackBehaviour.Popi:
            case StackBehaviour.Pop1:       Depth -= 1; break;
            case StackBehaviour.Pop1_pop1:
            case StackBehaviour.Popi_pop1:
            case StackBehaviour.Popi_popi:
            case StackBehaviour.Popi_popi8:
            case StackBehaviour.Popi_popr4:
            case StackBehaviour.Popi_popr8:
            case StackBehaviour.Popref_pop1:
            case StackBehaviour.Popref_popi:    Depth -= 2; break;
            case StackBehaviour.Popi_popi_popi:
            case StackBehaviour.Popref_popi_popi:
            case StackBehaviour.Popref_popi_popi8:
            case StackBehaviour.Popref_popi_popr4:
            case StackBehaviour.Popref_popi_popr8:
            case StackBehaviour.Popref_popi_popref: Depth -= 3; break;
            default: throw new ArgumentException("unknown opcode " + op.ToString());
            }
            switch (op.StackBehaviourPush) {
            case StackBehaviour.Varpush: break;
            case StackBehaviour.Push0: break;
            case StackBehaviour.Pushi:
            case StackBehaviour.Pushi8:
            case StackBehaviour.Pushr4:
            case StackBehaviour.Pushr8:
            case StackBehaviour.Pushref:
            case StackBehaviour.Push1: Depth += 1; break;
            case StackBehaviour.Push1_push1: Depth += 2; break;
            default: throw new ArgumentException("unknown opcode " + op.ToString());
            }
        }
        override public void Emit(OpCode op) { Emit(op, new string[0]); }
        override public void Emit(OpCode op, Constructor x) { Emit(op, (Method)x); }
        override public void Emit(OpCode op, float x) { Emit(op, x.ToString()); }
        override public void Emit(OpCode op, double x) { Emit(op, x.ToString()); }
        override public void Emit(OpCode op, Field x) {
            Emit(op, Name(x.Type), FieldRef(x));
        }
        override public void Emit(OpCode op, Formal x) { Emit(op, (Local)x); }
        override public void Emit(OpCode op, int x) { Emit(op, x.ToString()); }
        override public void Emit(OpCode op, Local x) {
            Emit(op, String.Format("{0} // {1}", x.ordinal, x.Name));
        }
        override public void Emit(OpCode op, Method x) {
            Emit(op, x.IsInstance ? "instance" : "", Name(x.Type), MethodRef(x));
            if (op.Name == "newobj")
                Depth -= x.ArgCount;
            else if (op.Name.StartsWith("call")) {
                Depth -= x.ArgCount + (x.IsInstance ? 1 : 0);
                if (x.Name == "set_Item")
                    Depth -= 1; // value argument
                if (x.Type != parent.T.Void)
                    Depth += 1;
            }
        }
        override public void Emit(OpCode op, string x) { Emit(op, quote(x)); }
        override public void Emit(OpCode op, Type x) { Emit(op, Name(x)); }
        override public void EmitCreateArray(ArrayType ty, Type ety, int rank) {
            Emit(OpCodes.Newobj, "instance void",
                String.Format("{0}::.ctor({1})", Name(ty), repl(",int32", rank).Substring(1)));
            Depth -= rank;
        }
        override public void EmitLoad(int index) {
            Emit(OpCodes.Ldloc, String.Format("{0} // t{0}", index));
        }
        override public void EmitLoadAddress(int index) {
            Emit(OpCodes.Ldloca, String.Format("{0} // t{0}", index));
        }
        override public void EmitLoadElement(ArrayType ty, Type ety, int rank) {
            Emit(OpCodes.Call, "instance", Name(ety),
                String.Format("{0}::Get({1})", Name(ty), repl(",int32", rank).Substring(1)));
            Depth -= rank + 1;
            Depth += 1;
        }
        override public void EmitLoadElementAddress(ArrayType ty, Type ety, int rank) {
            Emit(OpCodes.Call, "instance", Name(ety), "&",
                String.Format("{0}::Address({1})", Name(ty), repl(",int32", rank).Substring(1)));
            Depth -= rank + 1;
            Depth += 1;
        }
        override public void EmitStore(int index) {
            Emit(OpCodes.Stloc, String.Format("{0} // t{0}", index));
        }
        override public void EmitStoreElement(ArrayType ty, Type ety, int rank) {
            Emit(OpCodes.Call, "instance void",
                String.Format("{0}::Set({1}{2})", Name(ty), repl("int32,", rank), Name(ety)));
            Depth -= rank + 2;
        }
        override public void EmitSwitch(int[] caselabels) {
            Emit(OpCodes.Switch, "(");
            for (int i = 0; i < caselabels.Length; i++)
                Emit("{0},", getLabel(caselabels[i]));
            Emit(")");
        }
        override public void EndTry(object handle) {
            if ((int)handle < 0) {
                Emit("endfinally");
                handle = -(int)handle;
            } else
                gotoLabel(OpCodes.Leave, (int)handle);
            Emit("}} // end .try");
            base.EndTry(handle);
        }
        string FieldRef(Field f) { return parent.FieldRef(f); }
        public string getLabel(int lab) {
            return String.Format("${0}", lab);
        }
        override public void gotoLabel(OpCode inst, int lab) {
            Emit(inst, getLabel(lab));
        }
        string MethodRef(Method m) { return parent.MethodRef(m); }
        string Name(Type ty) { return parent.Name(ty); }
        override public int newLocal(Type ty) {
            return newLocal(ty, String.Format("t{0}", offset));
        }
        override public int newLocal(Type ty, string name) {
            int var = offset++;
            Emit(".locals init ([{0}]{1} '{2}')", var, Name(ty), name);
            return var;
        }
        public static string quote(string str) {
            return '"' + str.Replace("\"", "\\\"") + '"';
        }
        public static string repl(string str, int n) {
            string s = "";
            while (n-- > 0)
                s += str;
            return s;
        }
        override public void statement(statement ast, Method m) {
            base.statement(ast, m);
            if (m.Type != parent.T.Void)
                Depth -= 1;
        }
        override public void string_literal(string_literal ast) {
            Emit(OpCodes.Ldstr, ast.token.str);
        }
    }
    public virtual void Assemble(stringList argv, string input, string output, bool verbose, bool debug) {
        Process p = new Process();
        p.StartInfo.UseShellExecute = false;
        string ilasm = "ilasm";
        foreach (string arg in argv) {
            if (arg.StartsWith("/ilasm:") || arg.StartsWith("-ilasm:")) {
                ilasm  = arg.Substring("/ilasm:".Length);
            }
        }
        
        p.StartInfo.FileName = ilasm;
        p.StartInfo.Arguments = String.Format("/nologo /quiet {0} /{1} /noautoinherit /output=\"{2}\" \"{3}\"",
                                              debug ? "/pdb" : "",
                                              output.ToLower().EndsWith(".exe") ? "exe" : "dll",
                                              output,
                                              input);
        if (verbose)
            Console.Error.WriteLine("{0} {1}", p.StartInfo.FileName, p.StartInfo.Arguments);
        p.Start();
        p.WaitForExit();
        if (p.ExitCode != 0)
        {
            msg.Error("ilasm failed, command line: \"{0}\" {1}", p.StartInfo.FileName, p.StartInfo.Arguments);
        }
    }
    public virtual void class_declaration(class_declaration ast) {
        EmitClassHead(ast.sym);
        WriteLine("{{");
        EmitClassBody(ast.attrs, ast.body);
        WriteLine("}} // end of class {0}", ast.sym.FullName);
    }
    public string ClassRef(Type ty) {
        string module = "";
        if (ty.module != null)
            module = String.Format("[{0}]", ModuleName(ty.module));
        string name = "";
        if (ty.RealName.StartsWith("System.") && ty.enclosingType == null)
            name = ty.RealName;
        else {
            // quoting syntax for nested types has changed
            // correct 2.0 syntax is 'namespace-name'.'outer-class-name'/'inner-class-name' 
            // incorrect 1.0 syntax is 'namespace-name.outer-class-name/inner-class-name'
            for ( ; ty.enclosingType != null; ty = ty.enclosingType)
                name = "/'" + ty.Name + "'" + name;
            name = "'" + ty.FullName + "'" + name;
        }
        return module + name;
    }
    public virtual void comment(AST ast) {
        if (ast != null) {
            Write("// {0}: ", ast.begin.ToString());
            source.visit((compilation)ast, wr, null, msg);
            WriteLine();
        }
    }
    public virtual void comment(AST ast, string fmt, params string[] args) {
        if (ast != null) {
            Write("// {0}: ", ast.begin.ToString());
            WriteLine(fmt, args);
        }
    }
    public virtual void compilation(compilation ast) {
        T = ast.global.Types;
        string target = ".exe", outdir = "", oname = null, iname = null;
        foreach (string s in ast.args)
            if (s[0] == '/' || s[0] == '-') {
                string arg = s.Substring(1);
                if (arg.StartsWith("out:")) {
                    oname = arg.Substring(4);
                    iname = Path.GetFileNameWithoutExtension(oname) + ".il";
                } else if (arg.StartsWith("outdir:"))
                    outdir = arg.Substring(7).TrimEnd('/', '\\') + "\\";
                else if (arg.StartsWith("main:") || arg.StartsWith("m:"))
                    mainTypeName = arg.Substring(arg.IndexOf(':')+1);
                else if (arg.StartsWith("target:") || arg.StartsWith("t:"))
                    switch (arg.Substring(arg.IndexOf(':')+1).ToLower()) {
                    case "winexe": case "exe": target = ".exe"; break;
                    case "library": target = ".dll"; break;
                    }
                else if (arg.StartsWith("r:") || arg.StartsWith("reference:")) {
                    foreach (string f in arg.Substring(arg.IndexOf(':')+1).Split(','))
                        if (f.IndexOf('=') >= 0) {
                            string[] map = f.Split('=');
                            map[0] = Path.GetFileNameWithoutExtension(map[0]);
                            moduleMap[map[0]] = map[1];
                        }
                }
            } else if ((s.EndsWith(".cs") || s.EndsWith(".csi")) && iname == null)
                iname = Path.GetFileNameWithoutExtension(s) + ".il";
        if (oname == null && iname == null)
            oname = "a" + target;
        else {
            if (oname == null)
                oname = Path.GetFileNameWithoutExtension(iname) + target;
            iname = outdir + iname;
        }
        try {
            wr.Close();
            wr = new StreamWriter(iname);
        } catch (Exception e) {
            msg.Error("can't write \"{0}\": {1}", iname, e.Message);
            iname = null;
        }
        try {
            SHA1 sha = new SHA1CryptoServiceProvider();
            foreach (string path in ast.assemblyRefs) {
                PELoader pe = new PELoader(path);
                MetaData md = MetaData.loadMetaData(path, pe, false, false);
                MetaDataAssembly mda = (MetaDataAssembly)md.Assemblies[0];

                WriteLine(".assembly extern '{0}' {{", ModuleName(path));
                WriteLine(".ver {0}:{1}:{2}:{3}",
                          mda.MajorVersion,
                          mda.MinorVersion,
                          mda.BuildNumber,
                          mda.RevisionNumber);

                byte[] hash = sha.ComputeHash(mda.PublicKey);
                Write(".publickeytoken = (");
                int len = hash.Length - 1;
                for (int i = 0; i < 8; i++)
                    Emitint(1, (ulong)hash[len - i]);
                WriteLine(" )");

#if VERIFY_HASH_USING_REFLECTION
                try {
                    Assembly asm = Assembly.LoadFrom(path);
                    AssemblyName name = asm.GetName();
                    WriteLine("// .assembly extern '{0}' {{", ModuleName(asm.Location));
                    WriteLine("// .ver {0}", name.Version.ToString().Replace('.',':'));
                    byte[] bytes = name.GetPublicKeyToken();
                    if (bytes != null) {
                        Write("// .publickeytoken = (");
                        for (int i = 0; i < bytes.Length; i++)
                            Emitint(1, (ulong)bytes[i]);
                        WriteLine(" )");
                    }
                }
                catch {
                }
#endif

#if false
                foreach (MetaData md in resolver.MetaDataList) {
                    MetaDataAssembly mda = (MetaDataAssembly)md.Assemblies[0];
                }
                XmlNode asm = manifest.CreateNode(XmlNodeType.Element, "assembly", "");
                AddAttribute(asm, "filename", assemblyname);
                if (mda.MajorVersion != 0 || mda.MinorVersion != 0 ||
                    mda.BuildNumber != 0 || mda.RevisionNumber != 0) {
                    AddAttribute(asm, "version", String.Format("{0}.{1}.{2}.{3}",
                                                               mda.MajorVersion,
                                                               mda.MinorVersion,
                                                               mda.BuildNumber,
                                                               mda.RevisionNumber));
                }
                if (mda.PublicKey != null & mda.PublicKey.Length > 0) {
                    AddAttribute(asm, "publickey", String.Format("{0}",
                                                                 KeyToString(mda.PublicKey)));
                }
                if (mda.Locale != null && mda.Locale != "") {
                    AddAttribute(asm, "locale", mda.Locale);
                }
                AddAttribute(asm, "cache", StripCacheFromPath(file));
                assembliesNode.AppendChild(asm);
#endif

                WriteLine("}}");
            }
            WriteLine(".assembly '{0}' {{", Path.GetFileNameWithoutExtension(oname));

            object o;
            o = Evalattribute(ast.inputs, "System.Reflection.AssemblyVersionAttribute");
            if (o != null && o is String) {
                WriteLine(".ver {0}", ((string)o).Replace('.',':'));
            }
            else {
                WriteLine(".ver 0:0:0:0");
            }

            o = Evalattribute(ast.inputs, "System.Reflection.AssemblyKeyFileAttribute");
            if (o != null && o is String) {
                string file = outdir + (string)o;
                WriteLine("//publickey({0})", file);
                try {
                    FileStream fs = new FileStream(outdir + (string)o,
                                                   FileMode.Open, FileAccess.Read);
                    BinaryReader r = new BinaryReader(fs);
                    byte[] values = r.ReadBytes(0x10000);
                    if (values != null) {
                        Write(".publickey = (", file);
                        int len = values.Length;
                        for (int i = 0; i < len; i++) {
                            Write(" {0:x2}", values[i]);
                            if (i % 16 == 15) {
                                WriteLine();
                                Write("              ");
                            }
                        }
                        WriteLine(" )");
                    }
                    r.Close();
                    if (values.Length >= 8) {
                        WriteLine(".hash algorithm 0x{0:x2}{1:x2}{2:x2}{3:x2}",
                                  values[7],
                                  values[6],
                                  values[5],
                                  values[4]);
                    }
                }
                catch (Exception e) {
                    Console.WriteLine("Couldn't access public key {0}", file);
                    Console.WriteLine("Exception: {0}", e);
                }
            }

            foreach (compilation_unit x in ast.inputs)
                compilation_unit_attr(x);
            WriteLine("}}");

            foreach (compilation_unit x in ast.inputs)
                compilation_unit(x);
        } finally {
            if (wr != Console.Out)
                wr.Close();
        }
        if (iname != null && msg.Count == 0)
            Assemble(ast.args, iname, oname, false, true);
    }

    public virtual object Evalattribute(compilation_unitList units, string name) {
        if (units != null) {
            foreach (compilation_unit x in units) {
                if (x.attributes != null) {
                    foreach (attribute_section s in x.attributes) {
                        foreach (attribute a in s.attributes) {
                            if (a.sym.RealName == name) {
                                foreach (argument n in a.arguments.args) {
                                    return n.expr.value;
                                }
                                return "false";
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

    public virtual void compilation_unit_attr(compilation_unit ast) {
        EmitattributeSectionList(ast.attributes);
    }
    public virtual void compilation_unit(compilation_unit ast) {
        foreach (declaration d in ast.declarations)
            declaration(d);
    }
    public virtual void constant_declaration(constant_declaration ast) {
        foreach (const_declarator x in ast.decls) {
            Constant f = x.sym;
            Write(".field ");
            EmitModifiers(f);
            Write("static literal ");
            Write("{0} '{1}' = ", Name(f.Type), f.Name);
            if (f.value is string)
                WriteLine("{0}", ILState.quote((string)f.value));
            else if (f.Type is EnumType)
                EmitDataItem(((EnumType)f.Type).baseType, f.value);
            else
                EmitDataItem(f.Type, f.value);
        }
    }
    public virtual void constructor_declaration(constructor_declaration ast) {
        Method m = ast.sym;
        Write(".method hidebysig specialname rtspecialname ");
        EmitModifiers(m);
        WriteLine("{0} {1}({2}) {3}{{", Name(m.Type), Name(m), Signature(m),
             ast.block == null && m.declSpace.Owner is DelegateType ? "runtime managed " : "");
        EmitattributeSectionList(ast.attrs);
        declarationList body;
        if (ast.parent is class_declaration)
            body = ((class_declaration)ast.parent).body;
        else
            body = ((struct_declaration)ast.parent).body;
        ILState g = NewILState(this);
        if (ast.decl.init != null)
            constructor_initializer(ast.decl.init, body, g);
        if (m.IsStatic)
            foreach (declaration d in body)
                if (d is field_declaration) {
                    foreach (field_declarator f in ((field_declaration)d).decls)
                        if (f.sym.IsStatic)
                            variable_initializer(f.init, f.sym, g);
                } else if (d is event_declaration1)
                    foreach (event_declarator e in ((event_declaration1)d).decls)
                        if (e.sym.IsStatic)
                            variable_initializer(e.init, e.sym, g);
        int i;
        for (i = 0; i < m.ArgCount; i++)
            m[i].ordinal = m.IsInstance ? i + 1 : i;
        if (ast.block != null)
            EmitMethodBody(m, ast.block, g);
        WriteLine("}} // end of constructor {0}", m.FullName);
    }
    public virtual void constructor_initializer(constructor_initializer ast, declarationList body, ILState g) {
        if (ast is base_initializer) {
            foreach (declaration d in body)
                if (d is field_declaration) {
                    foreach (field_declarator f in ((field_declaration)d).decls)
                        if (f.sym.IsInstance)
                            variable_initializer(f.init, f.sym, g);
                } else if (d is event_declaration1)
                    foreach (event_declarator e in ((event_declaration1)d).decls)
                        if (e.sym.IsInstance)
                        variable_initializer(e.init, e.sym, g);
            g.base_initializer((base_initializer)ast);
        } else
            g.this_initializer((this_initializer)ast);
    }
    public virtual void declaration(declaration ast) {
        asts.Push(ast);
             if (ast is class_declaration) class_declaration((class_declaration)ast);
        else if (ast is constant_declaration) constant_declaration((constant_declaration)ast);
        else if (ast is constructor_declaration) constructor_declaration((constructor_declaration)ast);
        else if (ast is delegate_declaration) delegate_declaration((delegate_declaration)ast);
        else if (ast is destructor_declaration) destructor_declaration((destructor_declaration)ast);
        else if (ast is enum_declaration) enum_declaration((enum_declaration)ast);
        else if (ast is event_declaration1) event_declaration1((event_declaration1)ast);
        else if (ast is event_declaration2) event_declaration2((event_declaration2)ast);
        else if (ast is field_declaration) field_declaration((field_declaration)ast);
        else if (ast is indexer_declaration) indexer_declaration((indexer_declaration)ast);
        else if (ast is interface_declaration) interface_declaration((interface_declaration)ast);
        else if (ast is interface_event_declaration) interface_event_declaration((interface_event_declaration)ast);
        else if (ast is interface_indexer_declaration) interface_indexer_declaration((interface_indexer_declaration)ast);
        else if (ast is interface_method_declaration) interface_method_declaration((interface_method_declaration)ast);
        else if (ast is interface_property_declaration) interface_property_declaration((interface_property_declaration)ast);
        else if (ast is method_declaration) method_declaration((method_declaration)ast);
        else if (ast is namespace_declaration) namespace_declaration((namespace_declaration)ast);
        else if (ast is operator_declaration) operator_declaration((operator_declaration)ast);
        else if (ast is property_declaration) property_declaration((property_declaration)ast);
        else if (ast is struct_declaration) struct_declaration((struct_declaration)ast);
        else throw new ArgumentException();
        asts.Pop();
    }
    public virtual void delegate_declaration(delegate_declaration ast) {
        EmitClassHead(ast.sym);
        WriteLine("{{");
        EmitattributeSectionList(ast.attrs);
        foreach (Symbol s in ast.sym)
            if (s is Method)
                EmitMethod((Method)s, null, null, null);
        WriteLine("}} // end of delegate {0}", ast.sym.FullName);
    }
    public virtual void destructor_declaration(destructor_declaration ast) {
        EmitMethod(ast.sym, ast.attrs, ast.block, "specialname rtspecialname");
    }
    public virtual void Emitattribute(attribute ast) {
        if (ast.arguments != null) {
            Write(".custom instance void {0}::.ctor({1}) = (", ClassRef(ast.sym),
                  Signature(ast.method, false));
            Emitint(1, 1UL); Emitint(1, 0UL);   // prolog 01 00
            foreach (argument x in ast.arguments.args) {
                WriteLine();
                EmitFixedArg(x.expr.value);
            }
            WriteLine();
            Emitint(2, (ulong)ast.arguments.namedargs.Count);
            foreach (named_argument x in ast.arguments.namedargs) {
                WriteLine();
                EmitNamedArg(x);
            }
        } else {
            Write(".custom instance void {0}::.ctor() = (", ClassRef(ast.sym));
            Emitint(1, 1UL); Emitint(1, 0UL);   // prolog 01 00
            Emitint(2, 0UL);    // no named arguments
        }
        WriteLine(" )");
    }
    public virtual void EmitattributeSectionList(attribute_sectionList attrs) {
        if (attrs != null)
            foreach (attribute_section s in attrs)
                foreach (attribute a in s.attributes)
                    Emitattribute(a);
    }
    public virtual void EmitClassHead(ClassType c) {
        Write(".class ");
        if (c.enclosingType != null)
            Write("nested ");
        EmitModifiers(c);
        if (c is InterfaceType)
            Write("interface abstract ");
        WriteLine("'{0}'", c.Name);
        if (c.baseClass != null)
            WriteLine("\textends {0}", ClassRef(c.baseClass));
        if (c.interfaces.Count > 0) {
            Write("\timplements ");
            for (int i = 0; i < c.interfaces.Count; i++) {
                if (i > 0)
                    Write(",");
                Write("{0}", ClassRef(c.interfaces[i]));
            }
            WriteLine();
        }
    }
    public virtual void EmitClassBody(attribute_sectionList attrs, declarationList body) {
        EmitattributeSectionList(attrs);
        foreach (declaration d in body)
            declaration(d);
    }
    public virtual void EmitDataItem(Type ty, object value) {
        string name = Name(ty);
        if (name.StartsWith("unsigned "))
            name = name.Substring(9);
        WriteLine("{0}({1})", name, value.ToString());
    }
    public virtual void EmitDefaultAccessor(Event e, Method m) {
        Write(".method hidebysig specialname ");
        EmitModifiers(m);
        if (m.IsInstance) {
            Write("instance ");
            m[0].ordinal = 1;
        } else
            m[0].ordinal = 0;
        WriteLine("void {0}({1} 'value') cil managed synchronized {{", Name(m), Name(m[0].Type));
        if (!m.Is("abstract")) {
            ILState g = NewILState(this);
            g.EmitDefaultAccessor(e, m);
            EmitMethodBody(m, null, g);
        }
        WriteLine("}} // end of method {0}", m.FullName);
    }
    public virtual void EmitEvent(Event t, event_accessorList accessors) {
        WriteLine(".event '{0}' '{1}' {{", t.Type.FullName, t.Name);
        WriteLine(".addon {0}void {1}", t.IsStatic ? "" : "instance ", MethodRef(t.Add));
        WriteLine(".removeon {0}void {1}", t.IsStatic ? "" : "instance ", MethodRef(t.Remove));
        WriteLine("}} // end of event {0}", t.FullName);
        if (accessors != null)
            foreach (event_accessor a in accessors)
                EmitMethod(a.sym, a.attrs, a.block, "specialname rtspecialname");
    }
    public virtual void EmitFixedArg(object value) {
        if (value == null)
            Emitint(1, 0xFFUL);
        else if (value is string)
            Emitstring((string)value);
        else if (value is char)
            Emitint(2, (ulong)(ushort)(char)value);
        else if (value is bool)
            Emitint(1, (bool)value ? 1UL : 0UL);
        else if (value is  sbyte) Emitint(1, (ulong)( sbyte)value);
        else if (value is   byte) Emitint(1, (ulong)(  byte)value);
        else if (value is  short) Emitint(2, (ulong)( short)value);
        else if (value is ushort) Emitint(2, (ulong)(ushort)value);
        else if (value is    int) Emitint(4, (ulong)(   int)value);
        else if (value is   uint) Emitint(4, (ulong)(  uint)value);
        else if (value is   long) Emitint(8, (ulong)(  long)value);
        else if (value is  ulong) Emitint(8, (ulong)        value);
        else
            msg.Error("attribute type '{0}' not yet supported", value.GetType().FullName);
    }
    public virtual void Emitint(int len, ulong value) {
        for (int i = 0; i < len; i++) {
            Write(" {0:x2}", value&0xff);
            value >>= 8;
        }
    }
    public virtual void EmitMethod(Method m, attribute_sectionList attrs, statement body, string extraattributes) {
        Write(".method hidebysig ");
        if (extraattributes != null)
            Write("{0} ", extraattributes);
        EmitModifiers(m);
        if (m.IsInstance)
            Write("instance ");
        WriteLine("{0} {1}({2}) {3}{{", Name(m.Type), Name(m), Signature(m),
            body == null && m.declSpace.Owner is DelegateType ? "runtime managed " : "");
        EmitattributeSectionList(attrs);
        if ((mainTypeName == null || mainTypeName == m.GetType().Name)
        && m.Name == "Main" && m.IsStatic)
            WriteLine(".entrypoint");
        if (m.interfaceMethod != null)
            WriteLine(".override {0}::'{1}'", ClassRef(m.interfaceMethod.GetType()),
                m.interfaceMethod.Name);
        int i;
        for (i = 0; i < m.ArgCount; i++)
            m[i].ordinal = m.IsInstance ? i + 1 : i;
        if (m.Name == "set_Item")
            ((Formal)m.locals["value"]).ordinal = m.IsInstance ? i + 1 : i;
        if (body != null)
            EmitMethodBody(m, body, NewILState(this));
        WriteLine("}} // end of method {0}", m.FullName);
    }
    public virtual void EmitMethodBody(Method m, statement body, ILState g) {
        if (body != null)
            g.statement(body, m);
        WriteLine(".maxstack {0}", g.maxstack);
        Debug.Assert(g.Depth == 0);
    }
    public virtual void EmitModifiers(Symbol t) {
        string emit = "";
        foreach (string mod in t.Modifiers)
            switch (mod) {
            case "new":
                emit += " newslot"; break;
            case "override":
                emit += " virtual"; break;
            case "protected":
            case "internal": case "readonly": case "unsafe":
                break;
            case "sealed":
                emit += t is Method ? " final" : " sealed"; break;
            default:
                emit += " " + mod;
                break;
            }
        if (emit.Length > 1)
            Write("{0} ", emit.Substring(1));
    }
    public virtual void EmitNamedArg(named_argument ast) {
        if (ast.sym is Field)
            Emitint(1, 0x53UL);
        else if (ast.sym is Property)
            Emitint(1, 0x54UL);
        Type ty = ast.sym.Type;
             if (ty == T.Bool)   Emitint(1, (ulong)ElementTypes.BOOLEAN);
        else if (ty == T.Byte)   Emitint(1, (ulong)ElementTypes.U1);
        else if (ty == T.Char)   Emitint(1, (ulong)ElementTypes.CHAR);
        else if (ty == T.Double) Emitint(1, (ulong)ElementTypes.R8);
        else if (ty == T.Short)  Emitint(1, (ulong)ElementTypes.I2);
        else if (ty == T.Int)    Emitint(1, (ulong)ElementTypes.I4);
        else if (ty == T.Long)   Emitint(1, (ulong)ElementTypes.I8);
        else if (ty == T.SByte)  Emitint(1, (ulong)ElementTypes.I1);
        else if (ty == T.Float)  Emitint(1, (ulong)ElementTypes.R4);
        else if (ty == T.String) Emitint(1, (ulong)ElementTypes.STRING);
        else if (ty == T.UShort) Emitint(1, (ulong)ElementTypes.U2);
        else if (ty == T.UInt)   Emitint(1, (ulong)ElementTypes.U4);
        else if (ty == T.ULong)  Emitint(1, (ulong)ElementTypes.U8);
        else Debug.Assert(false);
        Emitstring(ast.id.str);
        EmitFixedArg(ast.expr.value);
    }
    public virtual void EmitProperty(Property p, attribute_sectionList attrs, accessor_declarationList decls) {
        Write(".property ");
        if (p.IsInstance)
            Write("instance ");
        WriteLine("{1} {0}() {{",  p.Name, Name(p.Type));
        EmitattributeSectionList(attrs);
        foreach (accessor_declaration d in decls) {
            Write(".{0}", d.id.str);
            if (d.sym.IsInstance)
                Write(" instance");
            WriteLine(" {0} {1}", d.id.str == "get" ? Name(p.Type) : "void", MethodRef(d.sym));
        }
        WriteLine("}} // end of {0} {1}", p.Kind, p.FullName);
        foreach (accessor_declaration d in decls)
            EmitMethod(d.sym, d.attrs, d.body, "specialname");
    }
    public virtual void Emitstring(string value) {
        if (value.Length <= 127)
            Emitint(1, (ulong)value.Length);
        else if (value.Length >= 128 && value.Length <= 16383) {
            uint n = (uint)value.Length;
            Emitint(1, ((n>>8)&0x7fUL)|0x80UL);
            Emitint(1,       n&0xffUL);
        } else {
            uint n = (uint)value.Length;
            Emitint(1, ((n>>24)&0x1fUL)|0xc0UL);
            Emitint(1,  (n>>16)&0xffUL);
            Emitint(1,  (n>> 8)&0xffUL);
            Emitint(1,        n&0xffUL);
        }
        foreach (char c in value)
            Emitint(1, (ulong)(byte)c);
    }
    public virtual void enum_declaration(enum_declaration ast) {
        EnumType e = ast.sym;
        Write(".class ");
        if (e.enclosingType != null)
            Write("nested ");
        EmitModifiers(e);
        WriteLine("auto ansi serializable sealed '{0}' extends {1}", e.Name, ClassRef(T.Enum));
        WriteLine("{{");
        EmitattributeSectionList(ast.attrs);
        string name = "";
        Type ty;
        for (ty = e; ty.enclosingType != null; ty = ty.enclosingType)
            name = "/" + ty.Name + name;
        name = ty.FullName + name;
        foreach (enum_member_declaration d in ast.body) {
            Write(".field public static literal valuetype '{0}' {1} = ", name, d.sym.Name);
            EmitDataItem(e.baseType, d.sym.value);
        }
        WriteLine(".field public rtspecialname specialname {0} value__", Name(e.baseType));
        WriteLine("}} // end of enumeration {0}", e.FullName);
    }
    public virtual void event_declaration1(event_declaration1 ast) {
        EmitattributeSectionList(ast.attrs);
        foreach (event_declarator d in ast.decls) {
            WriteLine(".field private {2}{0} {1}", Name(d.sym.Type), d.sym.Name, d.sym.IsStatic ? "static " : "");
            EmitEvent(d.sym, null);
            EmitDefaultAccessor(d.sym, d.sym.Add);
            EmitDefaultAccessor(d.sym, d.sym.Remove);
        }
    }
    public virtual void event_declaration2(event_declaration2 ast) {
        EmitattributeSectionList(ast.attrs);
        EmitEvent(ast.sym, ast.accessors);
    }
    public virtual void field_declaration(field_declaration ast) {
        foreach (field_declarator x in ast.decls) {
            Field f = x.sym;
            Write(".field ");
            EmitModifiers(f);
            WriteLine("{0} {1}", Name(f.Type), Name(f));
            EmitattributeSectionList(ast.attrs);
        }
    }
    public string FieldRef(Symbol f) {
        return String.Format("{0}::'{1}'", ClassRef(f.GetType()), f.Name);
    }
    public virtual void indexer_declaration(indexer_declaration ast) {
        foreach (accessor_declaration d in ast.accessors)
            if (d.id.str == "get") {
                WriteLine(".property instance {0} Item({1}) {{", Name(d.sym.Type),
                    Signature(d.sym, false));
                break;
            }
        EmitattributeSectionList(ast.attrs);
        foreach (accessor_declaration d in ast.accessors) {
            Write(".{0} instance", d.id.str);
            WriteLine(" {0} {1}", Name(d.sym.Type), MethodRef(d.sym));
        }
        WriteLine("}} // end of property Item");
        foreach (accessor_declaration d in ast.accessors)
            EmitMethod(d.sym, d.attrs, d.body, "specialname");
    }
    public virtual void interface_declaration(interface_declaration ast) {
        EmitClassHead(ast.sym);
        WriteLine("{{");
        EmitClassBody(ast.attrs, ast.body);
        WriteLine("}} // end of interface {0}", ast.sym.FullName);
    }
    public virtual void interface_event_declaration(interface_event_declaration ast) {
        EmitattributeSectionList(ast.attrs);
        EmitEvent(ast.sym, null);
        EmitDefaultAccessor(ast.sym, ast.sym.Add);
        EmitDefaultAccessor(ast.sym, ast.sym.Remove);
    }
    public virtual void interface_indexer_declaration(interface_indexer_declaration ast) {
        foreach (accessor_declaration d in ast.accessors)
            EmitMethod(d.sym, d.attrs, d.body, "specialname");
    }
    public virtual void interface_method_declaration(interface_method_declaration ast) {
        EmitMethod(ast.sym, ast.attrs, null, null);
    }
    public virtual void interface_property_declaration(interface_property_declaration ast) {
        EmitProperty(ast.sym, ast.attrs, ast.accessors);
    }
    public string ModuleName(string path) {
        string name = Path.GetFileNameWithoutExtension(path).ToLower();
        if (moduleMap.ContainsKey(name))
            name = moduleMap[name];
        return name;
    }
    public virtual void method_declaration(method_declaration ast) {
        EmitMethod(ast.sym, ast.attrs, ast.body, null);
    }
    public string MethodRef(Method m) {
        ClassType ty = m.GetType();
        return String.Format("{0}::{1}({2})", ClassRef(ty), Name(m), Signature(m, false));
    }
    public static string Name(Symbol m) {
        if (m is Method && ((Method)m).GetType().Name == m.Name)
            return m.IsStatic ? ".cctor" : ".ctor";
        return String.Format("'{0}'", m.Name);
    }
    public string Name(Type ty) {
             if (ty == T.Bool)   return "bool";
        else if (ty == T.Byte)   return "unsigned int8";
        else if (ty == T.Char)   return "char";
        else if (ty == T.Double) return "float64";
        //else if (ty == T.Decimal)return "decimal";
        else if (ty == T.Short)  return "int16";
        else if (ty == T.Int)    return "int32";
        else if (ty == T.Long)   return "int64";
        else if (ty == T.Object) return "object";
        else if (ty == T.SByte)  return "int8";
        else if (ty == T.Float)  return "float32";
        else if (ty == T.String) return "string";
        else if (ty == T.UShort) return "unsigned int16";
        else if (ty == T.UInt)   return "unsigned int32";
        else if (ty == T.ULong)  return "unsigned int64";
        else if (ty == T.Void)   return "void";
        else if (ty == T.IntPtr) return "native int";
        else if (ty == T.UIntPtr)return "native unsigned int";
        else if (ty is ManagedPointerType)
            return String.Format("{0}&", Name(((PointerType)ty).elementType));
        else if (ty is UnmanagedPointerType)
            return String.Format("{0}*", Name(((PointerType)ty).elementType));
        else if (ty is ArrayType && ((ArrayType)ty).rank == 1)
            return String.Format("{0}[]", Name(((ArrayType)ty).elementType));
        else if (ty is ArrayType)
            return String.Format("{0}[{1}]", Name(((ArrayType)ty).elementType),
                ILState.repl(",0...", ((ArrayType)ty).rank).Substring(1));
        else if (ty is EnumType || ty is StructType)
            return String.Format("value class {0}", ClassRef(ty));
        else if (ty is ClassType)
            return String.Format("class {0}", ClassRef(ty));
        else
            throw new ArgumentException(ty.ToString());
    }
    public virtual void namespace_declaration(namespace_declaration ast) {
        WriteLine(".namespace {0} {{", ast.sym.FullName);
        foreach (declaration d in ast.nb.declarations)
            declaration(d);
        WriteLine("}} // end of namespace {0}", ast.sym.FullName);
    }
    public virtual void operator_declaration(operator_declaration ast) {
        EmitMethod(ast.op.method, ast.attrs, ast.block, "specialname");
    }
    public virtual void property_declaration(property_declaration ast) {
        EmitProperty(ast.sym, ast.attrs, ast.decls);
    }
    public string Signature(Method m, bool showNames) {
        Signature sig = m.signature;
        StringBuilder sb = new StringBuilder();
        int i;
        for (i = 0; i < sig.ArgCount; i++) {
            if (i > 0)
                sb.Append(',');
            Formal f = sig[i];
            if (f.modifier != null && f.modifier.str == "out")
                sb.Append("[out] ");
            sb.Append(Name(f.Type));
            if (f.modifier != null)
                sb.Append('&');
            if (showNames)
                sb.AppendFormat(" '{0}'", f.Name);
        }
        if (m.Name == "set_Item") {
            Formal f = (Formal)m.locals["value"];
            if (i > 0)
                sb.Append(',');
            sb.Append(Name(f.Type));
            if (showNames)
                sb.AppendFormat(" '{0}'", f.Name);
        }
        return sb.ToString();
    }
    public string Signature(Method m) {
        return Signature(m, true);
    }
    public virtual void struct_declaration(struct_declaration ast) {
        EmitClassHead(ast.sym);
        WriteLine("{{");
        EmitClassBody(ast.attrs, ast.body);
        WriteLine("}} // end of struct {0}", ast.sym.FullName);
    }
    public virtual void variable_initializer(variable_initializer ast, Symbol sym, ILState g) {
        if (ast != null) {
            if (sym.IsInstance)
                g.this_access();
            g.variable_initializer(ast);
            g.EmitStore(sym);
        }
    }
    public virtual void Write(string fmt, params object[] args) {
        wr.Write(fmt, args);
    }
    public virtual void WriteLine() {
        wr.WriteLine();
    }
    public virtual void WriteLine(string fmt, params object[] args) {
        wr.WriteLine(fmt, args);
    }
}
