| Index: src/code-stub-assembler.cc
|
| diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc
|
| index e5041756644d52698ce5b3702f1150397afcb544..c047aab2e31584d0b37df129264c9c0278795101 100644
|
| --- a/src/code-stub-assembler.cc
|
| +++ b/src/code-stub-assembler.cc
|
| @@ -1416,5 +1416,141 @@ void CodeStubAssembler::TryLookupElement(Node* object, Node* map,
|
| }
|
| }
|
|
|
| +Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable,
|
| + Node* object) {
|
| + Variable var_result(this, MachineRepresentation::kTagged);
|
| + Label return_false(this), return_true(this),
|
| + return_runtime(this, Label::kDeferred), return_result(this);
|
| +
|
| + // Goto runtime if {object} is a Smi.
|
| + GotoIf(WordIsSmi(object), &return_runtime);
|
| +
|
| + // Load map of {object}.
|
| + Node* object_map = LoadMap(object);
|
| +
|
| + // Lookup the {callable} and {object} map in the global instanceof cache.
|
| + // Note: This is safe because we clear the global instanceof cache whenever
|
| + // we change the prototype of any object.
|
| + Node* instanceof_cache_function =
|
| + LoadRoot(Heap::kInstanceofCacheFunctionRootIndex);
|
| + Node* instanceof_cache_map = LoadRoot(Heap::kInstanceofCacheMapRootIndex);
|
| + {
|
| + Label instanceof_cache_miss(this);
|
| + GotoUnless(WordEqual(instanceof_cache_function, callable),
|
| + &instanceof_cache_miss);
|
| + GotoUnless(WordEqual(instanceof_cache_map, object_map),
|
| + &instanceof_cache_miss);
|
| + var_result.Bind(LoadRoot(Heap::kInstanceofCacheAnswerRootIndex));
|
| + Goto(&return_result);
|
| + Bind(&instanceof_cache_miss);
|
| + }
|
| +
|
| + // Goto runtime if {callable} is a Smi.
|
| + GotoIf(WordIsSmi(callable), &return_runtime);
|
| +
|
| + // Load map of {callable}.
|
| + Node* callable_map = LoadMap(callable);
|
| +
|
| + // Goto runtime if {callable} is not a JSFunction.
|
| + Node* callable_instance_type = LoadMapInstanceType(callable_map);
|
| + GotoUnless(
|
| + Word32Equal(callable_instance_type, Int32Constant(JS_FUNCTION_TYPE)),
|
| + &return_runtime);
|
| +
|
| + // Goto runtime if {callable} is not a constructor or has
|
| + // a non-instance "prototype".
|
| + Node* callable_bitfield = LoadMapBitField(callable_map);
|
| + GotoUnless(
|
| + 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);
|
| + {
|
| + Variable var_callable_prototype(this, MachineRepresentation::kTagged);
|
| + Label callable_prototype_valid(this);
|
| + 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.
|
| + Node* callable_prototype_instance_type =
|
| + LoadInstanceType(callable_prototype);
|
| + GotoUnless(
|
| + Word32Equal(callable_prototype_instance_type, Int32Constant(MAP_TYPE)),
|
| + &callable_prototype_valid);
|
| + var_callable_prototype.Bind(
|
| + LoadObjectField(callable_prototype, Map::kPrototypeOffset));
|
| + Goto(&callable_prototype_valid);
|
| + Bind(&callable_prototype_valid);
|
| + callable_prototype = var_callable_prototype.value();
|
| + }
|
| +
|
| + // Update the global instanceof cache with the current {object} map and
|
| + // {callable}. The cached answer will be set when it is known below.
|
| + StoreRoot(Heap::kInstanceofCacheFunctionRootIndex, callable);
|
| + StoreRoot(Heap::kInstanceofCacheMapRootIndex, object_map);
|
| +
|
| + // Loop through the prototype chain looking for the {callable} prototype.
|
| + Variable var_object_map(this, MachineRepresentation::kTagged);
|
| + var_object_map.Bind(object_map);
|
| + Label loop(this, &var_object_map);
|
| + Goto(&loop);
|
| + Bind(&loop);
|
| + {
|
| + Node* object_map = var_object_map.value();
|
| +
|
| + // Check if the current {object} needs to be access checked.
|
| + Node* object_bitfield = LoadMapBitField(object_map);
|
| + GotoUnless(
|
| + Word32Equal(Word32And(object_bitfield,
|
| + Int32Constant(1 << Map::kIsAccessCheckNeeded)),
|
| + Int32Constant(0)),
|
| + &return_runtime);
|
| +
|
| + // Check if the current {object} is a proxy.
|
| + Node* object_instance_type = LoadMapInstanceType(object_map);
|
| + GotoIf(Word32Equal(object_instance_type, Int32Constant(JS_PROXY_TYPE)),
|
| + &return_runtime);
|
| +
|
| + // Check the current {object} prototype.
|
| + Node* object_prototype = LoadMapPrototype(object_map);
|
| + GotoIf(WordEqual(object_prototype, callable_prototype), &return_true);
|
| + GotoIf(WordEqual(object_prototype, NullConstant()), &return_false);
|
| +
|
| + // Continue with the prototype.
|
| + var_object_map.Bind(LoadMap(object_prototype));
|
| + Goto(&loop);
|
| + }
|
| +
|
| + Bind(&return_true);
|
| + StoreRoot(Heap::kInstanceofCacheAnswerRootIndex, BooleanConstant(true));
|
| + var_result.Bind(BooleanConstant(true));
|
| + Goto(&return_result);
|
| +
|
| + Bind(&return_false);
|
| + StoreRoot(Heap::kInstanceofCacheAnswerRootIndex, BooleanConstant(false));
|
| + var_result.Bind(BooleanConstant(false));
|
| + Goto(&return_result);
|
| +
|
| + Bind(&return_runtime);
|
| + {
|
| + // Invalidate the global instanceof cache.
|
| + StoreRoot(Heap::kInstanceofCacheFunctionRootIndex, SmiConstant(0));
|
| + // Fallback to the runtime implementation.
|
| + var_result.Bind(
|
| + CallRuntime(Runtime::kOrdinaryHasInstance, context, callable, object));
|
| + }
|
| + Goto(&return_result);
|
| +
|
| + Bind(&return_result);
|
| + return var_result.value();
|
| +}
|
| +
|
| } // namespace internal
|
| } // namespace v8
|
|
|