Backout c0d337401801,78d17e22a223 (bug 712714) for talos regressions across the board.

This commit is contained in:
Marco Bonardo 2012-01-06 13:49:52 +01:00
parent 803a44e0dd
commit 2d516bb575
33 changed files with 2148 additions and 804 deletions

View File

@ -2717,30 +2717,6 @@ CheckSideEffects(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool *ans
return ok;
}
bool
BytecodeEmitter::needsImplicitThis()
{
if (!compileAndGo())
return true;
if (!inFunction()) {
JSObject *scope = scopeChain();
while (scope) {
if (scope->isWith())
return true;
scope = scope->enclosingScope();
}
}
for (const FunctionBox *funbox = this->funbox; funbox; funbox = funbox->parent) {
if (funbox->tcflags & TCF_IN_WITH)
return true;
}
for (StmtInfo *stmt = topStmt; stmt; stmt = stmt->down) {
if (stmt->type == STMT_WITH)
return true;
}
return false;
}
static JSBool
EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContext)
{
@ -2776,6 +2752,9 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContex
if (op == JSOP_ARGUMENTS || op == JSOP_CALLEE) {
if (Emit1(cx, bce, op) < 0)
return JS_FALSE;
/* Need to provide |this| value for call */
if (callContext && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return JS_FALSE;
} else {
if (!pn->pn_cookie.isFree()) {
EMIT_UINT16_IMM_OP(op, pn->pn_cookie.asInteger());
@ -2785,17 +2764,6 @@ EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContex
}
}
/* Need to provide |this| value for call */
if (callContext) {
if (op == JSOP_CALLNAME && bce->needsImplicitThis()) {
if (!EmitAtomOp(cx, pn, JSOP_IMPLICITTHIS, bce))
return false;
} else {
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return false;
}
}
return JS_TRUE;
}
@ -2817,10 +2785,7 @@ EmitXMLName(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pn2->pn_offset) < 0)
return false;
if (Emit1(cx, bce, op) < 0)
return false;
return true;
return Emit1(cx, bce, op) >= 0;
}
#endif
@ -2830,8 +2795,6 @@ EmitElemOpBase(JSContext *cx, BytecodeEmitter *bce, JSOp op)
if (Emit1(cx, bce, op) < 0)
return false;
CheckTypeSet(cx, bce, op);
if (op == JSOP_CALLELEM)
return Emit1(cx, bce, JSOP_SWAP) >= 0;
return true;
}
@ -2848,17 +2811,7 @@ EmitSpecialPropOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
return false;
if (!EmitIndexOp(cx, JSOP_QNAMEPART, index, bce))
return false;
if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_DUP) < 0)
return false;
if (!EmitElemOpBase(cx, bce, op))
return false;
if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_SWAP) < 0)
return false;
return true;
return EmitElemOpBase(cx, bce, op);
}
static bool
@ -2938,19 +2891,10 @@ EmitPropOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce,
return false;
}
if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_DUP) < 0)
return false;
if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pn2->pn_offset) < 0)
return false;
if (!EmitAtomOp(cx, pn, op, bce, psuffix))
return false;
if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_SWAP) < 0)
return false;
return true;
return EmitAtomOp(cx, pn, op, bce, psuffix);
}
static bool
@ -3114,9 +3058,6 @@ EmitElemOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
if (!EmitTree(cx, bce, left))
return false;
if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_DUP) < 0)
return false;
/* The right side of the descendant operator is implicitly quoted. */
JS_ASSERT(op != JSOP_DESCENDANTS || !right->isKind(PNK_STRING) ||
right->isOp(JSOP_QNAMEPART));

View File

@ -745,8 +745,6 @@ struct BytecodeEmitter : public TreeContext
return true;
}
bool needsImplicitThis();
TokenStream *tokenStream() { return &parser->tokenStream; }
jsbytecode *base() const { return current->base; }

View File

@ -242,14 +242,6 @@ Parser::newFunctionBox(JSObject *obj, ParseNode *fn, TreeContext *tc)
funbox->tcflags = (TCF_IN_FUNCTION | (tc->flags & (TCF_COMPILE_N_GO | TCF_STRICT_MODE_CODE)));
if (tc->innermostWith)
funbox->tcflags |= TCF_IN_WITH;
if (!tc->inFunction()) {
JSObject *scope = tc->scopeChain();
while (scope) {
if (scope->isWith())
funbox->tcflags |= TCF_IN_WITH;
scope = scope->enclosingScope();
}
}
return funbox;
}

View File

@ -6,5 +6,5 @@ function caller(code, obj) {
eval(code); // Make the compiler give up on binding analysis.
return x;
}
trap(caller, 13, "var x = 'success'; nop()");
trap(caller, 12, "var x = 'success'; nop()");
assertEq(caller("var y = 'ignominy'", this), "success");

View File

@ -7,5 +7,5 @@ function caller(obj) {
var x = "failure";
return x;
}
trap(caller, 15, "x = 'success'; nop()");
trap(caller, 14, "x = 'success'; nop()");
assertEq(caller(this), "success");

View File

@ -5,7 +5,7 @@ x = "notset";
function myparent(nested) {
if (nested) {
/* noop call in myparent */
trap(myparent, 48, "success()");
trap(myparent, 49, "success()");
} else {
myparent(true);
x = "failure";

View File

@ -7,14 +7,14 @@ function doNothing() { }
function myparent(nested) {
if (nested) {
/* JSOP_CALL to doNothing in myparent with nested = true. */
trap(myparent, 26, "success()");
trap(myparent, 24, "success()");
doNothing();
} else {
doNothing();
}
}
/* JSOP_CALL to doNothing in myparent with nested = false. */
trap(myparent, 37, "myparent(true)");
trap(myparent, 34, "myparent(true)");
function success() {
x = "success";

View File

@ -1392,6 +1392,14 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
break;
}
case JSOP_CALLARG:
case JSOP_CALLLOCAL: {
uint32_t slot = GetBytecodeSlot(script, pc);
if (trackSlot(slot))
stack[stackDepth - 2] = code->poppedValues[0] = values[slot];
break;
}
/* Short circuit ops which push back one of their operands. */
case JSOP_MOREITER:
@ -1851,6 +1859,21 @@ CrossScriptSSA::foldValue(const CrossSSAValue &cv)
break;
}
case JSOP_CALLPROP: {
/*
* The second value pushed by CALLPROP is the same as its popped
* value. We don't do this folding during the SSA analysis itself
* as we still need to distinguish the two values during type
* inference --- any popped null or undefined value will throw an
* exception, and not actually end up in the pushed set.
*/
if (v.pushedIndex() == 1) {
ScriptAnalysis *analysis = frame.script->analysis();
return foldValue(CrossSSAValue(cv.frame, analysis->poppedValue(pc, 0)));
}
break;
}
case JSOP_TOID: {
/*
* TOID acts as identity for integers, so to get better precision

View File

@ -1067,12 +1067,9 @@ class ScriptAnalysis
/* For a JSOP_CALL* op, get the pc of the corresponding JSOP_CALL/NEW/etc. */
jsbytecode *getCallPC(jsbytecode *pc)
{
SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script->code, 0));
JS_ASSERT(uses && uses->popped);
JS_ASSERT_IF(uses->next,
!uses->next->next &&
uses->next->popped &&
script->code[uses->next->offset] == JSOP_SWAP);
JS_ASSERT(js_CodeSpec[*pc].format & JOF_CALLOP);
SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script->code, 1));
JS_ASSERT(uses && !uses->next && uses->popped);
return script->code + uses->offset;
}

View File

