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
diff --git a/Source/JavaScriptCore/runtime/ClassInfo.h b/Source/JavaScriptCore/runtime/ClassInfo.h
index 0e1747b..e8823d5 100644
--- a/Source/JavaScriptCore/runtime/ClassInfo.h
+++ b/Source/JavaScriptCore/runtime/ClassInfo.h
@@ -81,8 +81,8 @@
typedef String (*ClassNameFunctionPtr)(const JSObject*);
ClassNameFunctionPtr className;
- typedef bool (*HasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue, JSValue);
- HasInstanceFunctionPtr hasInstance;
+ typedef bool (*CustomHasInstanceFunctionPtr)(JSObject*, ExecState*, JSValue);
+ CustomHasInstanceFunctionPtr customHasInstance;
typedef void (*PutWithAttributesFunctionPtr)(JSObject*, ExecState*, PropertyName propertyName, JSValue, unsigned attributes);
PutWithAttributesFunctionPtr putDirectVirtual;
@@ -130,7 +130,7 @@
&ClassName::getOwnNonIndexPropertyNames, \
&ClassName::getPropertyNames, \
&ClassName::className, \
- &ClassName::hasInstance, \
+ &ClassName::customHasInstance, \
&ClassName::putDirectVirtual, \
&ClassName::defineOwnProperty, \
&ClassName::getOwnPropertyDescriptor, \
diff --git a/Source/JavaScriptCore/runtime/CommonSlowPaths.h b/Source/JavaScriptCore/runtime/CommonSlowPaths.h
index e4c76ad..7edd909 100644
--- a/Source/JavaScriptCore/runtime/CommonSlowPaths.h
+++ b/Source/JavaScriptCore/runtime/CommonSlowPaths.h
@@ -75,28 +75,6 @@
return newExec;
}
-ALWAYS_INLINE bool opInstanceOfSlow(ExecState* exec, JSValue value, JSValue baseVal, JSValue proto)
-{
- ASSERT(!value.isCell() || !baseVal.isCell() || !proto.isCell()
- || !value.isObject() || !baseVal.isObject() || !proto.isObject()
- || !asObject(baseVal)->structure()->typeInfo().implementsDefaultHasInstance());
-
-
- // 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).
- TypeInfo typeInfo(UnspecifiedType);
- if (!baseVal.isObject() || !(typeInfo = asObject(baseVal)->structure()->typeInfo()).implementsHasInstance()) {
- exec->globalData().exception = createInvalidParamError(exec, "instanceof", baseVal);
- return false;
- }
- ASSERT(typeInfo.type() != UnspecifiedType);
-
- if (!typeInfo.overridesHasInstance() && !value.isObject())
- return false;
-
- return asObject(baseVal)->methodTable()->hasInstance(asObject(baseVal), exec, value, proto);
-}
-
inline bool opIn(ExecState* exec, JSValue propName, JSValue baseVal)
{
if (!baseVal.isObject()) {
diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
index 08d6831..3815c14 100644
--- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
+++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
@@ -89,14 +89,9 @@
return function;
}
-bool JSBoundFunction::hasInstance(JSObject* object, ExecState* exec, JSValue value, JSValue)
+bool JSBoundFunction::customHasInstance(JSObject* object, ExecState* exec, JSValue value)
{
- JSBoundFunction* thisObject = jsCast<JSBoundFunction*>(object);
- // FIXME: our instanceof implementation will have already (incorrectly) performed
- // a [[Get]] of .prototype from the bound function object, which is incorrect!
- // https://bugs.webkit.org/show_bug.cgi?id=68656
- JSValue proto = thisObject->m_targetFunction->get(exec, exec->propertyNames().prototype);
- return thisObject->m_targetFunction->methodTable()->hasInstance(thisObject->m_targetFunction.get(), exec, value, proto);
+ return jsCast<JSBoundFunction*>(object)->m_targetFunction->hasInstance(exec, value);
}
JSBoundFunction::JSBoundFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs)
diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.h b/Source/JavaScriptCore/runtime/JSBoundFunction.h
index 5067d19..05a6ad8 100644
--- a/Source/JavaScriptCore/runtime/JSBoundFunction.h
+++ b/Source/JavaScriptCore/runtime/JSBoundFunction.h
@@ -39,7 +39,7 @@
static JSBoundFunction* create(ExecState*, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const String&);
- static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue proto);
+ static bool customHasInstance(JSObject*, ExecState*, JSValue);
JSObject* targetFunction() { return m_targetFunction.get(); }
JSValue boundThis() { return m_boundThis.get(); }
diff --git a/Source/JavaScriptCore/runtime/JSCell.cpp b/Source/JavaScriptCore/runtime/JSCell.cpp
index e050a53..739247f 100644
--- a/Source/JavaScriptCore/runtime/JSCell.cpp
+++ b/Source/JavaScriptCore/runtime/JSCell.cpp
@@ -199,7 +199,7 @@
ASSERT_NOT_REACHED();
}
-bool JSCell::hasInstance(JSObject*, ExecState*, JSValue, JSValue)
+bool JSCell::customHasInstance(JSObject*, ExecState*, JSValue)
{
ASSERT_NOT_REACHED();
return false;
diff --git a/Source/JavaScriptCore/runtime/JSCell.h b/Source/JavaScriptCore/runtime/JSCell.h
index d6abcba..cf6f4ec 100644
--- a/Source/JavaScriptCore/runtime/JSCell.h
+++ b/Source/JavaScriptCore/runtime/JSCell.h
@@ -160,7 +160,7 @@
static NO_RETURN_DUE_TO_ASSERT void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
static NO_RETURN_DUE_TO_ASSERT void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
static String className(const JSObject*);
- static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue prototypeProperty);
+ JS_EXPORT_PRIVATE static bool customHasInstance(JSObject*, ExecState*, JSValue);
static NO_RETURN_DUE_TO_ASSERT void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes);
static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp
index 2bea795..acff8a6 100644
--- a/Source/JavaScriptCore/runtime/JSObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSObject.cpp
@@ -783,7 +783,18 @@
return 0;
}
-bool JSObject::hasInstance(JSObject*, ExecState* exec, JSValue value, JSValue proto)
+bool JSObject::hasInstance(ExecState* exec, JSValue value)
+{
+ TypeInfo info = structure()->typeInfo();
+ if (info.implementsDefaultHasInstance())
+ return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
+ if (info.implementsHasInstance())
+ return methodTable()->customHasInstance(this, exec, value);
+ throwError(exec, createInvalidParamError(exec, "instanceof" , this));
+ return false;
+}
+
+bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
{
if (!value.isObject())
return false;
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h
index de5935f..bb59eb3 100644
--- a/Source/JavaScriptCore/runtime/JSObject.h
+++ b/Source/JavaScriptCore/runtime/JSObject.h
@@ -306,7 +306,8 @@
JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
- JS_EXPORT_PRIVATE static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue prototypeProperty);
+ bool hasInstance(ExecState*, JSValue);
+ static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty);
JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);