[ES6] Add support for Symbol.hasInstance
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Dec 2015 21:43:45 +0000 (21:43 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Dec 2015 21:43:45 +0000 (21:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151839

Reviewed by Saam Barati.

Source/JavaScriptCore:

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.

* 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: https://svn.webkit.org/repository/webkit/trunk@193974 268f45cc-cd09-0410-ab3c-d52691b4dbfc

69 files changed:
LayoutTests/ChangeLog
LayoutTests/js/Object-getOwnPropertyNames-expected.txt
LayoutTests/js/exception-for-nonobject-expected.txt
LayoutTests/js/exception-instanceof-expected.txt
LayoutTests/js/instance-of-immediates-expected.txt
LayoutTests/js/regress/instanceof-bound-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/instanceof-bound.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/instanceof-bound.js [new file with mode: 0644]
LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
Source/JavaScriptCore/API/JSCallbackObject.h
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/FunctionPrototype.js
Source/JavaScriptCore/bytecode/BytecodeBasicBlock.cpp
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/BytecodeUseDef.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/ExitKind.cpp
Source/JavaScriptCore/bytecode/ExitKind.h
Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
Source/JavaScriptCore/dfg/DFGHeapLocation.h
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLIntrinsicRepository.h
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/llint/LLIntData.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.h
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
Source/JavaScriptCore/runtime/ExceptionHelpers.h
Source/JavaScriptCore/runtime/FunctionPrototype.cpp
Source/JavaScriptCore/runtime/FunctionPrototype.h
Source/JavaScriptCore/runtime/JSBoundFunction.cpp
Source/JavaScriptCore/runtime/JSBoundFunction.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSTypeInfo.h
Source/JavaScriptCore/runtime/WriteBarrier.h
Source/JavaScriptCore/tests/es6.yaml
Source/JavaScriptCore/tests/stress/instanceof-custom-hasinstancesymbol.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/symbol-hasInstance.js [new file with mode: 0644]

index 4a355ab..61ff724 100644 (file)
@@ -1,3 +1,27 @@
+2015-12-11  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.
+
+        * 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-11  Ryan Haddad  <ryanhaddad@apple.com>
 
         Updating mac-wk1 TestExpectations for fast/replaced/replaced-breaking.html to Yosemite+ to fix EWS bot results.
index cae219b..7f5de5f 100644 (file)
@@ -61,7 +61,7 @@ PASS getSortedOwnPropertyNames(Error) is ['length', 'name', 'prototype']
 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
index 9f9ae69..774d227 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 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
index ed68b6c..0efd7a8 100644 (file)
@@ -3,11 +3,11 @@ Test for error messages for instanceof
 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
index 1100db8..e6efc6a 100644 (file)
@@ -3,9 +3,9 @@ This test makes sure that instance of behaves correctly when the value, construc
 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 (file)
index 0000000..882bcbe
--- /dev/null
@@ -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 (file)
index 0000000..6512064
--- /dev/null
@@ -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 (file)
index 0000000..ddf8e53
--- /dev/null
@@ -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();
index cb5ec24..74bcb78 100644 (file)
@@ -70,7 +70,7 @@ var expectedPropertyNamesSet = {
     "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']"
 };
 
index a1a77fd..2b4734d 100644 (file)
@@ -127,7 +127,7 @@ protected:
 
 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();
 
index ba46766..11c0cf2 100644 (file)
@@ -1,3 +1,162 @@
+2015-12-11  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.
+
+        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-11  Joseph Pecoraro  <pecoraro@apple.com>
 
         check-for-inappropriate-objc-class-names should check all class names, not just externally visible ones
index 1aa0242..364bc43 100644 (file)
@@ -36,3 +36,18 @@ function apply(thisValue, argumentValues)
 
     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);
+}
index 596064c..f329d0b 100644 (file)
@@ -58,7 +58,6 @@ static bool isBranch(OpcodeID opcodeID)
     case op_switch_imm:
     case op_switch_char:
     case op_switch_string:
-    case op_check_has_instance:
     case op_save:
         return true;
     default:
