/*
 *  A bit messy direct eval tests.
 */

var global = this;
var orig_eval = eval;

/*===
false
"this" value
10
20
false
"this" value
20
20
5
===*/

/* Direct eval call needs to (1) be referenced with the name 'eval',
 * and (2) bind to the original eval() function.
 *
 * It does NOT need to be bound through the original environment
 * record, i.e. the global object.  A local variable with the name
 * 'eval' and which points to the built-in eval function is OK.
 */

var x = 5;

function f1() {
    var x = 10;

    // direct eval, non-strict eval code ->
    // * this binding maintained
    // * lexical/variable environment maintained

    print(eval("this === global"));
    print("" + eval("this"));
    print(eval("x"));           // binds to f1()'s var 'x'
    eval("var x = 20");         // modifies f1()'s var 'x'
    print(x);

    // direct eval, strict eval code ->
    // * this binding maintained
    // * lexical/variable environment is a fresh one

    print(eval("'use strict'; this === global"));
    print("" + eval("'use strict'; this"));
    print(eval("'use strict'; x"));           // "bleeds out", matches f1()'x var 'x'
    eval("'use strict'; var x = 30;");        // own env, no change to our 'x'
    print(x);
}

try {
    f1.call('"this" value');
    print(x);
} catch (e) {
    print(e.name);
}
/*===
true
Infinity
5
50
20
true
Infinity
15
50
20
15
20
===*/

function f2() {
    var x = 50;
    var indirect_eval = eval;

    // indirect_eval("x") and indirect_eval("var x = 15") fails
    // in V8 for some reason

    // indirect eval call, non-strict eval code ->
    // * this binding is global object
    // * lexical/variable environment is the global object

    print(indirect_eval("this === global"));
    print(indirect_eval("this.Number.POSITIVE_INFINITY"));
    print(indirect_eval("x"));    // binds to global 'x'
    indirect_eval("var x = 15");  // to global object
    indirect_eval("var y = 20");  // to global object
    print(x);                     // our 'x'
    print(y);                     // still prints, bleeds to global object

    // indirect eval call, strict eval code
    // * this binding is global object
    // * lexical/variable environment is a fresh one

    print(indirect_eval("'use strict'; this === global"));
    print(indirect_eval("'use strict'; this.Number.POSITIVE_INFINITY"));
    print(indirect_eval("'use strict'; x"));  // bleeds out, binds to global 'x'
    indirect_eval("'use strict'; var x = 100");  // own copy
    indirect_eval("'use strict'; var y = 200");  // own copy
    print(x);                     // our 'x'
    print(y);                     // still prints, bleeds to global object
}

try {
    f2.call('"this" value');
    print(x);
    print(y);
} catch (e) {
    print(e.name);
}

/*===
"this" value
===*/

/* Direct eval call through a local variable of name 'eval'.
 * Note that we can't simply say 'var eval = eval' because
 * that would ALWAYS assign undefined to 'eval' (variable
 * declarations happen before any right-hand-sides are
 * evaluated).
 */

function f3() {
    var eval = orig_eval;

    // this is a direct eval -> this binding should remain
    print("" + eval('this'));
}

try {
    f3.call('"this" value');
} catch (e) {
    print(e.name);
}

/*===
false
fake eval
true
false
===*/


/* An illustration that a certain eval() call may change from a direct eval
 * to an indirect one dynamically.  There are other ways to get the same
 * effect.
 *
 * The impact is that whenever a variable named 'eval' is encountered by
 * the compiler, it must always generate slow path code for it.  In
 * particular, CSVAR must be used for call setup (not CSREG).
 */

var binding_obj = {};

function wrapper() {
    var ret;
    with (binding_obj) {
        ret = function() {
            // direct eval   --> this_binding is caller's "this binding" --> prints false
            // indirect eval --> this_binding is global object --> prints true
            print(eval('this === global'));
        }
    }
    return ret;
}

try {
    var func = wrapper();
    var my_obj = { func: func };  // call through this to get a this binding != global object

    // initially, binding_obj has no 'eval' binding, hence direct eval
    my_obj.func();

    // add 'intercepting' eval binding, hence indirect eval
    binding_obj.eval = function(x) { print('fake eval'); return orig_eval(x); };
    my_obj.func();

    // remove intercepting binding, again a direct eval
    delete binding_obj.eval;
    my_obj.func();
} catch (e) {
    print(e.name);
}

/*===
forced this
Infinity
Infinity
===*/

/* Is it a direct eval call if an "eval()" target is a bound function
 * whose target function is the built-in eval?
 *
 * The answer in the spec is no: the result of GetValue() for "eval"
 * must be the built-in eval function for the call to be considered
 * direct.
 *
 * Check that we follow these semantics.
 */

var bound_eval = eval.bind('bound this');  // bind 'this' to 'bound this'
var indirect_eval = eval;

function bound1() {
    // direct eval, this should bound to 'forced this'
    print("" + eval("this"));
}

function bound2() {
    // indirect eval through a bound function, this should be
    // bound to the global object
    print(bound_eval("this.Number.POSITIVE_INFINITY"));
}

function bound3() {
    // indirect eval through a bound function, but eval code is
    // strict; this should still be bound to the global object
    print(bound_eval("'use strict'; this.Number.POSITIVE_INFINITY"));
}

try {
    /* The 'this' binding for eval() itself (here, the string "bound this")
     * should not really matter; the 'this' binding for the evaluated code
     * should still be as specified in E5 Section 10.4.2.
     *
     * The initial [[Call]] would happen using semantics from E5 Section
     * 15.3.4.5.1.  That algorithm would [[Call]] the built-in eval
     * function (E5 Section 10.4.2) which would ignore any 'this' binding.
     */

    bound1.call('forced this');
} catch (e) {
    print(e.name);
}

try {
    bound2.call('forced this');
} catch (e) {
    print(e.name);
}

try {
    bound3.call('forced this');
} catch (e) {
    print(e.name);
}

/*===
NaN
NaN
bar
bar
===*/

/* The "this binding" for a direct eval is always the caller's current this
 * binding; this is the case for both strict and non-strict eval code.
 *
 * For an indirect eval, "this binding" is set to the global object.
 */

function thisBinding_indirect_nonstrict() {
    var my_eval = eval;
    my_eval('print(this.NaN)');
}

function thisBinding_indirect_strict() {
    var my_eval = eval;
    my_eval('"use strict"; print(this.NaN)');
}

function thisBinding_direct_nonstrict() {
    eval('print(this.foo)');
}

function thisBinding_direct_strict() {
    eval('"use strict"; print(this.foo)');
}

try {
    thisBinding_indirect_nonstrict.call({foo:'bar'});
} catch (e) {
    print(e.name);
}

try {
    thisBinding_indirect_strict.call({foo:'bar'});
} catch (e) {
    print(e.name);
}

try {
    thisBinding_direct_nonstrict.call({foo:'bar'});
} catch (e) {
    print(e.name);
}

try {
    thisBinding_direct_strict.call({foo:'bar'});
} catch (e) {
    print(e.name);
}