@ -3572,8 +3572,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
/* Handle as a property access. */
PropertyAccess(cx, script, pc, script->global()->getType(cx), false, seen, id);
if (op == JSOP_CALLGNAME)
if (op == JSOP_CALLGNAME) {
pushed[1].addType(cx, Type::UnknownType());
pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
}
if (CheckNextTest(pc))
pushed[0].addType(cx, Type::UndefinedType());
@ -3601,8 +3603,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
addTypeBarrier(cx, pc, seen, Type::UnknownType());
}
if (op == JSOP_CALLNAME)
if (op == JSOP_CALLNAME) {
pushed[1].addType(cx, Type::UnknownType());
pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
}
break;
}
@ -3642,8 +3646,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
TypeSet *seen = bytecodeTypes(pc);
addTypeBarrier(cx, pc, seen, Type::UnknownType());
seen->addSubset(cx, &pushed[0]);
if (op == JSOP_CALLFCSLOT)
if (op == JSOP_CALLFCSLOT) {
pushed[1].addType(cx, Type::UndefinedType());
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
}
break;
}
@ -3666,8 +3672,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
/* Local 'let' variable. Punt on types for these, for now. */
pushed[0].addType(cx, Type::UnknownType());
}
if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL) {
pushed[1].addType(cx, Type::UndefinedType());
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
}
break;
}
@ -3744,6 +3752,8 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
poppedTypes(pc, 0)->addCallProperty(cx, script, pc, id);
seen->addSubset(cx, &pushed[0]);
if (op == JSOP_CALLPROP)
poppedTypes(pc, 0)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID);
if (CheckNextTest(pc))
pushed[0].addType(cx, Type::UndefinedType());
break;
@ -3761,8 +3771,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
poppedTypes(pc, 1)->addGetProperty(cx, script, pc, seen, JSID_VOID);
seen->addSubset(cx, &pushed[0]);
if (op == JSOP_CALLELEM)
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), poppedTypes(pc, 1));
if (op == JSOP_CALLELEM) {
poppedTypes(pc, 1)->addFilterPrimitives(cx, &pushed[1], TypeSet::FILTER_NULL_VOID);
pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), &pushed[1]);
}
if (CheckNextTest(pc))
pushed[0].addType(cx, Type::UndefinedType());
break;
@ -4034,7 +4046,6 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
/* Pushes information about whether an exception was thrown. */
break;
case JSOP_IMPLICITTHIS:
case JSOP_EXCEPTION:
pushed[0].addType(cx, Type::UnknownType());
break;
@ -4077,8 +4088,10 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
pushed[0].addType(cx, Type::UnknownType());
break;
case JSOP_XMLNAME:
case JSOP_CALLXMLNAME:
pushed[1].addType(cx, Type::UnknownType());
/* FALLTHROUGH */
case JSOP_XMLNAME:
pushed[0].addType(cx, Type::UnknownType());
break;
@ -4654,7 +4667,7 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO
if (calleev.kind() != SSAValue::PUSHED)
return false;
jsbytecode *calleepc = script->code + calleev.pushedOffset();
if (JSOp(*calleepc) != JSOP_CALLPROP)
if (JSOp(*calleepc) != JSOP_CALLPROP || calleev.pushedIndex() != 0)
return false;
/*
@ -4664,8 +4677,8 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO
analysis->breakTypeBarriersSSA(cx, analysis->poppedValue(calleepc, 0));
analysis->breakTypeBarriers(cx, calleepc - script->code, true);
TypeSet *funcallTypes = analysis->poppedTypes(pc, GET_ARGC(pc) + 1);
TypeSet *scriptTypes = analysis->poppedTypes(pc, GET_ARGC(pc));
TypeSet *funcallTypes = analysis->pushedTypes(calleepc, 0);
TypeSet *scriptTypes = analysis->pushedTypes(calleepc, 1);
/* Need to definitely be calling Function.call on a specific script. */
JSObject *funcallObj = funcallTypes->getSingleton(cx, false);
@ -4682,9 +4695,9 @@ AnalyzeNewScriptProperties(JSContext *cx, TypeObject *type, JSFunction *fun, JSO
* Generate constraints to clear definite properties from the type
* should the Function.call or callee itself change in the future.
*/
funcallTypes->add(cx,
analysis->pushedTypes(calleev.pushedOffset(), 0)->add(cx,
cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
scriptTypes->add(cx,
analysis->pushedTypes(calleev.pushedOffset(), 1)->add(cx,
cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
TypeNewScript::Initializer pushframe(TypeNewScript::Initializer::FRAME_PUSH, uses->offset);

View File

@ -386,8 +386,11 @@ Class js_NoSuchMethodClass = {
* parameters.
*/
bool
js::OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp)
js::OnUnknownMethod(JSContext *cx, Value *vp)
{
JS_ASSERT(!vp[1].isPrimitive());
JSObject *obj = &vp[1].toObject();
jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
AutoValueRooter tvr(cx);
if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, tvr.addr()))
@ -395,14 +398,14 @@ js::OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp)
TypeScript::MonitorUnknown(cx, cx->fp()->script(), cx->regs().pc);
if (tvr.value().isPrimitive()) {
*vp = tvr.value();
vp[0] = tvr.value();
} else {
#if JS_HAS_XML_SUPPORT
/* Extract the function name from function::name qname. */
if (idval.isObject()) {
obj = &idval.toObject();
if (vp[0].isObject()) {
obj = &vp[0].toObject();
if (js_GetLocalNameFromFunctionQName(obj, &id, cx))
idval = IdToValue(id);
vp[0] = IdToValue(id);
}
#endif
@ -411,8 +414,8 @@ js::OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp)
return false;
obj->setSlot(JSSLOT_FOUND_FUNCTION, tvr.value());
obj->setSlot(JSSLOT_SAVED_ID, idval);
vp->setObject(*obj);
obj->setSlot(JSSLOT_SAVED_ID, vp[0]);
vp[0].setObject(*obj);
}
return true;
}
@ -1259,15 +1262,25 @@ inline InterpreterFrames::~InterpreterFrames()
JS_THREAD_DATA(context)->interpreterFrames = older;
}
#if defined(DEBUG) && !defined(JS_THREADSAFE)
void
js::AssertValidPropertyCacheHit(JSContext *cx,
JSObject *start, JSObject *found,
PropertyCacheEntry *entry)
{
JSScript *script = cx->fp()->script();
FrameRegs& regs = cx->regs();
/*
* Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
* single-thread DEBUG js shell testing to verify property cache hits.
*/
#if defined DEBUG && !defined JS_THREADSAFE
# define ASSERT_VALID_PROPERTY_CACHE_HIT(obj,pobj,entry) \
JS_BEGIN_MACRO \
if (!AssertValidPropertyCacheHit(cx, script, regs, obj, pobj, \
entry)) { \
goto error; \
} \
JS_END_MACRO
static bool
AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, FrameRegs& regs,
JSObject *start, JSObject *found,
PropertyCacheEntry *entry)
{
uint32_t sample = cx->runtime->gcNumber;
PropertyCacheEntry savedEntry = *entry;
@ -1285,8 +1298,8 @@ js::AssertValidPropertyCacheHit(JSContext *cx,
obj = start;
ok = LookupProperty(cx, obj, name, &pobj, &prop);
}
JS_ASSERT(ok);
if (!ok)
return false;
if (cx->runtime->gcNumber != sample)
JS_PROPERTY_CACHE(cx).restore(&savedEntry);
JS_ASSERT(prop);
@ -1294,8 +1307,13 @@ js::AssertValidPropertyCacheHit(JSContext *cx,
const Shape *shape = (Shape *) prop;
JS_ASSERT(entry->prop == shape);
return true;
}
#endif /* DEBUG && !JS_THREADSAFE */
#else
# define ASSERT_VALID_PROPERTY_CACHE_HIT(obj,pobj,entry) ((void) 0)
#endif
/*
* Ensure that the intrepreter switch can close call-bytecode cases in the
@ -2219,6 +2237,35 @@ BEGIN_CASE(JSOP_PICK)
}
END_CASE(JSOP_PICK)
#define NATIVE_GET(cx,obj,pobj,shape,getHow,vp) \
JS_BEGIN_MACRO \
if (shape->isDataDescriptor() && shape->hasDefaultGetter()) { \
/* Fast path for Object instance properties. */ \
JS_ASSERT((shape)->slot() != SHAPE_INVALID_SLOT || \
!shape->hasDefaultSetter()); \
if (((shape)->slot() != SHAPE_INVALID_SLOT)) \
*(vp) = (pobj)->nativeGetSlot((shape)->slot()); \
else \
(vp)->setUndefined(); \
} else { \
if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp)) \
goto error; \
} \
JS_END_MACRO
#define NATIVE_SET(cx,obj,shape,entry,strict,vp) \
JS_BEGIN_MACRO \
if (shape->hasDefaultSetter() && \
(shape)->hasSlot() && \
!(shape)->isMethod()) { \
/* Fast path for, e.g., plain Object instance properties. */ \
(obj)->nativeSetSlotWithType(cx, shape, *vp); \
} else { \
if (!js_NativeSet(cx, obj, shape, false, strict, vp)) \
goto error; \
} \
JS_END_MACRO
BEGIN_CASE(JSOP_SETCONST)
{
PropertyName *name;
@ -2279,8 +2326,14 @@ BEGIN_CASE(JSOP_BINDNAME)
if (obj->isGlobal())
break;
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
LOAD_NAME(0, name);
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, name);
if (!name) {
ASSERT_VALID_PROPERTY_CACHE_HIT(obj, obj2, entry);
break;
}
obj = FindIdentifierBase(cx, &regs.fp()->scopeChain(), name);
if (!obj)
@ -2852,11 +2905,72 @@ END_CASE(JSOP_THIS)
BEGIN_CASE(JSOP_GETPROP)
BEGIN_CASE(JSOP_GETXPROP)
BEGIN_CASE(JSOP_LENGTH)
BEGIN_CASE(JSOP_CALLPROP)
{
Value rval;
if (!GetPropertyOperation(cx, regs.pc, regs.sp[-1], &rval))
goto error;
do {
Value *vp = &regs.sp[-1];
if (op == JSOP_LENGTH) {
/* Optimize length accesses on strings, arrays, and arguments. */
if (vp->isString()) {
rval = Int32Value(vp->toString()->length());
break;
}
if (vp->isMagic(JS_LAZY_ARGUMENTS)) {
rval = Int32Value(regs.fp()->numActualArgs());
break;
}
if (vp->isObject()) {
JSObject *obj = &vp->toObject();
if (obj->isArray()) {
jsuint length = obj->getArrayLength();
rval = NumberValue(length);
break;
}
if (obj->isArguments()) {
ArgumentsObject &argsobj = obj->asArguments();
if (!argsobj.hasOverriddenLength()) {
uint32_t length = argsobj.initialLength();
JS_ASSERT(length < INT32_MAX);
rval = Int32Value(int32_t(length));
break;
}
}
if (js_IsTypedArray(obj)) {
JSObject *tarray = TypedArray::getTypedArray(obj);
rval = Int32Value(TypedArray::getLength(tarray));
break;
}
}
}
JSObject *obj;
VALUE_TO_OBJECT(cx, vp, obj);
JSObject *aobj = js_GetProtoIfDenseArray(obj);
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, name);
if (!name) {
ASSERT_VALID_PROPERTY_CACHE_HIT(aobj, obj2, entry);
NATIVE_GET(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, &rval);
break;
}
if (JS_LIKELY(!aobj->getOps()->getProperty)
? !GetPropertyHelper(cx, obj, name,
(regs.pc[JSOP_GETPROP_LENGTH] == JSOP_IFEQ)
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
&rval)
: !obj->getProperty(cx, name, &rval))
{
goto error;
}
} while (0);
TypeScript::Monitor(cx, script, regs.pc, rval);
@ -2865,16 +2979,177 @@ BEGIN_CASE(JSOP_CALLPROP)
}
END_CASE(JSOP_GETPROP)
BEGIN_CASE(JSOP_CALLPROP)
{
Value lval = regs.sp[-1];
Value objv;
if (lval.isObject()) {
objv = lval;
} else {
GlobalObject &global = regs.fp()->scopeChain().global();
JSObject *pobj;
if (lval.isString()) {
pobj = global.getOrCreateStringPrototype(cx);
} else if (lval.isNumber()) {
pobj = global.getOrCreateNumberPrototype(cx);
} else if (lval.isBoolean()) {
pobj = global.getOrCreateBooleanPrototype(cx);
} else {
JS_ASSERT(lval.isNull() || lval.isUndefined());
js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
goto error;
}
if (!pobj)
goto error;
objv.setObject(*pobj);
}
JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
Value rval;
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, aobj, obj2, entry, name);
if (!name) {
ASSERT_VALID_PROPERTY_CACHE_HIT(aobj, obj2, entry);
NATIVE_GET(cx, &objv.toObject(), obj2, entry->prop, JSGET_NO_METHOD_BARRIER, &rval);
regs.sp[-1] = rval;
assertSameCompartment(cx, regs.sp[-1]);
PUSH_COPY(lval);
} else {
/* Cache miss: use the name loaded for us under PropertyCache::test. */
PUSH_NULL();
if (lval.isObject()) {
if (!GetMethod(cx, &objv.toObject(), name,
JS_LIKELY(!aobj->getOps()->getProperty)
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_NO_METHOD_BARRIER,
&rval))
{
goto error;
}
regs.sp[-1] = objv;
regs.sp[-2] = rval;
} else {
JS_ASSERT(!objv.toObject().getOps()->getProperty);
if (!GetPropertyHelper(cx, &objv.toObject(), name,
JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER, &rval))
{
goto error;
}
regs.sp[-1] = lval;
regs.sp[-2] = rval;
}
assertSameCompartment(cx, regs.sp[-1], regs.sp[-2]);
}
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) {
LOAD_NAME(0, name);
regs.sp[-2].setString(name);
if (!OnUnknownMethod(cx, regs.sp - 2))
goto error;
}
#endif
TypeScript::Monitor(cx, script, regs.pc, rval);
}
END_CASE(JSOP_CALLPROP)
BEGIN_CASE(JSOP_SETGNAME)
BEGIN_CASE(JSOP_SETNAME)
BEGIN_CASE(JSOP_SETPROP)
BEGIN_CASE(JSOP_SETMETHOD)
{
const Value &rval = regs.sp[-1];
const Value &lval = regs.sp[-2];
Value rval = regs.sp[-1];
JS_ASSERT_IF(op == JSOP_SETMETHOD, IsFunctionObject(rval));
Value &lref = regs.sp[-2];
JS_ASSERT_IF(op == JSOP_SETNAME, lref.isObject());
JSObject *obj;
VALUE_TO_OBJECT(cx, &lref, obj);
if (!SetPropertyOperation(cx, regs.pc, lval, rval))
goto error;
JS_ASSERT_IF(op == JSOP_SETGNAME, obj == &regs.fp()->scopeChain().global());
do {
PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
/*
* Probe the property cache, specializing for two important
* set-property cases. First:
*
* function f(a, b, c) {
* var o = {p:a, q:b, r:c};
* return o;
* }
*
* or similar real-world cases, which evolve a newborn native
* object predicatably through some bounded number of property
* additions. And second:
*
* o.p = x;
*
* in a frequently executed method or loop body, where p will
* (possibly after the first iteration) always exist in native
* object o.
*/
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &name)) {
/*
* Property cache hit, only partially confirmed by testForSet. We
* know that the entry applies to regs.pc and that obj's shape
* matches.
*
* The entry predicts a set either an existing "own" property, or
* on a prototype property that has a setter.
*/
const Shape *shape = entry->prop;
if (entry->isOwnPropertyHit() ||
((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape))
{
JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
#ifdef DEBUG
if (entry->isOwnPropertyHit()) {
JS_ASSERT(obj->nativeContains(cx, *shape));
} else {
JS_ASSERT(obj2->nativeContains(cx, *shape));
JS_ASSERT(entry->isPrototypePropertyHit());
JS_ASSERT(entry->kshape != entry->pshape);
JS_ASSERT(!shape->hasSlot());
}
#endif
PCMETER(cache->pchits++);
PCMETER(cache->setpchits++);
NATIVE_SET(cx, obj, shape, entry, script->strictModeCode, &rval);
break;
}
PCMETER(cache->setpcmisses++);
LOAD_NAME(0, name);
} else {
JS_ASSERT(name);
}
if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
uintN defineHow;
if (op == JSOP_SETMETHOD)
defineHow = DNP_CACHE_RESULT | DNP_SET_METHOD;
else if (op == JSOP_SETNAME)
defineHow = DNP_CACHE_RESULT | DNP_UNQUALIFIED;
else
defineHow = DNP_CACHE_RESULT;
if (!SetPropertyHelper(cx, obj, name, defineHow, &rval, script->strictModeCode))
goto error;
} else {
if (!obj->setProperty(cx, name, &rval, script->strictModeCode))
goto error;
}
} while (0);
regs.sp[-2] = regs.sp[-1];
regs.sp--;
@ -2979,14 +3254,18 @@ BEGIN_CASE(JSOP_CALLELEM)
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) {
if (!OnUnknownMethod(cx, &thisv.toObject(), regs.sp[-1], regs.sp - 2))
/* For OnUnknownMethod, sp[-2] is the index, and sp[-1] is the object missing it. */
regs.sp[-2] = regs.sp[-1];
regs.sp[-1].setObject(*thisObj);
if (!OnUnknownMethod(cx, regs.sp - 2))
goto error;
}
} else
#endif
{
regs.sp[-1] = thisv;
}
regs.sp--;
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-2]);
}
END_CASE(JSOP_CALLELEM)
@ -3152,34 +3431,82 @@ BEGIN_CASE(JSOP_SETCALL)
}
END_CASE(JSOP_SETCALL)
BEGIN_CASE(JSOP_IMPLICITTHIS)
{
PropertyName *name;
LOAD_NAME(0, name);
JSObject *obj, *obj2;
JSProperty *prop;
if (!FindPropertyHelper(cx, name, false, false, &obj, &obj2, &prop))
goto error;
Value v;
if (!ComputeImplicitThis(cx, obj, &v))
goto error;
PUSH_COPY(v);
}
END_CASE(JSOP_IMPLICITTHIS)
#define PUSH_IMPLICIT_THIS(cx, obj, funval) \
JS_BEGIN_MACRO \
Value v; \
if (!ComputeImplicitThis(cx, obj, funval, &v)) \
goto error; \
PUSH_COPY(v); \
JS_END_MACRO \
BEGIN_CASE(JSOP_GETGNAME)
BEGIN_CASE(JSOP_CALLGNAME)
BEGIN_CASE(JSOP_NAME)
BEGIN_CASE(JSOP_CALLNAME)
{
JSObject *obj = &regs.fp()->scopeChain();
bool global = js_CodeSpec[op].format & JOF_GNAME;
if (global)
obj = &obj->global();
Value rval;
if (!NameOperation(cx, regs.pc, &rval))
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, regs.pc, obj, obj2, entry, name);
if (!name) {
ASSERT_VALID_PROPERTY_CACHE_HIT(obj, obj2, entry);
NATIVE_GET(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, &rval);
PUSH_COPY(rval);
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
PUSH_IMPLICIT_THIS(cx, obj, regs.sp[-1]);
len = JSOP_NAME_LENGTH;
DO_NEXT_OP(len);
}
JSProperty *prop;
if (!FindPropertyHelper(cx, name, true, global, &obj, &obj2, &prop))
goto error;
if (!prop) {
/* Kludge to allow (typeof foo == "undefined") tests. */
JSOp op2 = JSOp(regs.pc[JSOP_NAME_LENGTH]);
if (op2 == JSOP_TYPEOF) {
PUSH_UNDEFINED();
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
len = JSOP_NAME_LENGTH;
DO_NEXT_OP(len);
}
JSAutoByteString bytes;
if (js_AtomToPrintableString(cx, name, &bytes))
js_ReportIsNotDefined(cx, bytes.ptr());
goto error;
}
/* Take the slow path if prop was not found in a native object. */
if (!obj->isNative() || !obj2->isNative()) {
if (!obj->getProperty(cx, name, &rval))
goto error;
} else {
Shape *shape = (Shape *)prop;
JSObject *normalized = obj;
if (normalized->isWith() && !shape->hasDefaultGetter())
normalized = &normalized->asWith().object();
NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval);
}
PUSH_COPY(rval);
TypeScript::Monitor(cx, script, regs.pc, rval);
/* obj must be on the scope chain, thus not a function. */
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
PUSH_IMPLICIT_THIS(cx, obj, rval);
}
END_CASE(JSOP_NAME)
@ -3456,6 +3783,8 @@ BEGIN_CASE(JSOP_CALLARG)
uint32_t slot = GET_ARGNO(regs.pc);
JS_ASSERT(slot < regs.fp()->numFormalArgs());
PUSH_COPY(argv[slot]);
if (op == JSOP_CALLARG)
PUSH_UNDEFINED();
}
END_CASE(JSOP_GETARG)
@ -3468,7 +3797,6 @@ BEGIN_CASE(JSOP_SETARG)
END_CASE(JSOP_SETARG)
BEGIN_CASE(JSOP_GETLOCAL)
BEGIN_CASE(JSOP_CALLLOCAL)
{
/*
* Skip the same-compartment assertion if the local will be immediately
@ -3487,6 +3815,15 @@ BEGIN_CASE(JSOP_CALLLOCAL)
}
END_CASE(JSOP_GETLOCAL)
BEGIN_CASE(JSOP_CALLLOCAL)
{
uint32_t slot = GET_SLOTNO(regs.pc);
JS_ASSERT(slot < script->nslots);
PUSH_COPY(regs.fp()->slots()[slot]);
PUSH_UNDEFINED();
}
END_CASE(JSOP_CALLLOCAL)
BEGIN_CASE(JSOP_SETLOCAL)
{
uint32_t slot = GET_SLOTNO(regs.pc);
@ -3504,6 +3841,8 @@ BEGIN_CASE(JSOP_CALLFCSLOT)
PUSH_COPY(obj->toFunction()->getFlatClosureUpvar(index));
TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
if (op == JSOP_CALLFCSLOT)
PUSH_UNDEFINED();
}
END_CASE(JSOP_GETFCSLOT)
@ -4059,18 +4398,37 @@ BEGIN_CASE(JSOP_INITMETHOD)
JSObject *obj = &regs.sp[-2].toObject();
JS_ASSERT(obj->isObject());
JSAtom *atom;
LOAD_ATOM(0, atom);
jsid id = ATOM_TO_JSID(atom);
/*
* Probe the property cache to see if this is a set on an existing property
* added by a NEWOBJECT or a previous INITPROP. If the cached shape has a
* non-default setter, it must be __proto__, so don't handle this.
*/
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
if (JS_PROPERTY_CACHE(cx).testForSet(cx, regs.pc, obj, &entry, &obj2, &name) &&
entry->prop->hasDefaultSetter() &&
entry->isOwnPropertyHit())
{
JS_ASSERT(obj == obj2);
/* Fast path. Property cache hit. */
obj->nativeSetSlotWithType(cx, entry->prop, rval);
} else {
PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
LOAD_NAME(0, name);
uintN defineHow = (op == JSOP_INITMETHOD) ? DNP_SET_METHOD : 0;
if (JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, script->strictModeCode)
: !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, defineHow)) {
goto error;
uintN defineHow = (op == JSOP_INITMETHOD)
? DNP_CACHE_RESULT | DNP_SET_METHOD
: DNP_CACHE_RESULT;
if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
? !SetPropertyHelper(cx, obj, name, defineHow, &rval, script->strictModeCode)
: !DefineNativeProperty(cx, obj, name, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, defineHow)) {
goto error;
}
}
/* Common tail for property cache hit and miss cases. */
regs.sp--;
}
END_CASE(JSOP_INITPROP);
@ -4476,12 +4834,8 @@ BEGIN_CASE(JSOP_XMLNAME)
if (!obj->getGeneric(cx, id, &rval))
goto error;
regs.sp[-1] = rval;
if (op == JSOP_CALLXMLNAME) {
Value v;
if (!ComputeImplicitThis(cx, obj, &v))
goto error;
PUSH_COPY(v);
}
if (op == JSOP_CALLXMLNAME)
PUSH_IMPLICIT_THIS(cx, obj, rval);
}
END_CASE(JSOP_XMLNAME)