index bccab04..053b8dc 100644 (file)
@@ -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 },
index d8ccc67..9eda937 100644 (file)
@@ -169,7 +169,6 @@ void computeUsesForBytecodeOffset(
     case op_get_by_val:
     case op_in:
     case op_instanceof:
-    case op_check_has_instance:
     case op_add:
     case op_mul:
     case op_div:
@@ -195,6 +194,8 @@ void computeUsesForBytecodeOffset(
         functor(codeBlock, instruction, opcodeID, instruction[3].u.operand);
         return;
     }
+    case op_overrides_has_instance:
+    case op_instanceof_custom:
     case op_has_structure_property:
     case op_construct_varargs:
     case op_call_varargs:
@@ -350,8 +351,9 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* bloc
     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:
index a2f326b..7baf93c 100644 (file)
@@ -1013,13 +1013,12 @@ void CodeBlock::dumpBytecode(
             ++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 @@ void CodeBlock::dumpBytecode(
             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;
index 2524300..30c5e64 100644 (file)
@@ -50,6 +50,8 @@ const char* exitKindToString(ExitKind kind)
         return "BadConstantCache";
     case BadIndexingType:
         return "BadIndexingType";
+    case BadTypeInfoFlags:
+        return "BadTypeInfoFlags";
     case Overflow:
         return "Overflow";
     case NegativeZero:
index 6f8c512..700d388 100644 (file)
@@ -37,6 +37,7 @@ enum ExitKind : uint8_t {
     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.
index 2bbf313..e2cb000 100644 (file)
@@ -74,9 +74,6 @@ static void getJumpTargetsForBytecodeOffset(CodeBlock* codeBlock, Interpreter* i
         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;
index 4a4f633..dc9b370 100644 (file)
@@ -2099,14 +2099,13 @@ void BytecodeGenerator::createVariable(
     }
 }
 
-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 @@ RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value
     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)
 {
     m_codeBlock->addPropertyAccessInstruction(instructions().size());
index acc72cf..00b4fde 100644 (file)
@@ -534,8 +534,9 @@ namespace JSC {
         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()); }
 
index add979c..4794b3c 100644 (file)
@@ -1659,22 +1659,49 @@ RegisterID* ThrowableBinaryOpNode::emitBytecode(BytecodeGenerator& generator, Re
 
 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 ----------------------------
index dc57fdb..6a60647 100644 (file)
@@ -2448,12 +2448,17 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     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;
             
@@ -2510,6 +2515,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case CountExecution:
     case CheckTierUpInLoop:
     case CheckTierUpAtReturn:
+    case CheckTypeInfoFlags:
         break;
 
     case CopyRest:
index 00e91a4..691d517 100644 (file)
@@ -3497,9 +3497,15 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             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));
@@ -3507,7 +3513,15 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             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));
index bb5b1d1..b5b2bee 100644 (file)
@@ -128,8 +128,9 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     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:
index 29fe50c..e83bb08 100644 (file)
@@ -727,9 +727,14 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         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:
@@ -737,6 +742,11 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         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);
index 16f973d..f099483 100644 (file)
@@ -141,8 +141,9 @@ bool doesGC(Graph& graph, Node* node)
     case ProfileDidCall:
     case ProfileType:
     case ProfileControlFlow:
-    case CheckHasInstance:
+    case OverridesHasInstance:
     case InstanceOf:
+    case InstanceOfCustom:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
@@ -180,6 +181,7 @@ bool doesGC(Graph& graph, Node* node)
     case CheckInBounds:
     case ConstantStoragePointer:
     case Check:
+    case CheckTypeInfoFlags:
     case MultiGetByOffset:
     case ValueRep:
     case DoubleRep:
