// Compile all methods not already compiled by above
 private void CompileMethods(Seq<JST.Statement> body, JST.NameSupply outerNameSupply)
 {
     switch (Env.CompilationMode)
     {
     case CompilationMode.Plain:
     case CompilationMode.Collecting:
         // Already compiled above
         return;
     case CompilationMode.Traced:
         foreach (var methodDef in
             Methods.Where(d => TypeTrace.Methods.Contains(d.MethodSignature)))
         {
             if (TypeTrace.Parent.Parent.Flavor == TraceFlavor.Remainder)
             {
                 // Compile method into stand-alone loader
                 var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.SelfContained);
                 compiler.Emit(body, null);
             }
             else
             {
                 // Bind method definition into method cache
                 var target = TypeDefinitionId.ToE();
                 if (TyconEnv.Type.Arity == 0 && !Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef))
                     target = JST.Expression.Dot
                         (target, Constants.TypeConstructObject, Constants.prototype);
                 target = JST.Expression.Dot(target, Constants.TypeMethodCache);
                 var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind);
                 compiler.Emit(body, target);
             }
         }
         break;
     default:
         throw new ArgumentOutOfRangeException();
     }
 }
        // ----------------------------------------------------------------------
        // Methods
        // ----------------------------------------------------------------------

        // Emit bindings for static or instance methods, but not for virtuals or interface methods
        //  - Invoked from TypeDefinitionCompiler for higher-kinded type definitions
        //  - Invoked from TypeCompiler for first-kinded type definitions
        public void EmitMethods(Seq<JST.Statement> body, JST.Expression lhs, JST.NameSupply outerNameSupply, JST.Expression target, bool isStatic)
        {
            switch (Env.CompilationMode)
            {
                case CompilationMode.Plain:
                {
                    // Method definitions are bound directly into target
                    foreach (var methodDef in Methods.Where(m => m.Invalid == null))
                    {
                        if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic)
                        {
                            var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind);
                            compiler.Emit(body, target);
                        }
                    }
                    break;
                }
            case CompilationMode.Collecting:
                {
                    // Method definitions are bound into MethodCache, redirectors are bound into target
                    foreach (var methodDef in Methods.Where(m => m.Invalid == null))
                    {
                        if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic)
                        {
                            var slot = Env.GlobalMapping.ResolveMethodDefToSlot(TyconEnv.Assembly, TyconEnv.Type, methodDef);
                            var methodName = CST.CSTWriter.WithAppend
                                (Env.Global, CST.WriterStyle.Uniform, methodDef.MethodSignature.Append);
                            body.Add
                                (JST.Statement.DotCall
                                     (RootId.ToE(),
                                      Constants.RootCollectingBindMethodBuilder,
                                      lhs,
                                      new JST.BooleanLiteral(isStatic),
                                      new JST.StringLiteral(slot),
                                      new JST.StringLiteral(methodName)));
                            var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind);
                            compiler.Emit(body, JST.Expression.Dot(target, Constants.TypeMethodCache));
                        }
                    }
                    break;
                }
            case CompilationMode.Traced:
                {
                    // Methods in the initial trace or this trace will be bound directly.
                    // Methods in a trace other than above are bound via builder which is given trace name.
                    // Remaining methods are built via builder with null trace name.
                    var traceToArgs = new Map<string, Seq<JST.Expression>>();
                    var remainingArgs = new Seq<JST.Expression>();
                    remainingArgs.Add(TypeDefinitionId.ToE());
                    remainingArgs.Add(new JST.BooleanLiteral(isStatic));
                    remainingArgs.Add(new JST.NullExpression());
                    foreach (var methodDef in Methods.Where(m => m.Invalid == null))
                    {
                        if (Env.InteropManager.IsStatic(TyconEnv.Assembly, TyconEnv.Type, methodDef) == isStatic)
                        {
                            var slot = Env.GlobalMapping.ResolveMethodDefToSlot(TyconEnv.Assembly, TyconEnv.Type, methodDef);
                            var defTrace = Env.Traces.MethodToTrace[methodDef.QualifiedMemberName(Env.Global, TyconEnv.Assembly, TyconEnv.Type)];
                            if (defTrace.Flavor == TraceFlavor.OnDemand && defTrace != TypeTrace.Parent.Parent)
                            {
                                // Method definition in in another trace, bind redirector for it.
                                var args = default(Seq<JST.Expression>);
                                if (!traceToArgs.TryGetValue(defTrace.Name, out args))
                                {
                                    args = new Seq<JST.Expression>();
                                    args.Add(lhs);
                                    args.Add(new JST.BooleanLiteral(isStatic));
                                    args.Add(new JST.StringLiteral(defTrace.Name));
                                    traceToArgs.Add(defTrace.Name, args);
                                }
                                args.Add(new JST.StringLiteral(slot));
                            }
                            else if (defTrace.Flavor == TraceFlavor.Remainder)
                                // Method definition is in a stand-alone loader, bind redirector for it.
                                remainingArgs.Add(new JST.StringLiteral(slot));
                            else
                            {
                                // Method definition is bound directly
                                var compiler = new MethodCompiler(this, outerNameSupply, methodDef, MethodCompilationMode.DirectBind);
                                compiler.Emit(body, target);
                            }
                        }
                    }
                    foreach (var kv in traceToArgs)
                        body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootBindMethodBuilders, kv.Value));
                    if (remainingArgs.Count > 3)
                        body.Add(JST.Statement.DotCall(RootId.ToE(), Constants.RootBindMethodBuilders, remainingArgs));
                    break;
                }
            default:
                throw new ArgumentOutOfRangeException();
            }
        }