/*
 *  Various identifier shadowing cases
 */

/*===
function
undefined
function
inner foo
number
===*/

/* Function expression name is bound outside the function scope;
 * a single-identifier scope is created between the surrounding
 * scope and the function scope.
 *
 * Thus you can declare a variable or a function of the same name,
 * and it will hide the function name binding.
 */

try {
    /* Base case */
    eval("(function foo() { print(typeof foo); })();");
} catch (e) {
    print(e.name);
}

try {
    /* Var declaration shadows and is undefined, since variable
     * declaration happens on entry but assignment only when
     * statement is reached.
     */
    eval("(function foo() { print(typeof foo); var foo = 1; })();");
} catch (e) {
    print(e.name);
}

try {
     /* Function declaration shadows and is defined as the inner
      * function: function declarations happen on entry.
      */
    eval("(function foo() { print(typeof foo); foo(); function foo() { print('inner foo'); }; })();");
} catch (e) {
    print(e.name);
}

try {
    /* Argument shadows the function name binding, again simply 
     * because the function name binding exists "outside" the
     * function scope.
     */
    eval("(function foo(foo) { print(typeof foo); })(1);");
} catch (e) {
    print(e.name);
}

/*===
123
function
inner func
1
1
10
===*/

/* An argument name shadows automatic 'arguments' binding */

try {
    eval("(function foo(arguments) { print(arguments); })(123);");
} catch (e) {
    print(e.name);
}

/* A function declaration inside the function shadows automatic 'arguments'
 * binding.
 */

try {
    eval("(function foo() { function arguments() { print('inner func'); }; print(typeof arguments); arguments(); })();");
} catch (e) {
    print(e.name);
}

/* A variable declaration does NOT shadow the automatic 'arguments' binding. */

try {
    eval("(function foo() { var arguments; print(arguments[0]); })(1,2)");
} catch (e) {
    print(e.name);
}

try {
    eval("(function foo() { print(arguments[0]); var arguments = 10; print(arguments); })(1,2)");
} catch (e) {
    print(e.name);
}