index be4e0ec..318987b 100644 (file)
@@ -1100,10 +1100,29 @@ private:
             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: {
@@ -1164,7 +1183,11 @@ private:
             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.
@@ -1410,6 +1433,7 @@ private:
         case NotifyWrite:
         case VarInjectionWatchpoint:
         case Call:
+        case CheckTypeInfoFlags:
         case TailCallInlinedCaller:
         case Construct:
         case CallVarargs:
index 6923656..55d84cf 100644 (file)
@@ -95,9 +95,13 @@ void printInternal(PrintStream& out, LocationKind kind)
     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:
index 495d300..b85fa26 100644 (file)
@@ -40,7 +40,8 @@ enum LocationKind {
     ArrayLengthLoc,
     ButterflyLoc,
     ButterflyReadOnlyLoc,
-    CheckHasInstanceLoc,
+    CheckTypeInfoFlagsLoc,
+    OverridesHasInstanceLoc,
     ClosureVariableLoc,
     DirectArgumentsLoc,
     GetterLoc,
index f96a7ef..fd98658 100644 (file)
@@ -1354,6 +1354,7 @@ struct Node {
     {
         switch (op()) {
         case CheckCell:
+        case OverridesHasInstance:
         case NewFunction:
         case NewArrowFunction:
         case CreateActivation:
@@ -1415,6 +1416,17 @@ struct Node {
         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()) {
index 008d8c7..b695feb 100644 (file)
@@ -221,6 +221,7 @@ namespace JSC { namespace DFG {
     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) \
@@ -278,8 +279,9 @@ namespace JSC { namespace DFG {
     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) \
index a34d446..450f668 100644 (file)
@@ -402,7 +402,9 @@ private:
         case CompareGreaterEq:
         case CompareEq:
         case CompareStrictEq:
+        case OverridesHasInstance:
         case InstanceOf:
+        case InstanceOfCustom:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
@@ -569,6 +571,7 @@ private:
         case DoubleAsInt32:
         case GetLocalUnlinked:
         case CheckArray:
+        case CheckTypeInfoFlags:
         case Arrayify:
         case ArrayifyToStructure:
         case CheckTierUpInLoop:
@@ -680,7 +683,6 @@ private:
         case ProfileDidCall:
         case ProfileType:
         case ProfileControlFlow:
-        case CheckHasInstance:
         case ThrowReferenceError:
         case ForceOSRExit:
         case SetArgument:
index 616c3b0..5d89bfb 100644 (file)
@@ -241,8 +241,10 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case ProfileDidCall:
     case ProfileType:
     case ProfileControlFlow:
-    case CheckHasInstance:
+    case CheckTypeInfoFlags:
+    case OverridesHasInstance:
     case InstanceOf:
+    case InstanceOfCustom:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
index 4b40e67..e1d1267 100755 (executable)
@@ -2738,6 +2738,17 @@ void SpeculativeJIT::compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg p
     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) {
@@ -2950,6 +2961,28 @@ void SpeculativeJIT::compileValueAdd(Node* node)
     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()) {
index 939aca1..ff25664 100755 (executable)
@@ -727,6 +727,7 @@ public:
     
     void compileInstanceOfForObject(Node*, GPRReg valueReg, GPRReg prototypeReg, GPRReg scratchAndResultReg, GPRReg scratch2Reg);
     void compileInstanceOf(Node*);
+    void compileInstanceOfCustom(Node*);
     
     void emitCall(Node*);
     
@@ -1511,6 +1512,17 @@ public:
         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));
@@ -1832,6 +1844,16 @@ public:
         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));
@@ -2204,6 +2226,7 @@ public:
 
     void compileGetArrayLength(Node*);
 
+    void compileCheckTypeInfoFlags(Node*);
     void compileCheckIdent(Node*);
     
     void compileValueRep(Node*);
index 3b7197d..f190295 100644 (file)
@@ -4159,17 +4159,48 @@ void SpeculativeJIT::compile(Node* node)
         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;
     }
 
@@ -4178,6 +4209,11 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case InstanceOfCustom: {
+        compileInstanceOfCustom(node);
+        break;
+    }
+
     case IsUndefined: {
         JSValueOperand value(this, node->child1());
         GPRTemporary result(this);
index b987cd4..154a0d8 100644 (file)
@@ -4149,17 +4149,42 @@ void SpeculativeJIT::compile(Node* node)
         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;
     }
 
@@ -4167,6 +4192,11 @@ void SpeculativeJIT::compile(Node* node)
         compileInstanceOf(node);
         break;
     }
+
+    case InstanceOfCustom: {
+        compileInstanceOfCustom(node);
+        break;
+    }
         
     case IsUndefined: {
         JSValueOperand value(this, node->child1());
index 8728d9e..fa92cfe 100644 (file)
@@ -174,8 +174,10 @@ inline CapabilityLevel canCompile(Node* node)
     case IsObject:
     case IsObjectOrNull:
     case IsFunction:
-    case CheckHasInstance:
+    case CheckTypeInfoFlags:
+    case OverridesHasInstance:
     case InstanceOf:
+    case InstanceOfCustom:
     case DoubleRep:
     case ValueRep:
     case Int52Rep:
index f5f4ea7..c8f081c 100644 (file)
@@ -128,6 +128,7 @@ namespace JSC { namespace FTL {
     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))
index 3033f50..6b19cd7 100644 (file)
@@ -941,12 +941,18 @@ private:
         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;
@@ -5715,13 +5721,40 @@ private:
 #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()
@@ -5774,6 +5807,15 @@ private:
         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()
     {
index f3431c9..a3bdfa7 100644 (file)
@@ -228,8 +228,9 @@ void JIT::privateCompileMainPass()
         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 @@ void JIT::privateCompileSlowCases()
         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)
index 8b61cc1..f49a9a4 100755 (executable)
@@ -509,8 +509,9 @@ namespace JSC {
         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 @@ namespace JSC {
         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 @@ namespace JSC {
         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 @@ namespace JSC {
         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);
index cadb083..77adc02 100755 (executable)
@@ -503,6 +503,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EJJBy opera
     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 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperationNoExceptionCheck(V_JITOpera
     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));
index 3a97dd8..b2235bc 100755 (executable)
@@ -105,17 +105,29 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas
     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(hasInstanceValue, regT0);
 
-    emitGetVirtualRegister(baseVal, regT0);
+    // 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 is a cell.
-    emitJumpSlowCaseIfNotJSCell(regT0, baseVal);
+    emitGetVirtualRegister(constructor, regT0);
 
-    // Check that baseVal 'ImplementsHasInstance'.
-    addSlowCase(branchTest8(Zero, Address(regT0, JSCell::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
+    // 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 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
     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 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
     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,33 +847,34 @@ void JIT::emitSlow_op_nstricteq(Instruction* currentInstruction, Vector<SlowCase
     slowPathCall.call();
 }
 
-void JIT::emitSlow_op_check_has_instance(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 {
     int dst = currentInstruction[1].u.operand;
     int value = currentInstruction[2].u.operand;
-    int baseVal = currentInstruction[3].u.operand;
+    int proto = currentInstruction[3].u.operand;
 
-    linkSlowCaseIfNotJSCell(iter, baseVal);
+    linkSlowCaseIfNotJSCell(iter, value);
+    linkSlowCaseIfNotJSCell(iter, proto);
     linkSlowCase(iter);
     emitGetVirtualRegister(value, regT0);
-    emitGetVirtualRegister(baseVal, regT1);
-    callOperation(operationCheckHasInstance, dst, regT0, regT1);
-
-    emitJumpSlowToHot(jump(), currentInstruction[4].u.operand);
+    emitGetVirtualRegister(proto, regT1);
+    callOperation(operationInstanceOf, dst, regT0, regT1);
 }
 
-void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+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 proto = currentInstruction[3].u.operand;
+    int constructor = currentInstruction[3].u.operand;
+    int hasInstanceValue = currentInstruction[4].u.operand;
 
-    linkSlowCaseIfNotJSCell(iter, value);
-    linkSlowCaseIfNotJSCell(iter, proto);
     linkSlowCase(iter);
     emitGetVirtualRegister(value, regT0);
-    emitGetVirtualRegister(proto, regT1);
-    callOperation(operationInstanceOf, dst, regT0, regT1);
+    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)
index 89455ed..8fc3b92 100644 (file)
@@ -183,17 +183,31 @@ void JIT::emitSlow_op_new_object(Instruction* currentInstruction, Vector<SlowCas
     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()));
+
+    // 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);
 
-    // Check that baseVal is a cell.
-    emitJumpSlowCaseIfNotJSCell(baseVal);
-    
-    // Check that baseVal 'ImplementsHasInstance'.
-    addSlowCase(branchTest8(Zero, Address(regT0, JSCell::typeInfoFlagsOffset()), TrustedImm32(ImplementsDefaultHasInstance)));
 }
 
 void JIT::emit_op_instanceof(Instruction* currentInstruction)
@@ -207,7 +221,7 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
     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 @@ void JIT::emit_op_instanceof(Instruction* currentInstruction)
     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 @@ void JIT::emitSlow_op_instanceof(Instruction* currentInstruction, Vector<SlowCas
     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;
index cad9346..cff8458 100644 (file)
@@ -1487,25 +1487,19 @@ void JIT_OPERATION operationProfileWillCall(ExecState* exec, EncodedJSValue enco
         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);
-
-    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));
-        }
-    }
+    JSValue hasInstanceValue = JSValue::decode(encodedHasInstance);
 
-    vm.throwException(exec, createInvalidInstanceofParameterError(exec, baseVal));
-    return JSValue::encode(JSValue());
+    ASSERT(hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction() || !constructor->structure()->typeInfo().implementsDefaultHasInstance());
+
+    if (constructor->hasInstance(exec, value, hasInstanceValue))
+        return 1;
+    return 0;
 }
 
 }
index 2542550..3de1784 100644 (file)
@@ -175,6 +175,7 @@ typedef int32_t JIT_OPERATION (*Z_JITOperation_EC)(ExecState*, JSCell*);
 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*);