View File

@ -325,7 +325,7 @@ extern void
UnwindScope(JSContext *cx, uint32_t stackDepth);
extern bool
OnUnknownMethod(JSContext *cx, JSObject *obj, Value idval, Value *vp);
OnUnknownMethod(JSContext *cx, js::Value *vp);
extern bool
IsActiveWithOrBlock(JSContext *cx, JSObject &obj, uint32_t stackDepth);

View File

@ -51,8 +51,6 @@
#include "methodjit/MethodJIT.h"
#include "jsfuninlines.h"
#include "jspropertycacheinlines.h"
#include "jstypedarrayinlines.h"
#include "vm/Stack-inl.h"
@ -81,28 +79,34 @@ class AutoPreserveEnumerators {
* We can avoid computing |this| eagerly and push the implicit callee-coerced
* |this| value, undefined, if any of these conditions hold:
*
* 1. The nominal |this|, obj, is a global object.
* 1. The callee funval is not an object.
*
* 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
* 2. The nominal |this|, obj, is a global object.
*
* 3. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
* is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
* censored with undefined.
*
* Otherwise, we bind |this| to obj->thisObject(). Only names inside |with|
* statements and embedding-specific scope objects fall into this category.
* Only if funval is an object and obj is neither a declarative scope object to
* be censored, nor a global object, do we bind |this| to obj->thisObject().
* Only |with| statements and embedding-specific scope objects fall into this
* last ditch.
*
* If the callee is a strict mode function, then code implementing JSOP_THIS
* in the interpreter and JITs will leave undefined as |this|. If funval is a
* function not in strict mode, JSOP_THIS code replaces undefined with funval's
* global.
* If funval is a strict mode function, then code implementing JSOP_THIS in the
* interpreter and JITs will leave undefined as |this|. If funval is a function
* not in strict mode, JSOP_THIS code replaces undefined with funval's global.
*
* We set *vp to undefined early to reduce code size and bias this code for the
* common and future-friendly cases.
*/
inline bool
ComputeImplicitThis(JSContext *cx, JSObject *obj, Value *vp)
ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
{
vp->setUndefined();
if (!funval.isObject())
return true;
if (obj->isGlobal())
return true;
@ -174,264 +178,6 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
return pobj;
}
inline bool
NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow, Value *vp)
{
if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
/* Fast path for Object instance properties. */
JS_ASSERT(shape->hasSlot());
*vp = pobj->nativeGetSlot(shape->slot());
} else {
if (!js_NativeGet(cx, obj, pobj, shape, getHow, vp))
return false;
}
return true;
}
#if defined(DEBUG) && !defined(JS_THREADSAFE)
extern void
AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
PropertyCacheEntry *entry);
#else
inline void
AssertValidPropertyCacheHit(JSContext *cx, JSObject *start, JSObject *found,
PropertyCacheEntry *entry)
{}
#endif
inline bool
GetPropertyGenericMaybeCallXML(JSContext *cx, JSOp op, JSObject *obj, jsid id, Value *vp)
{
/*
* Various XML properties behave differently when accessed in a
* call vs. normal context, and getGeneric will not work right.
*/
#if JS_HAS_XML_SUPPORT
if (op == JSOP_CALLPROP && obj->isXML())
return js_GetXMLMethod(cx, obj, id, vp);
#endif
return obj->getGeneric(cx, id, vp);
}
inline bool
GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp)
{
JS_ASSERT(vp != &lval);
JSOp op = JSOp(*pc);
if (op == JSOP_LENGTH) {
/* Optimize length accesses on strings, arrays, and arguments. */
if (lval.isString()) {
*vp = Int32Value(lval.toString()->length());
return true;
}
if (lval.isMagic(JS_LAZY_ARGUMENTS)) {
*vp = Int32Value(cx->fp()->numActualArgs());
return true;
}
if (lval.isObject()) {
JSObject *obj = &lval.toObject();
if (obj->isArray()) {
jsuint length = obj->getArrayLength();
*vp = NumberValue(length);
return true;
}
if (obj->isArguments()) {
ArgumentsObject *argsobj = &obj->asArguments();
if (!argsobj->hasOverriddenLength()) {
uint32_t length = argsobj->initialLength();
JS_ASSERT(length < INT32_MAX);
*vp = Int32Value(int32_t(length));
return true;
}
}
if (js_IsTypedArray(obj)) {
JSObject *tarray = TypedArray::getTypedArray(obj);
*vp = Int32Value(TypedArray::getLength(tarray));
return true;
}
}
}
JSObject *obj = ValueToObjectOrPrototype(cx, lval);
if (!obj)
return false;
uintN flags = (op == JSOP_CALLPROP)
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER;
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
if (!name) {
AssertValidPropertyCacheHit(cx, obj, obj2, entry);
if (!NativeGet(cx, obj, obj2, entry->prop, flags, vp))
return false;
return true;
}
jsid id = ATOM_TO_JSID(name);
if (obj->getOps()->getProperty) {
if (!GetPropertyGenericMaybeCallXML(cx, op, obj, id, vp))
return false;
} else {
if (!GetPropertyHelper(cx, obj, id, flags, vp))
return false;
}
#if JS_HAS_NO_SUCH_METHOD
if (op == JSOP_CALLPROP &&
JS_UNLIKELY(vp->isPrimitive()) &&
lval.isObject())
{
if (!OnUnknownMethod(cx, obj, IdToValue(id), vp))
return false;
}
#endif
return true;
}
inline bool
SetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, const Value &rval)
{
JSObject *obj = ValueToObject(cx, lval);
if (!obj)
return false;
JS_ASSERT_IF(*pc == JSOP_SETMETHOD, IsFunctionObject(rval));
JS_ASSERT_IF(*pc == JSOP_SETNAME || *pc == JSOP_SETGNAME, lval.isObject());
JS_ASSERT_IF(*pc == JSOP_SETGNAME, obj == &cx->fp()->scopeChain().global());
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
if (JS_PROPERTY_CACHE(cx).testForSet(cx, pc, obj, &entry, &obj2, &name)) {
/*
* Property cache hit, only partially confirmed by testForSet. We
* know that the entry applies to regs.pc and that obj's shape
* matches.
*
* The entry predicts a set either an existing "own" property, or
* on a prototype property that has a setter.
*/
const Shape *shape = entry->prop;
JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
if (entry->isOwnPropertyHit() ||
((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
#ifdef DEBUG
if (entry->isOwnPropertyHit()) {
JS_ASSERT(obj->nativeContains(cx, *shape));
} else {
JS_ASSERT(obj2->nativeContains(cx, *shape));
JS_ASSERT(entry->isPrototypePropertyHit());
JS_ASSERT(entry->kshape != entry->pshape);
JS_ASSERT(!shape->hasSlot());
}
#endif
if (shape->hasDefaultSetter() && shape->hasSlot() && !shape->isMethod()) {
/* Fast path for, e.g., plain Object instance properties. */
obj->nativeSetSlotWithType(cx, shape, rval);
} else {
Value rref = rval;
bool strict = cx->stack.currentScript()->strictModeCode;
if (!js_NativeSet(cx, obj, shape, false, strict, &rref))
return false;
}
return true;
}
GET_NAME_FROM_BYTECODE(cx->stack.currentScript(), pc, 0, name);
}
bool strict = cx->stack.currentScript()->strictModeCode;
Value rref = rval;
JSOp op = JSOp(*pc);
jsid id = ATOM_TO_JSID(name);
if (JS_LIKELY(!obj->getOps()->setProperty)) {
uintN defineHow;
if (op == JSOP_SETMETHOD)
defineHow = DNP_CACHE_RESULT | DNP_SET_METHOD;
else if (op == JSOP_SETNAME)
defineHow = DNP_CACHE_RESULT | DNP_UNQUALIFIED;
else
defineHow = DNP_CACHE_RESULT;
if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rref, strict))
return false;
} else {
if (!obj->setGeneric(cx, id, &rref, strict))
return false;
}
return true;
}
inline bool
NameOperation(JSContext *cx, jsbytecode *pc, Value *vp)
{
JSObject *obj = cx->stack.currentScriptedScopeChain();
bool global = js_CodeSpec[*pc].format & JOF_GNAME;
if (global)
obj = &obj->global();
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, pc, obj, obj2, entry, name);
if (!name) {
AssertValidPropertyCacheHit(cx, obj, obj2, entry);
if (!NativeGet(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, vp))
return false;
return true;
}
jsid id = ATOM_TO_JSID(name);
JSProperty *prop;
if (!FindPropertyHelper(cx, name, true, global, &obj, &obj2, &prop))
return false;
if (!prop) {
/* Kludge to allow (typeof foo == "undefined") tests. */
JSOp op2 = JSOp(pc[JSOP_NAME_LENGTH]);
if (op2 == JSOP_TYPEOF) {
vp->setUndefined();
return true;
}
JSAutoByteString printable;
if (js_AtomToPrintableString(cx, name, &printable))
js_ReportIsNotDefined(cx, printable.ptr());
return false;
}
/* Take the slow path if prop was not found in a native object. */
if (!obj->isNative() || !obj2->isNative()) {
if (!obj->getGeneric(cx, id, vp))
return false;
} else {
Shape *shape = (Shape *)prop;
JSObject *normalized = obj;
if (normalized->getClass() == &WithClass && !shape->hasDefaultGetter())
normalized = &normalized->asWith().object();
if (!NativeGet(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, vp))
return false;
}
return true;
}
inline bool
FunctionNeedsPrologue(JSContext *cx, JSFunction *fun)
{

View File

@ -4643,6 +4643,7 @@ PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
}
shape = obj->nativeLookup(cx, id);
if (shape) {
PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
if (!obj->shadowingShapeChange(cx, *shape))
return false;
@ -5083,12 +5084,13 @@ js::LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
return LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp);
}
bool
PropertyCacheEntry *
js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool global,
JSObject **objp, JSObject **pobjp, JSProperty **propp)
{
jsid id = ATOM_TO_JSID(name);
JSObject *scopeChain, *obj, *parent, *pobj;
PropertyCacheEntry *entry;
int scopeIndex;
JSProperty *prop;
@ -5108,6 +5110,7 @@ js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool
}
/* Scan entries on the scope chain that we can cache across. */
entry = JS_NO_PROP_CACHE_FILL;
obj = scopeChain;
parent = obj->enclosingScope();
for (scopeIndex = 0;
@ -5116,7 +5119,7 @@ js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool
: !obj->getOps()->lookupProperty;
++scopeIndex) {
if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
return false;
return NULL;
if (prop) {
#ifdef DEBUG
@ -5139,16 +5142,14 @@ js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool
JS_ASSERT(obj->isNative());
}
#endif
/*
* We must check if pobj is native as a global object can have
* non-native prototype.
*/
if (cacheResult && pobj->isNative()) {
JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj,
(Shape *) prop);
entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj,
(Shape *) prop);
}
goto out;
}
@ -5162,9 +5163,11 @@ js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool
for (;;) {
if (!obj->lookupGeneric(cx, id, &pobj, &prop))
return false;
if (prop)
return NULL;
if (prop) {
PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
goto out;
}
/*
* We conservatively assume that a resolve hook could mutate the scope
@ -5183,7 +5186,7 @@ js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool
*objp = obj;
*pobjp = pobj;
*propp = prop;
return true;
return entry;
}
/*
@ -5230,7 +5233,9 @@ js::FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name)
return obj;
}
JS_ASSERT_IF(obj->isScope(), pobj->getClass() == obj->getClass());
JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, (Shape *) prop);
DebugOnly<PropertyCacheEntry*> entry =
JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, (Shape *) prop);
JS_ASSERT(entry);
return obj;
}
@ -5379,6 +5384,8 @@ js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsi
if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
return JS_FALSE;
PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++);
/* Record non-undefined values produced by the class getter hook. */
if (!vp->isUndefined())
AddTypePropertyId(cx, obj, id, *vp);
@ -5505,6 +5512,7 @@ js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp)
#endif
return GetPropertyHelper(cx, obj, id, getHow, vp);
}
JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, obj->isDenseArray());
#if JS_HAS_XML_SUPPORT
if (obj->isXML())
return js_GetXMLMethod(cx, obj, id, vp);
@ -5667,6 +5675,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
JS_ASSERT(shape->isDataDescriptor());
if (!shape->writable()) {
PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
/* Error in strict mode code, warn with strict option, otherwise do nothing. */
if (strict)
return obj->reportReadOnly(cx, id);

View File

@ -1822,7 +1822,7 @@ static const uintN RESOLVE_INFER = 0xffff;
/*
* If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success.
*/
extern bool
extern PropertyCacheEntry *
FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, bool global,
JSObject **objp, JSObject **pobjp, JSProperty **propp);
@ -1855,9 +1855,9 @@ js_FindVariableScope(JSContext *cx, JSFunction **funp);
* barrier, which is not needed when invoking a lambda that otherwise does not
* leak its callee reference (via arguments.callee or its name).
*/
const uintN JSGET_CACHE_RESULT = 1; // from a caching interpreter opcode
const uintN JSGET_METHOD_BARRIER = 0; // get can leak joined function object
const uintN JSGET_NO_METHOD_BARRIER = 1; // call to joined function can't leak
const uintN JSGET_CACHE_RESULT = 2; // from a caching interpreter opcode
const uintN JSGET_NO_METHOD_BARRIER = 2; // call to joined function can't leak
/*
* NB: js_NativeGet and js_NativeSet are called with the scope containing shape
@ -1941,13 +1941,13 @@ js_IsDelegate(JSContext *cx, JSObject *obj, const js::Value &v);
extern JSBool
js_PrimitiveToObject(JSContext *cx, js::Value *vp);
/*
* v and vp may alias. On successful return, vp->isObjectOrNull(). If vp is not
* rooted, the caller must root vp before the next possible GC.
*/
extern JSBool
js_ValueToObjectOrNull(JSContext *cx, const js::Value &v, JSObject **objp);
/* Throws if v could not be converted to an object. */
extern JSObject *
js_ValueToNonNullObject(JSContext *cx, const js::Value &v);
namespace js {
/*
@ -1965,17 +1965,15 @@ ToObject(JSContext *cx, Value *vp)
return ToObjectSlow(cx, vp);
}
/* As for ToObject, but preserves the original value. */
inline JSObject *
ValueToObject(JSContext *cx, const Value &v)
{
if (v.isObject())
return &v.toObject();
return js_ValueToNonNullObject(cx, v);
}
} /* namespace js */
/*
* v and vp may alias. On successful return, vp->isObject(). If vp is not
* rooted, the caller must root vp before the next possible GC.
*/
extern JSObject *
js_ValueToNonNullObject(JSContext *cx, const js::Value &v);
extern JSBool
js_XDRObject(JSXDRState *xdr, JSObject **objp);

