You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

306 lines
7.0 KiB

/*
* 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);
}