@@ -328,7 +329,6 @@ void JIT_OPERATION operationPushFunctionNameScope(ExecState*, int32_t, SymbolTab
 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;
@@ -357,6 +357,7 @@ void JIT_OPERATION operationInitGlobalConst(ExecState*, Instruction*);
 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);
index 01927b6..f33c0b3 100644 (file)
@@ -34,6 +34,7 @@
 #include "MaxFrameExtentForSlowPathCall.h"
 #include "Opcode.h"
 #include "PropertyOffset.h"
+#include "WriteBarrier.h"
 
 namespace JSC { namespace LLInt {
 
@@ -154,6 +155,7 @@ void Data::performAssertions(VM& vm)
     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");
index 98fbed8..5d8bfc3 100644 (file)
@@ -522,30 +522,28 @@ LLINT_SLOW_PATH_DECL(slow_path_new_regexp)
     LLINT_RETURN(RegExpObject::create(vm, exec->lexicalGlobalObject()->regExpStructure(), regExp));
 }
 
-LLINT_SLOW_PATH_DECL(slow_path_check_has_instance)
+LLINT_SLOW_PATH_DECL(slow_path_instanceof)
 {
     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));
+    JSValue proto = LLINT_OP_C(3).jsValue();
+    ASSERT(!value.isObject() || !proto.isObject());
+    LLINT_RETURN(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
 }
 