View File

@ -633,23 +633,6 @@ JSObject::denseArrayHasInlineSlots() const
namespace js {
inline JSObject *
ValueToObjectOrPrototype(JSContext *cx, const Value &v)
{
if (v.isObject())
return &v.toObject();
GlobalObject *global = &cx->fp()->scopeChain().global();
if (v.isString())
return global->getOrCreateStringPrototype(cx);
if (v.isNumber())
return global->getOrCreateNumberPrototype(cx);
if (v.isBoolean())
return global->getOrCreateBooleanPrototype(cx);
JS_ASSERT(v.isNull() || v.isUndefined());
js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
return NULL;
}
/*
* Any name atom for a function which will be added as a DeclEnv object to the
* scope chain above call objects for fun.

View File

@ -2467,15 +2467,6 @@ InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
return JS_TRUE;
}
template <typename T>
void
Swap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
/*
* If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
* the decompiler starts at pc and continues until it reaches an opcode for
@ -4217,13 +4208,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = SprintCString(&ss->sprinter, rval);
break;
case JSOP_SWAP:
Swap(ss->offsets[ss->top-1], ss->offsets[ss->top-2]);
Swap(ss->opcodes[ss->top-1], ss->opcodes[ss->top-2]);
Swap(ss->bytecodes[ss->top-1], ss->bytecodes[ss->top-2]);
todo = -2;
break;
case JSOP_SETARG:
atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
LOCAL_ASSERT(atom);
@ -4305,8 +4289,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
op == JSOP_EVAL ||
op == JSOP_FUNCALL ||
op == JSOP_FUNAPPLY ||
op == JSOP_CALLPROP ||
op == JSOP_CALLELEM))
(js_CodeSpec[op].format & JOF_CALLOP)))
? JSOP_NAME
: saveop,
&lvalpc);
@ -5394,6 +5377,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
CopyDecompiledTextForDecomposedOp(jp, pc);
}
if (cs->format & JOF_CALLOP) {
todo = Sprint(&ss->sprinter, "");
if (todo < 0 || !PushOff(ss, todo, saveop))
return NULL;
}
pc += len;
}
@ -5819,16 +5808,6 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
}
default:;
}
/*
* Include the trailing SWAP when decompiling CALLPROP or CALLELEM ops,
* so that the result is the entire access rather than the lvalue.
*/
if (op == JSOP_CALLPROP || op == JSOP_CALLELEM) {
JS_ASSERT(*end == JSOP_SWAP);
end += JSOP_SWAP_LENGTH;
}
ptrdiff_t len = end - begin;
if (len <= 0)
return FAILED_EXPRESSION_DECOMPILER;

View File

@ -117,6 +117,8 @@ typedef enum JSOp {
#define JOF_LEFTASSOC (1U<<16) /* left-associative operator */
#define JOF_DECLARING (1U<<17) /* var, const, or function declaration op */
#define JOF_INDEXBASE (1U<<18) /* atom segment base setting prefix op */
#define JOF_CALLOP (1U<<19) /* call operation that pushes function and
this */
#define JOF_PARENHEAD (1U<<20) /* opcode consumes value of expression in
parenthesized statement head */
#define JOF_INVOKE (1U<<21) /* JSOP_CALL, JSOP_NEW, JSOP_EVAL */

View File

@ -176,7 +176,7 @@ OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_ATOM|J
OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC)
OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET)
OPDEF(JSOP_CALLNAME, 57, "callname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_CALLOP)
OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET)
OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET)
OPDEF(JSOP_DOUBLE, 60, "double", NULL, 3, 0, 1, 16, JOF_ATOM)
@ -367,10 +367,11 @@ OPDEF(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
/*
* Get a slot from a flat closure function object that contains a snapshot of
* the closure-invariant upvar values. The immediate operand indexes the upvar
* in the function's u.i.script->upvars() array.
* in the function's u.i.script->upvars() array. The CALL variant computes the
* callee and this-object in preparation for a JSOP_CALL.
*/
OPDEF(JSOP_GETFCSLOT, 136,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET)
OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET)
OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_TYPESET|JOF_CALLOP)
/*
* Define a local function object as a local variable.
@ -438,7 +439,7 @@ OPDEF(JSOP_XMLCOMMENT, 181,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLPI, 182,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM)
OPDEF(JSOP_DELDESC, 183,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3)
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_CALLOP|JOF_TMPSLOT3)
/* Enter a let block/expr whose slots are at the top of the stack. */
OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 3, -1, -1, 0, JOF_OBJECT)
@ -467,7 +468,7 @@ OPDEF(JSOP_RESETBASE0, 190,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STARTXML, 191,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STARTXMLEXPR, 192,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_CALLELEM, 193, "callelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC)
OPDEF(JSOP_CALLELEM, 193, "callelem", NULL, 1, 2, 2, 18, JOF_BYTE |JOF_ELEM|JOF_TYPESET|JOF_LEFTASSOC|JOF_CALLOP)
/*
* Stop interpretation, emitted at end of script to save the threaded bytecode
@ -481,7 +482,7 @@ OPDEF(JSOP_STOP, 194,"stop", NULL, 1, 0, 0, 0, JOF_BYTE)
*/
OPDEF(JSOP_GETXPROP, 195,"getxprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET)
OPDEF(JSOP_CALLXMLNAME, 196, "callxmlname", NULL, 1, 1, 2, 19, JOF_BYTE)
OPDEF(JSOP_CALLXMLNAME, 196, "callxmlname", NULL, 1, 1, 2, 19, JOF_BYTE|JOF_CALLOP)
/*
* Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef).
@ -529,9 +530,9 @@ OPDEF(JSOP_INDEXBASE1, 208,"indexbase1", NULL, 1, 0, 0, 0, JOF_BYTE |
OPDEF(JSOP_INDEXBASE2, 209,"indexbase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE3, 210,"indexbase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_CALLGNAME, 211, "callgname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME)
OPDEF(JSOP_CALLLOCAL, 212, "calllocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME)
OPDEF(JSOP_CALLARG, 213, "callarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME)
OPDEF(JSOP_CALLGNAME, 211, "callgname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_CALLOP|JOF_GNAME)
OPDEF(JSOP_CALLLOCAL, 212, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLARG, 213, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_BINDGNAME, 214, "bindgname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME)
/*
@ -569,6 +570,3 @@ OPDEF(JSOP_SHARPINIT, 224,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16
/* Pop the stack, convert to a jsid (int or string), and push back. */
OPDEF(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, 0, JOF_BYTE)
/* Push the implicit 'this' value for calls to the associated name. */
OPDEF(JSOP_IMPLICITTHIS, 226, "implicitthis", "", 3, 0, 1, 0, JOF_ATOM)

View File

@ -199,6 +199,9 @@ class InlineMap;
class LifoAlloc;
class PropertyCache;
struct PropertyCacheEntry;
class BaseShape;
class UnownedBaseShape;
struct Shape;

View File

@ -226,7 +226,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32_t id);
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 102)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 101)
/*
* Library-private functions.

File diff suppressed because it is too large Load Diff

View File

@ -103,6 +103,7 @@ class Compiler : public BaseCompiler
Call slowPathCall;
DataLabelPtr shape;
DataLabelPtr addrLabel;
bool usePropertyCache;
void copyTo(ic::GlobalNameIC &to, JSC::LinkBuffer &full, JSC::LinkBuffer &stub) {
to.fastPathStart = full.locationOf(fastPathStart);
@ -112,6 +113,7 @@ class Compiler : public BaseCompiler
JS_ASSERT(to.shapeOffset == offset);
to.slowPathCall = stub.locationOf(slowPathCall);
to.usePropertyCache = usePropertyCache;
}
};
@ -235,14 +237,15 @@ class Compiler : public BaseCompiler
};
struct PICGenInfo : public BaseICInfo {
PICGenInfo(ic::PICInfo::Kind kind, JSOp op)
: BaseICInfo(op), kind(kind), typeMonitored(false)
PICGenInfo(ic::PICInfo::Kind kind, JSOp op, bool usePropCache)
: BaseICInfo(op), kind(kind), usePropCache(usePropCache), typeMonitored(false)
{ }
ic::PICInfo::Kind kind;
Label typeCheck;
RegisterID shapeReg;
RegisterID objReg;
RegisterID typeReg;
bool usePropCache;
Label shapeGuard;
jsbytecode *pc;
PropertyName *name;
@ -258,7 +261,7 @@ class Compiler : public BaseCompiler
};
ic::GetPropLabels &getPropLabels() {
JS_ASSERT(kind == ic::PICInfo::GET);
JS_ASSERT(kind == ic::PICInfo::GET || kind == ic::PICInfo::CALL);
return getPropLabels_;
}
ic::SetPropLabels &setPropLabels() {
@ -270,7 +273,7 @@ class Compiler : public BaseCompiler
return bindNameLabels_;
}
ic::ScopeNameLabels &scopeNameLabels() {
JS_ASSERT(kind == ic::PICInfo::NAME ||
JS_ASSERT(kind == ic::PICInfo::NAME || kind == ic::PICInfo::CALLNAME ||
kind == ic::PICInfo::XNAME);
return scopeNameLabels_;
}
@ -280,6 +283,7 @@ class Compiler : public BaseCompiler
ic.shapeReg = shapeReg;
ic.objReg = objReg;
ic.name = name;
ic.usePropCache = usePropCache;
if (ic.isSet()) {
ic.u.vr = vr;
} else if (ic.isGet()) {
@ -594,9 +598,9 @@ private:
bool jumpAndRun(Jump j, jsbytecode *target, Jump *slow = NULL, bool *trampoline = NULL);
bool startLoop(jsbytecode *head, Jump entry, jsbytecode *entryTarget);
bool finishLoop(jsbytecode *head);
void jsop_bindname(PropertyName *name);
void jsop_bindname(PropertyName *name, bool usePropCache);
void jsop_setglobal(uint32_t index);
void jsop_getprop_slow(PropertyName *name, bool forPrototype = false);
void jsop_getprop_slow(PropertyName *name, bool usePropCache = true);
void jsop_getarg(uint32_t slot);
void jsop_setarg(uint32_t slot, bool popped);
void jsop_this();
@ -618,18 +622,25 @@ private:
void fixPrimitiveReturn(Assembler *masm, FrameEntry *fe);
void jsop_getgname(uint32_t index);
void jsop_getgname_slow(uint32_t index);
void jsop_setgname(PropertyName *name, bool popGuaranteed);
void jsop_setgname_slow(PropertyName *name);
void jsop_callgname_epilogue();
void jsop_setgname(PropertyName *name, bool usePropertyCache, bool popGuaranteed);
void jsop_setgname_slow(PropertyName *name, bool usePropertyCache);
void jsop_bindgname();
void jsop_setelem_slow();
void jsop_getelem_slow();
void jsop_callelem_slow();
bool jsop_getprop(PropertyName *name, JSValueType type,
bool typeCheck = true, bool forPrototype = false);
bool jsop_getprop_dispatch(PropertyName *name);
bool jsop_setprop(PropertyName *name, bool popGuaranteed);
void jsop_setprop_slow(PropertyName *name);
bool typeCheck = true, bool usePropCache = true);
bool jsop_setprop(PropertyName *name, bool usePropCache, bool popGuaranteed);
void jsop_setprop_slow(PropertyName *name, bool usePropCache = true);
bool jsop_callprop_slow(PropertyName *name);
bool jsop_callprop(PropertyName *name);
bool jsop_callprop_obj(PropertyName *name);
bool jsop_callprop_str(PropertyName *name);
bool jsop_callprop_generic(PropertyName *name);
bool jsop_callprop_dispatch(PropertyName *name);
bool jsop_instanceof();
void jsop_name(PropertyName *name, JSValueType type);
void jsop_name(PropertyName *name, JSValueType type, bool isCall);
bool jsop_xname(PropertyName *name);
void enterBlock(JSObject *obj);
void leaveBlock();
@ -687,7 +698,7 @@ private:
void convertForTypedArray(int atype, ValueRemat *vr, bool *allocated);
#endif
bool jsop_setelem(bool popGuaranteed);
bool jsop_getelem();
bool jsop_getelem(bool isCall);
void jsop_getelem_dense(bool isPacked);
void jsop_getelem_args();
#ifdef JS_METHODJIT_TYPED_ARRAY

View File

@ -2088,19 +2088,22 @@ mjit::Compiler::jsop_getelem_typed(int atype)
#endif /* JS_METHODJIT_TYPED_ARRAY */
bool
mjit::Compiler::jsop_getelem()
mjit::Compiler::jsop_getelem(bool isCall)
{
FrameEntry *obj = frame.peek(-2);
FrameEntry *id = frame.peek(-1);
if (!IsCacheableGetElem(obj, id)) {
jsop_getelem_slow();
if (isCall)
jsop_callelem_slow();
else
jsop_getelem_slow();
return true;
}
// If the object is definitely an arguments object, a dense array or a typed array
// we can generate code directly without using an inline cache.
if (cx->typeInferenceEnabled() && !id->isType(JSVAL_TYPE_STRING)) {
if (cx->typeInferenceEnabled() && !id->isType(JSVAL_TYPE_STRING) && !isCall) {
types::TypeSet *types = analysis->poppedTypes(PC, 1);
if (types->isLazyArguments(cx) && !outerScript->analysis()->modifiesArguments()) {
// Inline arguments path.
@ -2134,7 +2137,10 @@ mjit::Compiler::jsop_getelem()
frame.forgetMismatchedObject(obj);
if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) {
jsop_getelem_slow();
if (isCall)
jsop_callelem_slow();
else
jsop_getelem_slow();
return true;
}
@ -2162,6 +2168,14 @@ mjit::Compiler::jsop_getelem()
// Get a mutable register for the object. This will be the data reg.
ic.objReg = frame.copyDataIntoReg(obj);
// For potential dense array calls, grab an extra reg to save the
// outgoing object.
MaybeRegisterID thisReg;
if (isCall && id->mightBeType(JSVAL_TYPE_INT32)) {
thisReg = frame.allocReg();
masm.move(ic.objReg, thisReg.reg());
}
// Get a mutable register for pushing the result type. We kill two birds
// with one stone by making sure, if the key type is not known, to be loaded
// into this register. In this case it is both an input and an output.
@ -2212,6 +2226,14 @@ mjit::Compiler::jsop_getelem()
Assembler::FastArrayLoadFails fails =
masm.fastArrayLoad(ic.objReg, key, ic.typeReg, ic.objReg);
// Store the object back to sp[-1] for calls. This must occur after
// all guards because otherwise sp[-1] will be clobbered.
if (isCall) {
Address thisSlot = frame.addressOf(id);
masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), thisReg.reg(), thisSlot);
frame.freeReg(thisReg.reg());
}
stubcc.linkExitDirect(fails.rangeCheck, ic.slowPathStart);
stubcc.linkExitDirect(fails.holeCheck, ic.slowPathStart);
} else {
@ -2226,9 +2248,15 @@ mjit::Compiler::jsop_getelem()
objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm);
#ifdef JS_POLYIC
passICAddress(&ic);
ic.slowPathCall = OOL_STUBCALL(ic::GetElement, REJOIN_FALLTHROUGH);
if (isCall)
ic.slowPathCall = OOL_STUBCALL(ic::CallElement, REJOIN_FALLTHROUGH);
else
ic.slowPathCall = OOL_STUBCALL(ic::GetElement, REJOIN_FALLTHROUGH);
#else
ic.slowPathCall = OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
if (isCall)
ic.slowPathCall = OOL_STUBCALL(stubs::CallElem, REJOIN_FALLTHROUGH);
else
ic.slowPathCall = OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
#endif
testPushedType(REJOIN_FALLTHROUGH, -2);
@ -2242,15 +2270,17 @@ mjit::Compiler::jsop_getelem()
frame.pushRegs(ic.typeReg, ic.objReg, knownPushedType(0));
BarrierState barrier = testBarrier(ic.typeReg, ic.objReg, false, false,
/* force = */ ic.forcedTypeBarrier);
if (isCall)
frame.pushSynced(knownPushedType(1));
stubcc.rejoin(Changes(1));
stubcc.rejoin(Changes(isCall ? 2 : 1));
#ifdef JS_POLYIC
if (!getElemICs.append(ic))
return false;
#endif
finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
finishBarrier(barrier, REJOIN_FALLTHROUGH, isCall ? 1 : 0);
return true;
}

