Fix unqualified function invocation etc., part deux (635582, r=gal). (relanding in a CLOSED TREE)

This commit is contained in:
Brendan Eich 2011-02-19 23:13:56 -08:00
parent a2f58bb615
commit 79b4ee7847
6 changed files with 80 additions and 23 deletions

View File

@ -3,5 +3,10 @@ var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
function f() { return this.name; }
assertEq(evalcx('this.f = parent.f; var s = ""; for (i = 0; i < 10; ++i) s += f(); s', sb),
assertEq(evalcx('this.f = parent.f;\n' +
'var s = "";\n' +
'for (i = 0; i < 10; ++i)\n' +
' s += f();\n' +
's',
sb),
"innerinnerinnerinnerinnerinnerinnerinnerinnerinner");

View File

@ -0,0 +1,19 @@
this.name = "outer";
var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
function f() { return this.name; }
f.notMuchTodo = '42';
assertEq(evalcx('{\n' +
' let f = parent.f;\n' +
' let name = "block";\n' +
' (function () {\n' +
' eval(f.notMuchTodo);\n' + // reify Block
' var s = "";\n' +
' for (i = 0; i < 10; ++i)\n' +
' s += f();\n' +
' return s;\n' +
' })();\n' +
'}',
sb),
"outerouterouterouterouterouterouterouterouterouter");

View File

@ -0,0 +1,20 @@
this.name = "outer";
var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
function f() { return this.name; }
f.notMuchTodo = '42';
assertEq(evalcx('(function () {\n' +
' arguments = null;\n' + // force heavyweight
' var f = parent.f;\n' +
' var name = "call";\n' +
' return (function () {\n' +
' eval(f.notMuchTodo);\n' + // reify Call, make f() compile to JSOP_CALLNAME
' var s = "";\n' +
' for (i = 0; i < 10; ++i)\n' +
' s += f();\n' +
' return s;\n' +
' })();\n' +
'})()',
sb),
"outerouterouterouterouterouterouterouterouterouter");

View File

@ -0,0 +1,15 @@
this.name = "outer";
var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
this.f = function name(outer) {
if (outer) return name(false);
return this.name;
}
assertEq(evalcx('this.f = parent.f;\n' +
'var s = "";\n' +
'for (i = 0; i < 10; ++i)\n' +
' s += f(true);\n' +
's',
sb),
"outerouterouterouterouterouterouterouterouterouter");

View File

@ -55,11 +55,16 @@ static inline bool
IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee)
{
/*
* Look past any wrappers. If the callee is a strict function it is always
* safe because we won't do 'this' coercion in strict mode. Otherwise the
* callee is only safe to transform into the lazy 'this' token (undefined)
* if it is in the current scope. Without this restriction, lazy 'this'
* coercion would pick up the wrong global in the other scope.
* Look past any function wrappers. If the callee is a wrapped strict-mode
* function, lazy 'this' coercion is vacuously safe because strict-mode
* functions don't coerce 'this' at all. Otherwise, the callee is safe to
* transform into the lazy 'this' cookie (the undefined value) only if it
* is in the current scope.
*
* Without this restriction, lazy 'this' coercion would pick up the "wrong"
* global at the end of the callee function object's scope, rather than the
* "right" (backward compatible since 1995) global that was the "Reference
* base object" in the callee expression.
*/
if (callee->isProxy()) {
callee = callee->unwrap();

View File

@ -2179,11 +2179,12 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp)
}
static inline bool
SlowThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
ComputeThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
{
if (!funval.isObject() ||
((obj->isGlobal() || IsCacheableNonGlobalScope(obj)) &&
IsSafeForLazyThisCoercion(cx, &funval.toObject()))) {
(obj->isGlobal()
? IsSafeForLazyThisCoercion(cx, &funval.toObject())
: IsCacheableNonGlobalScope(obj))) {
/*
* We can avoid computing 'this' eagerly and push the implicit 'this'
* value (undefined), as long the scope is cachable and we are not
@ -4816,10 +4817,10 @@ BEGIN_CASE(JSOP_SETCALL)
}
END_CASE(JSOP_SETCALL)
#define SLOW_PUSH_THISV(cx, obj, funval) \
#define PUSH_THISV(cx, obj, funval) \
JS_BEGIN_MACRO \
Value v; \
if (!SlowThis(cx, obj, funval, &v)) \
if (!ComputeThis(cx, obj, funval, &v)) \
goto error; \
PUSH_COPY(v); \
JS_END_MACRO \
@ -4853,16 +4854,8 @@ BEGIN_CASE(JSOP_CALLNAME)
}
JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME) {
if (regs.sp[-1].isObject() &&
!IsSafeForLazyThisCoercion(cx, &regs.sp[-1].toObject())) {
if (!(obj = obj->thisObject(cx)))
return false;
PUSH_OBJECT(*obj);
} else {
PUSH_UNDEFINED();
}
}
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
PUSH_THISV(cx, obj, regs.sp[-1]);
len = JSOP_NAME_LENGTH;
DO_NEXT_OP(len);
}
@ -4900,7 +4893,7 @@ BEGIN_CASE(JSOP_CALLNAME)
/* obj must be on the scope chain, thus not a function. */
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
SLOW_PUSH_THISV(cx, obj, rval);
PUSH_THISV(cx, obj, rval);
}
END_CASE(JSOP_NAME)
@ -6403,7 +6396,7 @@ BEGIN_CASE(JSOP_XMLNAME)
goto error;
regs.sp[-1] = rval;
if (op == JSOP_CALLXMLNAME)
SLOW_PUSH_THISV(cx, obj, rval);
PUSH_THISV(cx, obj, rval);
}
END_CASE(JSOP_XMLNAME)