-LLINT_SLOW_PATH_DECL(slow_path_instanceof)
+LLINT_SLOW_PATH_DECL(slow_path_instanceof_custom)
 {
     LLINT_BEGIN();
+
     JSValue value = LLINT_OP_C(2).jsValue();
-    JSValue proto = LLINT_OP_C(3).jsValue();
-    ASSERT(!value.isObject() || !proto.isObject());
-    LLINT_RETURN(jsBoolean(JSObject::defaultHasInstance(exec, value, proto)));
+    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)
index 271324b..0d5c8da 100644 (file)
@@ -67,8 +67,8 @@ LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_new_array);
 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);
index c5a87e3..4a3aacf 100644 (file)
@@ -1175,17 +1175,35 @@ _llint_op_bitor:
         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 @@ _llint_op_instanceof:
     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()
index 69f3e0b..837802e 100644 (file)
@@ -1069,16 +1069,28 @@ _llint_op_bitor:
         5)
 
 
-_llint_op_check_has_instance:
+_llint_op_overrides_has_instance:
     traceExecution()
+    loadisFromInstruction(1, t3)
+
     loadisFromInstruction(3, t1)
-    loadConstantOrVariableCell(t1, t0, .opCheckHasInstanceSlow)
-    btbz JSCell::m_flags[t0], ImplementsDefaultHasInstance, .opCheckHasInstanceSlow
-    dispatch(5)
+    loadConstantOrVariable(t1, t0)
+    loadp CodeBlock[cfr], t2
+    loadp CodeBlock::m_globalObject[t2], t2
+    loadp JSGlobalObject::m_functionProtoHasInstanceSymbolFunction[t2], t2
+    bqneq t0, t2, .opOverridesHasInstanceNotDefaultSymbol
 