View File

@ -51,6 +51,7 @@
#include "jsiter.h"
#include "jstypes.h"
#include "methodjit/StubCalls.h"
#include "jspropertycache.h"
#include "methodjit/MonoIC.h"
#include "jsanalyze.h"
#include "methodjit/BaseCompiler.h"
@ -58,6 +59,7 @@
#include "vm/Debugger.h"
#include "jsinterpinlines.h"
#include "jspropertycacheinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jsobjinlines.h"
@ -860,10 +862,25 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
/*
* We don't rejoin until after the native stub finishes execution, in
* which case the return value will be in memory. For lowered natives,
* the return value will be in the 'this' value's slot.
* the return value will be in the 'this' value's slot. For getters,
* the result is at nextsp[0] (see ic::CallProp).
*/
if (rejoin != REJOIN_NATIVE)
if (rejoin == REJOIN_NATIVE_LOWERED) {
nextsp[-1] = nextsp[0];
} else if (rejoin == REJOIN_NATIVE_GETTER) {
if (js_CodeSpec[op].format & JOF_CALLOP) {
/*
* If we went through jsop_callprop_obj then the 'this' value
* is still in its original slot and hasn't been shifted yet,
* so fix that now. Yuck.
*/
if (nextsp[-2].isObject())
nextsp[-1] = nextsp[-2];
nextsp[-2] = nextsp[0];
} else {
nextsp[-1] = nextsp[0];
}
}
/* Release this reference on the orphaned native stub. */
RemoveOrphanedNative(cx, fp);
@ -1012,6 +1029,42 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
* fused opcode which needs to be finished.
*/
switch (op) {
case JSOP_NAME:
case JSOP_GETGNAME:
case JSOP_GETFCSLOT:
case JSOP_GETPROP:
case JSOP_GETXPROP:
case JSOP_LENGTH:
/* Non-fused opcode, state is already correct for the next op. */
f.regs.pc = nextpc;
break;
case JSOP_CALLGNAME:
case JSOP_CALLNAME:
if (!ComputeImplicitThis(cx, &fp->scopeChain(), nextsp[-2], &nextsp[-1]))
return js_InternalThrow(f);
f.regs.pc = nextpc;
break;
case JSOP_CALLFCSLOT:
/* |this| is always undefined for CALLGLOBAL/CALLFCSLOT. */
nextsp[-1].setUndefined();
f.regs.pc = nextpc;
break;
case JSOP_CALLPROP: {
/*
* CALLPROP is compiled in terms of GETPROP for known strings.
* In such cases the top two entries are in place, but are swapped.
*/
JS_ASSERT(nextsp[-2].isString());
Value tmp = nextsp[-2];
nextsp[-2] = nextsp[-1];
nextsp[-1] = tmp;
f.regs.pc = nextpc;
break;
}
case JSOP_INSTANCEOF: {
/*
* If we recompiled from a getprop used within JSOP_INSTANCEOF,
@ -1028,8 +1081,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
}
default:
f.regs.pc = nextpc;
break;
JS_NOT_REACHED("Bad rejoin getter op");
}
break;
@ -1084,8 +1136,10 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM
* The result may not have been marked if we bailed out while inside a stub
* for the op.
*/
if (f.regs.pc == nextpc && (js_CodeSpec[op].format & JOF_TYPESET))
types::TypeScript::Monitor(cx, script, pc, f.regs.sp[-1]);
if (f.regs.pc == nextpc && (js_CodeSpec[op].format & JOF_TYPESET)) {
int which = (js_CodeSpec[op].format & JOF_CALLOP) ? -2 : -1; /* Yuck. */
types::TypeScript::Monitor(cx, script, pc, f.regs.sp[which]);
}
/* Mark the entry frame as unfinished, and update the regs to resume at. */
JaegerStatus status = skipTrap ? Jaeger_UnfinishedAtTrap : Jaeger_Unfinished;

View File

@ -1363,6 +1363,9 @@ PICPCComparator(const void *key, const void *entry)
const jsbytecode *pc = (const jsbytecode *)key;
const ic::PICInfo *pic = (const ic::PICInfo *)entry;
if (ic::PICInfo::CALL != pic->kind)
return ic::PICInfo::CALL - pic->kind;
/*
* We can't just return |pc - pic->pc| because the pointers may be
* far apart and an int (or even a ptrdiff_t) may not be large

View File

@ -79,7 +79,7 @@ static void
PatchGetFallback(VMFrame &f, ic::GetGlobalNameIC *ic)
{
Repatcher repatch(f.jit());
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::Name));
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName));
repatch.relink(ic->slowPathCall, fptr);
}
@ -94,7 +94,7 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
const Shape *shape = obj.nativeLookup(f.cx, js_CheckForStringIndex(ATOM_TO_JSID(name)));
if (monitor.recompiled()) {
stubs::Name(f);
stubs::GetGlobalName(f);
return;
}
@ -104,7 +104,7 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
{
if (shape)
PatchGetFallback(f, ic);
stubs::Name(f);
stubs::GetGlobalName(f);
return;
}
uint32_t slot = shape->slot();
@ -119,7 +119,7 @@ ic::GetGlobalName(VMFrame &f, ic::GetGlobalNameIC *ic)
repatcher.patchAddressOffsetForValueLoad(label, index * sizeof(Value));
/* Do load anyway... this time. */
stubs::Name(f);
stubs::GetGlobalName(f);
}
template <JSBool strict>
@ -132,12 +132,24 @@ DisabledSetGlobal(VMFrame &f, ic::SetGlobalNameIC *ic)
template void JS_FASTCALL DisabledSetGlobal<true>(VMFrame &f, ic::SetGlobalNameIC *ic);
template void JS_FASTCALL DisabledSetGlobal<false>(VMFrame &f, ic::SetGlobalNameIC *ic);
template <JSBool strict>
static void JS_FASTCALL
DisabledSetGlobalNoCache(VMFrame &f, ic::SetGlobalNameIC *ic)
{
stubs::SetGlobalNameNoCache<strict>(f, f.script()->getName(GET_INDEX(f.pc())));
}
template void JS_FASTCALL DisabledSetGlobalNoCache<true>(VMFrame &f, ic::SetGlobalNameIC *ic);
template void JS_FASTCALL DisabledSetGlobalNoCache<false>(VMFrame &f, ic::SetGlobalNameIC *ic);
static void
PatchSetFallback(VMFrame &f, ic::SetGlobalNameIC *ic)
{
JSScript *script = f.script();
Repatcher repatch(f.jit());
VoidStubSetGlobal stub = STRICT_VARIANT(DisabledSetGlobal);
VoidStubSetGlobal stub = ic->usePropertyCache
? STRICT_VARIANT(DisabledSetGlobal)
: STRICT_VARIANT(DisabledSetGlobalNoCache);
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stub));
repatch.relink(ic->slowPathCall, fptr);
}
@ -205,7 +217,10 @@ ic::SetGlobalName(VMFrame &f, ic::SetGlobalNameIC *ic)
THROW();
}
STRICT_VARIANT(stubs::SetGlobalName)(f, name);
if (ic->usePropertyCache)
STRICT_VARIANT(stubs::SetGlobalName)(f, name);
else
STRICT_VARIANT(stubs::SetGlobalNameNoCache)(f, name);
}
class EqualityICLinker : public LinkerHelper

View File

@ -128,6 +128,7 @@ struct GlobalNameIC
*/
int32_t loadStoreOffset : 15;
int32_t shapeOffset : 15;
bool usePropertyCache : 1;
};
struct GetGlobalNameIC : public GlobalNameIC

View File

