[ES6] Add support for Symbol.hasInstance
https://bugs.webkit.org/show_bug.cgi?id=151839
Reviewed by Saam Barati.
Source/JavaScriptCore:
Fixed version of r193986, r193983, and r193974.
This patch adds support for Symbol.hasInstance, unfortunately in order to prevent
regressions several new bytecodes and DFG IR nodes were necessary. Before, Symbol.hasInstance
when executing an instanceof expression we would emit three bytecodes: overrides_has_instance, get_by_id,
then instanceof. As the spec has changed, we emit a more complicated set of bytecodes in addition to some
new ones. First the role of overrides_has_instance and its corresponding DFG node have changed. Now it returns
a js-boolean indicating whether the RHS of the instanceof expression (from here on called the constructor for simplicity)
needs non-default behavior for resolving the expression. i.e. The constructor has a Symbol.hasInstance that differs from the one on
Function.prototype[Symbol.hasInstance] or is a bound/C-API function. Once we get to the DFG this node is generally eliminated as
we can prove the value of Symbol.hasInstance is a constant. The second new bytecode is instanceof_custom. insntanceof_custom, just
emits a call to slow path code that computes the result.
In the DFG, there is also a new node, CheckTypeInfoFlags, which checks the type info flags are consistent with the ones provided and
OSR exits if the flags are not. Additionally, we attempt to prove that the result of CheckHasValue will be a constant and transform
it into a CheckTypeInfoFlags followed by a JSConstant.
* API/JSCallbackObject.h:
* builtins/FunctionPrototype.js:
(symbolHasInstance):
* bytecode/BytecodeBasicBlock.cpp:
(JSC::isBranch): Deleted.
* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecode/ExitKind.cpp:
(JSC::exitKindToString):
* bytecode/ExitKind.h:
* bytecode/PreciseJumpTargets.cpp:
(JSC::getJumpTargetsForBytecodeOffset): Deleted.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitOverridesHasInstance):
(JSC::BytecodeGenerator::emitInstanceOfCustom):
(JSC::BytecodeGenerator::emitCheckHasInstance): Deleted.
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::InstanceOfNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGHeapLocation.cpp:
(WTF::printInternal):
* dfg/DFGHeapLocation.h:
* dfg/DFGNode.h:
(JSC::DFG::Node::hasCellOperand):
(JSC::DFG::Node::hasTypeInfoOperand):
(JSC::DFG::Node::typeInfoOperand):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileCheckTypeInfoFlags):
(JSC::DFG::SpeculativeJIT::compileInstanceOfCustom):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLIntrinsicRepository.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileOverridesHasInstance):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCheckTypeInfoFlags):
(JSC::FTL::DFG::LowerDFGToLLVM::compileInstanceOfCustom):
(JSC::FTL::DFG::LowerDFGToLLVM::compileCheckHasInstance): Deleted.
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileSlowCases):
* jit/JIT.h:
* jit/JITInlines.h:
(JSC::JIT::callOperation):
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_overrides_has_instance):
(JSC::JIT::emit_op_instanceof):
(JSC::JIT::emit_op_instanceof_custom):
(JSC::JIT::emitSlow_op_instanceof):
(JSC::JIT::emitSlow_op_instanceof_custom):
(JSC::JIT::emit_op_check_has_instance): Deleted.
(JSC::JIT::emitSlow_op_check_has_instance): Deleted.
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_overrides_has_instance):
(JSC::JIT::emit_op_instanceof):
(JSC::JIT::emit_op_instanceof_custom):
(JSC::JIT::emitSlow_op_instanceof_custom):
(JSC::JIT::emit_op_check_has_instance): Deleted.
(JSC::JIT::emitSlow_op_check_has_instance): Deleted.
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CommonIdentifiers.h:
* runtime/ExceptionHelpers.cpp:
(JSC::invalidParameterInstanceofSourceAppender):
(JSC::invalidParameterInstanceofNotFunctionSourceAppender):
(JSC::invalidParameterInstanceofhasInstanceValueNotFunctionSourceAppender):
(JSC::createInvalidInstanceofParameterErrorNotFunction):
(JSC::createInvalidInstanceofParameterErrorhasInstanceValueNotFunction):
(JSC::createInvalidInstanceofParameterError): Deleted.
* runtime/ExceptionHelpers.h:
* runtime/FunctionPrototype.cpp:
(JSC::FunctionPrototype::addFunctionProperties):
* runtime/FunctionPrototype.h:
* runtime/JSBoundFunction.cpp:
(JSC::isBoundFunction):
(JSC::hasInstanceBoundFunction):
* runtime/JSBoundFunction.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::functionProtoHasInstanceSymbolFunction):
* runtime/JSObject.cpp:
(JSC::JSObject::hasInstance):
(JSC::objectPrivateFuncInstanceOf):
* runtime/JSObject.h:
* runtime/JSTypeInfo.h:
(JSC::TypeInfo::TypeInfo):
(JSC::TypeInfo::overridesHasInstance):
* runtime/WriteBarrier.h:
(JSC::WriteBarrierBase<Unknown>::slot):
* tests/es6.yaml:
* tests/stress/instanceof-custom-hasinstancesymbol.js: Added.
(Constructor):
(value):
(instanceOf):
(body):
* tests/stress/symbol-hasInstance.js: Added.
(Constructor):
(value):
(ObjectClass.Symbol.hasInstance):
(NumberClass.Symbol.hasInstance):
LayoutTests:
Fix tests to reflect the changes to instanceof in ES6.
Added a new regression test for bound functions in instanceof
as the perfomance on bound functions should, to some degree,
reflect the performance on C-API users.
* inspector/model/remote-object-get-properties-expected.txt:
* js/Object-getOwnPropertyNames-expected.txt:
* js/exception-for-nonobject-expected.txt:
* js/exception-instanceof-expected.txt:
* js/instance-of-immediates-expected.txt:
* js/regress/instanceof-bound-expected.txt: Added.
* js/regress/instanceof-bound.html: Added.
* js/regress/script-tests/instanceof-bound.js: Added.
(Constructor):
(test):
* js/script-tests/Object-getOwnPropertyNames.js:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@194248 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 79ee225..53b57f8 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,28 @@
+2015-12-17 Keith Miller <keith_miller@apple.com>
+
+ [ES6] Add support for Symbol.hasInstance
+ https://bugs.webkit.org/show_bug.cgi?id=151839
+
+ Reviewed by Saam Barati.
+
+ Fix tests to reflect the changes to instanceof in ES6.
+
+ Added a new regression test for bound functions in instanceof
+ as the perfomance on bound functions should, to some degree,
+ reflect the performance on C-API users.
+
+ * inspector/model/remote-object-get-properties-expected.txt:
+ * js/Object-getOwnPropertyNames-expected.txt:
+ * js/exception-for-nonobject-expected.txt:
+ * js/exception-instanceof-expected.txt:
+ * js/instance-of-immediates-expected.txt:
+ * js/regress/instanceof-bound-expected.txt: Added.
+ * js/regress/instanceof-bound.html: Added.
+ * js/regress/script-tests/instanceof-bound.js: Added.
+ (Constructor):
+ (test):
+ * js/script-tests/Object-getOwnPropertyNames.js:
+
2015-12-17 Simon Fraser <simon.fraser@apple.com>
Disable viewport "shrink to fit" outside of multitasking mode
diff --git a/LayoutTests/inspector/model/remote-object-get-properties-expected.txt b/LayoutTests/inspector/model/remote-object-get-properties-expected.txt
index bc5f590..e6c0d03 100644
--- a/LayoutTests/inspector/model/remote-object-get-properties-expected.txt
+++ b/LayoutTests/inspector/model/remote-object-get-properties-expected.txt
@@ -202,6 +202,7 @@
call
bind
constructor
+ Symbol(Symbol.hasInstance)
toLocaleString
valueOf
hasOwnProperty
@@ -251,6 +252,7 @@
call
bind
constructor
+ Symbol(Symbol.hasInstance)
toLocaleString
valueOf
hasOwnProperty
diff --git a/LayoutTests/js/Object-getOwnPropertyNames-expected.txt b/LayoutTests/js/Object-getOwnPropertyNames-expected.txt
index cae219b..7f5de5f 100644
--- a/LayoutTests/js/Object-getOwnPropertyNames-expected.txt
+++ b/LayoutTests/js/Object-getOwnPropertyNames-expected.txt
@@ -61,7 +61,7 @@
PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString']
PASS getSortedOwnPropertyNames(Math) is ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']
PASS getSortedOwnPropertyNames(JSON) is ['parse', 'stringify']
-PASS getSortedOwnPropertyNames(Symbol) is ['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'toStringTag', 'unscopables']
+PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'toStringTag', 'unscopables']
PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf']
PASS globalPropertyNames.indexOf('NaN') != -1 is true
PASS globalPropertyNames.indexOf('Infinity') != -1 is true
diff --git a/LayoutTests/js/exception-for-nonobject-expected.txt b/LayoutTests/js/exception-for-nonobject-expected.txt
index 9f9ae69..774d227 100644
--- a/LayoutTests/js/exception-for-nonobject-expected.txt
+++ b/LayoutTests/js/exception-for-nonobject-expected.txt
@@ -4,7 +4,7 @@
PASS new {}.undefined threw exception TypeError: undefined is not a constructor (evaluating 'new {}.undefined').
-PASS 1 instanceof {}.undefined threw exception TypeError: {}.undefined is not a function. (evaluating '1 instanceof {}.undefined').
+PASS 1 instanceof {}.undefined threw exception TypeError: Right hand side of instanceof is not an object.
PASS successfullyParsed is true
TEST COMPLETE
diff --git a/LayoutTests/js/exception-instanceof-expected.txt b/LayoutTests/js/exception-instanceof-expected.txt
index ed68b6c..0efd7a8 100644
--- a/LayoutTests/js/exception-instanceof-expected.txt
+++ b/LayoutTests/js/exception-instanceof-expected.txt
@@ -3,11 +3,11 @@
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-PASS 'instanceof' instanceof 'instanceof' threw exception TypeError: "instanceof" is not a function. (evaluating ''instanceof' instanceof 'instanceof'').
-PASS 20 instanceof 'hello' threw exception TypeError: 'hello' is not a function. (evaluating '20 instanceof 'hello'').
+PASS 'instanceof' instanceof 'instanceof' threw exception TypeError: Right hand side of instanceof is not an object.
+PASS 20 instanceof 'hello' threw exception TypeError: Right hand side of instanceof is not an object.
PASS 20 instanceof {} threw exception TypeError: {} is not a function. (evaluating '20 instanceof {}').
-PASS 20 instanceof {}.foo threw exception TypeError: {}.foo is not a function. (evaluating '20 instanceof {}.foo').
-PASS 20 instanceof true threw exception TypeError: true is not a function. (evaluating '20 instanceof true').
+PASS 20 instanceof {}.foo threw exception TypeError: Right hand side of instanceof is not an object.
+PASS 20 instanceof true threw exception TypeError: Right hand side of instanceof is not an object.
PASS successfullyParsed is true
TEST COMPLETE
diff --git a/LayoutTests/js/instance-of-immediates-expected.txt b/LayoutTests/js/instance-of-immediates-expected.txt
index 1100db8..e6efc6a 100644
--- a/LayoutTests/js/instance-of-immediates-expected.txt
+++ b/LayoutTests/js/instance-of-immediates-expected.txt
@@ -3,9 +3,9 @@
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-PASS (1 instanceof 1) threw exception TypeError: 1 is not a function. (evaluating '1 instanceof 1').
-PASS ({} instanceof 1) threw exception TypeError: 1 is not a function. (evaluating '{} instanceof 1').
-PASS (obj instanceof 1) threw exception TypeError: 1 is not a function. (evaluating 'obj instanceof 1').
+PASS (1 instanceof 1) threw exception TypeError: Right hand side of instanceof is not an object.
+PASS ({} instanceof 1) threw exception TypeError: Right hand side of instanceof is not an object.
+PASS (obj instanceof 1) threw exception TypeError: Right hand side of instanceof is not an object.
PASS (1 instanceof {}) threw exception TypeError: {} is not a function. (evaluating '1 instanceof {}').
PASS ({} instanceof {}) threw exception TypeError: {} is not a function. (evaluating '{} instanceof {}').
PASS (obj instanceof {}) threw exception TypeError: {} is not a function. (evaluating 'obj instanceof {}').
diff --git a/LayoutTests/js/regress/instanceof-bound-expected.txt b/LayoutTests/js/regress/instanceof-bound-expected.txt
new file mode 100644
index 0000000..882bcbe
--- /dev/null
+++ b/LayoutTests/js/regress/instanceof-bound-expected.txt
@@ -0,0 +1,10 @@
+JSRegress/instanceof-bound
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/instanceof-bound.html b/LayoutTests/js/regress/instanceof-bound.html
new file mode 100644
index 0000000..6512064
--- /dev/null
+++ b/LayoutTests/js/regress/instanceof-bound.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/instanceof-bound.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/instanceof-bound.js b/LayoutTests/js/regress/script-tests/instanceof-bound.js
new file mode 100644
index 0000000..ddf8e53
--- /dev/null
+++ b/LayoutTests/js/regress/script-tests/instanceof-bound.js
@@ -0,0 +1,30 @@
+// This tests that we do not constantly OSR on instanceof where the RHS is a bound function.
+// While this bound functions are unlikely to be passed to instanceof often C-API users use
+// the same method of overriding instanceof expressions.
+
+
+function Constructor(x) {
+ this.x = x;
+}
+
+Constructor.prototype = {}
+
+BoundConstructor = Constructor.bind();
+foo = new Constructor(1);
+bar = new BoundConstructor(1);
+
+i = 0;
+
+function test()
+{
+ if (!(foo instanceof BoundConstructor)) {
+ throw new Error("foo should be an instanceof BoundConstructor");
+ }
+ let j = 0;
+ for (;j < 1000; j++) {}
+ return j;
+}
+noInline(test);
+
+for (i = 0; i < 50000; i++)
+ test();
diff --git a/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js b/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
index cb5ec24..74bcb78 100644
--- a/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
+++ b/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
@@ -70,7 +70,7 @@
"Error.prototype": "['constructor', 'message', 'name', 'toString']",
"Math": "['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']",
"JSON": "['parse', 'stringify']",
- "Symbol": "['for', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'toStringTag', 'unscopables']",
+ "Symbol": "['for', 'hasInstance', 'iterator', 'keyFor', 'length', 'name', 'prototype', 'toStringTag', 'unscopables']",
"Symbol.prototype": "['constructor', 'toString', 'valueOf']"
};
diff --git a/Source/JavaScriptCore/API/JSCallbackObject.h b/Source/JavaScriptCore/API/JSCallbackObject.h
index a1a77fd..2b4734d 100644
--- a/Source/JavaScriptCore/API/JSCallbackObject.h
+++ b/Source/JavaScriptCore/API/JSCallbackObject.h
@@ -127,7 +127,7 @@
public:
typedef Parent Base;
- static const unsigned StructureFlags = Base::StructureFlags | ProhibitsPropertyCaching | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | ImplementsHasInstance | OverridesHasInstance | OverridesGetPropertyNames | TypeOfShouldCallGetCallData;
+ static const unsigned StructureFlags = Base::StructureFlags | ProhibitsPropertyCaching | OverridesGetOwnPropertySlot | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | ImplementsHasInstance | OverridesHasInstanceFlag | OverridesGetPropertyNames | TypeOfShouldCallGetCallData;
~JSCallbackObject();
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 7314ed7..302a125 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,164 @@
+2015-12-17 Keith Miller <keith_miller@apple.com>
+
+ [ES6] Add support for Symbol.hasInstance
+ https://bugs.webkit.org/show_bug.cgi?id=151839
+
+ Reviewed by Saam Barati.
+
+ Fixed version of r193986, r193983, and r193974.
+
+ This patch adds support for Symbol.hasInstance, unfortunately in order to prevent
+ regressions several new bytecodes and DFG IR nodes were necessary. Before, Symbol.hasInstance
+ when executing an instanceof expression we would emit three bytecodes: overrides_has_instance, get_by_id,
+ then instanceof. As the spec has changed, we emit a more complicated set of bytecodes in addition to some
+ new ones. First the role of overrides_has_instance and its corresponding DFG node have changed. Now it returns
+ a js-boolean indicating whether the RHS of the instanceof expression (from here on called the constructor for simplicity)
+ needs non-default behavior for resolving the expression. i.e. The constructor has a Symbol.hasInstance that differs from the one on
+ Function.prototype[Symbol.hasInstance] or is a bound/C-API function. Once we get to the DFG this node is generally eliminated as
+ we can prove the value of Symbol.hasInstance is a constant. The second new bytecode is instanceof_custom. insntanceof_custom, just
+ emits a call to slow path code that computes the result.
+
+ In the DFG, there is also a new node, CheckTypeInfoFlags, which checks the type info flags are consistent with the ones provided and
+ OSR exits if the flags are not. Additionally, we attempt to prove that the result of CheckHasValue will be a constant and transform
+ it into a CheckTypeInfoFlags followed by a JSConstant.
+
+ * API/JSCallbackObject.h:
+ * builtins/FunctionPrototype.js:
+ (symbolHasInstance):
+ * bytecode/BytecodeBasicBlock.cpp:
+ (JSC::isBranch): Deleted.
+ * bytecode/BytecodeList.json:
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset):
+ (JSC::computeDefsForBytecodeOffset):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ * bytecode/ExitKind.cpp:
+ (JSC::exitKindToString):
+ * bytecode/ExitKind.h:
+ * bytecode/PreciseJumpTargets.cpp:
+ (JSC::getJumpTargetsForBytecodeOffset): Deleted.
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::emitOverridesHasInstance):
+ (JSC::BytecodeGenerator::emitInstanceOfCustom):
+ (JSC::BytecodeGenerator::emitCheckHasInstance): Deleted.
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::InstanceOfNode::emitBytecode):
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGHeapLocation.cpp:
+ (WTF::printInternal):
+ * dfg/DFGHeapLocation.h:
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::hasCellOperand):
+ (JSC::DFG::Node::hasTypeInfoOperand):
+ (JSC::DFG::Node::typeInfoOperand):
+ * dfg/DFGNodeType.h:
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileCheckTypeInfoFlags):
+ (JSC::DFG::SpeculativeJIT::compileInstanceOfCustom):
+ * dfg/DFGSpeculativeJIT.h:
+ (JSC::DFG::SpeculativeJIT::callOperation):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLIntrinsicRepository.h:
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileOverridesHasInstance):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileCheckTypeInfoFlags):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileInstanceOfCustom):
+ (JSC::FTL::DFG::LowerDFGToLLVM::compileCheckHasInstance): Deleted.
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ (JSC::JIT::privateCompileSlowCases):
+ * jit/JIT.h:
+ * jit/JITInlines.h:
+ (JSC::JIT::callOperation):
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_overrides_has_instance):
+ (JSC::JIT::emit_op_instanceof):
+ (JSC::JIT::emit_op_instanceof_custom):
+ (JSC::JIT::emitSlow_op_instanceof):
+ (JSC::JIT::emitSlow_op_instanceof_custom):
+ (JSC::JIT::emit_op_check_has_instance): Deleted.
+ (JSC::JIT::emitSlow_op_check_has_instance): Deleted.
+ * jit/JITOpcodes32_64.cpp:
+ (JSC::JIT::emit_op_overrides_has_instance):
+ (JSC::JIT::emit_op_instanceof):
+ (JSC::JIT::emit_op_instanceof_custom):
+ (JSC::JIT::emitSlow_op_instanceof_custom):
+ (JSC::JIT::emit_op_check_has_instance): Deleted.
+ (JSC::JIT::emitSlow_op_check_has_instance): Deleted.
+ * jit/JITOperations.cpp:
+ * jit/JITOperations.h:
+ * llint/LLIntData.cpp:
+ (JSC::LLInt::Data::performAssertions):
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+ * llint/LLIntSlowPaths.h:
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+ * runtime/CommonIdentifiers.h:
+ * runtime/ExceptionHelpers.cpp:
+ (JSC::invalidParameterInstanceofSourceAppender):
+ (JSC::invalidParameterInstanceofNotFunctionSourceAppender):
+ (JSC::invalidParameterInstanceofhasInstanceValueNotFunctionSourceAppender):
+ (JSC::createInvalidInstanceofParameterErrorNotFunction):
+ (JSC::createInvalidInstanceofParameterErrorhasInstanceValueNotFunction):
+ (JSC::createInvalidInstanceofParameterError): Deleted.
+ * runtime/ExceptionHelpers.h:
+ * runtime/FunctionPrototype.cpp:
+ (JSC::FunctionPrototype::addFunctionProperties):
+ * runtime/FunctionPrototype.h:
+ * runtime/JSBoundFunction.cpp:
+ (JSC::isBoundFunction):
+ (JSC::hasInstanceBoundFunction):
+ * runtime/JSBoundFunction.h:
+ * runtime/JSGlobalObject.cpp:
+ (JSC::JSGlobalObject::init):
+ (JSC::JSGlobalObject::visitChildren):
+ * runtime/JSGlobalObject.h:
+ (JSC::JSGlobalObject::functionProtoHasInstanceSymbolFunction):
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::hasInstance):
+ (JSC::objectPrivateFuncInstanceOf):
+ * runtime/JSObject.h:
+ * runtime/JSTypeInfo.h:
+ (JSC::TypeInfo::TypeInfo):
+ (JSC::TypeInfo::overridesHasInstance):
+ * runtime/WriteBarrier.h:
+ (JSC::WriteBarrierBase<Unknown>::slot):
+ * tests/es6.yaml:
+ * tests/stress/instanceof-custom-hasinstancesymbol.js: Added.
+ (Constructor):
+ (value):
+ (instanceOf):
+ (body):
+ * tests/stress/symbol-hasInstance.js: Added.
+ (Constructor):
+ (value):
+ (ObjectClass.Symbol.hasInstance):
+ (NumberClass.Symbol.hasInstance):
+
2015-12-17 Joseph Pecoraro <pecoraro@apple.com>
Web Inspector: Improve names in Debugger Call Stack section when paused
diff --git a/Source/JavaScriptCore/builtins/FunctionPrototype.js b/Source/JavaScriptCore/builtins/FunctionPrototype.js
index 1aa0242..364bc43 100644
--- a/Source/JavaScriptCore/builtins/FunctionPrototype.js
+++ b/Source/JavaScriptCore/builtins/FunctionPrototype.js
@@ -36,3 +36,18 @@
return this.@apply(thisValue, argumentValues);
}
+
+// FIXME: this should have a different name: https://bugs.webkit.org/show_bug.cgi?id=151363
+function symbolHasInstance(value)
+{
+ "use strict";
+
+ if (typeof this !== "function")
+ return false;
+
+ if (@isBoundFunction(this))
+ return @hasInstanceBoundFunction(this, value);
+
+ let target = this.prototype;
+ return @instanceOf(value, target);
+}
diff --git a/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp b/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp
index 596064c..f329d0b 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp
@@ -58,7 +58,6 @@
case op_switch_imm:
case op_switch_char:
case op_switch_string:
- case op_check_has_instance:
case op_save:
return true;
default:
diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json
index bccab04..053b8dc 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeList.json
+++ b/Source/JavaScriptCore/bytecode/BytecodeList.json
@@ -45,8 +45,9 @@
{ "name" : "op_bitand", "length" : 5 },
{ "name" : "op_bitxor", "length" : 5 },
{ "name" : "op_bitor", "length" : 5 },
- { "name" : "op_check_has_instance", "length" : 5 },
+ { "name" : "op_overrides_has_instance", "length" : 4 },
{ "name" : "op_instanceof", "length" : 4 },
+ { "name" : "op_instanceof_custom", "length" : 5 },
{ "name" : "op_typeof", "length" : 3 },
{ "name" : "op_is_undefined", "length" : 3 },
{ "name" : "op_is_boolean", "length" : 3 },
diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
index d8ccc67..801114c 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
@@ -73,6 +73,7 @@
case op_dec:
case op_inc:
case op_resume: {
+ ASSERT(opcodeLengths[opcodeID] >= 1);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
return;
}
@@ -85,12 +86,14 @@
case op_jngreatereq:
case op_jless:
case op_copy_rest: {
+ ASSERT(opcodeLengths[opcodeID] >= 2);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
return;
}
case op_put_by_val_direct:
case op_put_by_val: {
+ ASSERT(opcodeLengths[opcodeID] >= 3);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
@@ -100,17 +103,20 @@
case op_put_by_id:
case op_put_to_scope:
case op_put_to_arguments: {
+ ASSERT(opcodeLengths[opcodeID] >= 3);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
return;
}
case op_put_getter_by_id:
case op_put_setter_by_id: {
+ ASSERT(opcodeLengths[opcodeID] >= 4);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[4].u.operand);
return;
}
case op_put_getter_setter_by_id: {
+ ASSERT(opcodeLengths[opcodeID] >= 5);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[4].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[5].u.operand);
@@ -118,6 +124,7 @@
}
case op_put_getter_by_val:
case op_put_setter_by_val: {
+ ASSERT(opcodeLengths[opcodeID] >= 4);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[4].u.operand);
@@ -159,6 +166,7 @@
case op_get_parent_scope:
case op_create_scoped_arguments:
case op_get_from_arguments: {
+ ASSERT(opcodeLengths[opcodeID] >= 2);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
return;
}
@@ -168,8 +176,8 @@
case op_enumerator_generic_pname:
case op_get_by_val:
case op_in:
+ case op_overrides_has_instance:
case op_instanceof:
- case op_check_has_instance:
case op_add:
case op_mul:
case op_div:
@@ -191,20 +199,24 @@
case op_eq:
case op_push_with_scope:
case op_del_by_val: {
+ ASSERT(opcodeLengths[opcodeID] > 3);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
return;
}
+ case op_instanceof_custom:
case op_has_structure_property:
case op_construct_varargs:
case op_call_varargs:
case op_tail_call_varargs: {
+ ASSERT(opcodeLengths[opcodeID] > 4);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[4].u.operand);
return;
}
case op_get_direct_pname: {
+ ASSERT(opcodeLengths[opcodeID] > 5);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[4].u.operand);
@@ -214,6 +226,7 @@
case op_switch_string:
case op_switch_char:
case op_switch_imm: {
+ ASSERT(opcodeLengths[opcodeID] > 3);
functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
return;
}
@@ -350,8 +363,9 @@
case op_construct:
case op_get_by_id:
case op_get_array_length:
- case op_check_has_instance:
+ case op_overrides_has_instance:
case op_instanceof:
+ case op_instanceof_custom:
case op_get_by_val:
case op_typeof:
case op_is_undefined:
@@ -402,10 +416,12 @@
case op_unsigned:
case op_get_from_arguments:
case op_get_rest_length: {
+ ASSERT(opcodeLengths[opcodeID] > 1);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
return;
}
case op_catch: {
+ ASSERT(opcodeLengths[opcodeID] > 2);
functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
return;
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index a4799fe..879e418 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1013,13 +1013,12 @@
++it;
break;
}
- case op_check_has_instance: {
+ case op_overrides_has_instance: {
int r0 = (++it)->u.operand;
int r1 = (++it)->u.operand;
int r2 = (++it)->u.operand;
- int offset = (++it)->u.operand;
- printLocationAndOp(out, exec, location, it, "check_has_instance");
- out.printf("%s, %s, %s, %d(->%d)", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), offset, location + offset);
+ printLocationAndOp(out, exec, location, it, "overrides_has_instance");
+ out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
break;
}
case op_instanceof: {
@@ -1030,6 +1029,15 @@
out.printf("%s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data());
break;
}
+ case op_instanceof_custom: {
+ int r0 = (++it)->u.operand;
+ int r1 = (++it)->u.operand;
+ int r2 = (++it)->u.operand;
+ int r3 = (++it)->u.operand;
+ printLocationAndOp(out, exec, location, it, "instanceof_custom");
+ out.printf("%s, %s, %s, %s", registerName(r0).data(), registerName(r1).data(), registerName(r2).data(), registerName(r3).data());
+ break;
+ }
case op_unsigned: {
printUnaryOp(out, exec, location, it, "unsigned");
break;
diff --git a/Source/JavaScriptCore/bytecode/ExitKind.cpp b/Source/JavaScriptCore/bytecode/ExitKind.cpp
index 2524300..30c5e64 100644
--- a/Source/JavaScriptCore/bytecode/ExitKind.cpp
+++ b/Source/JavaScriptCore/bytecode/ExitKind.cpp
@@ -50,6 +50,8 @@
return "BadConstantCache";
case BadIndexingType:
return "BadIndexingType";
+ case BadTypeInfoFlags:
+ return "BadTypeInfoFlags";
case Overflow:
return "Overflow";
case NegativeZero:
diff --git a/Source/JavaScriptCore/bytecode/ExitKind.h b/Source/JavaScriptCore/bytecode/ExitKind.h
index 6f8c512..700d388 100644
--- a/Source/JavaScriptCore/bytecode/ExitKind.h
+++ b/Source/JavaScriptCore/bytecode/ExitKind.h
@@ -37,6 +37,7 @@
BadCache, // We exited because an inline cache was wrong.
BadConstantCache, // We exited because a cache on a weak constant (usually a prototype) was wrong.
BadIndexingType, // We exited because an indexing type was wrong.
+ BadTypeInfoFlags, // We exited because we made an incorrect assumption about what TypeInfo flags we would see.
Overflow, // We exited because of overflow.
NegativeZero, // We exited because we encountered negative zero.
Int52Overflow, // We exited because of an Int52 overflow.
diff --git a/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp b/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp
index 2bbf313..e2cb000 100644
--- a/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp
+++ b/Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp
@@ -74,9 +74,6 @@
out.append(bytecodeOffset + current[2].u.operand);
break;
}
- case op_check_has_instance:
- out.append(bytecodeOffset + current[4].u.operand);
- break;
case op_loop_hint:
out.append(bytecodeOffset);
break;
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index b443e75..7856e30 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -2099,14 +2099,13 @@
}
}
-void BytecodeGenerator::emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target)
+RegisterID* BytecodeGenerator::emitOverridesHasInstance(RegisterID* dst, RegisterID* constructor, RegisterID* hasInstanceValue)
{
- size_t begin = instructions().size();
- emitOpcode(op_check_has_instance);
+ emitOpcode(op_overrides_has_instance);
instructions().append(dst->index());
- instructions().append(value->index());
- instructions().append(base->index());
- instructions().append(target->bind(begin, instructions().size()));
+ instructions().append(constructor->index());
+ instructions().append(hasInstanceValue->index());
+ return dst;
}
// Indicates the least upper bound of resolve type based on local scope. The bytecode linker
@@ -2273,6 +2272,16 @@
return dst;
}
+RegisterID* BytecodeGenerator::emitInstanceOfCustom(RegisterID* dst, RegisterID* value, RegisterID* constructor, RegisterID* hasInstanceValue)
+{
+ emitOpcode(op_instanceof_custom);
+ instructions().append(dst->index());
+ instructions().append(value->index());
+ instructions().append(constructor->index());
+ instructions().append(hasInstanceValue->index());
+ return dst;
+}
+
RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property)
{
ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties should be handled with get_by_val.");
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index acc72cf..00b4fde 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -534,8 +534,9 @@
RegisterID* emitInc(RegisterID* srcDst);
RegisterID* emitDec(RegisterID* srcDst);
- void emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target);
+ RegisterID* emitOverridesHasInstance(RegisterID* dst, RegisterID* constructor, RegisterID* hasInstanceValue);
RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* basePrototype);
+ RegisterID* emitInstanceOfCustom(RegisterID* dst, RegisterID* value, RegisterID* constructor, RegisterID* hasInstanceValue);
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()); }
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index 40608fa..8790509 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -1664,22 +1664,49 @@
RegisterID* InstanceOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
- RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator));
- RefPtr<RegisterID> src2 = generator.emitNode(m_expr2);
+ RefPtr<RegisterID> hasInstanceValue = generator.newTemporary();
+ RefPtr<RegisterID> isObject = generator.newTemporary();
+ RefPtr<RegisterID> isCustom = generator.newTemporary();
RefPtr<RegisterID> prototype = generator.newTemporary();
- RefPtr<RegisterID> dstReg = generator.finalDestination(dst, src1.get());
- RefPtr<Label> target = generator.newLabel();
+ RefPtr<RegisterID> value = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator));
+ RefPtr<RegisterID> constructor = generator.emitNode(m_expr2);
+ RefPtr<RegisterID> dstReg = generator.finalDestination(dst, value.get());
+ RefPtr<Label> custom = generator.newLabel();
+ RefPtr<Label> done = generator.newLabel();
+ RefPtr<Label> typeError = generator.newLabel();
generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
- generator.emitCheckHasInstance(dstReg.get(), src1.get(), src2.get(), target.get());
+ generator.emitIsObject(isObject.get(), constructor.get());
+ generator.emitJumpIfFalse(isObject.get(), typeError.get());
generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
- generator.emitGetById(prototype.get(), src2.get(), generator.vm()->propertyNames->prototype);
+ generator.emitGetById(hasInstanceValue.get(), constructor.get(), generator.vm()->propertyNames->hasInstanceSymbol);
generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
- RegisterID* result = generator.emitInstanceOf(dstReg.get(), src1.get(), prototype.get());
- generator.emitLabel(target.get());
- return result;
+ generator.emitOverridesHasInstance(isCustom.get(), constructor.get(), hasInstanceValue.get());
+
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitJumpIfTrue(isCustom.get(), custom.get());
+
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitGetById(prototype.get(), constructor.get(), generator.vm()->propertyNames->prototype);
+
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitInstanceOf(dstReg.get(), value.get(), prototype.get());
+
+ generator.emitJump(done.get());
+
+ generator.emitLabel(typeError.get());
+ generator.emitThrowTypeError("Right hand side of instanceof is not an object");
+
+ generator.emitLabel(custom.get());
+
+ generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+ generator.emitInstanceOfCustom(dstReg.get(), value.get(), constructor.get(), hasInstanceValue.get());
+
+ generator.emitLabel(done.get());
+
+ return dstReg.get();
}
// ------------------------------ LogicalOpNode ----------------------------
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 1a2d72a..34ef630 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -2465,12 +2465,17 @@
case NotifyWrite:
break;
- case CheckHasInstance:
- // Sadly, we don't propagate the fact that we've done CheckHasInstance
+ case OverridesHasInstance:
+ forNode(node).setType(SpecBoolean);
break;
case InstanceOf:
- // Again, sadly, we don't propagate the fact that we've done InstanceOf
+ // Sadly, we don't propagate the fact that we've done InstanceOf
+ forNode(node).setType(SpecBoolean);
+ break;
+
+ case InstanceOfCustom:
+ clobberWorld(node->origin.semantic, clobberLimit);
forNode(node).setType(SpecBoolean);
break;
@@ -2527,6 +2532,7 @@
case CountExecution:
case CheckTierUpInLoop:
case CheckTierUpAtReturn:
+ case CheckTypeInfoFlags:
break;
case CopyRest:
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 6ad2143..4c4dd19 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -3505,9 +3505,15 @@
NEXT_OPCODE(op_check_tdz);
}
- case op_check_has_instance:
- addToGraph(CheckHasInstance, get(VirtualRegister(currentInstruction[3].u.operand)));
- NEXT_OPCODE(op_check_has_instance);
+ case op_overrides_has_instance: {
+ JSFunction* defaultHasInstanceSymbolFunction = m_inlineStackTop->m_codeBlock->globalObjectFor(currentCodeOrigin())->functionProtoHasInstanceSymbolFunction();
+
+ Node* constructor = get(VirtualRegister(currentInstruction[2].u.operand));
+ Node* hasInstanceValue = get(VirtualRegister(currentInstruction[3].u.operand));
+
+ set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(OverridesHasInstance, OpInfo(m_graph.freeze(defaultHasInstanceSymbolFunction)), constructor, hasInstanceValue));
+ NEXT_OPCODE(op_overrides_has_instance);
+ }
case op_instanceof: {
Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
@@ -3515,7 +3521,15 @@
set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(InstanceOf, value, prototype));
NEXT_OPCODE(op_instanceof);
}
-
+
+ case op_instanceof_custom: {
+ Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
+ Node* constructor = get(VirtualRegister(currentInstruction[3].u.operand));
+ Node* hasInstanceValue = get(VirtualRegister(currentInstruction[4].u.operand));
+ set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(InstanceOfCustom, value, constructor, hasInstanceValue));
+ NEXT_OPCODE(op_instanceof_custom);
+ }
+
case op_is_undefined: {
Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsUndefined, value));
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
index d1c9bce..d100d97 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
@@ -128,8 +128,9 @@
case op_profile_type:
case op_profile_control_flow:
case op_mov:
- case op_check_has_instance:
+ case op_overrides_has_instance:
case op_instanceof:
+ case op_instanceof_custom:
case op_is_undefined:
case op_is_boolean:
case op_is_number:
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index 7a06808..72b24c7 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -740,9 +740,14 @@
read(JSCell_structureID);
return;
- case CheckHasInstance:
+ case CheckTypeInfoFlags:
read(JSCell_typeInfoFlags);
- def(HeapLocation(CheckHasInstanceLoc, JSCell_typeInfoFlags, node->child1()), LazyNode(node));
+ def(HeapLocation(CheckTypeInfoFlagsLoc, JSCell_typeInfoFlags, node->child1()), LazyNode(node));
+ return;
+
+ case OverridesHasInstance:
+ read(JSCell_typeInfoFlags);
+ def(HeapLocation(OverridesHasInstanceLoc, JSCell_typeInfoFlags, node->child1()), LazyNode(node));
return;
case InstanceOf:
@@ -750,6 +755,11 @@
def(HeapLocation(InstanceOfLoc, JSCell_structureID, node->child1(), node->child2()), LazyNode(node));
return;
+ case InstanceOfCustom:
+ read(World);
+ write(Heap);
+ return;
+
case PutStructure:
write(JSCell_structureID);
write(JSCell_typeInfoType);
diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
index f0f16ad..702da5b 100644
--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
@@ -142,8 +142,9 @@
case ProfileDidCall:
case ProfileType:
case ProfileControlFlow:
- case CheckHasInstance:
+ case OverridesHasInstance:
case InstanceOf:
+ case InstanceOfCustom:
case IsUndefined:
case IsBoolean:
case IsNumber:
@@ -181,6 +182,7 @@
case CheckInBounds:
case ConstantStoragePointer:
case Check:
+ case CheckTypeInfoFlags:
case MultiGetByOffset:
case ValueRep:
case DoubleRep:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 13c372d..a9e0b21 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1111,10 +1111,29 @@
fixEdge<FunctionUse>(node->child1());
break;
}
+
+ case OverridesHasInstance: {
+ if (node->child2().node()->isCellConstant()) {
+ if (node->child2().node()->asCell() != m_graph.globalObjectFor(node->origin.semantic)->functionProtoHasInstanceSymbolFunction()) {
+
+ m_graph.convertToConstant(node, jsBoolean(true));
+ break;
+ }
+
+ if (!m_graph.hasExitSite(node->origin.semantic, BadTypeInfoFlags)) {
+ // Here we optimistically assume that we will not see an bound/C-API function here.
+ m_insertionSet.insertNode(m_indexInBlock, SpecNone, CheckTypeInfoFlags, node->origin, OpInfo(ImplementsDefaultHasInstance), Edge(node->child1().node(), CellUse));
+ m_graph.convertToConstant(node, jsBoolean(false));
+ break;
+ }
+ }
+
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
case CheckStructure:
case CheckCell:
- case CheckHasInstance:
case CreateThis:
case GetButterfly:
case GetButterflyReadOnly: {
@@ -1175,7 +1194,11 @@
fixEdge<CellUse>(node->child2());
break;
}
-
+
+ case InstanceOfCustom:
+ fixEdge<CellUse>(node->child2());
+ break;
+
case In: {
// FIXME: We should at some point have array profiling on op_in, in which
// case we would be able to turn this into a kind of GetByVal.
@@ -1423,6 +1446,7 @@
case NotifyWrite:
case VarInjectionWatchpoint:
case Call:
+ case CheckTypeInfoFlags:
case TailCallInlinedCaller:
case Construct:
case CallVarargs:
diff --git a/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp b/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
index 6923656..55d84cf 100644
--- a/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
+++ b/Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
@@ -95,9 +95,13 @@
case ButterflyReadOnlyLoc:
out.print("ButterflyReadOnlyLoc");
return;
-
- case CheckHasInstanceLoc:
- out.print("CheckHasInstanceLoc");
+
+ case CheckTypeInfoFlagsLoc:
+ out.print("CheckTypeInfoFlagsLoc");
+ return;
+
+ case OverridesHasInstanceLoc:
+ out.print("OverridesHasInstanceLoc");
return;
case ClosureVariableLoc:
diff --git a/Source/JavaScriptCore/dfg/DFGHeapLocation.h b/Source/JavaScriptCore/dfg/DFGHeapLocation.h
index 495d300..b85fa26 100644
--- a/Source/JavaScriptCore/dfg/DFGHeapLocation.h
+++ b/Source/JavaScriptCore/dfg/DFGHeapLocation.h
@@ -40,7 +40,8 @@
ArrayLengthLoc,
ButterflyLoc,
ButterflyReadOnlyLoc,
- CheckHasInstanceLoc,
+ CheckTypeInfoFlagsLoc,
+ OverridesHasInstanceLoc,
ClosureVariableLoc,
DirectArgumentsLoc,
GetterLoc,
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index c6c2a3a..323a420 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -1364,6 +1364,7 @@
{
switch (op()) {
case CheckCell:
+ case OverridesHasInstance:
case NewFunction:
case NewArrowFunction:
case NewGeneratorFunction:
@@ -1426,6 +1427,17 @@
return reinterpret_cast<UniquedStringImpl*>(m_opInfo);
}
+ bool hasTypeInfoOperand()
+ {
+ return op() == CheckTypeInfoFlags;
+ }
+
+ unsigned typeInfoOperand()
+ {
+ ASSERT(hasTypeInfoOperand() && m_opInfo <= UCHAR_MAX);
+ return static_cast<unsigned>(m_opInfo);
+ }
+
bool hasTransition()
{
switch (op()) {
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 281cf9a..abcef65 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -222,6 +222,7 @@
macro(CheckBadCell, NodeMustGenerate) \
macro(CheckInBounds, NodeMustGenerate) \
macro(CheckIdent, NodeMustGenerate) \
+ macro(CheckTypeInfoFlags, NodeMustGenerate) /* Takes an OpInfo with the flags you want to test are set */\
\
/* Optimizations for array mutation. */\
macro(ArrayPush, NodeResultJS | NodeMustGenerate) \
@@ -280,8 +281,9 @@
macro(Breakpoint, NodeMustGenerate) \
macro(ProfileWillCall, NodeMustGenerate) \
macro(ProfileDidCall, NodeMustGenerate) \
- macro(CheckHasInstance, NodeMustGenerate) \
+ macro(OverridesHasInstance, NodeMustGenerate | NodeResultBoolean) \
macro(InstanceOf, NodeResultBoolean) \
+ macro(InstanceOfCustom, NodeMustGenerate | NodeResultBoolean) \
macro(IsUndefined, NodeResultBoolean) \
macro(IsBoolean, NodeResultBoolean) \
macro(IsNumber, NodeResultBoolean) \
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 72218d3..04fd274 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -408,7 +408,9 @@
case CompareGreaterEq:
case CompareEq:
case CompareStrictEq:
+ case OverridesHasInstance:
case InstanceOf:
+ case InstanceOfCustom:
case IsUndefined:
case IsBoolean:
case IsNumber:
@@ -575,6 +577,7 @@
case DoubleAsInt32:
case GetLocalUnlinked:
case CheckArray:
+ case CheckTypeInfoFlags:
case Arrayify:
case ArrayifyToStructure:
case CheckTierUpInLoop:
@@ -687,7 +690,6 @@
case ProfileDidCall:
case ProfileType:
case ProfileControlFlow:
- case CheckHasInstance:
case ThrowReferenceError:
case ForceOSRExit:
case SetArgument:
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index a1bb6e6..230efed 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -242,8 +242,10 @@
case ProfileDidCall:
case ProfileType:
case ProfileControlFlow:
- case CheckHasInstance:
+ case CheckTypeInfoFlags:
+ case OverridesHasInstance:
case InstanceOf:
+ case InstanceOfCustom:
case IsUndefined:
case IsBoolean:
case IsNumber:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 02ff82d..319521c 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -2744,6 +2744,17 @@
putResult.link(&m_jit);
}
+void SpeculativeJIT::compileCheckTypeInfoFlags(Node* node)
+{
+ SpeculateCellOperand base(this, node->child1());
+
+ GPRReg baseGPR = base.gpr();
+
+ speculationCheck(BadTypeInfoFlags, JSValueRegs(), 0, m_jit.branchTest8(MacroAssembler::Zero, MacroAssembler::Address(baseGPR, JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(node->typeInfoOperand())));
+
+ noResult(node);
+}
+
void SpeculativeJIT::compileInstanceOf(Node* node)
{
if (node->child1().useKind() == UntypedUse) {
@@ -3182,6 +3193,28 @@
return;
}
+void SpeculativeJIT::compileInstanceOfCustom(Node* node)
+{
+ // We could do something smarter here but this case is currently super rare and unless
+ // Symbol.hasInstance becomes popular will likely remain that way.
+
+ JSValueOperand value(this, node->child1());
+ SpeculateCellOperand constructor(this, node->child2());
+ JSValueOperand hasInstanceValue(this, node->child3());
+ GPRTemporary result(this);
+
+ JSValueRegs valueRegs = value.jsValueRegs();
+ GPRReg constructorGPR = constructor.gpr();
+ JSValueRegs hasInstanceRegs = hasInstanceValue.jsValueRegs();
+ GPRReg resultGPR = result.gpr();
+
+ MacroAssembler::Jump slowCase = m_jit.jump();
+
+ addSlowPathGenerator(slowPathCall(slowCase, this, operationInstanceOfCustom, resultGPR, valueRegs, constructorGPR, hasInstanceRegs));
+
+ unblessedBooleanResult(resultGPR, node);
+}
+
void SpeculativeJIT::compileArithAdd(Node* node)
{
switch (node->binaryUseKind()) {
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 3fcfd90..0322bb7 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -727,6 +727,7 @@
void compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchAndResultReg, GPRReg scratch2Reg);
void compileInstanceOf(Node*);
+ void compileInstanceOfCustom(Node*);
void emitCall(Node*);
@@ -1511,6 +1512,17 @@
m_jit.setupArgumentsWithExecState(arg1, arg2, TrustedImm32(arg3), arg4);
return appendCallSetResult(operation, result);
}
+
+ JITCompiler::Call callOperation(Z_JITOperation_EJOJ operation, GPRReg result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+ return appendCallSetResult(operation, result);
+ }
+ JITCompiler::Call callOperation(Z_JITOperation_EJOJ operation, GPRReg result, JSValueRegs arg1, GPRReg arg2, JSValueRegs arg3)
+ {
+ return callOperation(operation, result, arg1.payloadGPR(), arg2, arg3.payloadGPR());
+ }
+
JITCompiler::Call callOperation(Z_JITOperation_EJZ operation, GPRReg result, GPRReg arg1, unsigned arg2)
{
m_jit.setupArgumentsWithExecState(arg1, TrustedImm32(arg2));
@@ -1837,6 +1849,16 @@
return appendCall(operation);
}
+ JITCompiler::Call callOperation(Z_JITOperation_EJOJ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2, GPRReg arg3Tag, GPRReg arg3Payload)
+ {
+ m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag);
+ return appendCallSetResult(operation, result);
+ }
+ JITCompiler::Call callOperation(Z_JITOperation_EJOJ operation, GPRReg result, JSValueRegs arg1, GPRReg arg2, JSValueRegs arg3)
+ {
+ return callOperation(operation, result, arg1.tagGPR(), arg1.payloadGPR(), arg2, arg3.tagGPR(), arg3.payloadGPR());
+ }
+
JITCompiler::Call callOperation(Z_JITOperation_EJZZ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload, unsigned arg2, unsigned arg3)
{
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, TrustedImm32(arg2), TrustedImm32(arg3));
@@ -2209,6 +2231,7 @@
void compileGetArrayLength(Node*);
+ void compileCheckTypeInfoFlags(Node*);
void compileCheckIdent(Node*);
void compileValueRep(Node*);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 626975f..89a0dc3 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4163,17 +4163,48 @@
break;
}
- case CheckHasInstance: {
+ case CheckTypeInfoFlags: {
+ compileCheckTypeInfoFlags(node);
+ break;
+ }
+
+ case OverridesHasInstance: {
+
+ Node* hasInstanceValueNode = node->child2().node();
+ JSFunction* defaultHasInstanceFunction = jsCast<JSFunction*>(node->cellOperand()->value());
+
+ MacroAssembler::Jump notDefaulthasInstanceValue;
+ MacroAssembler::Jump hasInstanceValueNotCell;
SpeculateCellOperand base(this, node->child1());
- GPRTemporary structure(this);
+ JSValueOperand hasInstanceValue(this, node->child2());
+ GPRTemporary result(this);
- // Speculate that base 'ImplementsDefaultHasInstance'.
- speculationCheck(Uncountable, JSValueRegs(), 0, m_jit.branchTest8(
- MacroAssembler::Zero,
- MacroAssembler::Address(base.gpr(), JSCell::typeInfoFlagsOffset()),
- MacroAssembler::TrustedImm32(ImplementsDefaultHasInstance)));
+ GPRReg resultGPR = result.gpr();
- noResult(node);
+ // If we have proven that the constructor's Symbol.hasInstance will always be the one on
+ // Function.prototype[Symbol.hasInstance] then we don't need a runtime check here. We don't worry
+ // about the case where the constructor's Symbol.hasInstance is a constant but is not the default
+ // one as fixup should have converted this check to true.
+ ASSERT(!hasInstanceValueNode->isCellConstant() || defaultHasInstanceFunction == hasInstanceValueNode->asCell());
+ if (!hasInstanceValueNode->isCellConstant()) {
+
+ JSValueRegs hasInstanceValueRegs = hasInstanceValue.jsValueRegs();
+ hasInstanceValueNotCell = m_jit.branchIfNotCell(hasInstanceValueRegs);
+ notDefaulthasInstanceValue = m_jit.branchPtr(MacroAssembler::NotEqual, hasInstanceValueRegs.payloadGPR(), TrustedImmPtr(defaultHasInstanceFunction));
+ }
+
+ // Check that constructor 'ImplementsDefaultHasInstance'.
+ m_jit.test8(MacroAssembler::Zero, MacroAssembler::Address(base.gpr(), JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(ImplementsDefaultHasInstance), resultGPR);
+ MacroAssembler::Jump done = m_jit.jump();
+
+ if (!hasInstanceValueNode->isCellConstant()) {
+ hasInstanceValueNotCell.link(&m_jit);
+ notDefaulthasInstanceValue.link(&m_jit);
+ moveTrueTo(resultGPR);
+ }
+
+ done.link(&m_jit);
+ booleanResult(resultGPR, node);
break;
}
@@ -4182,6 +4213,11 @@
break;
}
+ case InstanceOfCustom: {
+ compileInstanceOfCustom(node);
+ break;
+ }
+
case IsUndefined: {
JSValueOperand value(this, node->child1());
GPRTemporary result(this);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 2facfaa..70e8ea8 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -4153,17 +4153,42 @@
break;
}
- case CheckHasInstance: {
+ case CheckTypeInfoFlags: {
+ compileCheckTypeInfoFlags(node);
+ break;
+ }
+
+ case OverridesHasInstance: {
+
+ Node* hasInstanceValueNode = node->child2().node();
+ JSFunction* defaultHasInstanceFunction = jsCast<JSFunction*>(node->cellOperand()->value());
+
+ MacroAssembler::Jump notDefault;
SpeculateCellOperand base(this, node->child1());
- GPRTemporary structure(this);
+ JSValueOperand hasInstanceValue(this, node->child2());
+ GPRTemporary result(this);
- // Speculate that base 'ImplementsDefaultHasInstance'.
- speculationCheck(Uncountable, JSValueRegs(), 0, m_jit.branchTest8(
- MacroAssembler::Zero,
- MacroAssembler::Address(base.gpr(), JSCell::typeInfoFlagsOffset()),
- MacroAssembler::TrustedImm32(ImplementsDefaultHasInstance)));
+ GPRReg resultGPR = result.gpr();
- noResult(node);
+ // If we have proven that the constructor's Symbol.hasInstance will always be the one on Function.prototype[Symbol.hasInstance]
+ // then we don't need a runtime check here. We don't worry about the case where the constructor's Symbol.hasInstance is a constant
+ // but is not the default one as fixup should have converted this check to true.
+ ASSERT(!hasInstanceValueNode->isCellConstant() || defaultHasInstanceFunction == hasInstanceValueNode->asCell());
+ if (!hasInstanceValueNode->isCellConstant())
+ notDefault = m_jit.branchPtr(MacroAssembler::NotEqual, hasInstanceValue.gpr(), TrustedImmPtr(defaultHasInstanceFunction));
+
+ // Check that base 'ImplementsDefaultHasInstance'.
+ m_jit.test8(MacroAssembler::Zero, MacroAssembler::Address(base.gpr(), JSCell::typeInfoFlagsOffset()), MacroAssembler::TrustedImm32(ImplementsDefaultHasInstance), resultGPR);
+ m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
+ MacroAssembler::Jump done = m_jit.jump();
+
+ if (notDefault.isSet()) {
+ notDefault.link(&m_jit);
+ moveTrueTo(resultGPR);
+ }
+
+ done.link(&m_jit);
+ jsValueResult(resultGPR, node, DataFormatJSBoolean);
break;
}
@@ -4171,6 +4196,11 @@
compileInstanceOf(node);
break;
}
+
+ case InstanceOfCustom: {
+ compileInstanceOfCustom(node);
+ break;
+ }
case IsUndefined: {
JSValueOperand value(this, node->child1());
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index 32870aa..d2a490c 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -176,8 +176,10 @@
case IsObject:
case IsObjectOrNull:
case IsFunction:
- case CheckHasInstance:
+ case CheckTypeInfoFlags:
+ case OverridesHasInstance:
case InstanceOf:
+ case InstanceOfCustom:
case DoubleRep:
case ValueRep:
case Int52Rep:
diff --git a/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h b/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
index f5f4ea7..c8f081c 100644
--- a/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
+++ b/Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
@@ -128,6 +128,7 @@
macro(Z_JITOperation_D, functionType(int32, doubleType)) \
macro(Z_JITOperation_EC, functionType(int32, intPtr, intPtr)) \
macro(Z_JITOperation_EGC, functionType(int32, intPtr, intPtr, intPtr)) \
+ macro(Z_JITOperation_EJOJ, functionType(int32, intPtr, int64, intPtr, int64)) \
macro(Z_JITOperation_EJZ, functionType(int32, intPtr, int64, int32)) \
macro(Z_JITOperation_ESJss, functionType(int32, intPtr, intPtr, int64)) \
macro(V_JITOperation_ECRUiUi, functionType(voidType, intPtr, intPtr, intPtr, int32, int32))
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
index 89019cb7..bab39b4 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
@@ -948,12 +948,18 @@
case TypeOf:
compileTypeOf();
break;
- case CheckHasInstance:
- compileCheckHasInstance();
+ case CheckTypeInfoFlags:
+ compileCheckTypeInfoFlags();
+ break;
+ case OverridesHasInstance:
+ compileOverridesHasInstance();
break;
case InstanceOf:
compileInstanceOf();
break;
+ case InstanceOfCustom:
+ compileInstanceOfCustom();
+ break;
case CountExecution:
compileCountExecution();
break;
@@ -5745,13 +5751,40 @@
#endif
}
- void compileCheckHasInstance()
+ void compileOverridesHasInstance()
+ {
+ JSFunction* defaultHasInstanceFunction = jsCast<JSFunction*>(m_node->cellOperand()->value());
+
+ LValue constructor = lowCell(m_node->child1());
+ LValue hasInstance = lowJSValue(m_node->child2());
+
+ LBasicBlock defaultHasInstance = FTL_NEW_BLOCK(m_out, ("OverridesHasInstance Symbol.hasInstance is default"));
+ LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("OverridesHasInstance continuation"));
+
+ // Unlike in the DFG, we don't worry about cleaning this code up for the case where we have proven the hasInstanceValue is a constant as LLVM should fix it for us.
+
+ ASSERT(!m_node->child2().node()->isCellConstant() || defaultHasInstanceFunction == m_node->child2().node()->asCell());
+
+ ValueFromBlock notDefaultHasInstanceResult = m_out.anchor(m_out.booleanTrue);
+ m_out.branch(m_out.notEqual(hasInstance, m_out.constIntPtr(defaultHasInstanceFunction)), unsure(continuation), unsure(defaultHasInstance));
+
+ LBasicBlock lastNext = m_out.appendTo(defaultHasInstance, continuation);
+ ValueFromBlock implementsDefaultHasInstanceResult = m_out.anchor(m_out.testIsZero32(
+ m_out.load8ZeroExt32(constructor, m_heaps.JSCell_typeInfoFlags),
+ m_out.constInt32(ImplementsDefaultHasInstance)));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setBoolean(m_out.phi(m_out.boolean, implementsDefaultHasInstanceResult, notDefaultHasInstanceResult));
+ }
+
+ void compileCheckTypeInfoFlags()
{
speculate(
- Uncountable, noValue(), 0,
+ BadTypeInfoFlags, noValue(), 0,
m_out.testIsZero32(
m_out.load8ZeroExt32(lowCell(m_node->child1()), m_heaps.JSCell_typeInfoFlags),
- m_out.constInt32(ImplementsDefaultHasInstance)));
+ m_out.constInt32(m_node->typeInfoOperand())));
}
void compileInstanceOf()
@@ -5804,6 +5837,15 @@
setBoolean(
m_out.phi(m_out.boolean, notCellResult, isInstanceResult, notInstanceResult));
}
+
+ void compileInstanceOfCustom()
+ {
+ LValue value = lowJSValue(m_node->child1());
+ LValue constructor = lowCell(m_node->child2());
+ LValue hasInstance = lowJSValue(m_node->child3());
+
+ setBoolean(m_out.bitNot(m_out.equal(m_out.constInt32(0), vmCall(m_out.int32, m_out.operation(operationInstanceOfCustom), m_callFrame, value, constructor, hasInstance))));
+ }
void compileCountExecution()
{
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index f3431c9..a3bdfa7 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -228,8 +228,9 @@
case op_get_array_length:
DEFINE_OP(op_get_by_id)
DEFINE_OP(op_get_by_val)
- DEFINE_OP(op_check_has_instance)
+ DEFINE_OP(op_overrides_has_instance)
DEFINE_OP(op_instanceof)
+ DEFINE_OP(op_instanceof_custom)
DEFINE_OP(op_is_undefined)
DEFINE_OP(op_is_boolean)
DEFINE_OP(op_is_number)
@@ -399,8 +400,8 @@
case op_get_array_length:
DEFINE_SLOWCASE_OP(op_get_by_id)
DEFINE_SLOWCASE_OP(op_get_by_val)
- DEFINE_SLOWCASE_OP(op_check_has_instance)
DEFINE_SLOWCASE_OP(op_instanceof)
+ DEFINE_SLOWCASE_OP(op_instanceof_custom)
DEFINE_SLOWCASE_OP(op_jfalse)
DEFINE_SLOWCASE_OP(op_jless)
DEFINE_SLOWCASE_OP(op_jlesseq)
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index ce22074..df7b3dc 100755
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -509,8 +509,9 @@
void emit_op_get_by_val(Instruction*);
void emit_op_get_argument_by_val(Instruction*);
void emit_op_init_lazy_reg(Instruction*);
- void emit_op_check_has_instance(Instruction*);
+ void emit_op_overrides_has_instance(Instruction*);
void emit_op_instanceof(Instruction*);
+ void emit_op_instanceof_custom(Instruction*);
void emit_op_is_undefined(Instruction*);
void emit_op_is_boolean(Instruction*);
void emit_op_is_number(Instruction*);
@@ -616,8 +617,8 @@
void emitSlow_op_get_arguments_length(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_get_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_get_argument_by_val(Instruction*, Vector<SlowCaseEntry>::iterator&);
- void emitSlow_op_check_has_instance(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_instanceof(Instruction*, Vector<SlowCaseEntry>::iterator&);
+ void emitSlow_op_instanceof_custom(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_jfalse(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_jless(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_jlesseq(Instruction*, Vector<SlowCaseEntry>::iterator&);
@@ -744,6 +745,7 @@
MacroAssembler::Call callOperation(J_JITOperation_EJJ, int, GPRReg, GPRReg);
MacroAssembler::Call callOperation(J_JITOperation_EJJAp, int, GPRReg, GPRReg, ArrayProfile*);
MacroAssembler::Call callOperation(J_JITOperation_EJJBy, int, GPRReg, GPRReg, ByValInfo*);
+ MacroAssembler::Call callOperation(Z_JITOperation_EJOJ, GPRReg, GPRReg, GPRReg);
MacroAssembler::Call callOperation(C_JITOperation_EJsc, GPRReg);
MacroAssembler::Call callOperation(J_JITOperation_EJscC, int, GPRReg, JSCell*);
MacroAssembler::Call callOperation(J_JITOperation_EJscCJ, int, GPRReg, JSCell*, GPRReg);
@@ -811,6 +813,7 @@
MacroAssembler::Call callOperation(J_JITOperation_EJ, int, GPRReg, GPRReg);
MacroAssembler::Call callOperation(J_JITOperation_EJIdc, int, GPRReg, GPRReg, const Identifier*);
MacroAssembler::Call callOperation(J_JITOperation_EJJ, int, GPRReg, GPRReg, GPRReg, GPRReg);
+ MacroAssembler::Call callOperation(Z_JITOperation_EJOJ, GPRReg, GPRReg, GPRReg, GPRReg, GPRReg);
MacroAssembler::Call callOperation(J_JITOperation_EJJAp, int, GPRReg, GPRReg, GPRReg, GPRReg, ArrayProfile*);
MacroAssembler::Call callOperation(J_JITOperation_EJJBy, int, GPRReg, GPRReg, GPRReg, GPRReg, ByValInfo*);
MacroAssembler::Call callOperation(P_JITOperation_EJS, GPRReg, GPRReg, size_t);
diff --git a/Source/JavaScriptCore/jit/JITInlines.h b/Source/JavaScriptCore/jit/JITInlines.h
index cadb083..77adc02 100755
--- a/Source/JavaScriptCore/jit/JITInlines.h
+++ b/Source/JavaScriptCore/jit/JITInlines.h
@@ -503,6 +503,12 @@
return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
}
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(Z_JITOperation_EJOJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3)
+{
+ setupArgumentsWithExecState(arg1, arg2, arg3);
+ return appendCallWithExceptionCheck(operation);
+}
+
ALWAYS_INLINE MacroAssembler::Call JIT::callOperationNoExceptionCheck(V_JITOperation_EJ operation, GPRReg arg1)
{
setupArgumentsWithExecState(arg1);
@@ -596,6 +602,12 @@
return appendCall(operation);
}
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(Z_JITOperation_EJOJ operation, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2, GPRReg arg3Tag, GPRReg arg3Payload)
+{
+ setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag);
+ return appendCallWithExceptionCheck(operation);
+}
+
ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(Z_JITOperation_EJZZ operation, GPRReg arg1Tag, GPRReg arg1Payload, int32_t arg2, int32_t arg3)
{
setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, TrustedImm32(arg2), TrustedImm32(arg3));
diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp
index 3a97dd8..b2235bc 100755
--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp
@@ -105,17 +105,29 @@
emitStoreCell(dst, returnValueGPR);
}
-void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
+void JIT::emit_op_overrides_has_instance(Instruction* currentInstruction)
{
- int baseVal = currentInstruction[3].u.operand;
+ int dst = currentInstruction[1].u.operand;
+ int constructor = currentInstruction[2].u.operand;
+ int hasInstanceValue = currentInstruction[3].u.operand;
- emitGetVirtualRegister(baseVal, regT0);
+ emitGetVirtualRegister(hasInstanceValue, regT0);
- // Check that baseVal is a cell.
- emitJumpSlowCaseIfNotJSCell(regT0, baseVal);
+ // We don't jump if we know what Symbol.hasInstance would do.
+ Jump customhasInstanceValue = branchPtr(NotEqual, regT0, TrustedImmPtr(m_codeBlock->globalObject()->functionProtoHasInstanceSymbolFunction()));
- // Check that baseVal 'ImplementsHasInstance'.
- addSlowCase(branchTest8(Zero, Address(regT0, JSCell::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
+ emitGetVirtualRegister(constructor, regT0);
+
+ // Check that constructor 'ImplementsHasInstance' i.e. the object is a C-API user or a bound function.
+ test8(Zero, Address(regT0, JSCell::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance), regT0);
+ emitTagBool(regT0);
+ Jump done = jump();
+
+ customhasInstanceValue.link(this);
+ move(TrustedImm32(ValueTrue), regT0);
+
+ done.link(this);
+ emitPutVirtualRegister(dst);
}
void JIT::emit_op_instanceof(Instruction* currentInstruction)
@@ -129,7 +141,7 @@
emitGetVirtualRegister(value, regT2);
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 the get_by_id for Symbol.hasInstance.
emitJumpSlowCaseIfNotJSCell(regT2, value);
emitJumpSlowCaseIfNotJSCell(regT1, proto);
@@ -157,6 +169,12 @@
emitPutVirtualRegister(dst);
}
+void JIT::emit_op_instanceof_custom(Instruction*)
+{
+ // This always goes to slow path since we expect it to be rare.
+ addSlowCase(jump());
+}
+
void JIT::emit_op_is_undefined(Instruction* currentInstruction)
{
int dst = currentInstruction[1].u.operand;
@@ -829,21 +847,6 @@
slowPathCall.call();
}
-void JIT::emitSlow_op_check_has_instance(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
-{
- int dst = currentInstruction[1].u.operand;
- int value = currentInstruction[2].u.operand;
- int baseVal = currentInstruction[3].u.operand;
-
- linkSlowCaseIfNotJSCell(iter, baseVal);
- linkSlowCase(iter);
- emitGetVirtualRegister(value, regT0);
- emitGetVirtualRegister(baseVal, regT1);
- callOperation(operationCheckHasInstance, dst, regT0, regT1);
-
- emitJumpSlowToHot(jump(), currentInstruction[4].u.operand);
-}
-
void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
int dst = currentInstruction[1].u.operand;
@@ -858,6 +861,22 @@
callOperation(operationInstanceOf, dst, regT0, regT1);
}
+void JIT::emitSlow_op_instanceof_custom(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ int dst = currentInstruction[1].u.operand;
+ int value = currentInstruction[2].u.operand;
+ int constructor = currentInstruction[3].u.operand;
+ int hasInstanceValue = currentInstruction[4].u.operand;
+
+ linkSlowCase(iter);
+ emitGetVirtualRegister(value, regT0);
+ emitGetVirtualRegister(constructor, regT1);
+ emitGetVirtualRegister(hasInstanceValue, regT2);
+ callOperation(operationInstanceOfCustom, regT0, regT1, regT2);
+ emitTagBool(returnValueGPR);
+ emitPutVirtualRegister(dst, returnValueGPR);
+}
+
void JIT::emitSlow_op_to_number(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
linkSlowCase(iter);
diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
index 89455ed..8fc3b92 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
@@ -183,17 +183,31 @@
emitStoreCell(dst, returnValueGPR);
}
-void JIT::emit_op_check_has_instance(Instruction* currentInstruction)
+void JIT::emit_op_overrides_has_instance(Instruction* currentInstruction)
{
- int baseVal = currentInstruction[3].u.operand;
+ int dst = currentInstruction[1].u.operand;
+ int constructor = currentInstruction[2].u.operand;
+ int hasInstanceValue = currentInstruction[3].u.operand;
- emitLoadPayload(baseVal, regT0);
+ emitLoadPayload(hasInstanceValue, regT0);
+ // We don't jump if we know what Symbol.hasInstance would do.
+ Jump hasInstanceValueNotCell = emitJumpIfNotJSCell(hasInstanceValue);
+ Jump customhasInstanceValue = branchPtr(NotEqual, regT0, TrustedImmPtr(m_codeBlock->globalObject()->functionProtoHasInstanceSymbolFunction()));
- // Check that baseVal is a cell.
- emitJumpSlowCaseIfNotJSCell(baseVal);
-
- // Check that baseVal 'ImplementsHasInstance'.
- addSlowCase(branchTest8(Zero, Address(regT0, JSCell::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
+ // We know that constructor is an object from the way bytecode is emitted for instanceof expressions.
+ emitLoadPayload(constructor, regT0);
+
+ // Check that constructor 'ImplementsHasInstance' i.e. the object is a C-API user or a bound function.
+ test8(Zero, Address(regT0, JSCell::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance), regT0);
+ Jump done = jump();
+
+ hasInstanceValueNotCell.link(this);
+ customhasInstanceValue.link(this);
+ move(TrustedImm32(1), regT0);
+
+ done.link(this);
+ emitStoreBool(dst, regT0);
+
}
void JIT::emit_op_instanceof(Instruction* currentInstruction)
@@ -207,7 +221,7 @@
emitLoadPayload(value, regT2);
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 the get_by_id for Symbol.hasInstance.
emitJumpSlowCaseIfNotJSCell(value);
emitJumpSlowCaseIfNotJSCell(proto);
@@ -235,20 +249,10 @@
emitStoreBool(dst, regT0);
}
-void JIT::emitSlow_op_check_has_instance(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+void JIT::emit_op_instanceof_custom(Instruction*)
{
- int dst = currentInstruction[1].u.operand;
- int value = currentInstruction[2].u.operand;
- int baseVal = currentInstruction[3].u.operand;
-
- linkSlowCaseIfNotJSCell(iter, baseVal);
- linkSlowCase(iter);
-
- emitLoad(value, regT1, regT0);
- emitLoad(baseVal, regT3, regT2);
- callOperation(operationCheckHasInstance, dst, regT1, regT0, regT3, regT2);
-
- emitJumpSlowToHot(jump(), currentInstruction[4].u.operand);
+ // This always goes to slow path since we expect it to be rare.
+ addSlowCase(jump());
}
void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
@@ -266,6 +270,22 @@
callOperation(operationInstanceOf, dst, regT1, regT0, regT3, regT2);
}
+void JIT::emitSlow_op_instanceof_custom(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ int dst = currentInstruction[1].u.operand;
+ int value = currentInstruction[2].u.operand;
+ int constructor = currentInstruction[3].u.operand;
+ int hasInstanceValue = currentInstruction[4].u.operand;
+
+ linkSlowCase(iter);
+
+ emitLoad(value, regT1, regT0);
+ emitLoadPayload(constructor, regT2);
+ emitLoad(hasInstanceValue, regT4, regT3);
+ callOperation(operationInstanceOfCustom, regT1, regT0, regT2, regT4, regT3);
+ emitStoreBool(dst, returnValueGPR);
+}
+
void JIT::emit_op_is_undefined(Instruction* currentInstruction)
{
int dst = currentInstruction[1].u.operand;
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp
index eb187b2..4aabff1 100644
--- a/Source/JavaScriptCore/jit/JITOperations.cpp
+++ b/Source/JavaScriptCore/jit/JITOperations.cpp
@@ -1487,25 +1487,19 @@
profiler->willExecute(exec, JSValue::decode(encodedValue));
}
-EncodedJSValue JIT_OPERATION operationCheckHasInstance(ExecState* exec, EncodedJSValue encodedValue, EncodedJSValue encodedBaseVal)
+int32_t JIT_OPERATION operationInstanceOfCustom(ExecState* exec, EncodedJSValue encodedValue, JSObject* constructor, EncodedJSValue encodedHasInstance)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
JSValue value = JSValue::decode(encodedValue);
- JSValue baseVal = JSValue::decode(encodedBaseVal);
+ JSValue hasInstanceValue = JSValue::decode(encodedHasInstance);
- if (baseVal.isObject()) {
- JSObject* baseObject = asObject(baseVal);
- ASSERT(!baseObject->structure(vm)->typeInfo().implementsDefaultHasInstance());
- if (baseObject->structure(vm)->typeInfo().implementsHasInstance()) {
- bool result = baseObject->methodTable(vm)->customHasInstance(baseObject, exec, value);
- return JSValue::encode(jsBoolean(result));
- }
- }
+ ASSERT(hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction() || !constructor->structure()->typeInfo().implementsDefaultHasInstance());
- vm.throwException(exec, createInvalidInstanceofParameterError(exec, baseVal));
- return JSValue::encode(JSValue());
+ if (constructor->hasInstance(exec, value, hasInstanceValue))
+ return 1;
+ return 0;
}
}
diff --git a/Source/JavaScriptCore/jit/JITOperations.h b/Source/JavaScriptCore/jit/JITOperations.h
index d44570b..70e3fc8 100644
--- a/Source/JavaScriptCore/jit/JITOperations.h
+++ b/Source/JavaScriptCore/jit/JITOperations.h
@@ -176,6 +176,7 @@
typedef int32_t JIT_OPERATION (*Z_JITOperation_EGC)(ExecState*, JSGlobalObject*, JSCell*);
typedef int32_t JIT_OPERATION (*Z_JITOperation_ESJss)(ExecState*, size_t, JSString*);
typedef int32_t JIT_OPERATION (*Z_JITOperation_EJ)(ExecState*, EncodedJSValue);
+typedef int32_t JIT_OPERATION (*Z_JITOperation_EJOJ)(ExecState*, EncodedJSValue, JSObject*, EncodedJSValue);
typedef int32_t JIT_OPERATION (*Z_JITOperation_EJZ)(ExecState*, EncodedJSValue, int32_t);
typedef int32_t JIT_OPERATION (*Z_JITOperation_EJZZ)(ExecState*, EncodedJSValue, int32_t, int32_t);
typedef size_t JIT_OPERATION (*S_JITOperation_ECC)(ExecState*, JSCell*, JSCell*);
@@ -329,7 +330,6 @@
void JIT_OPERATION operationPopScope(ExecState*, int32_t) WTF_INTERNAL;
void JIT_OPERATION operationProfileDidCall(ExecState*, EncodedJSValue) WTF_INTERNAL;
void JIT_OPERATION operationProfileWillCall(ExecState*, EncodedJSValue) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationCheckHasInstance(ExecState*, EncodedJSValue, EncodedJSValue baseVal) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationGetByValOptimize(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationGetByValString(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo*) WTF_INTERNAL;
@@ -358,6 +358,7 @@
void JIT_OPERATION operationExceptionFuzz(ExecState*);
int32_t JIT_OPERATION operationCheckIfExceptionIsUncatchableAndNotifyProfiler(ExecState*);
+int32_t JIT_OPERATION operationInstanceOfCustom(ExecState*, EncodedJSValue encodedValue, JSObject* constructor, EncodedJSValue encodedHasInstance) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationHasGenericProperty(ExecState*, EncodedJSValue, JSCell*);
EncodedJSValue JIT_OPERATION operationHasIndexedProperty(ExecState*, JSCell*, int32_t);
diff --git a/Source/JavaScriptCore/llint/LLIntData.cpp b/Source/JavaScriptCore/llint/LLIntData.cpp
index 01927b6..f33c0b3 100644
--- a/Source/JavaScriptCore/llint/LLIntData.cpp
+++ b/Source/JavaScriptCore/llint/LLIntData.cpp
@@ -34,6 +34,7 @@
#include "MaxFrameExtentForSlowPathCall.h"
#include "Opcode.h"
#include "PropertyOffset.h"
+#include "WriteBarrier.h"
namespace JSC { namespace LLInt {
@@ -154,6 +155,7 @@
ASSERT(FunctionCode == 2);
ASSERT(ModuleCode == 3);
+ ASSERT(!(reinterpret_cast<ptrdiff_t>((reinterpret_cast<WriteBarrier<JSCell>*>(0x4000)->slot())) - 0x4000));
static_assert(PutByIdPrimaryTypeMask == 0x6, "LLInt assumes PutByIdPrimaryTypeMask is == 0x6");
static_assert(PutByIdPrimaryTypeSecondary == 0x0, "LLInt assumes PutByIdPrimaryTypeSecondary is == 0x0");
static_assert(PutByIdPrimaryTypeObjectWithStructure == 0x2, "LLInt assumes PutByIdPrimaryTypeObjectWithStructure is == 0x2");
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index 61277a7..0a471cb 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -522,23 +522,6 @@
LLINT_RETURN(RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regExp));
}
-LLINT_SLOW_PATH_DECL(slow_path_check_has_instance)
-{
- LLINT_BEGIN();
-
- 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()) {
- JSValue result = jsBoolean(baseObject->methodTable()->customHasInstance(baseObject, exec, value));
- LLINT_RETURN_WITH_PC_ADJUSTMENT(result, pc[4].u.operand);
- }
- }
- LLINT_THROW(createInvalidInstanceofParameterError(exec, baseVal));
-}
-
LLINT_SLOW_PATH_DECL(slow_path_instanceof)
{
LLINT_BEGIN();
@@ -548,6 +531,21 @@
LLINT_RETURN(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
}
+LLINT_SLOW_PATH_DECL(slow_path_instanceof_custom)
+{
+ LLINT_BEGIN();
+
+ JSValue value = LLINT_OP_C(2).jsValue();
+ JSValue constructor = LLINT_OP_C(3).jsValue();
+ JSValue hasInstanceValue = LLINT_OP_C(4).jsValue();
+
+ ASSERT(constructor.isObject());
+ ASSERT(hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction() || !constructor.getObject()->structure()->typeInfo().implementsDefaultHasInstance());
+
+ JSValue result = jsBoolean(constructor.getObject()->hasInstance(exec, value, hasInstanceValue));
+ LLINT_RETURN(result);
+}
+
LLINT_SLOW_PATH_DECL(slow_path_get_by_id)
{
LLINT_BEGIN();
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.h b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
index 271324b..0d5c8da 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.h
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
@@ -67,8 +67,8 @@
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array_with_size);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array_buffer);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_regexp);
-LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_check_has_instance);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_instanceof_custom);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_by_id);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_get_arguments_length);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_by_id);
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
index c5a87e3..4a3aacf 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
@@ -1175,17 +1175,35 @@
5)
-_llint_op_check_has_instance:
+_llint_op_overrides_has_instance:
traceExecution()
- loadi 12[PC], t1
- loadConstantOrVariablePayload(t1, CellTag, t0, .opCheckHasInstanceSlow)
- btbz JSCell::m_flags[t0], ImplementsDefaultHasInstance, .opCheckHasInstanceSlow
- dispatch(5)
-.opCheckHasInstanceSlow:
- callSlowPath(_llint_slow_path_check_has_instance)
- dispatch(0)
+ loadisFromInstruction(1, t3)
+ storei BooleanTag, TagOffset[cfr, t3, 8]
+ # First check if hasInstanceValue is the one on Function.prototype[Symbol.hasInstance]
+ loadisFromInstruction(3, t0)
+ loadConstantOrVariablePayload(t0, CellTag, t2, .opOverrideshasInstanceValueNotCell)
+ loadConstantOrVariable(t0, t1, t2)
+ bineq t1, CellTag, .opOverrideshasInstanceValueNotCell
+
+ # We don't need hasInstanceValue's tag register anymore.
+ loadp CodeBlock[cfr], t1
+ loadp CodeBlock::m_globalObject[t1], t1
+ loadp JSGlobalObject::m_functionProtoHasInstanceSymbolFunction[t1], t1
+ bineq t1, t2, .opOverrideshasInstanceValueNotDefault
+
+ # We know the constructor is a cell.
+ loadisFromInstruction(2, t0)
+ loadConstantOrVariablePayloadUnchecked(t0, t1)
+ tbz JSCell::m_flags[t1], ImplementsDefaultHasInstance, t0
+ storei t0, PayloadOffset[cfr, t3, 8]
+ dispatch(4)
+
+.opOverrideshasInstanceValueNotCell:
+.opOverrideshasInstanceValueNotDefault:
+ storei 1, PayloadOffset[cfr, t3, 8]
+ dispatch(4)
_llint_op_instanceof:
traceExecution()
@@ -1215,6 +1233,11 @@
callSlowPath(_llint_slow_path_instanceof)
dispatch(4)
+_llint_op_instanceof_custom:
+ traceExecution()
+ callSlowPath(_llint_slow_path_instanceof_custom)
+ dispatch(5)
+
_llint_op_is_undefined:
traceExecution()
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
index 69f3e0b..bcb3447 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
@@ -1069,16 +1069,27 @@
5)
-_llint_op_check_has_instance:
+_llint_op_overrides_has_instance:
traceExecution()
- loadisFromInstruction(3, t1)
- loadConstantOrVariableCell(t1, t0, .opCheckHasInstanceSlow)
- btbz JSCell::m_flags[t0], ImplementsDefaultHasInstance, .opCheckHasInstanceSlow
- dispatch(5)
+ loadisFromInstruction(1, t3)
-.opCheckHasInstanceSlow:
- callSlowPath(_llint_slow_path_check_has_instance)
- dispatch(0)
+ loadisFromInstruction(3, t1)
+ loadConstantOrVariable(t1, t0)
+ loadp CodeBlock[cfr], t2
+ loadp CodeBlock::m_globalObject[t2], t2
+ loadp JSGlobalObject::m_functionProtoHasInstanceSymbolFunction[t2], t2
+ bqneq t0, t2, .opOverridesHasInstanceNotDefaultSymbol
+
+ loadisFromInstruction(2, t1)
+ loadConstantOrVariable(t1, t0)
+ tbz JSCell::m_flags[t0], ImplementsDefaultHasInstance, t1
+ orq ValueFalse, t1
+ storeq t1, [cfr, t3, 8]
+ dispatch(4)
+
+.opOverridesHasInstanceNotDefaultSymbol:
+ storeq ValueTrue, [cfr, t3, 8]
+ dispatch(4)
_llint_op_instanceof:
@@ -1109,6 +1120,10 @@
callSlowPath(_llint_slow_path_instanceof)
dispatch(4)
+_llint_op_instanceof_custom:
+ traceExecution()
+ callSlowPath(_llint_slow_path_instanceof_custom)
+ dispatch(5)
_llint_op_is_undefined:
traceExecution()
diff --git a/Source/JavaScriptCore/runtime/CommonIdentifiers.h b/Source/JavaScriptCore/runtime/CommonIdentifiers.h
index 8cd7bca..3be89b8 100644
--- a/Source/JavaScriptCore/runtime/CommonIdentifiers.h
+++ b/Source/JavaScriptCore/runtime/CommonIdentifiers.h
@@ -250,7 +250,6 @@
macro(yield)
#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL_NOT_IMPLEMENTED_YET(macro)\
- macro(hasInstance) \
macro(isConcatSpreadable) \
macro(match) \
macro(replace) \
@@ -260,6 +259,7 @@
macro(toPrimitive)
#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
+ macro(hasInstance) \
macro(iterator) \
macro(unscopables) \
macro(toStringTag)
@@ -345,6 +345,9 @@
macro(NumberFormat) \
macro(newTargetLocal) \
macro(derivedConstructor) \
+ macro(isBoundFunction) \
+ macro(hasInstanceBoundFunction) \
+ macro(instanceOf) \
namespace JSC {
diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
index 0131b9f..196be2e 100644
--- a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
+++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
@@ -209,7 +209,7 @@
return makeString(rightHandSide, " is not an Object. (evaluating '", sourceText, "')");
}
-static String invalidParameterInstanceofSourceAppender(const String& originalMessage, const String& sourceText, RuntimeType, ErrorInstance::SourceTextWhereErrorOccurred occurrence)
+inline String invalidParameterInstanceofSourceAppender(const String& content, const String& originalMessage, const String& sourceText, RuntimeType, ErrorInstance::SourceTextWhereErrorOccurred occurrence)
{
if (occurrence == ErrorInstance::FoundApproximateSource)
return defaultApproximateSourceError(originalMessage, sourceText);
@@ -222,7 +222,17 @@
static const unsigned instanceofLength = 10;
String rightHandSide = sourceText.substring(instanceofIndex + instanceofLength).simplifyWhiteSpace();
- return makeString(rightHandSide, " is not a function. (evaluating '", sourceText, "')");
+ return makeString(rightHandSide, content, ". (evaluating '", sourceText, "')");
+}
+
+static String invalidParameterInstanceofNotFunctionSourceAppender(const String& originalMessage, const String& sourceText, RuntimeType runtimeType, ErrorInstance::SourceTextWhereErrorOccurred occurrence)
+{
+ return invalidParameterInstanceofSourceAppender(WTF::makeString(" is not a function"), originalMessage, sourceText, runtimeType, occurrence);
+}
+
+static String invalidParameterInstanceofhasInstanceValueNotFunctionSourceAppender(const String& originalMessage, const String& sourceText, RuntimeType runtimeType, ErrorInstance::SourceTextWhereErrorOccurred occurrence)
+{
+ return invalidParameterInstanceofSourceAppender(WTF::makeString("[Symbol.hasInstance] is not a function, undefined, or null"), originalMessage, sourceText, runtimeType, occurrence);
}
JSObject* createError(ExecState* exec, JSValue value, const String& message, ErrorInstance::SourceAppender appender)
@@ -245,9 +255,14 @@
return createError(exec, value, makeString("is not an Object."), invalidParameterInSourceAppender);
}
-JSObject* createInvalidInstanceofParameterError(ExecState* exec, JSValue value)
+JSObject* createInvalidInstanceofParameterErrorNotFunction(ExecState* exec, JSValue value)
{
- return createError(exec, value, makeString("is not a function."), invalidParameterInstanceofSourceAppender);
+ return createError(exec, value, makeString(" is not a function"), invalidParameterInstanceofNotFunctionSourceAppender);
+}
+
+JSObject* createInvalidInstanceofParameterErrorhasInstanceValueNotFunction(ExecState* exec, JSValue value)
+{
+ return createError(exec, value, makeString("[Symbol.hasInstance] is not a function, undefined, or null"), invalidParameterInstanceofhasInstanceValueNotFunctionSourceAppender);
}
JSObject* createNotAConstructorError(ExecState* exec, JSValue value)
diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.h b/Source/JavaScriptCore/runtime/ExceptionHelpers.h
index c6ba02e..54c5d70 100644
--- a/Source/JavaScriptCore/runtime/ExceptionHelpers.h
+++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.h
@@ -45,7 +45,8 @@
JSObject* createNotAnObjectError(ExecState*, JSValue);
JSObject* createInvalidFunctionApplyParameterError(ExecState*, JSValue);
JSObject* createInvalidInParameterError(ExecState*, JSValue);
-JSObject* createInvalidInstanceofParameterError(ExecState*, JSValue);
+JSObject* createInvalidInstanceofParameterErrorNotFunction(ExecState*, JSValue);
+JSObject* createInvalidInstanceofParameterErrorhasInstanceValueNotFunction(ExecState*, JSValue);
JSObject* createNotAConstructorError(ExecState*, JSValue);
JSObject* createNotAFunctionError(ExecState*, JSValue);
JSObject* createErrorForInvalidGlobalAssignment(ExecState*, const String&);
diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp
index 22375a2..6b7a5d9 100644
--- a/Source/JavaScriptCore/runtime/FunctionPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/FunctionPrototype.cpp
@@ -53,7 +53,7 @@
putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(0), DontDelete | ReadOnly | DontEnum);
}
-void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction)
+void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction, JSFunction** hasInstanceSymbolFunction)
{
VM& vm = exec->vm();
@@ -62,6 +62,7 @@
*applyFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().applyPublicName(), functionPrototypeApplyCodeGenerator(vm), DontEnum);
*callFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().callPublicName(), functionPrototypeCallCodeGenerator(vm), DontEnum);
+ *hasInstanceSymbolFunction = putDirectBuiltinFunction(vm, globalObject, vm.propertyNames->hasInstanceSymbol, functionPrototypeSymbolHasInstanceCodeGenerator(vm), DontDelete | ReadOnly | DontEnum);
JSFunction* bindFunction = JSFunction::create(vm, globalObject, 1, vm.propertyNames->bind.string(), functionProtoFuncBind);
putDirectWithoutTransition(vm, vm.propertyNames->bind, bindFunction, DontEnum);
diff --git a/Source/JavaScriptCore/runtime/FunctionPrototype.h b/Source/JavaScriptCore/runtime/FunctionPrototype.h
index 52ce8f1..abe533a 100644
--- a/Source/JavaScriptCore/runtime/FunctionPrototype.h
+++ b/Source/JavaScriptCore/runtime/FunctionPrototype.h
@@ -36,7 +36,7 @@
return prototype;
}
- void addFunctionProperties(ExecState*, JSGlobalObject*, JSFunction** callFunction, JSFunction** applyFunction);
+ void addFunctionProperties(ExecState*, JSGlobalObject*, JSFunction** callFunction, JSFunction** applyFunction, JSFunction** hasInstanceSymbolFunction);
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
{
diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
index 8c5cc2e..0f049f9 100644
--- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
+++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp
@@ -74,6 +74,20 @@
return JSValue::encode(construct(exec, targetFunction, constructType, constructData, args));
}
+EncodedJSValue JSC_HOST_CALL isBoundFunction(ExecState* exec)
+{
+ return JSValue::encode(JSValue(static_cast<bool>(jsDynamicCast<JSBoundFunction*>(exec->uncheckedArgument(0)))));
+}
+
+EncodedJSValue JSC_HOST_CALL hasInstanceBoundFunction(ExecState* exec)
+{
+ JSBoundFunction* boundObject = jsCast<JSBoundFunction*>(exec->uncheckedArgument(0));
+ JSValue value = exec->uncheckedArgument(1);
+
+ return JSValue::encode(jsBoolean(boundObject->targetFunction()->hasInstance(exec, value)));
+}
+
+
JSBoundFunction* JSBoundFunction::create(VM& vm, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int length, const String& name)
{
ConstructData constructData;
diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.h b/Source/JavaScriptCore/runtime/JSBoundFunction.h
index af2a632..040ecbf 100644
--- a/Source/JavaScriptCore/runtime/JSBoundFunction.h
+++ b/Source/JavaScriptCore/runtime/JSBoundFunction.h
@@ -32,11 +32,13 @@
EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState*);
EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState*);
+EncodedJSValue JSC_HOST_CALL isBoundFunction(ExecState*);
+EncodedJSValue JSC_HOST_CALL hasInstanceBoundFunction(ExecState*);
class JSBoundFunction : public JSFunction {
public:
typedef JSFunction Base;
- const static unsigned StructureFlags = OverridesHasInstance | Base::StructureFlags;
+ const static unsigned StructureFlags = OverridesHasInstanceFlag | Base::StructureFlags;
static JSBoundFunction* create(VM&, JSGlobalObject*, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int, const String&);
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
index 43f442c..e31db69 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
@@ -286,12 +286,14 @@
m_internalFunctionStructure.set(vm, this, InternalFunction::createStructure(vm, this, m_functionPrototype.get()));
JSFunction* callFunction = 0;
JSFunction* applyFunction = 0;
- m_functionPrototype->addFunctionProperties(exec, this, &callFunction, &applyFunction);
+ JSFunction* hasInstanceSymbolFunction = 0;
+ m_functionPrototype->addFunctionProperties(exec, this, &callFunction, &applyFunction, &hasInstanceSymbolFunction);
m_callFunction.set(vm, this, callFunction);
m_applyFunction.set(vm, this, applyFunction);
m_arrayProtoValuesFunction.set(vm, this, JSFunction::create(vm, this, 0, vm.propertyNames->values.string(), arrayProtoFuncValues));
m_initializePromiseFunction.set(vm, this, JSFunction::createBuiltinFunction(vm, promiseOperationsInitializePromiseCodeGenerator(vm), this));
m_newPromiseCapabilityFunction.set(vm, this, JSFunction::createBuiltinFunction(vm, promiseOperationsNewPromiseCapabilityCodeGenerator(vm), this));
+ m_functionProtoHasInstanceSymbolFunction.set(vm, this, hasInstanceSymbolFunction);
m_nullGetterFunction.set(vm, this, NullGetterFunction::create(vm, NullGetterFunction::createStructure(vm, this, m_functionPrototype.get())));
m_nullSetterFunction.set(vm, this, NullSetterFunction::create(vm, NullSetterFunction::createStructure(vm, this, m_functionPrototype.get())));
m_objectPrototype.set(vm, this, ObjectPrototype::create(vm, this, ObjectPrototype::createStructure(vm, this, jsNull())));
@@ -521,6 +523,9 @@
JSFunction* privateFuncToInteger = JSFunction::createBuiltinFunction(vm, globalObjectToIntegerCodeGenerator(vm), this);
JSFunction* privateFuncTypedArrayLength = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncLength);
JSFunction* privateFuncTypedArraySort = JSFunction::create(vm, this, 0, String(), typedArrayViewPrivateFuncSort);
+ JSFunction* privateFuncIsBoundFunction = JSFunction::create(vm, this, 0, String(), isBoundFunction);
+ JSFunction* privateFuncHasInstanceBoundFunction = JSFunction::create(vm, this, 0, String(), hasInstanceBoundFunction);
+ JSFunction* privateFuncInstanceOf = JSFunction::create(vm, this, 0, String(), objectPrivateFuncInstanceOf);
GlobalPropertyInfo staticGlobals[] = {
GlobalPropertyInfo(vm.propertyNames->NaN, jsNaN(), DontEnum | DontDelete | ReadOnly),
@@ -535,6 +540,9 @@
GlobalPropertyInfo(vm.propertyNames->TypeErrorPrivateName, m_typeErrorConstructor.get(), DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->typedArrayLengthPrivateName, privateFuncTypedArrayLength, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->typedArraySortPrivateName, privateFuncTypedArraySort, DontEnum | DontDelete | ReadOnly),
+ GlobalPropertyInfo(vm.propertyNames->isBoundFunctionPrivateName, privateFuncIsBoundFunction, DontEnum | DontDelete | ReadOnly),
+ GlobalPropertyInfo(vm.propertyNames->hasInstanceBoundFunctionPrivateName, privateFuncHasInstanceBoundFunction, DontEnum | DontDelete | ReadOnly),
+ GlobalPropertyInfo(vm.propertyNames->instanceOfPrivateName, privateFuncInstanceOf, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->BuiltinLogPrivateName, builtinLog, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->ArrayPrivateName, arrayConstructor, DontEnum | DontDelete | ReadOnly),
GlobalPropertyInfo(vm.propertyNames->NumberPrivateName, numberConstructor, DontEnum | DontDelete | ReadOnly),
@@ -840,6 +848,7 @@
visitor.append(&thisObject->m_arrayProtoValuesFunction);
visitor.append(&thisObject->m_initializePromiseFunction);
visitor.append(&thisObject->m_newPromiseCapabilityFunction);
+ visitor.append(&thisObject->m_functionProtoHasInstanceSymbolFunction);
visitor.append(&thisObject->m_throwTypeErrorGetterSetter);
visitor.append(&thisObject->m_moduleLoader);
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObject.h b/Source/JavaScriptCore/runtime/JSGlobalObject.h
index 70c9764..8b1374bf 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObject.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalObject.h
@@ -222,6 +222,7 @@
WriteBarrier<JSFunction> m_arrayProtoValuesFunction;
WriteBarrier<JSFunction> m_initializePromiseFunction;
WriteBarrier<JSFunction> m_newPromiseCapabilityFunction;
+ WriteBarrier<JSFunction> m_functionProtoHasInstanceSymbolFunction;
WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter;
WriteBarrier<ModuleLoaderObject> m_moduleLoader;
@@ -438,6 +439,7 @@
JSFunction* arrayProtoValuesFunction() const { return m_arrayProtoValuesFunction.get(); }
JSFunction* initializePromiseFunction() const { return m_initializePromiseFunction.get(); }
JSFunction* newPromiseCapabilityFunction() const { return m_newPromiseCapabilityFunction.get(); }
+ JSFunction* functionProtoHasInstanceSymbolFunction() const { return m_functionProtoHasInstanceSymbolFunction.get(); }
GetterSetter* throwTypeErrorGetterSetter(VM& vm)
{
if (!m_throwTypeErrorGetterSetter)
diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp
index 9a70214..6237700 100644
--- a/Source/JavaScriptCore/runtime/JSObject.cpp
+++ b/Source/JavaScriptCore/runtime/JSObject.cpp
@@ -1437,18 +1437,40 @@
return 0;
}
-bool JSObject::hasInstance(ExecState* exec, JSValue value)
+bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue)
{
VM& vm = exec->vm();
+
+ if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) {
+ CallData callData;
+ CallType callType = JSC::getCallData(hasInstanceValue, callData);
+ if (callType == CallTypeNone) {
+ vm.throwException(exec, createInvalidInstanceofParameterErrorhasInstanceValueNotFunction(exec, this));
+ return false;
+ }
+
+ MarkedArgumentBuffer args;
+ args.append(value);
+ JSValue result = call(exec, hasInstanceValue, callType, callData, this, args);
+ return result.toBoolean(exec);
+ }
+
TypeInfo info = structure(vm)->typeInfo();
if (info.implementsDefaultHasInstance())
return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
if (info.implementsHasInstance())
return methodTable(vm)->customHasInstance(this, exec, value);
- vm.throwException(exec, createInvalidInstanceofParameterError(exec, this));
+ vm.throwException(exec, createInvalidInstanceofParameterErrorNotFunction(exec, this));
return false;
}
+bool JSObject::hasInstance(ExecState* exec, JSValue value)
+{
+ JSValue hasInstanceValue = get(exec, exec->propertyNames().hasInstanceSymbol);
+
+ return hasInstance(exec, value, hasInstanceValue);
+}
+
bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
{
if (!value.isObject())
@@ -1467,6 +1489,14 @@
return false;
}
+EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState* exec)
+{
+ JSValue value = exec->uncheckedArgument(0);
+ JSValue proto = exec->uncheckedArgument(1);
+
+ return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
+}
+
void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode);
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h
index 8a91dd1..56c268b 100644
--- a/Source/JavaScriptCore/runtime/JSObject.h
+++ b/Source/JavaScriptCore/runtime/JSObject.h
@@ -487,6 +487,7 @@
JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
+ JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue value, JSValue hasInstanceValue);
bool hasInstance(ExecState*, JSValue);
static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty);
@@ -978,6 +979,8 @@
}
};
+JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*);
+
inline JSFinalObject* JSFinalObject::create(
ExecState* exec, Structure* structure, Butterfly* butterfly)
{
diff --git a/Source/JavaScriptCore/runtime/JSTypeInfo.h b/Source/JavaScriptCore/runtime/JSTypeInfo.h
index ca1120c..09d81e9 100644
--- a/Source/JavaScriptCore/runtime/JSTypeInfo.h
+++ b/Source/JavaScriptCore/runtime/JSTypeInfo.h
@@ -38,7 +38,7 @@
static const unsigned MasqueradesAsUndefined = 1; // WebCore uses MasqueradesAsUndefined to make document.all undetectable.
static const unsigned ImplementsHasInstance = 1 << 1;
-static const unsigned OverridesHasInstance = 1 << 2;
+static const unsigned OverridesHasInstanceFlag = 1 << 2; // FIXME: This is only trivially used by the runtime and should be removed: https://bugs.webkit.org/show_bug.cgi?id=152005
static const unsigned ImplementsDefaultHasInstance = 1 << 3;
static const unsigned TypeOfShouldCallGetCallData = 1 << 4; // Need this flag if you override getCallData() and you want typeof to use this to determine if it should say "function". Currently we always set this flag when we override getCallData().
static const unsigned OverridesGetOwnPropertySlot = 1 << 5;
@@ -68,9 +68,9 @@
, m_flags2(outOfLineTypeFlags)
{
// No object that doesn't ImplementsHasInstance should override it!
- ASSERT((m_flags & (ImplementsHasInstance | OverridesHasInstance)) != OverridesHasInstance);
+ ASSERT((m_flags & (ImplementsHasInstance | OverridesHasInstanceFlag)) != OverridesHasInstanceFlag);
// ImplementsDefaultHasInstance means (ImplementsHasInstance & !OverridesHasInstance)
- if ((m_flags & (ImplementsHasInstance | OverridesHasInstance)) == ImplementsHasInstance)
+ if ((m_flags & (ImplementsHasInstance | OverridesHasInstanceFlag)) == ImplementsHasInstance)
m_flags |= ImplementsDefaultHasInstance;
}
@@ -83,7 +83,7 @@
unsigned flags() const { return (static_cast<unsigned>(m_flags2) << 8) | static_cast<unsigned>(m_flags); }
bool masqueradesAsUndefined() const { return isSetOnFlags1(MasqueradesAsUndefined); }
bool implementsHasInstance() const { return isSetOnFlags1(ImplementsHasInstance); }
- bool overridesHasInstance() const { return isSetOnFlags1(OverridesHasInstance); }
+ bool overridesHasInstance() const { return isSetOnFlags1(OverridesHasInstanceFlag); }
bool implementsDefaultHasInstance() const { return isSetOnFlags1(ImplementsDefaultHasInstance); }
bool typeOfShouldCallGetCallData() const { return isSetOnFlags1(TypeOfShouldCallGetCallData); }
bool overridesGetOwnPropertySlot() const { return overridesGetOwnPropertySlot(inlineTypeFlags()); }
diff --git a/Source/JavaScriptCore/runtime/WriteBarrier.h b/Source/JavaScriptCore/runtime/WriteBarrier.h
index 0f70e1e..b727d18 100644
--- a/Source/JavaScriptCore/runtime/WriteBarrier.h
+++ b/Source/JavaScriptCore/runtime/WriteBarrier.h
@@ -152,14 +152,9 @@
bool isGetterSetter() const { return get().isGetterSetter(); }
bool isCustomGetterSetter() const { return get().isCustomGetterSetter(); }
- JSValue* slot()
+ JSValue* slot() const
{
- union {
- EncodedJSValue* v;
- JSValue* slot;
- } u;
- u.v = &m_value;
- return u.slot;
+ return bitwise_cast<JSValue*>(&m_value);
}
int32_t* tagPointer() { return &bitwise_cast<EncodedValueDescriptor*>(&m_value)->asBits.tag; }
diff --git a/Source/JavaScriptCore/tests/es6.yaml b/Source/JavaScriptCore/tests/es6.yaml
index 7e63abd..f1197d1 100644
--- a/Source/JavaScriptCore/tests/es6.yaml
+++ b/Source/JavaScriptCore/tests/es6.yaml
@@ -1183,7 +1183,7 @@
- path: es6/WeakSet_iterator_closing.js
cmd: runES6 :fail
- path: es6/well-known_symbols_Symbol.hasInstance.js
- cmd: runES6 :fail
+ cmd: runES6 :normal
- path: es6/well-known_symbols_Symbol.isConcatSpreadable.js
cmd: runES6 :fail
- path: es6/well-known_symbols_Symbol.match.js
diff --git a/Source/JavaScriptCore/tests/stress/instanceof-custom-hasinstancesymbol.js b/Source/JavaScriptCore/tests/stress/instanceof-custom-hasinstancesymbol.js
new file mode 100644
index 0000000..12f2423
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/instanceof-custom-hasinstancesymbol.js
@@ -0,0 +1,24 @@
+function Constructor(x) {}
+
+Object.defineProperty(Constructor, Symbol.hasInstance, {value: function() { return false; }});
+
+x = new Constructor();
+
+function instanceOf(a, b) {
+ return a instanceof b;
+}
+noInline(instanceOf);
+
+function body() {
+ var result = 0;
+ for (var i = 0; i < 100000; i++) {
+ if (instanceOf(x, Constructor))
+ result++;
+ }
+
+ return result;
+}
+noInline(body);
+
+if (body())
+ throw "result incorrect";
diff --git a/Source/JavaScriptCore/tests/stress/symbol-hasInstance.js b/Source/JavaScriptCore/tests/stress/symbol-hasInstance.js
new file mode 100644
index 0000000..8801eb8
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/symbol-hasInstance.js
@@ -0,0 +1,54 @@
+// This file tests the functionality of Symbol.hasInstance.
+
+
+// Test a custom Symbol.hasInstance on a function object.
+function Constructor(x) {}
+foo = new Constructor();
+
+if (!(foo instanceof Constructor))
+ throw "should be instanceof";
+
+Object.defineProperty(Constructor, Symbol.hasInstance, {value: function(value) {
+ if (this !== Constructor)
+ throw "|this| should be Constructor";
+ if (value !== foo)
+ throw "first argument should be foo";
+ return false;
+} });
+
+
+if (foo instanceof Constructor)
+ throw "should not be instanceof";
+
+
+// Test Symbol.hasInstance on an ordinary object.
+ObjectClass = {}
+ObjectClass[Symbol.hasInstance] = function (value) {
+ return value !== null && (typeof value === "object" || typeof value === "function");
+}
+
+if (!(foo instanceof ObjectClass))
+ throw "foo should be an instanceof ObjectClass";
+
+if (!(Constructor instanceof ObjectClass))
+ throw "Constructor should be an instanceof ObjectClass";
+
+NumberClass = {}
+NumberClass[Symbol.hasInstance] = function (value) {
+ return typeof value === "number";
+}
+
+if (!(1 instanceof NumberClass))
+ throw "1 should be an instanceof NumberClass";
+
+if (foo instanceof NumberClass)
+ throw "foo should be an instanceof NumberClass";
+
+
+// Test the Function.prototype[Symbol.hasInstance] works when actually called.
+descriptor = Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance);
+if (descriptor.writable !== false || descriptor.configurable !== false || descriptor.enumerable !== false)
+ throw "Function.prototype[Symbol.hasInstance] has a bad descriptor";
+
+if (!Function.prototype[Symbol.hasInstance].call(Constructor, foo))
+ throw "Function.prototype[Symbol.hasInstance] should claim that foo is an instanceof Constructor";