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> 2012-09-21 Benjamin Poulain <bpoulain@apple.com>
fast/dom/Geolocation/disconnected-frame.html test asserts fast/dom/Geolocation/disconnected-frame.html test asserts
...@@ -26,7 +26,7 @@ PASS abcAt(1) is "b" ...@@ -26,7 +26,7 @@ PASS abcAt(1) is "b"
PASS new abcAt(1) threw exception TypeError: 'function charAt() { PASS new abcAt(1) threw exception TypeError: 'function charAt() {
[native code] [native code]
}' is not a constructor (evaluating 'new abcAt(1)'). }' 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 Function.bind.length is 1
PASS successfullyParsed is true PASS successfullyParsed is true
......
...@@ -186,7 +186,7 @@ private: ...@@ -186,7 +186,7 @@ private:
static bool deleteProperty(JSCell*, ExecState*, PropertyName); static bool deleteProperty(JSCell*, ExecState*, PropertyName);
static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned); 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); static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
......
...@@ -389,7 +389,7 @@ EncodedJSValue JSCallbackObject<Parent>::construct(ExecState* exec) ...@@ -389,7 +389,7 @@ EncodedJSValue JSCallbackObject<Parent>::construct(ExecState* exec)
} }
template <class Parent> 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); JSCallbackObject* thisObject = jsCast<JSCallbackObject*>(object);
JSContextRef execRef = toRef(exec); JSContextRef execRef = toRef(exec);
......
...@@ -175,7 +175,7 @@ bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObject ...@@ -175,7 +175,7 @@ bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObject
JSObject* jsConstructor = toJS(constructor); JSObject* jsConstructor = toJS(constructor);
if (!jsConstructor->structure()->typeInfo().implementsHasInstance()) if (!jsConstructor->structure()->typeInfo().implementsHasInstance())
return false; 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 (exec->hadException()) {
if (exception) if (exception)
*exception = toRef(exec, exec->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> 2012-09-21 Filip Pizlo <fpizlo@apple.com>
Unreviewed, fix ARM build. Unreviewed, fix ARM build.
......
...@@ -873,8 +873,11 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& ...@@ -873,8 +873,11 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
break; break;
} }
case op_check_has_instance: { case op_check_has_instance: {
int base = (++it)->u.operand; int r0 = (++it)->u.operand;
dataLog("[%4d] check_has_instance\t\t %s", location, registerName(exec, base).data()); 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); dumpBytecodeCommentAndNewLine(location);
break; break;
} }
......
...@@ -84,7 +84,7 @@ namespace JSC { ...@@ -84,7 +84,7 @@ namespace JSC {
macro(op_bitxor, 5) \ macro(op_bitxor, 5) \
macro(op_bitor, 5) \ macro(op_bitor, 5) \
\ \
macro(op_check_has_instance, 2) \ macro(op_check_has_instance, 5) \
macro(op_instanceof, 5) \ macro(op_instanceof, 5) \
macro(op_typeof, 3) \ macro(op_typeof, 3) \
macro(op_is_undefined, 3) \ macro(op_is_undefined, 3) \
......
...@@ -1457,10 +1457,14 @@ ResolveResult BytecodeGenerator::resolveConstDecl(const Identifier& property) ...@@ -1457,10 +1457,14 @@ ResolveResult BytecodeGenerator::resolveConstDecl(const Identifier& property)
return ResolveResult::dynamicResolve(scopeDepth()); 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); emitOpcode(op_check_has_instance);
instructions().append(dst->index());
instructions().append(value->index());
instructions().append(base->index()); instructions().append(base->index());
instructions().append(target->bind(begin, instructions().size()));
} }
RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype) RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype)
......
...@@ -455,7 +455,7 @@ namespace JSC { ...@@ -455,7 +455,7 @@ namespace JSC {
RegisterID* emitPostInc(RegisterID* dst, RegisterID* srcDst); RegisterID* emitPostInc(RegisterID* dst, RegisterID* srcDst);
RegisterID* emitPostDec(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* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype);
RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); } 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()); } 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 ...@@ -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> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator));
RefPtr<RegisterID> src2 = generator.emitNode(m_expr2); 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.emitExpressionInfo(divot(), startOffset(), endOffset());
generator.emitCheckHasInstance(src2.get()); generator.emitCheckHasInstance(dstReg.get(), src1.get(), src2.get(), target.get());
generator.emitExpressionInfo(divot(), startOffset(), endOffset()); 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()); 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 ---------------------------- // ------------------------------ LogicalOpNode ----------------------------
......
...@@ -2049,7 +2049,7 @@ bool ByteCodeParser::parseBlock(unsigned limit) ...@@ -2049,7 +2049,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
} }
case op_check_has_instance: 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); NEXT_OPCODE(op_check_has_instance);
case op_instanceof: { case op_instanceof: {
......
...@@ -131,14 +131,6 @@ static NEVER_INLINE bool isInvalidParamForIn(CallFrame* callFrame, JSValue value ...@@ -131,14 +131,6 @@ static NEVER_INLINE bool isInvalidParamForIn(CallFrame* callFrame, JSValue value
exceptionData = createInvalidParamError(callFrame, "in" , value); exceptionData = createInvalidParamError(callFrame, "in" , value);
return true; 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 #endif
JSValue eval(CallFrame* callFrame) JSValue eval(CallFrame* callFrame)
...@@ -2401,14 +2393,32 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi ...@@ -2401,14 +2393,32 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
JSC API*). Raises an exception if register constructor is not JSC API*). Raises an exception if register constructor is not
an valid parameter for instanceof. 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(); JSValue baseVal = callFrame->r(base).jsValue();
if (isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue)) if (baseVal.isObject()) {
goto vm_throw; 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); vPC += target;
NEXT_INSTRUCTION(); NEXT_INSTRUCTION();
}
}
exceptionValue = createInvalidParamError(callFrame, "instanceof" , baseVal);
goto vm_throw;
} }
DEFINE_OPCODE(op_instanceof) { DEFINE_OPCODE(op_instanceof) {
/* instanceof dst(r) value(r) constructor(r) constructorProto(r) /* instanceof dst(r) value(r) constructor(r) constructorProto(r)
...@@ -2425,14 +2435,11 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi ...@@ -2425,14 +2435,11 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
*/ */
int dst = vPC[1].u.operand; int dst = vPC[1].u.operand;
int value = vPC[2].u.operand; int value = vPC[2].u.operand;
int base = vPC[3].u.operand;
int baseProto = vPC[4].u.operand; int baseProto = vPC[4].u.operand;
JSValue baseVal = callFrame->r(base).jsValue(); ASSERT(callFrame->r(vPC[3].u.operand).jsValue().isObject() && asObject(callFrame->r(vPC[3].u.operand).jsValue())->structure()->typeInfo().implementsDefaultHasInstance());
ASSERT(!isInvalidParamForInstanceOf(callFrame, baseVal, exceptionValue));
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(); CHECK_FOR_EXCEPTION();
callFrame->uncheckedR(dst) = jsBoolean(result); callFrame->uncheckedR(dst) = jsBoolean(result);
......
...@@ -552,6 +552,7 @@ inline void JIT::emitArrayProfilingSiteForBytecodeIndex(RegisterID structureAndI ...@@ -552,6 +552,7 @@ inline void JIT::emitArrayProfilingSiteForBytecodeIndex(RegisterID structureAndI
#if ENABLE(VALUE_PROFILER) #if ENABLE(VALUE_PROFILER)
emitArrayProfilingSite(structureAndIndexingType, scratch, m_codeBlock->getOrAddArrayProfile(bytecodeIndex)); emitArrayProfilingSite(structureAndIndexingType, scratch, m_codeBlock->getOrAddArrayProfile(bytecodeIndex));
#else #else
UNUSED_PARAM(bytecodeIndex);
emitArrayProfilingSite(structureAndIndexingType, scratch, 0); emitArrayProfilingSite(structureAndIndexingType, scratch, 0);
#endif #endif
} }
......
...@@ -407,7 +407,7 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas ...@@ -407,7 +407,7 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas
void JIT::emit_op_check_has_instance(Instruction* currentInstruction) void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
{ {
unsigned baseVal = currentInstruction[1].u.operand; unsigned baseVal = currentInstruction[3].u.operand;
emitGetVirtualRegister(baseVal, regT0); emitGetVirtualRegister(baseVal, regT0);
...@@ -416,20 +416,18 @@ void JIT::emit_op_check_has_instance(Instruction* currentInstruction) ...@@ -416,20 +416,18 @@ void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
// Check that baseVal 'ImplementsHasInstance'. // Check that baseVal 'ImplementsHasInstance'.
loadPtr(Address(regT0, JSCell::structureOffset()), regT0); 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) void JIT::emit_op_instanceof(Instruction* currentInstruction)
{ {
unsigned dst = currentInstruction[1].u.operand; unsigned dst = currentInstruction[1].u.operand;
unsigned value = currentInstruction[2].u.operand; unsigned value = currentInstruction[2].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
unsigned proto = currentInstruction[4].u.operand; unsigned proto = currentInstruction[4].u.operand;
// Load the operands (baseVal, proto, and value respectively) into registers. // 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. // 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(value, regT2);
emitGetVirtualRegister(baseVal, regT0);
emitGetVirtualRegister(proto, regT1); emitGetVirtualRegister(proto, regT1);
// Check that proto are cells. baseVal must be a cell - this is checked by op_check_has_instance. // 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) ...@@ -440,11 +438,6 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
loadPtr(Address(regT1, JSCell::structureOffset()), regT3); loadPtr(Address(regT1, JSCell::structureOffset()), regT3);
addSlowCase(emitJumpIfNotObject(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. // Optimistically load the result true, and start looping.
// Initially, regT1 still contains proto and regT2 still contains value. // 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. // 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 ...@@ -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) 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); linkSlowCaseIfNotJSCell(iter, baseVal);
linkSlowCase(iter); linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_check_has_instance); JITStubCall stubCall(this, cti_op_check_has_instance);
stubCall.addArgument(value, regT2);
stubCall.addArgument(baseVal, 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) void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
...@@ -1471,7 +1469,6 @@ void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCas ...@@ -1471,7 +1469,6 @@ void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCas
linkSlowCaseIfNotJSCell(iter, value); linkSlowCaseIfNotJSCell(iter, value);
linkSlowCaseIfNotJSCell(iter, proto); linkSlowCaseIfNotJSCell(iter, proto);
linkSlowCase(iter); linkSlowCase(iter);
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_instanceof); JITStubCall stubCall(this, cti_op_instanceof);
stubCall.addArgument(value, regT2); stubCall.addArgument(value, regT2);
stubCall.addArgument(baseVal, regT2); stubCall.addArgument(baseVal, regT2);
......
...@@ -543,7 +543,7 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas ...@@ -543,7 +543,7 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas
void JIT::emit_op_check_has_instance(Instruction* currentInstruction) void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
{ {
unsigned baseVal = currentInstruction[1].u.operand; unsigned baseVal = currentInstruction[3].u.operand;
emitLoadPayload(baseVal, regT0); emitLoadPayload(baseVal, regT0);
...@@ -552,20 +552,18 @@ void JIT::emit_op_check_has_instance(Instruction* currentInstruction) ...@@ -552,20 +552,18 @@ void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
// Check that baseVal 'ImplementsHasInstance'. // Check that baseVal 'ImplementsHasInstance'.
loadPtr(Address(regT0, JSCell::structureOffset()), regT0); 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) void JIT::emit_op_instanceof(Instruction* currentInstruction)
{ {
unsigned dst = currentInstruction[1].u.operand; unsigned dst = currentInstruction[1].u.operand;
unsigned value = currentInstruction[2].u.operand; unsigned value = currentInstruction[2].u.operand;
unsigned baseVal = currentInstruction[3].u.operand;
unsigned proto = currentInstruction[4].u.operand; unsigned proto = currentInstruction[4].u.operand;
// Load the operands into registers. // 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. // 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(value, regT2);
emitLoadPayload(baseVal, regT0);
emitLoadPayload(proto, regT1); emitLoadPayload(proto, regT1);
// Check that proto are cells. baseVal must be a cell - this is checked by op_check_has_instance. // 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) ...@@ -576,11 +574,6 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
loadPtr(Address(regT1, JSCell::structureOffset()), regT3); loadPtr(Address(regT1, JSCell::structureOffset()), regT3);
addSlowCase(emitJumpIfNotObject(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. // Optimistically load the result true, and start looping.
// Initially, regT1 still contains proto and regT2 still contains value. // 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. // 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) ...@@ -604,14 +597,19 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
void JIT::emitSlow_op_check_has_instance(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 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); linkSlowCaseIfNotJSCell(iter, baseVal);
linkSlowCase(iter); linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_check_has_instance); JITStubCall stubCall(this, cti_op_check_has_instance);
stubCall.addArgument(value);
stubCall.addArgument(baseVal); 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) void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
...@@ -624,7 +622,6 @@ void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCas ...@@ -624,7 +622,6 @@ void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCas
linkSlowCaseIfNotJSCell(iter, value); linkSlowCaseIfNotJSCell(iter, value);
linkSlowCaseIfNotJSCell(iter, proto); linkSlowCaseIfNotJSCell(iter, proto);
linkSlowCase(iter); linkSlowCase(iter);
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_instanceof); JITStubCall stubCall(this, cti_op_instanceof);
stubCall.addArgument(value); stubCall.addArgument(value);
......
...@@ -1937,21 +1937,27 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_string_fail) ...@@ -1937,21 +1937,27 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_string_fail)
return JSValue::encode(result); 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); STUB_INIT_STACK_FRAME(stackFrame);
CallFrame* callFrame = stackFrame.callFrame; CallFrame* callFrame = stackFrame.callFrame;
JSValue baseVal = stackFrame.args[0].jsValue(); JSValue value = stackFrame.args[0].jsValue