зеркало из https://github.com/mozilla/pjs.git
Unqualified function invocation doesn't use the global object the property was gotten from as |this| (bug 634590, r=gal).
This commit is contained in:
Родитель
728e47d59d
Коммит
7936bb72b0
|
@ -0,0 +1,12 @@
|
|||
this.name = "outer";
|
||||
var sb = evalcx('');
|
||||
sb.name = "inner";
|
||||
sb.parent = this;
|
||||
function f() { return this.name; }
|
||||
assertEq(evalcx('this.f = parent.f;\n' +
|
||||
'var s = "";\n' +
|
||||
'for (i = 0; i < 10; ++i)\n' +
|
||||
' s += f();\n' +
|
||||
's',
|
||||
sb),
|
||||
"innerinnerinnerinnerinnerinnerinnerinnerinnerinner");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -3051,3 +3051,34 @@ js_ReportIsNotFunction(JSContext *cx, const Value *vp, uintN flags)
|
|||
|
||||
js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
bool
|
||||
IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee)
|
||||
{
|
||||
/*
|
||||
* 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();
|
||||
if (!callee->isFunction())
|
||||
return true; // treat any non-wrapped-function proxy as strict
|
||||
|
||||
JSFunction *fun = callee->getFunctionPrivate();
|
||||
if (fun->isInterpreted() && fun->inStrictMode())
|
||||
return true;
|
||||
}
|
||||
return callee->getGlobal() == cx->fp()->scopeChain().getGlobal();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -610,4 +610,11 @@ js_fun_apply(JSContext *cx, uintN argc, js::Value *vp);
|
|||
extern JSBool
|
||||
js_fun_call(JSContext *cx, uintN argc, js::Value *vp);
|
||||
|
||||
namespace js {
|
||||
|
||||
bool
|
||||
IsSafeForLazyThisCoercion(JSContext *cx, JSObject *callee);
|
||||
|
||||
}
|
||||
|
||||
#endif /* jsfun_h___ */
|
||||
|
|
|
@ -2178,6 +2178,31 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ComputeThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
|
||||
{
|
||||
if (!funval.isObject() ||
|
||||
(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
|
||||
* crossing into another scope (in which case lazy calculation of 'this'
|
||||
* would pick up the new and incorrect scope). 'strict' functions are an
|
||||
* exception. We don't want to eagerly calculate 'this' for them even if
|
||||
* the callee is in a different scope.
|
||||
*/
|
||||
*vp = UndefinedValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj = obj->thisObject(cx)))
|
||||
return false;
|
||||
*vp = ObjectValue(*obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
JS_REQUIRES_STACK JS_NEVER_INLINE bool
|
||||
|
@ -2203,8 +2228,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
|
|||
* expect from looking at the code. (We do omit POPs after SETs;
|
||||
* unfortunate, but not worth fixing.)
|
||||
*/
|
||||
# define LOG_OPCODE(OP) JS_BEGIN_MACRO \
|
||||
if (JS_UNLIKELY(cx->logfp != NULL) && \
|
||||
# define LOG_OPCODE(OP) JS_BEGIN_MACRO \
|
||||
if (JS_UNLIKELY(cx->logfp != NULL) && \
|
||||
(OP) == *regs.pc) \
|
||||
js_LogOpcode(cx); \
|
||||
JS_END_MACRO
|
||||
|
@ -4792,26 +4817,13 @@ BEGIN_CASE(JSOP_SETCALL)
|
|||
}
|
||||
END_CASE(JSOP_SETCALL)
|
||||
|
||||
#define SLOW_PUSH_THISV(cx, obj) \
|
||||
JS_BEGIN_MACRO \
|
||||
Class *clasp; \
|
||||
JSObject *thisp = obj; \
|
||||
if (!thisp->getParent() || \
|
||||
(clasp = thisp->getClass()) == &js_CallClass || \
|
||||
clasp == &js_BlockClass || \
|
||||
clasp == &js_DeclEnvClass) { \
|
||||
/* Push the ImplicitThisValue for the Environment Record */ \
|
||||
/* associated with obj. See ES5 sections 10.2.1.1.6 and */ \
|
||||
/* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \
|
||||
/* (Function Calls). */ \
|
||||
PUSH_UNDEFINED(); \
|
||||
} else { \
|
||||
thisp = thisp->thisObject(cx); \
|
||||
if (!thisp) \
|
||||
goto error; \
|
||||
PUSH_OBJECT(*thisp); \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
#define PUSH_THISV(cx, obj, funval) \
|
||||
JS_BEGIN_MACRO \
|
||||
Value v; \
|
||||
if (!ComputeThis(cx, obj, funval, &v)) \
|
||||
goto error; \
|
||||
PUSH_COPY(v); \
|
||||
JS_END_MACRO \
|
||||
|
||||
BEGIN_CASE(JSOP_GETGNAME)
|
||||
BEGIN_CASE(JSOP_CALLGNAME)
|
||||
|
@ -4841,20 +4853,9 @@ BEGIN_CASE(JSOP_CALLNAME)
|
|||
PUSH_COPY(rval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Push results, the same as below, but with a prop$ hit there
|
||||
* is no need to test for the unusual and uncacheable case where
|
||||
* the caller determines |this|.
|
||||
*/
|
||||
#if DEBUG
|
||||
Class *clasp;
|
||||
JS_ASSERT(!obj->getParent() ||
|
||||
(clasp = obj->getClass()) == &js_CallClass ||
|
||||
clasp == &js_BlockClass ||
|
||||
clasp == &js_DeclEnvClass);
|
||||
#endif
|
||||
JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
|
||||
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
|
||||
PUSH_UNDEFINED();
|
||||
PUSH_THISV(cx, obj, regs.sp[-1]);
|
||||
len = JSOP_NAME_LENGTH;
|
||||
DO_NEXT_OP(len);
|
||||
}
|
||||
|
@ -4892,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);
|
||||
PUSH_THISV(cx, obj, rval);
|
||||
}
|
||||
END_CASE(JSOP_NAME)
|
||||
|
||||
|
@ -6395,7 +6396,7 @@ BEGIN_CASE(JSOP_XMLNAME)
|
|||
goto error;
|
||||
regs.sp[-1] = rval;
|
||||
if (op == JSOP_CALLXMLNAME)
|
||||
SLOW_PUSH_THISV(cx, obj);
|
||||
PUSH_THISV(cx, obj, rval);
|
||||
}
|
||||
END_CASE(JSOP_XMLNAME)
|
||||
|
||||
|
|
|
@ -479,7 +479,7 @@ JSStackFrame::callObj() const
|
|||
JS_ASSERT(hasCallObj());
|
||||
JSObject *pobj = &scopeChain();
|
||||
while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) {
|
||||
JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith());
|
||||
JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith());
|
||||
pobj = pobj->getParent();
|
||||
}
|
||||
return *pobj;
|
||||
|
|
|
@ -5052,7 +5052,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
|
|||
parent = obj->getParent();
|
||||
for (scopeIndex = 0;
|
||||
parent
|
||||
? js_IsCacheableNonGlobalScope(obj)
|
||||
? IsCacheableNonGlobalScope(obj)
|
||||
: !obj->getOps()->lookupProperty;
|
||||
++scopeIndex) {
|
||||
protoIndex =
|
||||
|
@ -5162,11 +5162,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
|
|||
* farther checks or lookups. For details see the JSOP_BINDNAME case of
|
||||
* js_Interpret.
|
||||
*
|
||||
* The test order here matters because js_IsCacheableNonGlobalScope
|
||||
* The test order here matters because IsCacheableNonGlobalScope
|
||||
* must not be passed a global object (i.e. one with null parent).
|
||||
*/
|
||||
for (int scopeIndex = 0;
|
||||
!obj->getParent() || js_IsCacheableNonGlobalScope(obj);
|
||||
!obj->getParent() || IsCacheableNonGlobalScope(obj);
|
||||
scopeIndex++) {
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
|
|
|
@ -1661,16 +1661,19 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||
JSObject **objp, JSProperty **propp);
|
||||
|
||||
|
||||
extern JS_FRIEND_DATA(js::Class) js_CallClass;
|
||||
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
|
||||
|
||||
namespace js {
|
||||
|
||||
/*
|
||||
* We cache name lookup results only for the global object or for native
|
||||
* non-global objects without prototype or with prototype that never mutates,
|
||||
* see bug 462734 and bug 487039.
|
||||
*/
|
||||
inline bool
|
||||
js_IsCacheableNonGlobalScope(JSObject *obj)
|
||||
static inline bool
|
||||
IsCacheableNonGlobalScope(JSObject *obj)
|
||||
{
|
||||
extern JS_FRIEND_DATA(js::Class) js_CallClass;
|
||||
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
|
||||
JS_ASSERT(obj->getParent());
|
||||
|
||||
js::Class *clasp = obj->getClass();
|
||||
|
@ -1682,6 +1685,8 @@ js_IsCacheableNonGlobalScope(JSObject *obj)
|
|||
return cacheable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success.
|
||||
*/
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "jsobj.h"
|
||||
#include "jsprobes.h"
|
||||
#include "jspropertytree.h"
|
||||
#include "jsproxy.h"
|
||||
#include "jsscope.h"
|
||||
#include "jsstaticcheck.h"
|
||||
#include "jsxml.h"
|
||||
|
@ -60,6 +61,7 @@
|
|||
#include "jsscopeinlines.h"
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "jsfuninlines.h"
|
||||
#include "jsgcinlines.h"
|
||||
#include "jsprobes.h"
|
||||
|
||||
|
|
|
@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f)
|
|||
*/
|
||||
JSObject* child = &cx->fp()->scopeChain();
|
||||
while (JSObject* parent = child->getParent()) {
|
||||
if (!js_IsCacheableNonGlobalScope(child)) {
|
||||
if (!IsCacheableNonGlobalScope(child)) {
|
||||
debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n");
|
||||
Blacklist((jsbytecode*) f->root->ip);
|
||||
return false;
|
||||
|
@ -15187,7 +15187,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target
|
|||
/* There was a call object, or should be a call object now. */
|
||||
for (;;) {
|
||||
if (obj != globalObj) {
|
||||
if (!js_IsCacheableNonGlobalScope(obj))
|
||||
if (!IsCacheableNonGlobalScope(obj))
|
||||
RETURN_STOP("scope chain lookup crosses non-cacheable object");
|
||||
|
||||
// We must guard on the shape of all call objects for heavyweight functions
|
||||
|
|
|
@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler
|
|||
JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj);
|
||||
|
||||
while (tobj && tobj != getprop.holder) {
|
||||
if (!js_IsCacheableNonGlobalScope(tobj))
|
||||
if (!IsCacheableNonGlobalScope(tobj))
|
||||
return disable("non-cacheable scope chain object");
|
||||
JS_ASSERT(tobj->isNative());
|
||||
|
||||
|
@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler
|
|||
JSObject *tobj = scopeChain;
|
||||
Address parent(pic.objReg, offsetof(JSObject, parent));
|
||||
while (tobj && tobj != obj) {
|
||||
if (!js_IsCacheableNonGlobalScope(tobj))
|
||||
if (!IsCacheableNonGlobalScope(tobj))
|
||||
return disable("non-cacheable obj in scope chain");
|
||||
masm.loadPtr(parent, pic.objReg);
|
||||
Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
|
||||
|
|
Загрузка…
Ссылка в новой задаче