diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index e4d18a8f288..8fbd93d6684 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2753,25 +2753,67 @@ mjit::Compiler::jsop_instanceof() return; } - Jump typeCheck; - if (!rhs->isTypeKnown()) { + Jump firstSlow; + bool typeKnown = rhs->isTypeKnown(); + if (!typeKnown) { Jump j = frame.testFunObj(Assembler::NotEqual, rhs); stubcc.linkExit(j); + stubcc.leave(); + stubcc.call(stubs::InstanceOf); + firstSlow = stubcc.masm.jump(); } - stubcc.leave(); - stubcc.call(stubs::InstanceOf); - + /* This is sadly necessary because the error case needs the object. */ frame.dup(); jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false); - prepareStubCall(); - stubCall(stubs::FastInstanceOf, Uses(3), Defs(1)); - frame.takeReg(Registers::ReturnReg); - frame.popn(3); - frame.pushTypedPayload(JSVAL_MASK32_BOOLEAN, Registers::ReturnReg); + /* Primitive prototypes are invalid. */ + rhs = frame.peek(-1); + Jump j = frame.testPrimitive(Assembler::Equal, rhs); + stubcc.linkExit(j); + /* Allocate registers up front, because of branchiness. */ + FrameEntry *lhs = frame.peek(-3); + RegisterID obj = frame.copyDataIntoReg(lhs); + RegisterID proto = frame.copyDataIntoReg(rhs); + RegisterID temp = frame.allocReg(); + + Jump isFalse = frame.testPrimitive(Assembler::Equal, lhs); + + /* Quick test to avoid wrapped objects. */ + masm.loadPtr(Address(obj, offsetof(JSObject, clasp)), temp); + masm.load32(Address(temp, offsetof(JSClass, flags)), temp); + masm.and32(Imm32(JSCLASS_IS_EXTENDED), temp); + j = masm.branchTest32(Assembler::NonZero, temp, temp); + stubcc.linkExit(j); + + Address protoAddr(obj, offsetof(JSObject, fslots) + JSSLOT_PROTO * sizeof(Value)); + Label loop = masm.label(); + + /* Walk prototype chain, break out on NULL or hit. */ + masm.loadData32(protoAddr, obj); + Jump isFalse2 = masm.branchTestPtr(Assembler::Zero, obj, obj); + Jump isTrue = masm.branchPtr(Assembler::NotEqual, obj, proto); + isTrue.linkTo(loop, &masm); + masm.move(Imm32(1), temp); + isTrue = masm.jump(); + + isFalse.linkTo(masm.label(), &masm); + isFalse2.linkTo(masm.label(), &masm); + masm.move(Imm32(0), temp); + isTrue.linkTo(masm.label(), &masm); + + frame.freeReg(proto); + frame.freeReg(obj); + + stubcc.leave(); + stubcc.call(stubs::FastInstanceOf); + + frame.popn(3); + frame.pushTypedPayload(JSVAL_MASK32_BOOLEAN, temp); + + firstSlow.linkTo(stubcc.masm.label(), &stubcc.masm); stubcc.rejoin(1); } diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 78d4e096aee..2b4db26bf24 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -2228,7 +2228,7 @@ stubs::InstanceOf(VMFrame &f) return cond; } -JSBool JS_FASTCALL +void JS_FASTCALL stubs::FastInstanceOf(VMFrame &f) { const Value &lref = f.regs.sp[-1]; @@ -2239,10 +2239,10 @@ stubs::FastInstanceOf(VMFrame &f) * has a non-object as its .prototype value. */ js_ReportValueError(f.cx, JSMSG_BAD_PROTOTYPE, -1, f.regs.sp[-2], NULL); - THROWV(JS_FALSE); + THROW(); } - return js_IsDelegate(f.cx, &lref.asObject(), f.regs.sp[-3]); + f.regs.sp[-3].setBoolean(js_IsDelegate(f.cx, &lref.asObject(), f.regs.sp[-3])); } void JS_FASTCALL diff --git a/js/src/methodjit/StubCalls.h b/js/src/methodjit/StubCalls.h index 2f7aa488df7..87f51f4d417 100644 --- a/js/src/methodjit/StubCalls.h +++ b/js/src/methodjit/StubCalls.h @@ -152,7 +152,7 @@ void JS_FASTCALL ForName(VMFrame &f, JSAtom *atom); JSBool JS_FASTCALL ValueToBoolean(VMFrame &f); JSString * JS_FASTCALL TypeOf(VMFrame &f); JSBool JS_FASTCALL InstanceOf(VMFrame &f); -JSBool JS_FASTCALL FastInstanceOf(VMFrame &f); +void JS_FASTCALL FastInstanceOf(VMFrame &f); void JS_FASTCALL ArgCnt(VMFrame &f); void JS_FASTCALL Unbrand(VMFrame &f);