-.opCheckHasInstanceSlow:
-    callSlowPath(_llint_slow_path_check_has_instance)
-    dispatch(0)
+    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, t1
+    storeq t1, [cfr, t3, 8]
+    dispatch(4)
 
 
 _llint_op_instanceof:
@@ -1109,6 +1121,10 @@ _llint_op_instanceof:
     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()
index 8cd7bca..3be89b8 100644 (file)
     macro(yield)
 
 #define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL_NOT_IMPLEMENTED_YET(macro)\
-    macro(hasInstance) \
     macro(isConcatSpreadable) \
     macro(match) \
     macro(replace) \
     macro(toPrimitive)
 
 #define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(macro) \
+    macro(hasInstance) \
     macro(iterator) \
     macro(unscopables) \
     macro(toStringTag)
     macro(NumberFormat) \
     macro(newTargetLocal) \
     macro(derivedConstructor) \
+    macro(isBoundFunction) \
+    macro(hasInstanceBoundFunction) \
+    macro(instanceOf) \
 
 
 namespace JSC {
index 0131b9f..196be2e 100644 (file)
@@ -209,7 +209,7 @@ static String invalidParameterInSourceAppender(const String& originalMessage, co
     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 String invalidParameterInstanceofSourceAppender(const String& originalMes
 
     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 @@ JSObject* createInvalidInParameterError(ExecState* exec, JSValue value)
     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"), invalidParameterInstanceofNotFunctionSourceAppender);
+}
+
+JSObject* createInvalidInstanceofParameterErrorhasInstanceValueNotFunction(ExecState* exec, JSValue value)
 {
-    return createError(exec, value, makeString("is not a function."), invalidParameterInstanceofSourceAppender);
+    return createError(exec, value, makeString("[Symbol.hasInstance] is not a function, undefined, or null"), invalidParameterInstanceofhasInstanceValueNotFunctionSourceAppender);
 }
 
 JSObject* createNotAConstructorError(ExecState* exec, JSValue value)
index c6ba02e..54c5d70 100644 (file)
@@ -45,7 +45,8 @@ JSObject* createTDZError(ExecState*);
 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&);
index 970d012..aa87561 100644 (file)
@@ -53,7 +53,7 @@ void FunctionPrototype::finishCreation(VM& vm, const String& name)
     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 @@ void FunctionPrototype::addFunctionProperties(ExecState* exec, JSGlobalObject* g
 
     *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);
index 52ce8f1..abe533a 100644 (file)
@@ -36,7 +36,7 @@ public:
         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)
     {
index 8c5cc2e..0f049f9 100644 (file)
@@ -74,6 +74,20 @@ EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState* exec)
     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;
index af2a632..040ecbf 100644 (file)
@@ -32,11 +32,13 @@ namespace JSC {
 
 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&);
     
index ecc82ba..82f2411 100644 (file)
@@ -286,12 +286,14 @@ void JSGlobalObject::init(VM& vm)
     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 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     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 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         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),
@@ -832,6 +840,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     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);
 
index 62d1495..9a0ae62 100644 (file)
@@ -222,6 +222,7 @@ protected:
     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 @@ public:
     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)
index a0f7719..03e88aa 100644 (file)
@@ -1457,18 +1457,40 @@ const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName)
     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())
@@ -1487,6 +1509,14 @@ bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto)
     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);
index abf5a5b..86716fc 100644 (file)
@@ -485,6 +485,7 @@ public:
 
     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);
 
@@ -974,6 +975,8 @@ private:
     }
 };
 
+JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*);
+
 inline JSFinalObject* JSFinalObject::create(
     ExecState* exec, Structure* structure, Butterfly* butterfly)
 {
index ca1120c..09d81e9 100644 (file)
@@ -38,7 +38,7 @@ class LLIntOffsetsExtractor;
 
 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 @@ public:
         , 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 @@ public:
     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()); }
index 0f70e1e..b727d18 100644 (file)
@@ -152,14 +152,9 @@ public:
     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; }
index 7e63abd..f1197d1 100644 (file)
 - 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 (file)
index 0000000..12f2423
--- /dev/null
@@ -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 (file)
index 0000000..8801eb8
--- /dev/null
@@ -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";