Commit b46d57b4 authored by barraclough@apple.com's avatar barraclough@apple.com

instanceof should not get the prototype for non-default HasInstance

https://bugs.webkit.org/show_bug.cgi?id=68656

Reviewed by Oliver Hunt.

Source/JavaScriptCore: 

Instanceof is currently implemented as a sequance of three opcodes:
    check_has_instance
    get_by_id(prototype)
    op_instanceof
There are three interesting types of base value that instanceof can be applied to:
    (A) Objects supporting default instanceof behaviour (functions, other than those created with bind)
    (B) Objects overriding the default instancecof behaviour with a custom one (API objects, bound functions)
    (C) Values that do not respond to the [[HasInstance]] trap.
Currently check_has_instance handles case (C), leaving the op_instanceof opcode to handle (A) & (B). There are
two problems with this apporach. Firstly, this is suboptimal for case (A), since we have to check for
hasInstance support twice (once in check_has_instance, then for default behaviour in op_instanceof). Secondly,
this means that in cases (B) we also perform the get_by_id, which is both suboptimal and an observable spec
violation.

The fix here is to move handing of non-default instanceof (cases (B)) to the check_has_instance op, leaving
op_instanceof to handle only cases (A).

* API/JSCallbackObject.h:
(JSCallbackObject):
* API/JSCallbackObjectFunctions.h:
(JSC::::customHasInstance):
* API/JSValueRef.cpp:
(JSValueIsInstanceOfConstructor):
    - renamed hasInstance to customHasInstance
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
    - added additional parameters to check_has_instance opcode
* bytecode/Opcode.h:
(JSC):
(JSC::padOpcodeName):
    - added additional parameters to check_has_instance opcode
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitCheckHasInstance):
    - added additional parameters to check_has_instance opcode
* bytecompiler/BytecodeGenerator.h:
(BytecodeGenerator):
    - added additional parameters to check_has_instance opcode
* bytecompiler/NodesCodegen.cpp:
(JSC::InstanceOfNode::emitBytecode):
    - added additional parameters to check_has_instance opcode
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
    - added additional parameters to check_has_instance opcode
* interpreter/Interpreter.cpp:
(JSC::isInvalidParamForIn):
(JSC::Interpreter::privateExecute):
    - Add handling for non-default instanceof to op_check_has_instance
* jit/JITInlineMethods.h:
(JSC::JIT::emitArrayProfilingSiteForBytecodeIndex):
    - Fixed no-LLInt no_DFG build
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_check_has_instance):
(JSC::JIT::emitSlow_op_check_has_instance):
    - check for ImplementsDefaultHasInstance, handle additional arguments to op_check_has_instance.
(JSC::JIT::emit_op_instanceof):
(JSC::JIT::emitSlow_op_instanceof):
    - no need to check for ImplementsDefaultHasInstance.
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_check_has_instance):
(JSC::JIT::emitSlow_op_check_has_instance):
    - check for ImplementsDefaultHasInstance, handle additional arguments to op_check_has_instance.
(JSC::JIT::emit_op_instanceof):
(JSC::JIT::emitSlow_op_instanceof):
    - no need to check for ImplementsDefaultHasInstance.
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* jit/JITStubs.h:
    - Add handling for non-default instanceof to op_check_has_instance
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
    - move check for ImplementsDefaultHasInstance, handle additional arguments to op_check_has_instance.
* runtime/ClassInfo.h:
(MethodTable):
(JSC):
    - renamed hasInstance to customHasInstance
* runtime/CommonSlowPaths.h:
(CommonSlowPaths):
    - removed opInstanceOfSlow (this was whittled down to one function call!)
* runtime/JSBoundFunction.cpp:
(JSC::JSBoundFunction::customHasInstance):
* runtime/JSBoundFunction.h:
(JSBoundFunction):
    - renamed hasInstance to customHasInstance, reimplemented.
