diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index dd7a3ac3e5..fefa4a7f2c 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1257,6 +1257,7 @@ void Genesis::InitializeGlobal(Handle global_object, JSObject::kHeaderSize, MaybeHandle(), Builtins::kFunctionPrototypeHasInstance, static_cast(DONT_ENUM | DONT_DELETE | READ_ONLY)); + has_instance->shared()->set_builtin_function_id(kFunctionHasInstance); native_context()->set_function_has_instance(*has_instance); // Set the expected parameters for @@hasInstance to 1; required by builtin. diff --git a/src/builtins/builtins-object.cc b/src/builtins/builtins-object.cc index 98bce48f3a..93c43ab5db 100644 --- a/src/builtins/builtins-object.cc +++ b/src/builtins/builtins-object.cc @@ -1060,5 +1060,19 @@ void Builtins::Generate_InstanceOf(compiler::CodeAssemblerState* state) { assembler.Return(assembler.InstanceOf(object, callable, context)); } +// ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) +void Builtins::Generate_OrdinaryHasInstance( + compiler::CodeAssemblerState* state) { + typedef compiler::Node Node; + typedef CompareDescriptor Descriptor; + CodeStubAssembler assembler(state); + + Node* constructor = assembler.Parameter(Descriptor::kLeft); + Node* object = assembler.Parameter(Descriptor::kRight); + Node* context = assembler.Parameter(Descriptor::kContext); + + assembler.Return(assembler.OrdinaryHasInstance(context, constructor, object)); +} + } // namespace internal } // namespace v8 diff --git a/src/builtins/builtins.h b/src/builtins/builtins.h index e5b675d6dd..de061528be 100644 --- a/src/builtins/builtins.h +++ b/src/builtins/builtins.h @@ -559,6 +559,7 @@ namespace internal { \ TFS(HasProperty, BUILTIN, kNoExtraICState, HasProperty) \ TFS(InstanceOf, BUILTIN, kNoExtraICState, Compare) \ + TFS(OrdinaryHasInstance, BUILTIN, kNoExtraICState, Compare) \ TFS(ForInFilter, BUILTIN, kNoExtraICState, ForInFilter) \ \ /* Promise */ \ diff --git a/src/code-factory.cc b/src/code-factory.cc index b6eb1744d4..128c709998 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -254,8 +254,11 @@ TFS_BUILTIN(ToLength) TFS_BUILTIN(ToObject) TFS_BUILTIN(Typeof) TFS_BUILTIN(InstanceOf) +TFS_BUILTIN(OrdinaryHasInstance) TFS_BUILTIN(ForInFilter) +#undef TFS_BUILTIN + // static Callable CodeFactory::Inc(Isolate* isolate) { IncStub stub(isolate); diff --git a/src/code-factory.h b/src/code-factory.h index 6be3265cfd..033e5d54fb 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -64,6 +64,7 @@ class V8_EXPORT_PRIVATE CodeFactory final { // Code stubs. Add methods here as needed to reduce dependency on // code-stubs.h. static Callable InstanceOf(Isolate* isolate); + static Callable OrdinaryHasInstance(Isolate* isolate); static Callable StringFromCharCode(Isolate* isolate); diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc index a7e05b8bfe..dc6eb1464f 100644 --- a/src/compiler/js-builtin-reducer.cc +++ b/src/compiler/js-builtin-reducer.cc @@ -962,6 +962,34 @@ Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) { return NoChange(); } +// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V ) +Reduction JSBuiltinReducer::ReduceFunctionHasInstance(Node* node) { + Node* receiver = NodeProperties::GetValueInput(node, 1); + Node* object = (node->op()->ValueInputCount() >= 3) + ? NodeProperties::GetValueInput(node, 2) + : jsgraph()->UndefinedConstant(); + Node* context = NodeProperties::GetContextInput(node); + Node* frame_state = NodeProperties::GetFrameStateInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the + // stack trace doesn't contain the @@hasInstance call; we have the + // corresponding bug in the baseline case. Some massaging of the frame + // state would be necessary here. + + // Morph this {node} into a JSOrdinaryHasInstance node. + node->ReplaceInput(0, receiver); + node->ReplaceInput(1, object); + node->ReplaceInput(2, context); + node->ReplaceInput(3, frame_state); + node->ReplaceInput(4, effect); + node->ReplaceInput(5, control); + node->TrimInputCount(6); + NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); + return Changed(node); +} + // ES6 section 18.2.2 isFinite ( number ) Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) { JSCallReduction r(node); @@ -1845,6 +1873,9 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { return ReduceArrayPush(node); case kDateGetTime: return ReduceDateGetTime(node); + case kFunctionHasInstance: + return ReduceFunctionHasInstance(node); + break; case kGlobalIsFinite: reduction = ReduceGlobalIsFinite(node); break; diff --git a/src/compiler/js-builtin-reducer.h b/src/compiler/js-builtin-reducer.h index 6eab5b2295..4af3084ea3 100644 --- a/src/compiler/js-builtin-reducer.h +++ b/src/compiler/js-builtin-reducer.h @@ -58,6 +58,7 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final Reduction ReduceArrayPop(Node* node); Reduction ReduceArrayPush(Node* node); Reduction ReduceDateGetTime(Node* node); + Reduction ReduceFunctionHasInstance(Node* node); Reduction ReduceGlobalIsFinite(Node* node); Reduction ReduceGlobalIsNaN(Node* node); Reduction ReduceMathAbs(Node* node); diff --git a/src/compiler/js-generic-lowering.cc b/src/compiler/js-generic-lowering.cc index 389a10a9d9..47dacaabef 100644 --- a/src/compiler/js-generic-lowering.cc +++ b/src/compiler/js-generic-lowering.cc @@ -352,6 +352,11 @@ void JSGenericLowering::LowerJSInstanceOf(Node* node) { ReplaceWithStubCall(node, callable, flags); } +void JSGenericLowering::LowerJSOrdinaryHasInstance(Node* node) { + CallDescriptor::Flags flags = FrameStateFlagForCall(node); + Callable callable = CodeFactory::OrdinaryHasInstance(isolate()); + ReplaceWithStubCall(node, callable, flags); +} void JSGenericLowering::LowerJSLoadContext(Node* node) { const ContextAccess& access = ContextAccessOf(node->op()); diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index 04911a2082..e96f4c11dd 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -69,6 +69,8 @@ JSNativeContextSpecialization::JSNativeContextSpecialization( Reduction JSNativeContextSpecialization::Reduce(Node* node) { switch (node->opcode()) { + case IrOpcode::kJSInstanceOf: + return ReduceJSInstanceOf(node); case IrOpcode::kJSLoadContext: return ReduceJSLoadContext(node); case IrOpcode::kJSLoadNamed: @@ -85,6 +87,92 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) { return NoChange(); } +Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { + DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode()); + Node* object = NodeProperties::GetValueInput(node, 0); + Node* constructor = NodeProperties::GetValueInput(node, 1); + Node* context = NodeProperties::GetContextInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); + + // If deoptimization is disabled, we cannot optimize. + if (!(flags() & kDeoptimizationEnabled)) return NoChange(); + + // Check if the right hand side is a known {receiver}. + HeapObjectMatcher m(constructor); + if (!m.HasValue() || !m.Value()->IsJSObject()) return NoChange(); + Handle receiver = Handle::cast(m.Value()); + Handle receiver_map(receiver->map(), isolate()); + + // Compute property access info for @@hasInstance on {receiver}. + PropertyAccessInfo access_info; + AccessInfoFactory access_info_factory(dependencies(), native_context(), + graph()->zone()); + if (!access_info_factory.ComputePropertyAccessInfo( + receiver_map, factory()->has_instance_symbol(), AccessMode::kLoad, + &access_info)) { + return NoChange(); + } + + if (access_info.IsNotFound()) { + // If there's no @@hasInstance handler, the OrdinaryHasInstance operation + // takes over, but that requires the {receiver} to be callable. + if (receiver->IsCallable()) { + // Determine actual holder and perform prototype chain checks. + Handle holder; + if (access_info.holder().ToHandle(&holder)) { + AssumePrototypesStable(access_info.receiver_maps(), holder); + } + + // Monomorphic property access. + effect = + BuildCheckMaps(constructor, effect, control, MapList{receiver_map}); + + // Lower to OrdinaryHasInstance(C, O). + NodeProperties::ReplaceValueInput(node, constructor, 0); + NodeProperties::ReplaceValueInput(node, object, 1); + NodeProperties::ReplaceEffectInput(node, effect); + NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); + return Changed(node); + } + } else if (access_info.IsDataConstant()) { + DCHECK(access_info.constant()->IsCallable()); + + // Determine actual holder and perform prototype chain checks. + Handle holder; + if (access_info.holder().ToHandle(&holder)) { + AssumePrototypesStable(access_info.receiver_maps(), holder); + } + + // Monomorphic property access. + effect = + BuildCheckMaps(constructor, effect, control, MapList{receiver_map}); + + // Call the @@hasInstance handler. + Node* target = jsgraph()->Constant(access_info.constant()); + node->InsertInput(graph()->zone(), 0, target); + node->ReplaceInput(1, constructor); + node->ReplaceInput(2, object); + NodeProperties::ChangeOp( + node, + javascript()->CallFunction(3, 0.0f, VectorSlotPair(), + ConvertReceiverMode::kNotNullOrUndefined)); + + // Rewire the value uses of {node} to ToBoolean conversion of the result. + Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), + node, context); + for (Edge edge : node->use_edges()) { + if (NodeProperties::IsValueEdge(edge) && edge.from() != value) { + edge.UpdateTo(value); + Revisit(edge.from()); + } + } + return Changed(node); + } + + return NoChange(); +} + Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); ContextAccess const& access = ContextAccessOf(node->op()); diff --git a/src/compiler/js-native-context-specialization.h b/src/compiler/js-native-context-specialization.h index 5280b09067..62f31da4f9 100644 --- a/src/compiler/js-native-context-specialization.h +++ b/src/compiler/js-native-context-specialization.h @@ -53,6 +53,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer { Reduction Reduce(Node* node) final; private: + Reduction ReduceJSInstanceOf(Node* node); Reduction ReduceJSLoadContext(Node* node); Reduction ReduceJSLoadNamed(Node* node); Reduction ReduceJSStoreNamed(Node* node); diff --git a/src/compiler/js-operator.cc b/src/compiler/js-operator.cc index 2fa29a5965..447c8611f6 100644 --- a/src/compiler/js-operator.cc +++ b/src/compiler/js-operator.cc @@ -449,6 +449,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) { V(HasProperty, Operator::kNoProperties, 2, 1) \ V(TypeOf, Operator::kPure, 1, 1) \ V(InstanceOf, Operator::kNoProperties, 2, 1) \ + V(OrdinaryHasInstance, Operator::kNoProperties, 2, 1) \ V(ForInNext, Operator::kNoProperties, 4, 1) \ V(ForInPrepare, Operator::kNoProperties, 1, 3) \ V(LoadMessage, Operator::kNoThrow, 0, 1) \ diff --git a/src/compiler/js-operator.h b/src/compiler/js-operator.h index 92ab9a7ba2..9cdd30594a 100644 --- a/src/compiler/js-operator.h +++ b/src/compiler/js-operator.h @@ -508,6 +508,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final const Operator* TypeOf(); const Operator* InstanceOf(); + const Operator* OrdinaryHasInstance(); const Operator* ForInNext(); const Operator* ForInPrepare(); diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc index 738fb345af..335ab45292 100644 --- a/src/compiler/js-typed-lowering.cc +++ b/src/compiler/js-typed-lowering.cc @@ -1312,47 +1312,35 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) { return NoChange(); } -Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) { - DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode()); - Node* const context = NodeProperties::GetContextInput(node); - Node* const frame_state = NodeProperties::GetFrameStateInput(node); +Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) { + DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); + Node* constructor = NodeProperties::GetValueInput(node, 0); + Type* constructor_type = NodeProperties::GetType(constructor); + Node* object = NodeProperties::GetValueInput(node, 1); + Node* context = NodeProperties::GetContextInput(node); + Node* frame_state = NodeProperties::GetFrameStateInput(node); + Node* effect = NodeProperties::GetEffectInput(node); + Node* control = NodeProperties::GetControlInput(node); - // If deoptimization is disabled, we cannot optimize. - if (!(flags() & kDeoptimizationEnabled)) return NoChange(); - - // If we are in a try block, don't optimize since the runtime call - // in the proxy case can throw. - if (NodeProperties::IsExceptionalCall(node)) return NoChange(); - - JSBinopReduction r(this, node); - Node* object = r.left(); - Node* effect = r.effect(); - Node* control = r.control(); - - if (!r.right_type()->IsHeapConstant() || - !r.right_type()->AsHeapConstant()->Value()->IsJSFunction()) { + // Check if the {constructor} is a (known) JSFunction. + if (!constructor_type->IsHeapConstant() || + !constructor_type->AsHeapConstant()->Value()->IsJSFunction()) { return NoChange(); } - Handle function = - Handle::cast(r.right_type()->AsHeapConstant()->Value()); - Handle shared(function->shared(), isolate()); + Handle::cast(constructor_type->AsHeapConstant()->Value()); - // Make sure the prototype of {function} is the %FunctionPrototype%, and it - // already has a meaningful initial map (i.e. we constructed at least one - // instance using the constructor {function}). - if (function->map()->prototype() != function->native_context()->closure() || - function->map()->has_non_instance_prototype() || - !function->has_initial_map()) { - return NoChange(); - } + // Check if the {function} already has an initial map (i.e. the + // {function} has been used as a constructor at least once). + if (!function->has_initial_map()) return NoChange(); - // We can only use the fast case if @@hasInstance was not used so far. - if (!isolate()->IsHasInstanceLookupChainIntact()) return NoChange(); - dependencies()->AssumePropertyCell(factory()->has_instance_protector()); + // Check if the {function}s "prototype" is a JSReceiver. + if (!function->prototype()->IsJSReceiver()) return NoChange(); + // Install a code dependency on the {function}s initial map. Handle initial_map(function->initial_map(), isolate()); dependencies()->AssumeInitialMapCantChange(initial_map); + Node* prototype = jsgraph()->Constant(handle(initial_map->prototype(), isolate())); @@ -1420,6 +1408,15 @@ Reduction JSTypedLowering::ReduceJSInstanceOf(Node* node) { javascript()->CallRuntime(Runtime::kHasInPrototypeChain), object, prototype, context, frame_state, efalse1, if_false1); if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1); + + // Replace any potential IfException on {node} to catch exceptions + // from this %HasInPrototypeChain runtime call instead. + for (Edge edge : node->use_edges()) { + if (edge.from()->opcode() == IrOpcode::kIfException) { + edge.UpdateTo(vfalse1); + Revisit(edge.from()); + } + } } // Load the {object} prototype. @@ -2199,6 +2196,8 @@ Reduction JSTypedLowering::Reduce(Node* node) { case IrOpcode::kJSDivide: case IrOpcode::kJSModulus: return ReduceNumberBinop(node); + case IrOpcode::kJSOrdinaryHasInstance: + return ReduceJSOrdinaryHasInstance(node); case IrOpcode::kJSToBoolean: return ReduceJSToBoolean(node); case IrOpcode::kJSToInteger: @@ -2221,8 +2220,6 @@ Reduction JSTypedLowering::Reduce(Node* node) { return ReduceJSLoadProperty(node); case IrOpcode::kJSStoreProperty: return ReduceJSStoreProperty(node); - case IrOpcode::kJSInstanceOf: - return ReduceJSInstanceOf(node); case IrOpcode::kJSLoadContext: return ReduceJSLoadContext(node); case IrOpcode::kJSStoreContext: diff --git a/src/compiler/js-typed-lowering.h b/src/compiler/js-typed-lowering.h index c7e52e2b01..3e710226b4 100644 --- a/src/compiler/js-typed-lowering.h +++ b/src/compiler/js-typed-lowering.h @@ -52,7 +52,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final Reduction ReduceJSLoadNamed(Node* node); Reduction ReduceJSLoadProperty(Node* node); Reduction ReduceJSStoreProperty(Node* node); - Reduction ReduceJSInstanceOf(Node* node); + Reduction ReduceJSOrdinaryHasInstance(Node* node); Reduction ReduceJSLoadContext(Node* node); Reduction ReduceJSStoreContext(Node* node); Reduction ReduceJSLoadModule(Node* node); diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index e1958e4303..fdbe001de3 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -140,7 +140,8 @@ V(JSStoreGlobal) \ V(JSDeleteProperty) \ V(JSHasProperty) \ - V(JSInstanceOf) + V(JSInstanceOf) \ + V(JSOrdinaryHasInstance) #define JS_CONTEXT_OP_LIST(V) \ V(JSLoadContext) \ diff --git a/src/compiler/operator-properties.cc b/src/compiler/operator-properties.cc index 68d884d62d..0a9e6448e2 100644 --- a/src/compiler/operator-properties.cc +++ b/src/compiler/operator-properties.cc @@ -61,6 +61,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) { case IrOpcode::kJSLessThanOrEqual: case IrOpcode::kJSHasProperty: case IrOpcode::kJSInstanceOf: + case IrOpcode::kJSOrdinaryHasInstance: // Object operations case IrOpcode::kJSCreate: diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index 303210bbe2..2642a1007a 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1242,6 +1242,10 @@ Type* Typer::Visitor::TypeJSHasProperty(Node* node) { return Type::Boolean(); } Type* Typer::Visitor::TypeJSInstanceOf(Node* node) { return Type::Boolean(); } +Type* Typer::Visitor::TypeJSOrdinaryHasInstance(Node* node) { + return Type::Boolean(); +} + // JS context operators. @@ -1402,9 +1406,15 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) { return Type::Range(-1, kMaxSafeInteger, t->zone()); case kArrayPush: return t->cache_.kPositiveSafeInteger; + // Object functions. case kObjectHasOwnProperty: return Type::Boolean(); + + // Function functions. + case kFunctionHasInstance: + return Type::Boolean(); + // Global functions. case kGlobalDecodeURI: case kGlobalDecodeURIComponent: diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 7bdc8ddf51..872305b40a 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -604,6 +604,7 @@ void Verifier::Visitor::Check(Node* node) { case IrOpcode::kJSDeleteProperty: case IrOpcode::kJSHasProperty: case IrOpcode::kJSInstanceOf: + case IrOpcode::kJSOrdinaryHasInstance: // Type is Boolean. CheckTypeIs(node, Type::Boolean()); break; diff --git a/src/objects.h b/src/objects.h index 8f38b1f6d4..c59beab8be 100644 --- a/src/objects.h +++ b/src/objects.h @@ -7309,6 +7309,7 @@ enum BuiltinFunctionId { kDataViewBuffer, kDataViewByteLength, kDataViewByteOffset, + kFunctionHasInstance, kGlobalDecodeURI, kGlobalDecodeURIComponent, kGlobalEncodeURI, diff --git a/test/mjsunit/compiler/instanceof.js b/test/mjsunit/compiler/instanceof.js new file mode 100644 index 0000000000..cb88e7c284 --- /dev/null +++ b/test/mjsunit/compiler/instanceof.js @@ -0,0 +1,133 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +function A() {} +var a = new A(); + +var B = { + [Symbol.hasInstance](o) { + return false; + } +}; +%ToFastProperties(B.__proto__); + +var C = Object.create({ + [Symbol.hasInstance](o) { + return true; + } +}); +%ToFastProperties(C.__proto__); + +var D = Object.create({ + [Symbol.hasInstance](o) { + return o === a; + } +}); +%ToFastProperties(D.__proto__); + +var E = Object.create({ + [Symbol.hasInstance](o) { + if (o === a) throw o; + return true; + } +}); +%ToFastProperties(E.__proto__); + +function F() {} +F.__proto__ = null; + +(function() { + function foo(o) { return o instanceof A; } + + assertTrue(foo(a)); + assertTrue(foo(a)); + assertTrue(foo(new A())); + %OptimizeFunctionOnNextCall(foo); + assertTrue(foo(a)); + assertTrue(foo(new A())); +})(); + +(function() { + function foo(o) { + try { + return o instanceof A; + } catch (e) { + return e; + } + } + + assertTrue(foo(a)); + assertTrue(foo(a)); + assertTrue(foo(new A())); + assertEquals(1, foo(new Proxy({}, {getPrototypeOf() { throw 1; }}))); + %OptimizeFunctionOnNextCall(foo); + assertTrue(foo(a)); + assertTrue(foo(new A())); + assertEquals(1, foo(new Proxy({}, {getPrototypeOf() { throw 1; }}))); +})(); + +(function() { + function foo(o) { return o instanceof B; } + + assertFalse(foo(a)); + assertFalse(foo(a)); + assertFalse(foo(new A())); + %OptimizeFunctionOnNextCall(foo); + assertFalse(foo(a)); + assertFalse(foo(new A())); +})(); + +(function() { + function foo(o) { return o instanceof C; } + + assertTrue(foo(a)); + assertTrue(foo(a)); + assertTrue(foo(new A())); + %OptimizeFunctionOnNextCall(foo); + assertTrue(foo(a)); + assertTrue(foo(new A())); +})(); + +(function() { + function foo(o) { return o instanceof D; } + + assertTrue(foo(a)); + assertTrue(foo(a)); + assertFalse(foo(new A())); + %OptimizeFunctionOnNextCall(foo); + assertTrue(foo(a)); + assertFalse(foo(new A())); +})(); + +(function() { + function foo(o) { + try { + return o instanceof E; + } catch (e) { + return false; + } + } + + assertFalse(foo(a)); + assertTrue(foo(new A())); + %OptimizeFunctionOnNextCall(foo); + assertFalse(foo(a)); + assertTrue(foo(new A())); +})(); + +(function() { + function foo(o) { + return o instanceof F; + } + + assertFalse(foo(a)); + assertFalse(foo(new A())); + assertTrue(foo(new F())); + %OptimizeFunctionOnNextCall(foo); + assertFalse(foo(a)); + assertFalse(foo(new A())); + assertTrue(foo(new F())); +})(); diff --git a/test/unittests/compiler/js-typed-lowering-unittest.cc b/test/unittests/compiler/js-typed-lowering-unittest.cc index 6cd0cc8e68..6883052abb 100644 --- a/test/unittests/compiler/js-typed-lowering-unittest.cc +++ b/test/unittests/compiler/js-typed-lowering-unittest.cc @@ -859,43 +859,6 @@ TEST_F(JSTypedLoweringTest, JSSubtractSmis) { lhs, rhs, effect, control)); } -// ----------------------------------------------------------------------------- -// JSInstanceOf -// Test that instanceOf is reduced if and only if the right-hand side is a -// function constant. Functional correctness is ensured elsewhere. - -TEST_F(JSTypedLoweringTest, JSInstanceOfSpecialization) { - Node* const context = Parameter(Type::Any()); - Node* const frame_state = EmptyFrameState(); - Node* const effect = graph()->start(); - Node* const control = graph()->start(); - - Node* instanceOf = - graph()->NewNode(javascript()->InstanceOf(), Parameter(Type::Any(), 0), - HeapConstant(isolate()->object_function()), context, - frame_state, effect, control); - Reduction r = Reduce(instanceOf); - ASSERT_TRUE(r.Changed()); -} - - -TEST_F(JSTypedLoweringTest, JSInstanceOfNoSpecialization) { - Node* const context = Parameter(Type::Any()); - Node* const frame_state = EmptyFrameState(); - Node* const effect = graph()->start(); - Node* const control = graph()->start(); - - // Do not reduce if right-hand side is not a function constant. - Node* instanceOf = graph()->NewNode( - javascript()->InstanceOf(), Parameter(Type::Any(), 0), - Parameter(Type::Any()), context, frame_state, effect, control); - Node* dummy = graph()->NewNode(javascript()->ToObject(), instanceOf, context, - frame_state, effect, control); - Reduction r = Reduce(instanceOf); - ASSERT_FALSE(r.Changed()); - ASSERT_EQ(instanceOf, dummy->InputAt(0)); -} - // ----------------------------------------------------------------------------- // JSBitwiseAnd