@ -49,6 +49,8 @@
#include "jsatominlines.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
#include "jspropertycache.h"
#include "jspropertycacheinlines.h"
#include "jsinterpinlines.h"
#include "jsautooplen.h"
@ -111,6 +113,12 @@ class PICStubCompiler : public BaseCompiler
gcNumber(f.cx->runtime->gcNumber), canCallHook(pic.canCallHook)
{ }
bool isCallOp() const {
if (pic.kind == ic::PICInfo::CALL)
return true;
return !!(js_CodeSpec[pic.op].format & JOF_CALLOP);
}
LookupStatus error() {
/*
* N.B. Do not try to disable the IC, we do not want to guard on
@ -776,7 +784,7 @@ struct GetPropHelper {
LookupStatus testForGet() {
if (!shape->hasDefaultGetter()) {
if (shape->isMethod()) {
if (JSOp(*f.pc()) != JSOP_CALLPROP)
if (!ic.isCallOp())
return ic.disable(cx, "method valued shape");
} else {
if (shape->hasGetterValue())
@ -819,9 +827,9 @@ class GetPropCompiler : public PICStubCompiler
int lastStubSecondShapeGuard;
public:
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, PropertyName *name,
VoidStubPIC stub)
: PICStubCompiler("getprop", f, script, pic,
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic,
PropertyName *name, VoidStubPIC stub)
: PICStubCompiler(pic.kind == ic::PICInfo::CALL ? "callprop" : "getprop", f, script, pic,
JS_FUNC_TO_DATA_PTR(void *, stub)),
obj(obj),
name(name),
@ -844,9 +852,20 @@ class GetPropCompiler : public PICStubCompiler
repatcher.relink(labels.getInlineTypeJump(pic.fastPathStart), pic.getSlowTypeCheck());
}
JS_ASSERT(pic.kind == ic::PICInfo::GET);
VoidStubPIC stub;
switch (pic.kind) {
case ic::PICInfo::GET:
stub = ic::GetProp;
break;
case ic::PICInfo::CALL:
stub = ic::CallProp;
break;
default:
JS_NOT_REACHED("invalid pic kind for GetPropCompiler::reset");
return;
}
FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::GetProp));
FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, stub));
repatcher.relink(pic.slowPathCall, target);
}
@ -969,20 +988,14 @@ class GetPropCompiler : public PICStubCompiler
return Lookup_Cacheable;
}
LookupStatus generateStringPropertyStub()
LookupStatus generateStringCallStub()
{
JS_ASSERT(pic.hasTypeCheck());
JS_ASSERT(pic.kind == ic::PICInfo::CALL);
if (!f.fp()->script()->hasGlobal())
return disable("String.prototype without compile-and-go global");
RecompilationMonitor monitor(f.cx);
JSObject *obj = f.fp()->scopeChain().global().getOrCreateStringPrototype(f.cx);
if (!obj)
return error();
if (monitor.recompiled())
return Lookup_Uncacheable;
GetPropHelper<GetPropCompiler> getprop(cx, obj, name, *this, f);
LookupStatus status = getprop.lookupAndTest();
if (status != Lookup_Cacheable)
@ -1000,6 +1013,18 @@ class GetPropCompiler : public PICStubCompiler
Jump notString = masm.branchPtr(Assembler::NotEqual, pic.typeReg(),
ImmType(JSVAL_TYPE_STRING));
/*
* Sink pic.objReg, since we're about to lose it.
*
* Note: This is really hacky, and relies on f.regs.sp being set
* correctly in ic::CallProp. Should we just move the store higher
* up in the fast path, or put this offset in PICInfo?
*/
uint32_t thisvOffset = uint32_t(f.regs.sp - f.fp()->slots()) - 1;
Address thisv(JSFrameReg, sizeof(StackFrame) + thisvOffset * sizeof(Value));
masm.storeValueFromComponents(ImmType(JSVAL_TYPE_STRING),
pic.objReg, thisv);
/*
* Clobber objReg with String.prototype and do some PIC stuff. Well,
* really this is now a MIC, except it won't ever be patched, so we
@ -1041,6 +1066,7 @@ class GetPropCompiler : public PICStubCompiler
/* Disable the PIC so we don't keep generating stubs on the above shape mismatch. */
disable("generated string call stub");
return Lookup_Cacheable;
}
@ -1446,6 +1472,9 @@ class ScopeNameCompiler : public PICStubCompiler
case ic::PICInfo::XNAME:
stub = ic::XName;
break;
case ic::PICInfo::CALLNAME:
stub = ic::CallName;
break;
default:
JS_NOT_REACHED("Invalid pic kind in ScopeNameCompiler::reset");
return;
@ -1461,7 +1490,7 @@ class ScopeNameCompiler : public PICStubCompiler
ScopeNameLabels &labels = pic.scopeNameLabels();
/* For GETXPROP, the object is already in objReg. */
if (pic.kind == ic::PICInfo::NAME)
if (pic.kind == ic::PICInfo::NAME || pic.kind == ic::PICInfo::CALLNAME)
masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg);
JS_ASSERT(obj == getprop.holder);
@ -1473,7 +1502,7 @@ class ScopeNameCompiler : public PICStubCompiler
/* If a scope chain walk was required, the final object needs a NULL test. */
MaybeJump finalNull;
if (pic.kind == ic::PICInfo::NAME)
if (pic.kind == ic::PICInfo::NAME || pic.kind == ic::PICInfo::CALLNAME)
finalNull = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
masm.loadShape(pic.objReg, pic.shapeReg);
Jump finalShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
@ -1481,6 +1510,18 @@ class ScopeNameCompiler : public PICStubCompiler
masm.loadObjProp(obj, pic.objReg, getprop.shape, pic.shapeReg, pic.objReg);
/*
* For CALLNAME we must store undefined as the this-value. A non-strict
* mode callee function replaces undefined with its global on demand in
* code generated for JSOP_THIS.
*/
if (pic.kind == ic::PICInfo::CALLNAME) {
/* Store undefined this-value. */
Value *thisVp = &cx->regs().sp[1];
Address thisSlot(JSFrameReg, StackFrame::offsetOfFixed(thisVp - cx->fp()->slots()));
masm.storeValue(UndefinedValue(), thisSlot);
}
Jump done = masm.jump();
/* All failures flow to here, so there is a common point to patch. */
@ -1533,7 +1574,7 @@ class ScopeNameCompiler : public PICStubCompiler
ScopeNameLabels &labels = pic.scopeNameLabels();
/* For GETXPROP, the object is already in objReg. */
if (pic.kind == ic::PICInfo::NAME)
if (pic.kind == ic::PICInfo::NAME || pic.kind == ic::PICInfo::CALLNAME)
masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg);
JS_ASSERT(obj == getprop.holder);
@ -1555,12 +1596,24 @@ class ScopeNameCompiler : public PICStubCompiler
/* If a scope chain walk was required, the final object needs a NULL test. */
MaybeJump finalNull;
if (pic.kind == ic::PICInfo::NAME)
if (pic.kind == ic::PICInfo::NAME || pic.kind == ic::PICInfo::CALLNAME)
finalNull = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
masm.loadShape(pic.objReg, pic.shapeReg);
Jump finalShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
ImmPtr(getprop.holder->lastProperty()));
/*
* For CALLNAME we have to store the this-value. Since we guarded on
* IsCacheableNonGlobalScope it's always undefined. This matches the decision
* tree in ComputeImplicitThis.
*/
if (pic.kind == ic::PICInfo::CALLNAME) {
JS_ASSERT(obj->isCall());
Value *thisVp = &cx->regs().sp[1];
Address thisSlot(JSFrameReg, StackFrame::offsetOfFixed(thisVp - cx->fp()->slots()));
masm.storeValue(UndefinedValue(), thisSlot);
}
/* Get callobj's stack frame. */
masm.loadObjPrivate(pic.objReg, pic.shapeReg, getprop.holder->numFixedSlots());
@ -1671,7 +1724,7 @@ class ScopeNameCompiler : public PICStubCompiler
return disable("scope object not handled yet");
}
bool retrieve(Value *vp, PICInfo::Kind kind)
bool retrieve(Value *vp, Value *thisvp, PICInfo::Kind kind)
{
JSObject *obj = getprop.obj;
JSObject *holder = getprop.holder;
@ -1695,6 +1748,8 @@ class ScopeNameCompiler : public PICStubCompiler
if (!getprop.shape) {
if (!obj->getProperty(cx, name, vp))
return false;
if (thisvp)
return ComputeImplicitThis(cx, obj, *vp, thisvp);
return true;
}
@ -1703,6 +1758,8 @@ class ScopeNameCompiler : public PICStubCompiler
if (obj->isWith() && !shape->hasDefaultGetter())
normalized = &obj->asWith().object();
NATIVE_GET(cx, normalized, holder, shape, JSGET_METHOD_BARRIER, vp, return false);
if (thisvp)
return ComputeImplicitThis(cx, normalized, *vp, thisvp);
return true;
}
};
@ -1835,14 +1892,34 @@ class BindNameCompiler : public PICStubCompiler
}
};
static inline void
GetPropWithStub(VMFrame &f, ic::PICInfo *pic, VoidStubPIC stub)
static void JS_FASTCALL
DisabledGetPropIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetProp(f);
}
static void JS_FASTCALL
DisabledGetPropICNoCache(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetPropNoCache(f, pic->name);
}
void JS_FASTCALL
ic::GetProp(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
PropertyName *name = pic->name;
if (name == f.cx->runtime->atomState.lengthAtom) {
if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) {
if (f.regs.sp[-1].isString()) {
GetPropCompiler cc(f, script, NULL, *pic, NULL, DisabledGetPropIC);
LookupStatus status = cc.generateStringLengthStub();
if (status == Lookup_Error)
THROW();
JSString *str = f.regs.sp[-1].toString();
f.regs.sp[-1].setInt32(str->length());
return;
} else if (f.regs.sp[-1].isMagic(JS_LAZY_ARGUMENTS)) {
f.regs.sp[-1].setInt32(f.regs.fp()->numActualArgs());
return;
} else if (!f.regs.sp[-1].isPrimitive()) {
@ -1850,7 +1927,7 @@ GetPropWithStub(VMFrame &f, ic::PICInfo *pic, VoidStubPIC stub)
if (obj->isArray() ||
(obj->isArguments() && !obj->asArguments().hasOverriddenLength()) ||
obj->isString()) {
GetPropCompiler cc(f, script, obj, *pic, NULL, stub);
GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledGetPropIC);
if (obj->isArray()) {
LookupStatus status = cc.generateArrayLengthStub();
if (status == Lookup_Error)
@ -1871,70 +1948,45 @@ GetPropWithStub(VMFrame &f, ic::PICInfo *pic, VoidStubPIC stub)
return;
}
}
name = f.cx->runtime->atomState.lengthAtom;
}
if (f.regs.sp[-1].isString()) {
GetPropCompiler cc(f, script, NULL, *pic, name, stub);
if (name == f.cx->runtime->atomState.lengthAtom) {
LookupStatus status = cc.generateStringLengthStub();
if (status == Lookup_Error)
THROW();
JSString *str = f.regs.sp[-1].toString();
f.regs.sp[-1].setInt32(str->length());
} else {
LookupStatus status = cc.generateStringPropertyStub();
if (status == Lookup_Error)
THROW();
JSObject *obj = ValueToObject(f.cx, f.regs.sp[-1]);
if (!obj)
THROW();
if (!obj->getProperty(f.cx, name, &f.regs.sp[-1]))
THROW();
}
return;
}
/*
* ValueToObject can trigger recompilations if it lazily initializes any
* of the primitive classes (Boolean, Number, String). :XXX: if these
* classes are made eager then this monitoring is not necessary.
*/
RecompilationMonitor monitor(f.cx);
JSObject *obj = ValueToObject(f.cx, f.regs.sp[-1]);
JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
if (!obj)
THROW();
if (!monitor.recompiled() && pic->shouldUpdate(f.cx)) {
VoidStubPIC stub = pic->usePropCache
? DisabledGetPropIC
: DisabledGetPropICNoCache;
GetPropCompiler cc(f, script, obj, *pic, name, stub);
if (!cc.update())
THROW();
}
Value v;
if (!GetPropertyGenericMaybeCallXML(f.cx, JSOp(*f.pc()), obj, ATOM_TO_JSID(name), &v))
if (!obj->getProperty(f.cx, name, &v))
THROW();
f.regs.sp[-1] = v;
}
static void JS_FASTCALL
DisabledGetPropIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetProp(f, pic->name);
}
static void JS_FASTCALL
DisabledGetPropNoCacheIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetPropNoCache(f, pic->name);
}
void JS_FASTCALL
ic::GetProp(VMFrame &f, ic::PICInfo *pic)
{
GetPropWithStub(f, pic, DisabledGetPropIC);
}
void JS_FASTCALL
ic::GetPropNoCache(VMFrame &f, ic::PICInfo *pic)
{
GetPropWithStub(f, pic, DisabledGetPropNoCacheIC);
/*
* The PIC stores whether to use the property cache or not. We use two different
* stub calls so we can distinguish uncached calls made to construct this from
* any cached calls at the first opcode in a script.
*/
GetProp(f, pic);
}
template <JSBool strict>
@ -1944,21 +1996,32 @@ DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic)
stubs::SetName<strict>(f, pic->name);
}
template <JSBool strict>
static void JS_FASTCALL
DisabledSetPropICNoCache(VMFrame &f, ic::PICInfo *pic)
{
stubs::SetPropNoCache<strict>(f, pic->name);
}
void JS_FASTCALL
ic::SetProp(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
JS_ASSERT(pic->isSet());
VoidStubPIC stub = STRICT_VARIANT(DisabledSetPropIC);
VoidStubPIC stub = pic->usePropCache
? STRICT_VARIANT(DisabledSetPropIC)
: STRICT_VARIANT(DisabledSetPropICNoCache);
// Save this in case the compiler triggers a recompilation of this script.
PropertyName *name = pic->name;
VoidStubName nstub = STRICT_VARIANT(stubs::SetName);
VoidStubName nstub = pic->usePropCache
? STRICT_VARIANT(stubs::SetName)
: STRICT_VARIANT(stubs::SetPropNoCache);
RecompilationMonitor monitor(f.cx);
JSObject *obj = ValueToObject(f.cx, f.regs.sp[-2]);
JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
if (!obj)
THROW();
@ -1974,6 +2037,122 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic)
nstub(f, name);
}
static void JS_FASTCALL
DisabledCallPropIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::CallProp(f, pic->name);
}
void JS_FASTCALL
ic::CallProp(VMFrame &f, ic::PICInfo *pic)
{
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
JSScript *script = f.fp()->script();
RecompilationMonitor monitor(cx);
Value lval;
lval = regs.sp[-1];
// Do this first in case js_GetClassPrototype triggers a recompilation.
PropertyName *name = pic->name;
Value objv;
if (lval.isObject()) {
objv = lval;
} else {
GlobalObject &global = f.fp()->scopeChain().global();
JSObject *pobj;
if (lval.isString()) {
pobj = global.getOrCreateStringPrototype(cx);
} else if (lval.isNumber()) {
pobj = global.getOrCreateNumberPrototype(cx);
} else if (lval.isBoolean()) {
pobj = global.getOrCreateBooleanPrototype(cx);
} else {
JS_ASSERT(lval.isNull() || lval.isUndefined());
js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
THROW();
}
if (!pobj)
THROW();
objv.setObject(*pobj);
}
JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
Value rval;
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name_;
JS_PROPERTY_CACHE(cx).test(cx, f.pc(), aobj, obj2, entry, name_);
if (!name_) {
NATIVE_GET(cx, &objv.toObject(), obj2, entry->prop, JSGET_NO_METHOD_BARRIER, &rval,
THROW());
/*
* Adjust the stack to reflect the height after the GETPROP, here and
* below. Getter hook ICs depend on this to know which value of sp they
* are updating for consistent rejoins, don't modify this!
*/
regs.sp++;
regs.sp[-2] = rval;
regs.sp[-1] = lval;
} else {
/* Cache miss: use the name loaded for us under PropertyCache::test. */
regs.sp++;
regs.sp[-1].setNull();
if (lval.isObject()) {
if (!GetMethod(cx, &objv.toObject(), name,
JS_LIKELY(!objv.toObject().getOps()->getProperty)
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_NO_METHOD_BARRIER,
&rval))
{
THROW();
}
regs.sp[-1] = objv;
regs.sp[-2] = rval;
} else {
JS_ASSERT(!objv.toObject().getOps()->getProperty);
if (!GetPropertyHelper(cx, &objv.toObject(), name,
JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
&rval))
{
THROW();
}
regs.sp[-1] = lval;
regs.sp[-2] = rval;
}
}
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) {
regs.sp[-2].setString(name);
if (!OnUnknownMethod(cx, regs.sp - 2))
THROW();
}
#endif
if (monitor.recompiled())
return;
GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->name, DisabledCallPropIC);
if (lval.isObject()) {
if (pic->shouldUpdate(cx)) {
LookupStatus status = cc.update();
if (status == Lookup_Error)
THROW();
}
} else if (lval.isString()) {
LookupStatus status = cc.generateStringCallStub();
if (status == Lookup_Error)
THROW();
} else {
cc.disable("non-string primitive");
}
}
static void JS_FASTCALL
DisabledNameIC(VMFrame &f, ic::PICInfo *pic)
{
@ -1983,7 +2162,7 @@ DisabledNameIC(VMFrame &f, ic::PICInfo *pic)
static void JS_FASTCALL
DisabledXNameIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::GetProp(f, pic->name);
stubs::GetProp(f);
}
void JS_FASTCALL
@ -2001,7 +2180,7 @@ ic::XName(VMFrame &f, ic::PICInfo *pic)
THROW();
Value rval;
if (!cc.retrieve(&rval, PICInfo::XNAME))
if (!cc.retrieve(&rval, NULL, PICInfo::XNAME))
THROW();
f.regs.sp[-1] = rval;
}
@ -2018,15 +2197,46 @@ ic::Name(VMFrame &f, ic::PICInfo *pic)
THROW();
Value rval;
if (!cc.retrieve(&rval, PICInfo::NAME))
if (!cc.retrieve(&rval, NULL, PICInfo::NAME))
THROW();
f.regs.sp[0] = rval;
}
static void JS_FASTCALL
DisabledCallNameIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::CallName(f);
}
void JS_FASTCALL
ic::CallName(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->name, DisabledCallNameIC);
LookupStatus status = cc.updateForName();
if (status == Lookup_Error)
THROW();
Value rval, thisval;
if (!cc.retrieve(&rval, &thisval, PICInfo::CALLNAME))
THROW();
f.regs.sp[0] = rval;
f.regs.sp[1] = thisval;
}
static void JS_FASTCALL
DisabledBindNameIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::BindName(f, pic->name);
stubs::BindName(f);
}
static void JS_FASTCALL
DisabledBindNameICNoCache(VMFrame &f, ic::PICInfo *pic)
{
stubs::BindNameNoCache(f, pic->name);
}
void JS_FASTCALL
@ -2034,7 +2244,9 @@ ic::BindName(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
VoidStubPIC stub = DisabledBindNameIC;
VoidStubPIC stub = pic->usePropCache
? DisabledBindNameIC
: DisabledBindNameICNoCache;
BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->name, stub);
JSObject *obj = cc.update();
@ -2044,6 +2256,12 @@ ic::BindName(VMFrame &f, ic::PICInfo *pic)
f.regs.sp[0].setObject(*obj);
}
bool
BaseIC::isCallOp()
{
return !!(js_CodeSpec[op].format & JOF_CALLOP);
}
void
BaseIC::spew(JSContext *cx, const char *event, const char *message)
{
@ -2115,6 +2333,12 @@ DisabledGetElem(VMFrame &f, ic::GetElementIC *ic)
stubs::GetElem(f);
}
static void JS_FASTCALL
DisabledCallElem(VMFrame &f, ic::GetElementIC *ic)
{
stubs::CallElem(f);
}
bool
GetElementIC::shouldUpdate(JSContext *cx)
{
@ -2131,7 +2355,9 @@ LookupStatus
GetElementIC::disable(JSContext *cx, const char *reason)
{
slowCallPatched = true;
void *stub = JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem);
void *stub = (op == JSOP_GETELEM)
? JS_FUNC_TO_DATA_PTR(void *, DisabledGetElem)
: JS_FUNC_TO_DATA_PTR(void *, DisabledCallElem);
BaseIC::disable(cx, reason, stub);
return Lookup_Uncacheable;
}
@ -2152,8 +2378,13 @@ GetElementIC::purge(Repatcher &repatcher)
repatcher.relink(fastPathStart.jumpAtOffset(inlineShapeGuard), slowPathStart);
if (slowCallPatched) {
repatcher.relink(slowPathCall,
FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, ic::GetElement)));
if (op == JSOP_GETELEM) {
repatcher.relink(slowPathCall,
FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, ic::GetElement)));
} else if (op == JSOP_CALLELEM) {
repatcher.relink(slowPathCall,
FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, ic::CallElement)));
}
}
reset();
@ -2610,6 +2841,65 @@ GetElementIC::update(VMFrame &f, JSObject *obj, const Value &v, jsid id, Value *
return disable(f.cx, "unhandled object and key type");
}
void JS_FASTCALL
ic::CallElement(VMFrame &f, ic::GetElementIC *ic)
{
JSContext *cx = f.cx;
// Right now, we don't optimize for strings.
if (!f.regs.sp[-2].isObject()) {
ic->disable(cx, "non-object");
stubs::CallElem(f);
return;
}
RecompilationMonitor monitor(cx);
Value thisv = f.regs.sp[-2];
JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
if (!thisObj)
THROW();
jsid id;
Value idval = f.regs.sp[-1];
if (idval.isInt32() && INT_FITS_IN_JSID(idval.toInt32()))
id = INT_TO_JSID(idval.toInt32());
else if (!js_InternNonIntElementId(cx, thisObj, idval, &id))
THROW();
if (!monitor.recompiled() && ic->shouldUpdate(cx)) {
#ifdef DEBUG
f.regs.sp[-2] = MagicValue(JS_GENERIC_MAGIC);
#endif
LookupStatus status = ic->update(f, thisObj, idval, id, &f.regs.sp[-2]);
if (status != Lookup_Uncacheable) {
if (status == Lookup_Error)
THROW();
// If the result can be cached, the value was already retrieved.
JS_ASSERT(!f.regs.sp[-2].isMagic());
f.regs.sp[-1].setObject(*thisObj);
return;
}
}
/* Get or set the element. */
if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &f.regs.sp[-2]))
THROW();
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(f.regs.sp[-2].isPrimitive()) && thisv.isObject()) {
f.regs.sp[-2] = f.regs.sp[-1];
f.regs.sp[-1].setObject(*thisObj);
if (!OnUnknownMethod(cx, f.regs.sp - 2))
THROW();
} else
#endif
{
f.regs.sp[-1] = thisv;
}
}
void JS_FASTCALL
ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
{
@ -2626,7 +2916,7 @@ ic::GetElement(VMFrame &f, ic::GetElementIC *ic)
RecompilationMonitor monitor(cx);
JSObject *obj = ValueToObject(cx, f.regs.sp[-2]);
JSObject *obj = ValueToObject(cx, &f.regs.sp[-2]);
if (!obj)
THROW();

View File

@ -384,11 +384,13 @@ struct PICInfo : public BasePolyIC {
#endif
{
GET, // JSOP_GETPROP
CALL, // JSOP_CALLPROP
SET, // JSOP_SETPROP, JSOP_SETNAME
SETMETHOD, // JSOP_SETMETHOD
NAME, // JSOP_NAME
BIND, // JSOP_BINDNAME
XNAME // JSOP_GETXPROP
XNAME, // JSOP_GETXPROP
CALLNAME // JSOP_CALLNAME
};
union {
@ -465,13 +467,13 @@ struct PICInfo : public BasePolyIC {
return kind == SET || kind == SETMETHOD;
}
inline bool isGet() const {
return kind == GET;
return kind == GET || kind == CALL;
}
inline bool isBind() const {
return kind == BIND;
}
inline bool isScopeName() const {
return kind == NAME || kind == XNAME;
return kind == NAME || kind == CALLNAME || kind == XNAME;
}
inline RegisterID typeReg() {
JS_ASSERT(isGet());
@ -484,6 +486,10 @@ struct PICInfo : public BasePolyIC {
inline bool shapeNeedsRemat() {
return !shapeRegHasBaseShape;
}
inline bool isFastCall() {
JS_ASSERT(kind == CALL);
return !hasTypeCheck();
}
union {
GetPropLabels getPropLabels_;
@ -504,7 +510,7 @@ struct PICInfo : public BasePolyIC {
bindNameLabels_ = labels;
}
void setLabels(const ic::ScopeNameLabels &labels) {
JS_ASSERT(kind == NAME || kind == XNAME);
JS_ASSERT(kind == NAME || kind == CALLNAME || kind == XNAME);
scopeNameLabels_ = labels;
}
@ -521,7 +527,7 @@ struct PICInfo : public BasePolyIC {
return bindNameLabels_;
}
ScopeNameLabels &scopeNameLabels() {
JS_ASSERT(kind == NAME || kind == XNAME);
JS_ASSERT(kind == NAME || kind == CALLNAME || kind == XNAME);
return scopeNameLabels_;
}
@ -544,10 +550,13 @@ struct PICInfo : public BasePolyIC {
void JS_FASTCALL GetProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL GetPropNoCache(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL SetProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL CallProp(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL Name(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL CallName(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL XName(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL BindName(VMFrame &f, ic::PICInfo *);
void JS_FASTCALL GetElement(VMFrame &f, ic::GetElementIC *);
void JS_FASTCALL CallElement(VMFrame &f, ic::GetElementIC *);
template <JSBool strict> void JS_FASTCALL SetElement(VMFrame &f, ic::SetElementIC *);
#endif

View File

@ -54,6 +54,14 @@ ThrowException(VMFrame &f)
#define THROW() do { mjit::ThrowException(f); return; } while (0)
#define THROWV(v) do { mjit::ThrowException(f); return v; } while (0)
static inline JSObject *
ValueToObject(JSContext *cx, Value *vp)
{
if (vp->isObject())
return &vp->toObject();
return js_ValueToNonNullObject(cx, *vp);
}
static inline void
ReportAtomNotDefined(JSContext *cx, JSAtom *atom)
{

View File

@ -57,6 +57,8 @@
#include "methodjit/Retcon.h"
#include "jsinterpinlines.h"
#include "jspropertycache.h"
#include "jspropertycacheinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jsnuminlines.h"
@ -82,7 +84,29 @@ using namespace js::types;
using namespace JSC;
void JS_FASTCALL
stubs::BindName(VMFrame &f, PropertyName *name)
stubs::BindName(VMFrame &f)
{
PropertyCacheEntry *entry;
/* Fast-path should have caught this. See comment in interpreter. */
JS_ASSERT(!f.fp()->scopeChain().isGlobal());
PropertyName *name;
JSObject *obj2;
JSContext *cx = f.cx;
JSObject *obj = &f.fp()->scopeChain();
JS_PROPERTY_CACHE(cx).test(cx, f.pc(), obj, obj2, entry, name);
if (name) {
obj = FindIdentifierBase(cx, &f.fp()->scopeChain(), name);
if (!obj)
THROW();
}
f.regs.sp++;
f.regs.sp[-1].setObject(*obj);
}
void JS_FASTCALL
stubs::BindNameNoCache(VMFrame &f, PropertyName *name)
{
JSObject *obj = FindIdentifierBase(f.cx, &f.fp()->scopeChain(), name);
if (!obj)
@ -98,21 +122,137 @@ stubs::BindGlobalName(VMFrame &f)
template<JSBool strict>
void JS_FASTCALL
stubs::SetName(VMFrame &f, PropertyName *name)
stubs::SetName(VMFrame &f, PropertyName *origName)
{
JSContext *cx = f.cx;
const Value &rval = f.regs.sp[-1];
const Value &lval = f.regs.sp[-2];
if (!SetPropertyOperation(cx, f.pc(), lval, rval))
Value rval = f.regs.sp[-1];
Value &lref = f.regs.sp[-2];
JSObject *obj = ValueToObject(cx, &lref);
if (!obj)
THROW();
do {
PropertyCache *cache = &JS_PROPERTY_CACHE(cx);
/*
* Probe the property cache, specializing for two important
* set-property cases. First:
*
* function f(a, b, c) {
* var o = {p:a, q:b, r:c};
* return o;
* }
*
* or similar real-world cases, which evolve a newborn native
* object predicatably through some bounded number of property
* additions. And second:
*
* o.p = x;
*
* in a frequently executed method or loop body, where p will
* (possibly after the first iteration) always exist in native
* object o.
*/
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
if (cache->testForSet(cx, f.pc(), obj, &entry, &obj2, &name)) {
/*
* Property cache hit, only partially confirmed by testForSet. We
* know that the entry applies to regs.pc and that obj's shape
* matches.
*
* The entry predicts a set either an existing "own" property, or
* on a prototype property that has a setter.
*/
const Shape *shape = entry->prop;
JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
if (entry->isOwnPropertyHit() ||
((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
#ifdef DEBUG
if (entry->isOwnPropertyHit()) {
JS_ASSERT(obj->nativeContains(cx, *shape));
} else {
JS_ASSERT(obj2->nativeContains(cx, *shape));
JS_ASSERT(entry->isPrototypePropertyHit());
JS_ASSERT(entry->kshape != entry->pshape);
JS_ASSERT(!shape->hasSlot());
}
#endif
PCMETER(cache->pchits++);
PCMETER(cache->setpchits++);
NATIVE_SET(cx, obj, shape, entry, strict, &rval);
break;
}
PCMETER(cache->setpcmisses++);
name = origName;
} else {
JS_ASSERT(name);
}
if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
uintN defineHow;
JSOp op = JSOp(*f.pc());
if (op == JSOP_SETMETHOD)
defineHow = DNP_CACHE_RESULT | DNP_SET_METHOD;
else if (op == JSOP_SETNAME)
defineHow = DNP_CACHE_RESULT | DNP_UNQUALIFIED;
else
defineHow = DNP_CACHE_RESULT;
if (!SetPropertyHelper(cx, obj, name, defineHow, &rval, strict))
THROW();
} else {
if (!obj->setProperty(cx, name, &rval, strict))
THROW();
}
} while (0);
f.regs.sp[-2] = f.regs.sp[-1];
}
template void JS_FASTCALL stubs::SetName<true>(VMFrame &f, PropertyName *origName);
template void JS_FASTCALL stubs::SetName<false>(VMFrame &f, PropertyName *origName);
template<JSBool strict>
void JS_FASTCALL
stubs::SetPropNoCache(VMFrame &f, PropertyName *name)
{
JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
if (!obj)
THROW();
Value rval = f.regs.sp[-1];
if (!obj->setProperty(f.cx, name, &f.regs.sp[-1], strict))
THROW();
f.regs.sp[-2] = rval;
}
template void JS_FASTCALL stubs::SetPropNoCache<true>(VMFrame &f, PropertyName *name);
template void JS_FASTCALL stubs::SetPropNoCache<false>(VMFrame &f, PropertyName *name);
template<JSBool strict>
void JS_FASTCALL
stubs::SetGlobalNameNoCache(VMFrame &f, PropertyName *name)
{
JSContext *cx = f.cx;
Value rval = f.regs.sp[-1];
Value &lref = f.regs.sp[-2];
JSObject *obj = ValueToObject(cx, &lref);
if (!obj || !obj->setProperty(cx, name, &rval, strict))
THROW();
f.regs.sp[-2] = f.regs.sp[-1];
}
template void JS_FASTCALL stubs::SetGlobalNameNoCache<true>(VMFrame &f, PropertyName *name);
template void JS_FASTCALL stubs::SetGlobalNameNoCache<false>(VMFrame &f, PropertyName *name);
template<JSBool strict>
void JS_FASTCALL
stubs::SetGlobalName(VMFrame &f, PropertyName *name)
@ -123,13 +263,90 @@ stubs::SetGlobalName(VMFrame &f, PropertyName *name)
template void JS_FASTCALL stubs::SetGlobalName<true>(VMFrame &f, PropertyName *name);
template void JS_FASTCALL stubs::SetGlobalName<false>(VMFrame &f, PropertyName *name);
static inline void
PushImplicitThis(VMFrame &f, JSObject *obj, Value &rval)
{
Value thisv;
if (!ComputeImplicitThis(f.cx, obj, rval, &thisv))
return;
*f.regs.sp++ = thisv;
}
static JSObject *
NameOp(VMFrame &f, JSObject *obj, bool callname)
{
JSContext *cx = f.cx;
Value rval;
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, f.pc(), obj, obj2, entry, name);
if (!name) {
NATIVE_GET(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, &rval, return NULL);
JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
} else {
JSProperty *prop;
bool global = (js_CodeSpec[*f.pc()].format & JOF_GNAME);
if (!FindPropertyHelper(cx, name, true, global, &obj, &obj2, &prop))
return NULL;
if (!prop) {
/* Kludge to allow (typeof foo == "undefined") tests. */
JSOp op2 = JSOp(f.pc()[JSOP_NAME_LENGTH]);
if (op2 == JSOP_TYPEOF) {
f.regs.sp++;
f.regs.sp[-1].setUndefined();
return obj;
}
ReportAtomNotDefined(cx, name);
return NULL;
}
/* Take the slow path if prop was not found in a native object. */
if (!obj->isNative() || !obj2->isNative()) {
if (!obj->getProperty(cx, name, &rval))
return NULL;
} else {
Shape *shape = (Shape *)prop;
JSObject *normalized = obj;
if (normalized->isWith() && !shape->hasDefaultGetter())
normalized = &normalized->asWith().object();
NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
}
/*
* If this is an incop, update the property's types themselves,
* to capture the type effect on the intermediate value.
*/
if (rval.isUndefined() && (js_CodeSpec[*f.pc()].format & (JOF_INC|JOF_DEC)))
AddTypePropertyId(cx, obj, ATOM_TO_JSID(name), Type::UndefinedType());
}
*f.regs.sp++ = rval;
if (callname)
PushImplicitThis(f, obj, rval);
return obj;
}
void JS_FASTCALL
stubs::Name(VMFrame &f)
{
if (!NameOperation(f.cx, f.pc(), &f.regs.sp[0]))
if (!NameOp(f, &f.fp()->scopeChain(), false))
THROW();
}
void JS_FASTCALL
stubs::GetGlobalName(VMFrame &f)
{
JSObject *globalObj = &f.fp()->scopeChain().global();
if (!NameOp(f, globalObj, false))
THROW();
}
void JS_FASTCALL
stubs::GetElem(VMFrame &f)
{
@ -160,8 +377,7 @@ stubs::GetElem(VMFrame &f)
JS_ASSERT(!lref.isMagic(JS_LAZY_ARGUMENTS));
}
bool isObject = lref.isObject();
JSObject *obj = ValueToObject(cx, lref);
JSObject *obj = ValueToObject(cx, &lref);
if (!obj)
THROW();
@ -199,13 +415,6 @@ stubs::GetElem(VMFrame &f)
}
}
}
#if JS_HAS_NO_SUCH_METHOD
if (*f.pc() == JSOP_CALLELEM && JS_UNLIKELY(rval.isPrimitive()) && isObject) {
if (!OnUnknownMethod(cx, obj, rref, &rval))
THROW();
}
#endif
}
static inline bool
@ -219,6 +428,40 @@ FetchElementId(VMFrame &f, JSObject *obj, const Value &idval, jsid &id, Value *v
return !!js_InternNonIntElementId(f.cx, obj, idval, &id, vp);
}
void JS_FASTCALL
stubs::CallElem(VMFrame &f)
{
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
/* Find the object on which to look for |this|'s properties. */
Value thisv = regs.sp[-2];
JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2);
if (!thisObj)
THROW();
/* Fetch index and convert it to id suitable for use with thisObj. */
jsid id;
if (!FetchElementId(f, thisObj, regs.sp[-1], id, &regs.sp[-2]))
THROW();
/* Get or set the element. */
if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, &regs.sp[-2]))
THROW();
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(regs.sp[-2].isPrimitive()) && thisv.isObject()) {
regs.sp[-2] = regs.sp[-1];
regs.sp[-1].setObject(*thisObj);
if (!OnUnknownMethod(cx, regs.sp - 2))
THROW();
} else
#endif
{
regs.sp[-1] = thisv;
}
}
template<JSBool strict>
void JS_FASTCALL
stubs::SetElem(VMFrame &f)
@ -233,7 +476,7 @@ stubs::SetElem(VMFrame &f)
JSObject *obj;
jsid id;
obj = ValueToObject(cx, objval);
obj = ValueToObject(cx, &objval);
if (!obj)
THROW();
@ -280,7 +523,7 @@ stubs::ToId(VMFrame &f)
Value &objval = f.regs.sp[-2];
Value &idval = f.regs.sp[-1];
JSObject *obj = ValueToObject(f.cx, objval);
JSObject *obj = ValueToObject(f.cx, &objval);
if (!obj)
THROW();
@ -293,15 +536,22 @@ stubs::ToId(VMFrame &f)
}
void JS_FASTCALL
stubs::ImplicitThis(VMFrame &f, PropertyName *name)
stubs::CallName(VMFrame &f)
{
JSObject *obj, *obj2;
JSProperty *prop;
if (!FindPropertyHelper(f.cx, name, false, false, &obj, &obj2, &prop))
JSObject *obj = NameOp(f, &f.fp()->scopeChain(), true);
if (!obj)
THROW();
}
if (!ComputeImplicitThis(f.cx, obj, &f.regs.sp[0]))
THROW();
/*
* Push the implicit this value, with the assumption that the callee
* (which is on top of the stack) was read as a property from the
* global object.
*/
void JS_FASTCALL
stubs::PushImplicitThisForGlobal(VMFrame &f)
{
return PushImplicitThis(f, &f.fp()->scopeChain().global(), f.regs.sp[-1]);
}
void JS_FASTCALL
@ -1199,38 +1449,150 @@ stubs::Lambda(VMFrame &f, JSFunction *fun)
return obj;
}
void JS_FASTCALL
stubs::GetProp(VMFrame &f, PropertyName *name)
static bool JS_ALWAYS_INLINE
InlineGetProp(VMFrame &f)
{
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
Value *vp = &f.regs.sp[-1];
if (vp->isMagic(JS_LAZY_ARGUMENTS)) {
JS_ASSERT(JSOp(*f.pc()) == JSOP_LENGTH);
regs.sp[-1] = Int32Value(regs.fp()->numActualArgs());
return true;
}
JSObject *obj = ValueToObject(f.cx, vp);
if (!obj)
return false;
Value rval;
if (!GetPropertyOperation(cx, f.pc(), f.regs.sp[-1], &rval))
THROW();
do {
JSObject *aobj = js_GetProtoIfDenseArray(obj);
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, f.pc(), aobj, obj2, entry, name);
if (!name) {
NATIVE_GET(cx, obj, obj2, entry->prop, JSGET_METHOD_BARRIER, &rval, return false);
break;
}
if (JS_LIKELY(!aobj->getOps()->getProperty)
? !GetPropertyHelper(cx, obj, name, JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER, &rval)
: !obj->getProperty(cx, name, &rval))
{
return false;
}
} while (false);
regs.sp[-1] = rval;
return true;
}
void JS_FASTCALL
stubs::GetProp(VMFrame &f)
{
if (!InlineGetProp(f))
THROW();
}
void JS_FASTCALL
stubs::GetPropNoCache(VMFrame &f, PropertyName *name)
{
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
const Value &lval = f.regs.sp[-1];
// Uncached lookups are only used for .prototype accesses at the start of constructors.
JS_ASSERT(lval.isObject());
JS_ASSERT(name == cx->runtime->atomState.classPrototypeAtom);
JSObject *obj = &lval.toObject();
Value rval;
if (!obj->getProperty(cx, name, &rval))
Value *vp = &f.regs.sp[-1];
JSObject *obj = ValueToObject(cx, vp);
if (!obj)
THROW();
regs.sp[-1] = rval;
if (!obj->getProperty(cx, name, vp))
THROW();
/* Don't check for undefined, this is only used for 'prototype'. See ic::GetProp. */
}
void JS_FASTCALL
stubs::CallProp(VMFrame &f, PropertyName *origName)
{
JSContext *cx = f.cx;
FrameRegs &regs = f.regs;
Value lval;
lval = regs.sp[-1];
Value objv;
if (lval.isObject()) {
objv = lval;
} else {
GlobalObject &global = f.fp()->scopeChain().global();
JSObject *pobj;
if (lval.isString()) {
pobj = global.getOrCreateStringPrototype(cx);
} else if (lval.isNumber()) {
pobj = global.getOrCreateNumberPrototype(cx);
} else if (lval.isBoolean()) {
pobj = global.getOrCreateBooleanPrototype(cx);
} else {
JS_ASSERT(lval.isNull() || lval.isUndefined());
js_ReportIsNullOrUndefined(cx, -1, lval, NULL);
THROW();
}
if (!pobj)
THROW();
objv.setObject(*pobj);
}
JSObject *aobj = js_GetProtoIfDenseArray(&objv.toObject());
Value rval;
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name;
JS_PROPERTY_CACHE(cx).test(cx, f.pc(), aobj, obj2, entry, name);
if (!name) {
NATIVE_GET(cx, &objv.toObject(), obj2, entry->prop, JSGET_NO_METHOD_BARRIER, &rval,
THROW());
regs.sp++;
regs.sp[-2] = rval;
regs.sp[-1] = lval;
} else {
/* Cache miss: use the name loaded for us under PropertyCache::test. */
regs.sp++;
regs.sp[-1].setNull();
if (lval.isObject()) {
if (!GetMethod(cx, &objv.toObject(), name,
JS_LIKELY(!aobj->getOps()->getProperty)
? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
: JSGET_NO_METHOD_BARRIER,
&rval))
{
THROW();
}
regs.sp[-1] = objv;
regs.sp[-2] = rval;
} else {
JS_ASSERT(!objv.toObject().getOps()->getProperty);
if (!GetPropertyHelper(cx, &objv.toObject(), name,
JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
&rval))
{
THROW();
}
regs.sp[-1] = lval;
regs.sp[-2] = rval;
}
}
#if JS_HAS_NO_SUCH_METHOD
if (JS_UNLIKELY(rval.isPrimitive()) && regs.sp[-1].isObject()) {
regs.sp[-2].setString(origName);
if (!OnUnknownMethod(cx, regs.sp - 2))
THROW();
}
#endif
}
void JS_FASTCALL
@ -1256,15 +1618,40 @@ InitPropOrMethod(VMFrame &f, PropertyName *name, JSOp op)
JSObject *obj = &regs.sp[-2].toObject();
JS_ASSERT(obj->isNative());
/* Get the immediate property name into id. */
jsid id = ATOM_TO_JSID(name);
/*
* Probe the property cache.
*
* We can not assume that the object created by JSOP_NEWINIT is still
* single-threaded as the debugger can access it from other threads.
* So check first.
*
* On a hit, if the cached shape has a non-default setter, it must be
* __proto__. If shape->previous() != obj->lastProperty(), there must be a
* repeated property name. The fast path does not handle these two cases.
*/
PropertyCacheEntry *entry;
JSObject *obj2;
PropertyName *name2;
if (JS_PROPERTY_CACHE(cx).testForSet(cx, f.pc(), obj, &entry, &obj2, &name2) &&
entry->prop->hasDefaultSetter() &&
entry->isOwnPropertyHit())
{
JS_ASSERT(obj == obj2);
/* Fast path. Property cache hit. */
obj->nativeSetSlotWithType(cx, entry->prop, rval);
} else {
PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
uintN defineHow = (op == JSOP_INITMETHOD) ? DNP_SET_METHOD : 0;
if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
? !js_SetPropertyHelper(cx, obj, id, defineHow, &rval, false)
: !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, defineHow)) {
THROW();
uintN defineHow = (op == JSOP_INITMETHOD)
? DNP_CACHE_RESULT | DNP_SET_METHOD
: DNP_CACHE_RESULT;
if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
? !SetPropertyHelper(cx, obj, name, defineHow, &rval, false)
: !DefineNativeProperty(cx, obj, name, rval, NULL, NULL,
JSPROP_ENUMERATE, 0, 0, defineHow))
{
THROW();
}
}
}
@ -1641,7 +2028,7 @@ stubs::DelProp(VMFrame &f, PropertyName *name)
{
JSContext *cx = f.cx;
JSObject *obj = ValueToObject(cx, f.regs.sp[-1]);
JSObject *obj = ValueToObject(cx, &f.regs.sp[-1]);
if (!obj)
THROW();
@ -1661,7 +2048,7 @@ stubs::DelElem(VMFrame &f)
{
JSContext *cx = f.cx;
JSObject *obj = ValueToObject(cx, f.regs.sp[-2]);
JSObject *obj = ValueToObject(cx, &f.regs.sp[-2]);
if (!obj)
THROW();

View File

@ -115,19 +115,26 @@ void JS_FASTCALL Throw(VMFrame &f);
void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc);
void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc);
void JS_FASTCALL BindName(VMFrame &f, PropertyName *name);
void JS_FASTCALL BindName(VMFrame &f);
void JS_FASTCALL BindNameNoCache(VMFrame &f, PropertyName *name);
JSObject * JS_FASTCALL BindGlobalName(VMFrame &f);
template<JSBool strict> void JS_FASTCALL SetName(VMFrame &f, PropertyName *name);
template<JSBool strict> void JS_FASTCALL SetPropNoCache(VMFrame &f, PropertyName *name);
template<JSBool strict> void JS_FASTCALL SetGlobalName(VMFrame &f, PropertyName *name);
template<JSBool strict> void JS_FASTCALL SetGlobalNameNoCache(VMFrame &f, PropertyName *name);
void JS_FASTCALL Name(VMFrame &f);
void JS_FASTCALL GetProp(VMFrame &f, PropertyName *name);
void JS_FASTCALL GetProp(VMFrame &f);
void JS_FASTCALL GetPropNoCache(VMFrame &f, PropertyName *name);
void JS_FASTCALL GetElem(VMFrame &f);
void JS_FASTCALL CallElem(VMFrame &f);
template<JSBool strict> void JS_FASTCALL SetElem(VMFrame &f);
void JS_FASTCALL ToId(VMFrame &f);
void JS_FASTCALL ImplicitThis(VMFrame &f, PropertyName *name);
void JS_FASTCALL CallName(VMFrame &f);
void JS_FASTCALL PushImplicitThisForGlobal(VMFrame &f);
void JS_FASTCALL GetUpvar(VMFrame &f, uint32_t index);
void JS_FASTCALL GetGlobalName(VMFrame &f);
void JS_FASTCALL CallProp(VMFrame &f, PropertyName *name);
template <JSBool strict> void JS_FASTCALL DelProp(VMFrame &f, PropertyName *name);
template <JSBool strict> void JS_FASTCALL DelElem(VMFrame &f);
void JS_FASTCALL DelName(VMFrame &f, PropertyName *name);