Index: src/code-stub-assembler.cc |
diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc |
index 000c064a7057d072cca4c9c14fb0b2223f1cdc17..50fcfa19bdd12373b984b31841a36642b8cba7c0 100644 |
--- a/src/code-stub-assembler.cc |
+++ b/src/code-stub-assembler.cc |
@@ -1,7 +1,10 @@ |
// 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. |
+ |
#include "src/code-stub-assembler.h" |
+ |
+#include "src/builtins/builtins-descriptors.h" |
#include "src/code-factory.h" |
#include "src/frames-inl.h" |
#include "src/frames.h" |
@@ -5997,60 +6000,78 @@ void CodeStubAssembler::TryPrototypeChainLookup( |
Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable, |
Node* object) { |
VARIABLE(var_result, MachineRepresentation::kTagged); |
- Label return_false(this), return_true(this), |
+ Label return_false(this, Label::kDeferred), return_true(this), |
+ return_instanceof(this, Label::kDeferred), |
return_runtime(this, Label::kDeferred), return_result(this); |
- // Goto runtime if {object} is a Smi. |
- GotoIf(TaggedIsSmi(object), &return_runtime); |
- |
- // Load map of {object}. |
- Node* object_map = LoadMap(object); |
- |
- // Goto runtime if {callable} is a Smi. |
- GotoIf(TaggedIsSmi(callable), &return_runtime); |
- |
- // Load map of {callable}. |
+ // Return false if {callable} is not Callable. |
+ GotoIf(TaggedIsSmi(callable), &return_false); |
Node* callable_map = LoadMap(callable); |
+ GotoIfNot(IsCallableMap(callable_map), &return_false); |
- // Goto runtime if {callable} is not a JSFunction. |
+ // If {callable} is a JSBoundFunction, call the instanceof operator |
+ // recursively with the bound target function. |
Node* callable_instance_type = LoadMapInstanceType(callable_map); |
- GotoIfNot( |
- Word32Equal(callable_instance_type, Int32Constant(JS_FUNCTION_TYPE)), |
- &return_runtime); |
+ GotoIf(InstanceTypeEqual(callable_instance_type, JS_BOUND_FUNCTION_TYPE), |
+ &return_instanceof); |
- // Goto runtime if {callable} is not a constructor or has |
- // a non-instance "prototype". |
+ // Return false if {object} is not a JSReceiver. |
+ GotoIf(TaggedIsSmi(object), &return_false); |
+ Node* object_map = LoadMap(object); |
+ GotoIfNot(IsJSReceiverMap(object_map), &return_false); |
+ |
+ // Load the "prototype" from the {callable}. We have a fast-path here for |
+ // JSFunction objects that are marked as constructors and have an instance |
+ // "prototype". |
+ VARIABLE(var_callable_prototype, MachineRepresentation::kTagged); |
+ Label fast_callable_prototype(this), |
+ slow_callable_prototype(this, Label::kDeferred), |
+ done_callable_prototype(this); |
+ GotoIfNot(IsJSFunctionInstanceType(callable_instance_type), |
+ &slow_callable_prototype); |
Node* callable_bitfield = LoadMapBitField(callable_map); |
- GotoIfNot( |
+ Branch( |
Word32Equal(Word32And(callable_bitfield, |
Int32Constant((1 << Map::kHasNonInstancePrototype) | |
(1 << Map::kIsConstructor))), |
Int32Constant(1 << Map::kIsConstructor)), |
- &return_runtime); |
- |
- // Get the "prototype" (or initial map) of the {callable}. |
- Node* callable_prototype = |
- LoadObjectField(callable, JSFunction::kPrototypeOrInitialMapOffset); |
- { |
- Label callable_prototype_valid(this); |
- VARIABLE(var_callable_prototype, MachineRepresentation::kTagged, |
- callable_prototype); |
- |
- // Resolve the "prototype" if the {callable} has an initial map. Afterwards |
- // the {callable_prototype} will be either the JSReceiver prototype object |
- // or the hole value, which means that no instances of the {callable} were |
- // created so far and hence we should return false. |
- Node* callable_prototype_instance_type = |
- LoadInstanceType(callable_prototype); |
- GotoIfNot( |
- Word32Equal(callable_prototype_instance_type, Int32Constant(MAP_TYPE)), |
- &callable_prototype_valid); |
+ &fast_callable_prototype, &slow_callable_prototype); |
+ BIND(&fast_callable_prototype); |
+ { |
+ // Get the "prototype" (or initial map) of the {callable}. |
+ Node* callable_prototype = |
+ LoadObjectField(callable, JSFunction::kPrototypeOrInitialMapOffset); |
+ var_callable_prototype.Bind(callable_prototype); |
+ |
+ // Resolve the "prototype" if the {callable} has an initial map. |
+ // Afterwards the {callable_prototype} will be either the JSReceiver |
+ // prototype object or the hole value, which means that no instances of |
+ // the {callable} were created so far and hence we should return false. |
+ GotoIfNot(IsMap(callable_prototype), &done_callable_prototype); |
var_callable_prototype.Bind( |
LoadObjectField(callable_prototype, Map::kPrototypeOffset)); |
- Goto(&callable_prototype_valid); |
- BIND(&callable_prototype_valid); |
- callable_prototype = var_callable_prototype.value(); |
+ Goto(&done_callable_prototype); |
+ } |
+ BIND(&slow_callable_prototype); |
+ { |
+ // Generic "prototype" lookup on the {callable}, including check |
+ // that the resulting prototype is a proper JSReceiver. |
+ Label callable_prototype_not_receiver(this, Label::kDeferred); |
+ Node* callable_prototype = |
+ GetProperty(context, callable, PrototypeStringConstant()); |
+ GotoIf(TaggedIsSmi(callable_prototype), &callable_prototype_not_receiver); |
+ var_callable_prototype.Bind(callable_prototype); |
+ Branch(IsJSReceiver(callable_prototype), &done_callable_prototype, |
+ &callable_prototype_not_receiver); |
+ BIND(&callable_prototype_not_receiver); |
+ { |
+ CallRuntime(Runtime::kThrowInstanceofNonobjectProto, context, |
+ callable_prototype); |
+ Unreachable(); |
+ } |
} |
+ BIND(&done_callable_prototype); |
+ Node* callable_prototype = var_callable_prototype.value(); |
// Loop through the prototype chain looking for the {callable} prototype. |
VARIABLE(var_object_map, MachineRepresentation::kTagged, object_map); |
@@ -6058,9 +6079,8 @@ Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable, |
Goto(&loop); |
BIND(&loop); |
{ |
- Node* object_map = var_object_map.value(); |
- |
// Check if the current {object} needs to be access checked. |
+ Node* object_map = var_object_map.value(); |
Node* object_bitfield = LoadMapBitField(object_map); |
GotoIfNot( |
Word32Equal(Word32And(object_bitfield, |
@@ -6070,7 +6090,7 @@ Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable, |
// Check if the current {object} is a proxy. |
Node* object_instance_type = LoadMapInstanceType(object_map); |
- GotoIf(Word32Equal(object_instance_type, Int32Constant(JS_PROXY_TYPE)), |
+ GotoIf(InstanceTypeEqual(object_instance_type, JS_PROXY_TYPE), |
&return_runtime); |
// Check the current {object} prototype. |
@@ -6094,8 +6114,21 @@ Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable, |
BIND(&return_runtime); |
{ |
// Fallback to the runtime implementation. |
- var_result.Bind( |
- CallRuntime(Runtime::kOrdinaryHasInstance, context, callable, object)); |
+ var_result.Bind(CallRuntime(Runtime::kHasInPrototypeChain, context, object, |
+ callable_prototype)); |
+ } |
+ Goto(&return_result); |
+ |
+ BIND(&return_instanceof); |
+ { |
+ // Use the InstanceOf algorithm on the [[BoundTargetFunction]]. |
+ ExternalReference ref(Builtins::kInstanceOf, isolate()); |
+ Node* code = Load(MachineType::AnyTagged(), ExternalConstant(ref)); |
+ Node* bound_target_function = |
+ LoadObjectField(callable, JSBoundFunction::kBoundTargetFunctionOffset); |
+ Node* result = CallStub(Builtin_InstanceOf_InterfaceDescriptor(isolate()), |
+ code, context, object, bound_target_function); |
+ var_result.Bind(result); |
} |
Goto(&return_result); |
@@ -8758,8 +8791,8 @@ Node* CodeStubAssembler::InstanceOf(Node* object, Node* callable, |
GotoIfNot(IsCallable(callable), &if_notcallable); |
// Use the OrdinaryHasInstance algorithm. |
- Node* result = CallStub(CodeFactory::OrdinaryHasInstance(isolate()), |
- context, callable, object); |
+ Node* result = |
+ CallBuiltin(Builtins::kOrdinaryHasInstance, context, callable, object); |
var_result.Bind(result); |
Goto(&return_result); |
} |