* runtime/JSCell.cpp:
(JSC::JSCell::customHasInstance):
* runtime/JSCell.h:
(JSCell):
* runtime/JSObject.cpp:
(JSC::JSObject::hasInstance):
(JSC):
(JSC::JSObject::defaultHasInstance):
* runtime/JSObject.h:
(JSObject):

LayoutTests: 

* fast/js/function-bind-expected.txt:
    - check in passing result.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@129281 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent b49e0c6a
2012-09-21 Gavin Barraclough <barraclough@apple.com>
instanceof should not get the prototype for non-default HasInstance
https://bugs.webkit.org/show_bug.cgi?id=68656
Reviewed by Oliver Hunt.
* fast/js/function-bind-expected.txt:
- check in passing result.
2012-09-21 Benjamin Poulain <bpoulain@apple.com>
fast/dom/Geolocation/disconnected-frame.html test asserts
......@@ -26,7 +26,7 @@ PASS abcAt(1) is "b"
PASS new abcAt(1) threw exception TypeError: 'function charAt() {
[native code]
}' is not a constructor (evaluating 'new abcAt(1)').
FAIL boundFunctionPrototypeAccessed should be false. Was true.
PASS boundFunctionPrototypeAccessed is false
PASS Function.bind.length is 1
PASS successfullyParsed is true
......
......@@ -186,7 +186,7 @@ private:
static bool deleteProperty(JSCell*, ExecState*, PropertyName);
static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned);
static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue proto);
static bool customHasInstance(JSObject*, ExecState*, JSValue);
static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
......
......@@ -389,7 +389,7 @@ EncodedJSValue JSCallbackObject<Parent>::construct(ExecState* exec)
}
template <class Parent>
bool JSCallbackObject<Parent>::hasInstance(JSObject* object, ExecState* exec, JSValue value, JSValue)
bool JSCallbackObject<Parent>::customHasInstance(JSObject* object, ExecState* exec, JSValue value)
{
JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object);
JSContextRef execRef = toRef(exec);
......
......@@ -175,7 +175,7 @@ bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObject
JSObject* jsConstructor = toJS(constructor);
if (!jsConstructor->structure()->typeInfo().implementsHasInstance())
return false;
bool result = jsConstructor->methodTable()->hasInstance(jsConstructor, exec, jsValue, jsConstructor->get(exec, exec->propertyNames().prototype)); // false if an exception is thrown
bool result = jsConstructor->hasInstance(exec, jsValue); // false if an exception is thrown
if (exec->hadException()) {
if (exception)
*exception = toRef(exec, exec->exception());
......
2012-09-21 Gavin Barraclough <barraclough@apple.com>
instanceof should not get the prototype for non-default HasInstance
https://bugs.webkit.org/show_bug.cgi?id=68656
Reviewed by Oliver Hunt.
Instanceof is currently implemented as a sequance of three opcodes:
check_has_instance
get_by_id(prototype)
op_instanceof
There are three interesting types of base value that instanceof can be applied to:
(A) Objects supporting default instanceof behaviour (functions, other than those created with bind)
(B) Objects overriding the default instancecof behaviour with a custom one (API objects, bound functions)
(C) Values that do not respond to the [[HasInstance]] trap.
Currently check_has_instance handles case (C), leaving the op_instanceof opcode to handle (A) & (B). There are
two problems with this apporach. Firstly, this is suboptimal for case (A), since we have to check for
hasInstance support twice (once in check_has_instance, then for default behaviour in op_instanceof). Secondly,
this means that in cases (B) we also perform the get_by_id, which is both suboptimal and an observable spec
violation.
The fix here is to move handing of non-default instanceof (cases (B)) to the check_has_instance op, leaving
op_instanceof to handle only cases (A).
* API/JSCallbackObject.h:
(JSCallbackObject):
* API/JSCallbackObjectFunctions.h:
(JSC::::customHasInstance):
* API/JSValueRef.cpp:
(JSValueIsInstanceOfConstructor):
- renamed hasInstance to customHasInstance
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
- added additional parameters to check_has_instance opcode
* bytecode/Opcode.h:
(JSC):
(JSC::padOpcodeName):
- added additional parameters to check_has_instance opcode
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitCheckHasInstance):
- added additional parameters to check_has_instance opcode
* bytecompiler/BytecodeGenerator.h:
(BytecodeGenerator):
- added additional parameters to check_has_instance opcode
* bytecompiler/NodesCodegen.cpp:
(JSC::InstanceOfNode::emitBytecode):
- added additional parameters to check_has_instance opcode
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
- added additional parameters to check_has_instance opcode
* interpreter/Interpreter.cpp:
(JSC::isInvalidParamForIn):
(JSC::Interpreter::privateExecute):
- Add handling for non-default instanceof to op_check_has_instance
* jit/JITInlineMethods.h:
(JSC::JIT::emitArrayProfilingSiteForBytecodeIndex):
- Fixed no-LLInt no_DFG build
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_check_has_instance):
(JSC::JIT::emitSlow_op_check_has_instance):
- check for ImplementsDefaultHasInstance, handle additional arguments to op_check_has_instance.
(JSC::JIT::emit_op_instanceof):
(JSC::JIT::emitSlow_op_instanceof):
- no need to check for ImplementsDefaultHasInstance.
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_check_has_instance):
(JSC::JIT::emitSlow_op_check_has_instance):
- check for ImplementsDefaultHasInstance, handle additional arguments to op_check_has_instance.
(JSC::JIT::emit_op_instanceof):
(JSC::JIT::emitSlow_op_instanceof):
- no need to check for ImplementsDefaultHasInstance.
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* jit/JITStubs.h:
- Add handling for non-default instanceof to op_check_has_instance
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
- move check for ImplementsDefaultHasInstance, handle additional arguments to op_check_has_instance.
* runtime/ClassInfo.h:
(MethodTable):
(JSC):
- renamed hasInstance to customHasInstance
* runtime/CommonSlowPaths.h:
(CommonSlowPaths):
- removed opInstanceOfSlow (this was whittled down to one function call!)
* runtime/JSBoundFunction.cpp:
(JSC::JSBoundFunction::customHasInstance):
* runtime/JSBoundFunction.h:
(JSBoundFunction):
- renamed hasInstance to customHasInstance, reimplemented.
* runtime/JSCell.cpp:
(JSC::JSCell::customHasInstance):
* runtime/JSCell.h:
(JSCell):
* runtime/JSObject.cpp:
(JSC::JSObject::hasInstance):
(JSC):
(JSC::JSObject::defaultHasInstance):
* runtime/JSObject.h:
(JSObject):
2012-09-21 Filip Pizlo <fpizlo@apple.com>
Unreviewed, fix ARM build.
......
......@@ -873,8 +873,11 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
break;
}
case op_check_has_instance: {
int base = (++it)->u.operand;
dataLog("[%4d] check_has_instance\t\t %s", location, registerName(exec, base).data());
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
int offset = (++it)->u.operand;
dataLog("[%4d] check_has_instance\t\t %s, %s, %s, %d(->%d)", location, registerName(exec, r0).data(), registerName(exec, r1).data(), registerName(exec, r2).data(), offset, location + offset);
dumpBytecodeCommentAndNewLine(location);
break;
}
......
......@@ -84,7 +84,7 @@ namespace JSC {
macro(op_bitxor, 5) \
macro(op_bitor, 5) \
\
macro(op_check_has_instance, 2) \
macro(op_check_has_instance, 5) \
macro(op_instanceof, 5) \
macro(op_typeof, 3) \
macro(op_is_undefined, 3) \
......
......@@ -1457,10 +1457,14 @@ ResolveResult BytecodeGenerator::resolveConstDecl(const Identifier& property)
return ResolveResult::dynamicResolve(scopeDepth());
}
void BytecodeGenerator::emitCheckHasInstance(RegisterID* base)
{
void BytecodeGenerator::emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target)
{
size_t begin = instructions().size();
emitOpcode(op_check_has_instance);
instructions().append(dst->index());
instructions().append(value->index());
instructions().append(base->index());
instructions().append(target->bind(begin, instructions().size()));
}
RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype)
......
......@@ -455,7 +455,7 @@ namespace JSC {
RegisterID* emitPostInc(RegisterID* dst, RegisterID* srcDst);
RegisterID* emitPostDec(RegisterID* dst, RegisterID* srcDst);
void emitCheckHasInstance(RegisterID* base);
void emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target);
RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype);
RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); }
RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); }
......
......@@ -1088,15 +1088,20 @@ RegisterID* InstanceOfNode::emitBytecode(BytecodeGenerator& generator, RegisterI
{
RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator));
RefPtr<RegisterID> src2 = generator.emitNode(m_expr2);
RefPtr<RegisterID> prototype = generator.newTemporary();
RefPtr<RegisterID> dstReg = generator.finalDestination(dst, src1.get());
RefPtr<Label> target = generator.newLabel();
generator.emitExpressionInfo(divot(), startOffset(), endOffset());
generator.emitCheckHasInstance(src2.get());
generator.emitCheckHasInstance(dstReg.get(), src1.get(), src2.get(), target.get());
generator.emitExpressionInfo(divot(), startOffset(), endOffset());
RegisterID* src2Prototype = generator.emitGetById(generator.newTemporary(), src2.get(), generator.globalData()->propertyNames->prototype);
generator.emitGetById(prototype.get(), src2.get(), generator.globalData()->propertyNames->prototype);
generator.emitExpressionInfo(divot(), startOffset(), endOffset());
return generator.emitInstanceOf(generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), src2Prototype);
RegisterID* result = generator.emitInstanceOf(dstReg.get(), src1.get(), src2.get(), prototype.get());
generator.emitLabel(target.get());
return result;
}
// ------------------------------ LogicalOpNode ----------------------------
......
......@@ -2049,7 +2049,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
}
case op_check_has_instance:
addToGraph(CheckHasInstance, get(currentInstruction[1].u.operand));
addToGraph(CheckHasInstance, get(currentInstruction[3].u.operand));
NEXT_OPCODE(op_check_has_instance);
case op_instanceof: {
......
......@@ -131,14 +131,6 @@ static NEVER_INLINE bool isInvalidParamForIn(CallFrame* callFrame, JSValue value
exceptionData = createInvalidParamError(callFrame, "in" , value);
return true;
}
static NEVER_INLINE bool isInvalidParamForInstanceOf(CallFrame* callFrame, JSValue value, JSValue& exceptionData)
{
if (value.isObject() && asObject(value)->structure()->typeInfo().implementsHasInstance())
return false;
exceptionData = createInvalidParamError(callFrame, "instanceof" , value);
return true;
}
#endif
JSValue eval(CallFrame* callFrame)
......@@ -2401,14 +2393,32 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
JSC API*). Raises an exception if register constructor is not
an valid parameter for instanceof.
*/
int base = vPC[1].u.operand;
int dst = vPC[1].u.operand;
int value = vPC[2].u.operand;
int base = vPC[3].u.operand;
int target = vPC[4].u.operand;
JSValue baseVal = callFrame->r(base).jsValue();
if (isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue))
goto vm_throw;
if (baseVal.isObject()) {
TypeInfo info = asObject(baseVal)->structure()->typeInfo();
if (info.implementsDefaultHasInstance()) {
vPC += OPCODE_LENGTH(op_check_has_instance);
NEXT_INSTRUCTION();
}
if (info.implementsHasInstance()) {
JSValue baseVal = callFrame->r(base).jsValue();
bool result = asObject(baseVal)->methodTable()->customHasInstance(asObject(baseVal), callFrame, callFrame->r(value).jsValue());
CHECK_FOR_EXCEPTION();
callFrame->uncheckedR(dst) = jsBoolean(result);
vPC += OPCODE_LENGTH(op_check_has_instance);
NEXT_INSTRUCTION();
vPC += target;
NEXT_INSTRUCTION();
}
}
exceptionValue = createInvalidParamError(callFrame, "instanceof" , baseVal);
goto vm_throw;
}
DEFINE_OPCODE(op_instanceof) {
/* instanceof dst(r) value(r) constructor(r) constructorProto(r)
......@@ -2425,14 +2435,11 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
*/
int dst = vPC[1].u.operand;
int value = vPC[2].u.operand;
int base = vPC[3].u.operand;
int baseProto = vPC[4].u.operand;
JSValue baseVal = callFrame->r(base).jsValue();
ASSERT(!isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue));
ASSERT(callFrame->r(vPC[3].u.operand).jsValue().isObject() && asObject(callFrame->r(vPC[3].u.operand).jsValue())->structure()->typeInfo().implementsDefaultHasInstance());
bool result = asObject(baseVal)->methodTable()->hasInstance(asObject(baseVal), callFrame, callFrame->r(value).jsValue(), callFrame->r(baseProto).jsValue());
bool result = JSObject::defaultHasInstance(callFrame, callFrame->r(value).jsValue(), callFrame->r(baseProto).jsValue());
CHECK_FOR_EXCEPTION();
callFrame->uncheckedR(dst) = jsBoolean(result);
......
......@@ -552,6 +552,7 @@ inline void JIT::emitArrayProfilingSiteForBytecodeIndex(RegisterID structureAndI
#if ENABLE(VALUE_PROFILER)
emitArrayProfilingSite(structureAndIndexingType, scratch, m_codeBlock->getOrAddArrayProfile(bytecodeIndex));
#else
UNUSED_PARAM(bytecodeIndex);
emitArrayProfilingSite(structureAndIndexingType, scratch, 0);
#endif
}
......
......@@ -407,7 +407,7 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas
void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
{
unsigned baseVal = currentInstruction[1].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
emitGetVirtualRegister(baseVal, regT0);
......@@ -416,20 +416,18 @@ void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
// Check that baseVal 'ImplementsHasInstance'.
loadPtr(Address(regT0, JSCell::structureOffset()), regT0);
addSlowCase(branchTest8(Zero, Address(regT0, Structure::typeInfoFlagsOffset()), TrustedImm32(ImplementsHasInstance)));
addSlowCase(branchTest8(Zero, Address(regT0, Structure::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
}
void JIT::emit_op_instanceof(Instruction* currentInstruction)
{
unsigned dst = currentInstruction[1].u.operand;
unsigned value = currentInstruction[2].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
unsigned proto = currentInstruction[4].u.operand;
// Load the operands (baseVal, proto, and value respectively) into registers.
// We use regT0 for baseVal since we will be done with this first, and we can then use it for the result.
emitGetVirtualRegister(value, regT2);
emitGetVirtualRegister(baseVal, regT0);
emitGetVirtualRegister(proto, regT1);
// Check that proto are cells. baseVal must be a cell - this is checked by op_check_has_instance.
......@@ -440,11 +438,6 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
loadPtr(Address(regT1, JSCell::structureOffset()), regT3);
addSlowCase(emitJumpIfNotObject(regT3));
// Fixme: this check is only needed because the JSC API allows HasInstance to be overridden; we should deprecate this.
// Check that baseVal 'ImplementsDefaultHasInstance'.
loadPtr(Address(regT0, JSCell::structureOffset()), regT0);
addSlowCase(branchTest8(Zero, Address(regT0, Structure::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
// Optimistically load the result true, and start looping.
// Initially, regT1 still contains proto and regT2 still contains value.
// As we loop regT2 will be updated with its prototype, recursively walking the prototype chain.
......@@ -1452,13 +1445,18 @@ void JIT::emitSlow_op_nstricteq(Instruction* currentInstruction, Vector<SlowCase
void JIT::emitSlow_op_check_has_instance(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
unsigned baseVal = currentInstruction[1].u.operand;
unsigned dst = currentInstruction[1].u.operand;
unsigned value = currentInstruction[2].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
linkSlowCaseIfNotJSCell(iter, baseVal);
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_check_has_instance);
stubCall.addArgument(value, regT2);
stubCall.addArgument(baseVal, regT2);
stubCall.call();
stubCall.call(dst);
emitJumpSlowToHot(jump(), currentInstruction[4].u.operand);
}
void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
......@@ -1471,7 +1469,6 @@ void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCas
linkSlowCaseIfNotJSCell(iter, value);
linkSlowCaseIfNotJSCell(iter, proto);
linkSlowCase(iter);
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_instanceof);
stubCall.addArgument(value, regT2);
stubCall.addArgument(baseVal, regT2);
......
......@@ -543,7 +543,7 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas
void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
{
unsigned baseVal = currentInstruction[1].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
emitLoadPayload(baseVal, regT0);
......@@ -552,20 +552,18 @@ void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
// Check that baseVal 'ImplementsHasInstance'.
loadPtr(Address(regT0, JSCell::structureOffset()), regT0);
addSlowCase(branchTest8(Zero, Address(regT0, Structure::typeInfoFlagsOffset()), TrustedImm32(ImplementsHasInstance)));
addSlowCase(branchTest8(Zero, Address(regT0, Structure::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
}
void JIT::emit_op_instanceof(Instruction* currentInstruction)
{
unsigned dst = currentInstruction[1].u.operand;
unsigned value = currentInstruction[2].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
unsigned proto = currentInstruction[4].u.operand;
// Load the operands into registers.
// We use regT0 for baseVal since we will be done with this first, and we can then use it for the result.
emitLoadPayload(value, regT2);
emitLoadPayload(baseVal, regT0);
emitLoadPayload(proto, regT1);
// Check that proto are cells. baseVal must be a cell - this is checked by op_check_has_instance.
......@@ -576,11 +574,6 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
loadPtr(Address(regT1, JSCell::structureOffset()), regT3);
addSlowCase(emitJumpIfNotObject(regT3));
// Fixme: this check is only needed because the JSC API allows HasInstance to be overridden; we should deprecate this.
// Check that baseVal 'ImplementsDefaultHasInstance'.
loadPtr(Address(regT0, JSCell::structureOffset()), regT0);
addSlowCase(branchTest8(Zero, Address(regT0, Structure::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
// Optimistically load the result true, and start looping.
// Initially, regT1 still contains proto and regT2 still contains value.
// As we loop regT2 will be updated with its prototype, recursively walking the prototype chain.
......@@ -604,14 +597,19 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
void JIT::emitSlow_op_check_has_instance(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
unsigned baseVal = currentInstruction[1].u.operand;
unsigned dst = currentInstruction[1].u.operand;
unsigned value = currentInstruction[2].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
linkSlowCaseIfNotJSCell(iter, baseVal);
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_check_has_instance);
stubCall.addArgument(value);
stubCall.addArgument(baseVal);
stubCall.call();
stubCall.call(dst);
emitJumpSlowToHot(jump(), currentInstruction[4].u.operand);
}
void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
......@@ -624,7 +622,6 @@ void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCas
linkSlowCaseIfNotJSCell(iter, value);
linkSlowCaseIfNotJSCell(iter, proto);
linkSlowCase(iter);
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_instanceof);
stubCall.addArgument(value);
......
......@@ -1937,21 +1937,27 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_string_fail)
return JSValue::encode(result);
}
DEFINE_STUB_FUNCTION(void, op_check_has_instance)
DEFINE_STUB_FUNCTION(EncodedJSValue, op_check_has_instance)
{
STUB_INIT_STACK_FRAME(stackFrame);
CallFrame* callFrame = stackFrame.callFrame;
JSValue baseVal = stackFrame.args[0].jsValue();
JSValue value = stackFrame.args[0].jsValue();
JSValue baseVal = stackFrame.args[1].jsValue();
if (baseVal.isObject()) {
JSObject* baseObject = asObject(baseVal);
ASSERT(!baseObject->structure()->typeInfo().implementsDefaultHasInstance());
if (baseObject->structure()->typeInfo().implementsHasInstance()) {
bool result = baseObject->methodTable()->customHasInstance(baseObject, callFrame, value);
CHECK_FOR_EXCEPTION_AT_END();
return JSValue::encode(jsBoolean(result));
}
}
// ECMA-262 15.3.5.3:
// Throw an exception either if baseVal is not an object, or if it does not implement 'HasInstance' (i.e. is a function).
#ifndef NDEBUG
TypeInfo typeInfo(UnspecifiedType);
ASSERT(!baseVal.isObject() || !(typeInfo = asObject(baseVal)->structure()->typeInfo()).implementsHasInstance());
#endif
stackFrame.globalData->exception = createInvalidParamError(callFrame, "instanceof", baseVal);
VM_THROW_EXCEPTION_AT_END();
return JSValue::encode(JSValue());
}
#if ENABLE(DFG_JIT)
......@@ -2082,10 +2088,12 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_instanceof)
CallFrame* callFrame = stackFrame.callFrame;
JSValue value = stackFrame.args[0].jsValue();
JSValue baseVal = stackFrame.args[1].jsValue();
JSValue proto = stackFrame.args[2].jsValue();
bool result = CommonSlowPaths::opInstanceOfSlow(callFrame, value, baseVal, proto);
ASSERT(stackFrame.args[1].jsValue().isObject() && asObject(stackFrame.args[1].jsValue())->structure()->typeInfo().implementsDefaultHasInstance());
ASSERT(!value.isObject() || !proto.isObject());
bool result = JSObject::defaultHasInstance(callFrame, value, proto);
CHECK_FOR_EXCEPTION_AT_END();
return JSValue::encode(jsBoolean(result));
}
......
......@@ -350,6 +350,7 @@ extern "C" {
EncodedJSValue JIT_STUB cti_op_call_NotJSFunction(STUB_ARGS_DECLARATION) WTF_INTERNAL;
EncodedJSValue JIT_STUB cti_op_call_eval(STUB_ARGS_DECLARATION) WTF_INTERNAL;
EncodedJSValue JIT_STUB cti_op_construct_NotJSConstruct(STUB_ARGS_DECLARATION) WTF_INTERNAL;
EncodedJSValue JIT_STUB cti_op_check_has_instance(STUB_ARGS_DECLARATION) WTF_INTERNAL;
EncodedJSValue JIT_STUB cti_op_create_this(STUB_ARGS_DECLARATION) WTF_INTERNAL;
EncodedJSValue JIT_STUB cti_op_convert_this(STUB_ARGS_DECLARATION) WTF_INTERNAL;
EncodedJSValue JIT_STUB cti_op_create_arguments(STUB_ARGS_DECLARATION) WTF_INTERNAL;
......@@ -431,7 +432,6 @@ extern "C" {
void* JIT_STUB cti_op_load_varargs(STUB_ARGS_DECLARATION) WTF_INTERNAL;
int JIT_STUB cti_timeout_check(STUB_ARGS_DECLARATION) WTF_INTERNAL;
int JIT_STUB cti_has_property(STUB_ARGS_DECLARATION) WTF_INTERNAL;
void JIT_STUB cti_op_check_has_instance(STUB_ARGS_DECLARATION) WTF_INTERNAL;
void JIT_STUB cti_op_debug(STUB_ARGS_DECLARATION) WTF_INTERNAL;
void JIT_STUB cti_op_end(STUB_ARGS_DECLARATION) WTF_INTERNAL;
void JIT_STUB cti_op_jmp_scopes(STUB_ARGS_DECLARATION) WTF_INTERNAL;
......
......@@ -718,19 +718,28 @@ LLINT_SLOW_PATH_DECL(slow_path_bitxor)
LLINT_SLOW_PATH_DECL(slow_path_check_has_instance)
{
LLINT_BEGIN();
JSValue baseVal = LLINT_OP_C(1).jsValue();
#ifndef NDEBUG
TypeInfo typeInfo(UnspecifiedType);
ASSERT(!baseVal.isObject()
|| !(typeInfo = asObject(baseVal)->structure()->typeInfo()).implementsHasInstance());
#endif
JSValue value = LLINT_OP_C(2).jsValue();
JSValue baseVal = LLINT_OP_C(3).jsValue();
if (baseVal.isObject()) {
JSObject* baseObject = asObject(baseVal);
ASSERT(!baseObject->structure()->typeInfo().implementsDefaultHasInstance());
if (baseObject->structure()->typeInfo().implementsHasInstance()) {
pc += pc[4].u.operand;
LLINT_RETURN(jsBoolean(baseObject->methodTable()->customHasInstance(baseObject, exec, value)));
}
}
LLINT_THROW(createInvalidParamError(exec, "instanceof", baseVal));
}
LLINT_SLOW_PATH_DECL(slow_path_instanceof)
{
LLINT_BEGIN();
LLINT_RETURN(jsBoolean(CommonSlowPaths::opInstanceOfSlow(exec, LLINT_OP_C(2).jsValue(), LLINT_OP_C(3).jsValue(), LLINT_OP_C(4).jsValue())));
JSValue value = LLINT_OP_C(2).jsValue();
JSValue proto = LLINT_OP_C(4).jsValue();
ASSERT(LLINT_OP_C(3).jsValue().isObject() && asObject(LLINT_OP_C(3).jsValue())->structure()->typeInfo().implementsDefaultHasInstance());
ASSERT(!value.isObject() || !proto.isObject());
LLINT_RETURN(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
}
LLINT_SLOW_PATH_DECL(slow_path_typeof)
......
......@@ -833,26 +833,19 @@ _llint_op_bitor:
_llint_op_check_has_instance:
traceExecution()
loadi 4[PC], t1
loadi 12[PC], t1
loadConstantOrVariablePayload(t1, CellTag, t0, .opCheckHasInstanceSlow)
loadp JSCell::m_structure[t0], t0
btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsHasInstance, .opCheckHasInstanceSlow
dispatch(2)
btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsDefaultHasInstance, .opCheckHasInstanceSlow
dispatch(5)
.opCheckHasInstanceSlow:
callSlowPath(_llint_slow_path_check_has_instance)
dispatch(2)
dispatch(0)
_llint_op_instanceof:
traceExecution()
# Check that baseVal implements the default HasInstance behavior.
# FIXME: This should be deprecated.
loadi 12[PC], t1
loadConstantOrVariablePayloadUnchecked(t1, t0)
loadp JSCell::m_structure[t0], t0
btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsDefaultHasInstance, .opInstanceofSlow
# Actually do the work.
loadi 16[PC], t0
loadi 4[PC], t3
......
......@@ -691,26 +691,19 @@ _llint_op_bitor:
_llint_op_check_has_instance:
traceExecution()
loadis 8[PB, PC, 8], t1
loadis 24[PB, PC, 8], t1
loadConstantOrVariableCell(t1, t0, .opCheckHasInstanceSlow)
loadp JSCell::m_structure[t0], t0
btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsHasInstance, .opCheckHasInstanceSlow
dispatch(2)
btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsDefaultHasInstance, .opCheckHasInstanceSlow
dispatch(5)
.opCheckHasInstanceSlow:
callSlowPath(_llint_slow_path_check_has_instance)
dispatch(2)
dispatch(0)
_llint_op_instanceof:
traceExecution()
# Check that baseVal implements the default HasInstance behavior.
# FIXME: This should be deprecated.
loadis 24[PB, PC, 8], t1
loadConstantOrVariable(t1, t0)
loadp JSCell::m_structure[t0], t0
btbz Structure::m_typeInfo + TypeInfo::m_flags[t0], ImplementsDefaultHasInstance, .opInstanceofSlow
# Actually do the work.
loadis 32[PB, PC, 8], t0
loadis 8[PB, PC, 8], t3
......
......@@ -81,8 +81,8 @@ namespace JSC {
typedef String (*ClassNameFunctionPtr)(const JSObject*);