mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backout c0d337401801,78d17e22a223 (bug 712714) for talos regressions across the board.
This commit is contained in:
parent
803a44e0dd
commit
2d516bb575
@ -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));
|
||||
|
@ -745,8 +745,6 @@ struct BytecodeEmitter : public TreeContext
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needsImplicitThis();
|
||||
|
||||
TokenStream *tokenStream() { return &parser->tokenStream; }
|
||||
|
||||
jsbytecode *base() const { return current->base; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
JSScript *script = cx->fp()->script();
|
||||
FrameRegs& regs = cx->regs();
|
||||
|
||||
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, ®s.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))
|
||||
do {
|
||||
Value *vp = ®s.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))
|
||||
JS_ASSERT_IF(op == JSOP_SETGNAME, obj == ®s.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 = ®s.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 = ®s.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,
|
||||
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)
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
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());
|
||||
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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -199,6 +199,9 @@ class InlineMap;
|
||||
|
||||
class LifoAlloc;
|
||||
|
||||
class PropertyCache;
|
||||
struct PropertyCacheEntry;
|
||||
|
||||
class BaseShape;
|
||||
class UnownedBaseShape;
|
||||
struct Shape;
|
||||
|
@ -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
@ -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
|
||||
|
@ -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)) {
|
||||
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,6 +2137,9 @@ mjit::Compiler::jsop_getelem()
|
||||
frame.forgetMismatchedObject(obj);
|
||||
|
||||
if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) {
|
||||
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,8 +2248,14 @@ mjit::Compiler::jsop_getelem()
|
||||
objTypeGuard.get().linkTo(stubcc.masm.label(), &stubcc.masm);
|
||||
#ifdef JS_POLYIC
|
||||
passICAddress(&ic);
|
||||
if (isCall)
|
||||
ic.slowPathCall = OOL_STUBCALL(ic::CallElement, REJOIN_FALLTHROUGH);
|
||||
else
|
||||
ic.slowPathCall = OOL_STUBCALL(ic::GetElement, REJOIN_FALLTHROUGH);
|
||||
#else
|
||||
if (isCall)
|
||||
ic.slowPathCall = OOL_STUBCALL(stubs::CallElem, REJOIN_FALLTHROUGH);
|
||||
else
|
||||
ic.slowPathCall = OOL_STUBCALL(stubs::GetElem, REJOIN_FALLTHROUGH);
|
||||
#endif
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
if (ic->usePropertyCache)
|
||||
STRICT_VARIANT(stubs::SetGlobalName)(f, name);
|
||||
else
|
||||
STRICT_VARIANT(stubs::SetGlobalNameNoCache)(f, name);
|
||||
}
|
||||
|
||||
class EqualityICLinker : public LinkerHelper
|
||||
|
@ -128,6 +128,7 @@ struct GlobalNameIC
|
||||
*/
|
||||
int32_t loadStoreOffset : 15;
|
||||
int32_t shapeOffset : 15;
|
||||
bool usePropertyCache : 1;
|
||||
};
|
||||
|
||||
struct GetGlobalNameIC : public GlobalNameIC
|
||||
|
@ -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 ®s = 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) {
|
||||
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();
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,10 +263,87 @@ 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();
|
||||
}
|
||||
|
||||
@ -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 ®s = 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, ®s.sp[-2]))
|
||||
THROW();
|
||||
|
||||
/* Get or set the element. */
|
||||
if (!js_GetMethod(cx, thisObj, id, JSGET_NO_METHOD_BARRIER, ®s.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 ®s = 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 ®s = 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 ®s = 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,16 +1618,41 @@ InitPropOrMethod(VMFrame &f, PropertyName *name, JSOp op)
|
||||
JSObject *obj = ®s.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;
|
||||
uintN defineHow = (op == JSOP_INITMETHOD)
|
||||
? DNP_CACHE_RESULT | DNP_SET_METHOD
|
||||
: DNP_CACHE_RESULT;
|
||||
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)) {
|
||||
? !SetPropertyHelper(cx, obj, name, defineHow, &rval, false)
|
||||
: !DefineNativeProperty(cx, obj, name, rval, NULL, NULL,
|
||||
JSPROP_ENUMERATE, 0, 0, defineHow))
|
||||
{
|
||||
THROW();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user