/**
 * Copyright (c) 2012-2013 André Bargull
 * Alle Rechte vorbehalten / All Rights Reserved.  Use is subject to license terms.
 *
 * <https://github.com/anba/es6draft>
 */
package com.github.anba.es6draft.compiler;

import static com.github.anba.es6draft.semantics.StaticSemantics.BoundNames;
import static com.github.anba.es6draft.semantics.StaticSemantics.IsConstantDeclaration;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.objectweb.asm.Type;

import com.github.anba.es6draft.ast.Declaration;
import com.github.anba.es6draft.ast.FunctionDeclaration;
import com.github.anba.es6draft.ast.GeneratorDeclaration;
import com.github.anba.es6draft.compiler.InstructionVisitor.MethodDesc;
import com.github.anba.es6draft.compiler.InstructionVisitor.MethodType;

/**
 * <h1>10 Executable Code and Execution Contexts</h1><br>
 * <h2>10.5 Declaration Binding Instantiation</h2>
 * <ul>
 * <li>10.5.4 Block Declaration Instantiation
 * </ul>
 */
class BlockDeclarationInstantiationGenerator extends DeclarationBindingInstantiationGenerator {
    private static class Methods {
        // class: LexicalEnvironment
        static final MethodDesc LexicalEnvironment_getEnvRec = MethodDesc.create(
                MethodType.Virtual, Types.LexicalEnvironment, "getEnvRec",
                Type.getMethodType(Types.EnvironmentRecord));
    }

    BlockDeclarationInstantiationGenerator(CodeGenerator codegen) {
        super(codegen);
    }

    /**
     * stack: [env] -> [env]
     */
    void generate(Collection<Declaration> declarations, StatementVisitor mv) {
        /* steps 1-2 */
        List<Declaration> functionsToInitialize = new ArrayList<>();

        // stack: [env] -> [env, envRec]
        mv.dup();
        mv.invoke(Methods.LexicalEnvironment_getEnvRec);

        /* step 3 */
        for (Declaration d : declarations) {
            for (String dn : BoundNames(d)) {
                mv.dup();
                if (IsConstantDeclaration(d)) {
                    // FIXME: spec bug (CreateImmutableBinding concrete method of `env`)
                    createImmutableBinding(dn, mv);
                } else {
                    // FIXME: spec bug (CreateMutableBinding concrete method of `env`)
                    createMutableBinding(dn, false, mv);
                }
            }
            if (d instanceof FunctionDeclaration || d instanceof GeneratorDeclaration) {
                functionsToInitialize.add(d);
            }
        }

        // stack: [env, envRec] -> [envRec, env]
        mv.swap();

        /* step 4 */
        for (Declaration f : functionsToInitialize) {
            String fn = BoundName(f);

            // stack: [envRec, env] -> [envRec, env, envRec, env, cx]
            mv.dup2();
            mv.loadExecutionContext();

            // stack: [envRec, env, envRec, env, cx] -> [envRec, env, envRec, fo]
            if (f instanceof FunctionDeclaration) {
                InstantiateFunctionObject((FunctionDeclaration) f, mv);
            } else {
                InstantiateGeneratorObject((GeneratorDeclaration) f, mv);
            }

            // stack: [envRec, env, envRec, fo] -> [envRec, env]
            initialiseBinding(fn, mv);
        }

        // stack: [envRec, env] -> [env]
        mv.swap();
        mv.pop();
    }
}

@@ -2487,32 +2487,84 @@
    %7D%0A        %7D%0A%0A
+        if (!functionsToInitialize.isEmpty()) %7B%0A    
         // stack
@@ -2596,32 +2596,36 @@
 c, env%5D%0A        
+    
 mv.swap();%0A%0A    
@@ -2616,24 +2616,28 @@
 mv.swap();%0A%0A
+    
         /* s
@@ -2645,32 +2645,36 @@
 ep 4 */%0A        
+    
 for (Declaration
@@ -2699,24 +2699,28 @@
 itialize) %7B%0A
+    
             
@@ -2750,32 +2750,36 @@
 );%0A%0A            
+    
 // stack: %5BenvRe
@@ -2828,24 +2828,28 @@
             
+    
 mv.dup2();%0A 
@@ -2859,16 +2859,20 @@
         
+ 
+   
 mv.loadE
@@ -2887,24 +2887,28 @@
 Context();%0A%0A
+    
             
@@ -2985,24 +2985,28 @@
             
+ 
+   
 if (f instan
@@ -3025,32 +3025,36 @@
 nDeclaration) %7B%0A
+    
                 
@@ -3113,32 +3113,36 @@
 v);%0A            
+  
+  
 %7D else %7B%0A       
@@ -3126,32 +3126,36 @@
        %7D else %7B%0A
+    
                 
@@ -3216,35 +3216,43 @@
 v);%0A            
+  
+  
 %7D%0A%0A
+    
             // s
@@ -3312,16 +3312,20 @@
         
+    
 initiali
@@ -3351,27 +3351,34 @@
         
-%7D%0A%0A
+    %7D%0A%0A   
         
 // stack
@@ -3361,32 +3361,33 @@
   %7D%0A%0A           
+ 
 // stack: %5BenvRe
@@ -3403,32 +3403,36 @@
 %3E %5Benv%5D%0A        
+    
 mv.swap();%0A     
@@ -3422,24 +3422,35 @@
  mv.swap();%0A
+        %7D%0A%0A
         mv.p

Avoid unnecessary swap instructions when 'functionsToInitialize' is empty