builtins-call-gen.cc 24.7 KB
Newer Older
1 2 3 4
// Copyright 2017 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.

5 6 7
#include "src/builtins/builtins-call-gen.h"

#include "src/builtins/builtins-utils-gen.h"
8
#include "src/builtins/builtins.h"
9
#include "src/codegen/macro-assembler.h"
10
#include "src/common/globals.h"
11
#include "src/execution/isolate.h"
12
#include "src/objects/api-callbacks.h"
13
#include "src/objects/arguments.h"
14
#include "src/objects/property-cell.h"
15
#include "src/objects/templates.h"
16 17 18 19

namespace v8 {
namespace internal {

20 21 22
template <typename T>
using TNode = compiler::TNode<T>;

23 24
void Builtins::Generate_CallFunction_ReceiverIsNullOrUndefined(
    MacroAssembler* masm) {
25
  Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined);
26 27 28 29
}

void Builtins::Generate_CallFunction_ReceiverIsNotNullOrUndefined(
    MacroAssembler* masm) {
30
  Generate_CallFunction(masm, ConvertReceiverMode::kNotNullOrUndefined);
31 32 33
}

void Builtins::Generate_CallFunction_ReceiverIsAny(MacroAssembler* masm) {
34
  Generate_CallFunction(masm, ConvertReceiverMode::kAny);
35 36 37
}

void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
38
  Generate_CallBoundFunctionImpl(masm);
39 40 41
}

void Builtins::Generate_Call_ReceiverIsNullOrUndefined(MacroAssembler* masm) {
42
  Generate_Call(masm, ConvertReceiverMode::kNullOrUndefined);
43 44 45 46
}

void Builtins::Generate_Call_ReceiverIsNotNullOrUndefined(
    MacroAssembler* masm) {
47
  Generate_Call(masm, ConvertReceiverMode::kNotNullOrUndefined);
48 49 50
}

void Builtins::Generate_Call_ReceiverIsAny(MacroAssembler* masm) {
51
  Generate_Call(masm, ConvertReceiverMode::kAny);
52 53
}

54 55 56 57
void Builtins::Generate_CallVarargs(MacroAssembler* masm) {
  Generate_CallOrConstructVarargs(masm, masm->isolate()->builtins()->Call());
}

58
void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm) {
59
  Generate_CallOrConstructForwardVarargs(masm, CallOrConstructMode::kCall,
60
                                         masm->isolate()->builtins()->Call());
61 62 63
}

void Builtins::Generate_CallFunctionForwardVarargs(MacroAssembler* masm) {
64
  Generate_CallOrConstructForwardVarargs(
65 66
      masm, CallOrConstructMode::kCall,
      masm->isolate()->builtins()->CallFunction());
67 68 69
}

void CallOrConstructBuiltinsAssembler::CallOrConstructWithArrayLike(
70 71
    TNode<Object> target, SloppyTNode<Object> new_target,
    TNode<Object> arguments_list, TNode<Context> context) {
72
  Label if_done(this), if_arguments(this), if_array(this),
73 74 75
      if_holey_array(this, Label::kDeferred),
      if_runtime(this, Label::kDeferred);

76 77 78 79 80 81
  // Perform appropriate checks on {target} (and {new_target} first).
  if (new_target == nullptr) {
    // Check that {target} is Callable.
    Label if_target_callable(this),
        if_target_not_callable(this, Label::kDeferred);
    GotoIf(TaggedIsSmi(target), &if_target_not_callable);
82 83
    Branch(IsCallable(CAST(target)), &if_target_callable,
           &if_target_not_callable);
84 85 86 87 88 89
    BIND(&if_target_not_callable);
    {
      CallRuntime(Runtime::kThrowApplyNonFunction, context, target);
      Unreachable();
    }
    BIND(&if_target_callable);
90 91 92 93 94
  } else {
    // Check that {target} is a Constructor.
    Label if_target_constructor(this),
        if_target_not_constructor(this, Label::kDeferred);
    GotoIf(TaggedIsSmi(target), &if_target_not_constructor);
95
    Branch(IsConstructor(CAST(target)), &if_target_constructor,
96 97 98 99 100 101 102 103 104 105 106 107
           &if_target_not_constructor);
    BIND(&if_target_not_constructor);
    {
      CallRuntime(Runtime::kThrowNotConstructor, context, target);
      Unreachable();
    }
    BIND(&if_target_constructor);

    // Check that {new_target} is a Constructor.
    Label if_new_target_constructor(this),
        if_new_target_not_constructor(this, Label::kDeferred);
    GotoIf(TaggedIsSmi(new_target), &if_new_target_not_constructor);
108
    Branch(IsConstructor(CAST(new_target)), &if_new_target_constructor,
109 110 111 112 113 114 115
           &if_new_target_not_constructor);
    BIND(&if_new_target_not_constructor);
    {
      CallRuntime(Runtime::kThrowNotConstructor, context, new_target);
      Unreachable();
    }
    BIND(&if_new_target_constructor);
116 117
  }

118
  GotoIf(TaggedIsSmi(arguments_list), &if_runtime);
119 120 121

  TNode<Map> arguments_list_map = LoadMap(CAST(arguments_list));
  TNode<Context> native_context = LoadNativeContext(context);
122 123

  // Check if {arguments_list} is an (unmodified) arguments object.
124 125
  TNode<Map> sloppy_arguments_map = CAST(
      LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX));
126
  GotoIf(TaggedEqual(arguments_list_map, sloppy_arguments_map), &if_arguments);
127 128
  TNode<Map> strict_arguments_map = CAST(
      LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX));
129
  GotoIf(TaggedEqual(arguments_list_map, strict_arguments_map), &if_arguments);
130 131 132 133

  // Check if {arguments_list} is a fast JSArray.
  Branch(IsJSArrayMap(arguments_list_map), &if_array, &if_runtime);

134 135
  TVARIABLE(FixedArrayBase, var_elements);
  TVARIABLE(Int32T, var_length);
136 137 138
  BIND(&if_array);
  {
    // Try to extract the elements from a JSArray object.
139 140 141
    var_elements = LoadElements(CAST(arguments_list));
    var_length =
        LoadAndUntagToWord32ObjectField(arguments_list, JSArray::kLengthOffset);
142 143

    // Holey arrays and double backing stores need special treatment.
144 145 146 147 148 149 150
    STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
    STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
    STATIC_ASSERT(PACKED_ELEMENTS == 2);
    STATIC_ASSERT(HOLEY_ELEMENTS == 3);
    STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
    STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
    STATIC_ASSERT(LAST_FAST_ELEMENTS_KIND == HOLEY_DOUBLE_ELEMENTS);
151

152
    TNode<Int32T> kind = LoadMapElementsKind(arguments_list_map);
153

154 155 156
    GotoIf(
        IsElementsKindGreaterThan(kind, LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND),
        &if_runtime);
157
    Branch(Word32And(kind, Int32Constant(1)), &if_holey_array, &if_done);
158 159 160 161 162 163
  }

  BIND(&if_holey_array);
  {
    // For holey JSArrays we need to check that the array prototype chain
    // protector is intact and our prototype is the Array.prototype actually.
164
    GotoIfNot(IsPrototypeInitialArrayPrototype(context, arguments_list_map),
165
              &if_runtime);
166
    Branch(IsNoElementsProtectorCellInvalid(), &if_runtime, &if_done);
167 168 169 170
  }

  BIND(&if_arguments);
  {
171
    TNode<JSArgumentsObject> js_arguments = CAST(arguments_list);
172 173 174
    // Try to extract the elements from an JSArgumentsObjectWithLength.
    TNode<Object> length = LoadObjectField(
        js_arguments, JSArgumentsObjectWithLength::kLengthOffset);
175 176
    TNode<FixedArrayBase> elements = LoadElements(js_arguments);
    TNode<Smi> elements_length = LoadFixedArrayBaseLength(elements);
177
    GotoIfNot(TaggedEqual(length, elements_length), &if_runtime);
178 179
    var_elements = elements;
    var_length = SmiToInt32(CAST(length));
180 181 182 183 184 185
    Goto(&if_done);
  }

  BIND(&if_runtime);
  {
    // Ask the runtime to create the list (actually a FixedArray).
186 187 188 189
    var_elements = CAST(CallRuntime(Runtime::kCreateListFromArrayLike, context,
                                    arguments_list));
    var_length = LoadAndUntagToWord32ObjectField(var_elements.value(),
                                                 FixedArray::kLengthOffset);
190 191 192 193 194 195 196
    Goto(&if_done);
  }

  // Tail call to the appropriate builtin (depending on whether we have
  // a {new_target} passed).
  BIND(&if_done);
  {
197
    Label if_not_double(this), if_double(this);
198
    TNode<Int32T> args_count = Int32Constant(0);  // args already on the stack
199

200
    TNode<Int32T> length = var_length.value();
201 202
    {
      Label normalize_done(this);
203 204
      CSA_ASSERT(this, Int32LessThanOrEqual(
                           length, Int32Constant(FixedArray::kMaxLength)));
205 206 207 208 209 210 211 212 213 214 215
      GotoIfNot(Word32Equal(length, Int32Constant(0)), &normalize_done);
      // Make sure we don't accidentally pass along the
      // empty_fixed_double_array since the tailed-called stubs cannot handle
      // the normalization yet.
      var_elements = EmptyFixedArrayConstant();
      Goto(&normalize_done);

      BIND(&normalize_done);
    }

    TNode<FixedArrayBase> elements = var_elements.value();
216 217 218
    Branch(IsFixedDoubleArray(elements), &if_double, &if_not_double);

    BIND(&if_not_double);
219 220 221
    {
      if (new_target == nullptr) {
        Callable callable = CodeFactory::CallVarargs(isolate());
222
        TailCallStub(callable, context, target, args_count, length, elements);
223 224
      } else {
        Callable callable = CodeFactory::ConstructVarargs(isolate());
225 226
        TailCallStub(callable, context, target, new_target, args_count, length,
                     elements);
227
      }
228 229 230 231 232 233
    }

    BIND(&if_double);
    {
      // Kind is hardcoded here because CreateListFromArrayLike will only
      // produce holey double arrays.
234
      CallOrConstructDoubleVarargs(target, new_target, CAST(elements), length,
235
                                   args_count, context,
236
                                   Int32Constant(HOLEY_DOUBLE_ELEMENTS));
237 238 239 240
    }
  }
}

241 242 243
// Takes a FixedArray of doubles and creates a new FixedArray with those doubles
// boxed as HeapNumbers, then tail calls CallVarargs/ConstructVarargs depending
// on whether {new_target} was passed.
244
void CallOrConstructBuiltinsAssembler::CallOrConstructDoubleVarargs(
245 246 247
    TNode<Object> target, SloppyTNode<Object> new_target,
    TNode<FixedDoubleArray> elements, TNode<Int32T> length,
    TNode<Int32T> args_count, TNode<Context> context, TNode<Int32T> kind) {
248
  const ElementsKind new_kind = PACKED_ELEMENTS;
249
  const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
250 251
  CSA_ASSERT(this, Int32LessThanOrEqual(length,
                                        Int32Constant(FixedArray::kMaxLength)));
252
  TNode<IntPtrT> intptr_length = ChangeInt32ToIntPtr(length);
253
  CSA_ASSERT(this, WordNotEqual(intptr_length, IntPtrConstant(0)));
254 255

  // Allocate a new FixedArray of Objects.
256 257
  TNode<FixedArray> new_elements = CAST(AllocateFixedArray(
      new_kind, intptr_length, CodeStubAssembler::kAllowLargeObjectAllocation));
258 259 260 261 262 263 264 265 266 267 268 269
  // CopyFixedArrayElements does not distinguish between holey and packed for
  // its first argument, so we don't need to dispatch on {kind} here.
  CopyFixedArrayElements(PACKED_DOUBLE_ELEMENTS, elements, new_kind,
                         new_elements, intptr_length, intptr_length,
                         barrier_mode);
  if (new_target == nullptr) {
    Callable callable = CodeFactory::CallVarargs(isolate());
    TailCallStub(callable, context, target, args_count, length, new_elements);
  } else {
    Callable callable = CodeFactory::ConstructVarargs(isolate());
    TailCallStub(callable, context, target, new_target, args_count, length,
                 new_elements);
270 271 272 273
  }
}

void CallOrConstructBuiltinsAssembler::CallOrConstructWithSpread(
274 275
    TNode<Object> target, TNode<Object> new_target, TNode<Object> spread,
    TNode<Int32T> args_count, TNode<Context> context) {
276
  Label if_smiorobject(this), if_double(this),
277
      if_generic(this, Label::kDeferred);
278

279 280 281
  TVARIABLE(Int32T, var_length);
  TVARIABLE(FixedArrayBase, var_elements);
  TVARIABLE(Int32T, var_elements_kind);
282

283
  GotoIf(TaggedIsSmi(spread), &if_generic);
284
  TNode<Map> spread_map = LoadMap(CAST(spread));
285
  GotoIfNot(IsJSArrayMap(spread_map), &if_generic);
286
  TNode<JSArray> spread_array = CAST(spread);
287

288
  // Check that we have the original Array.prototype.
289 290 291 292
  GotoIfNot(IsPrototypeInitialArrayPrototype(context, spread_map), &if_generic);

  // Check that there are no elements on the Array.prototype chain.
  GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic);
293

294
  // Check that the Array.prototype hasn't been modified in a way that would
295
  // affect iteration.
296
  TNode<PropertyCell> protector_cell = ArrayIteratorProtectorConstant();
297 298 299 300
  GotoIf(
      TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
                  SmiConstant(Isolate::kProtectorInvalid)),
      &if_generic);
301 302 303 304 305 306 307 308 309
  {
    // The fast-path accesses the {spread} elements directly.
    TNode<Int32T> spread_kind = LoadMapElementsKind(spread_map);
    var_elements_kind = spread_kind;
    var_length =
        LoadAndUntagToWord32ObjectField(spread_array, JSArray::kLengthOffset);
    var_elements = LoadElements(spread_array);

    // Check elements kind of {spread}.
310
    GotoIf(IsElementsKindLessThanOrEqual(spread_kind, HOLEY_ELEMENTS),
311
           &if_smiorobject);
312 313
    GotoIf(IsElementsKindLessThanOrEqual(spread_kind, LAST_FAST_ELEMENTS_KIND),
           &if_double);
314 315 316
    Branch(IsElementsKindLessThanOrEqual(spread_kind,
                                         LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND),
           &if_smiorobject, &if_generic);
317
  }
318

319
  BIND(&if_generic);
320
  {
321
    Label if_iterator_fn_not_callable(this, Label::kDeferred);
322 323 324
    TNode<Object> iterator_fn =
        GetProperty(context, spread, IteratorSymbolConstant());
    GotoIfNot(TaggedIsCallable(iterator_fn), &if_iterator_fn_not_callable);
325 326 327
    TNode<JSArray> list =
        CAST(CallBuiltin(Builtins::kIterableToListMayPreserveHoles, context,
                         spread, iterator_fn));
328
    var_length = LoadAndUntagToWord32ObjectField(list, JSArray::kLengthOffset);
329

330 331 332 333
    var_elements = LoadElements(list);
    var_elements_kind = LoadElementsKind(list);
    Branch(Int32LessThan(var_elements_kind.value(),
                         Int32Constant(PACKED_DOUBLE_ELEMENTS)),
334
           &if_smiorobject, &if_double);
335 336 337

    BIND(&if_iterator_fn_not_callable);
    ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable);
338 339
  }

340
  BIND(&if_smiorobject);
341
  {
342 343
    TNode<FixedArrayBase> elements = var_elements.value();
    TNode<Int32T> length = var_length.value();
344 345
    CSA_ASSERT(this, Int32LessThanOrEqual(
                         length, Int32Constant(FixedArray::kMaxLength)));
346

347 348
    if (new_target == nullptr) {
      Callable callable = CodeFactory::CallVarargs(isolate());
349
      TailCallStub(callable, context, target, args_count, length, elements);
350 351
    } else {
      Callable callable = CodeFactory::ConstructVarargs(isolate());
352 353
      TailCallStub(callable, context, target, new_target, args_count, length,
                   elements);
354
    }
355
  }
356

357 358
  BIND(&if_double);
  {
359
    GotoIf(Word32Equal(var_length.value(), Int32Constant(0)), &if_smiorobject);
360 361 362
    CallOrConstructDoubleVarargs(target, new_target, CAST(var_elements.value()),
                                 var_length.value(), args_count, context,
                                 var_elements_kind.value());
363 364 365 366
  }
}

TF_BUILTIN(CallWithArrayLike, CallOrConstructBuiltinsAssembler) {
367
  TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
368
  SloppyTNode<Object> new_target = nullptr;
369 370
  TNode<Object> arguments_list = CAST(Parameter(Descriptor::kArgumentsList));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
371
  CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
372 373
}

374
TF_BUILTIN(CallWithSpread, CallOrConstructBuiltinsAssembler) {
375
  TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
376
  SloppyTNode<Object> new_target = nullptr;
377 378 379 380
  TNode<Object> spread = CAST(Parameter(Descriptor::kSpread));
  TNode<Int32T> args_count =
      UncheckedCast<Int32T>(Parameter(Descriptor::kArgumentsCount));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
381 382 383
  CallOrConstructWithSpread(target, new_target, spread, args_count, context);
}

384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
TNode<JSReceiver> CallOrConstructBuiltinsAssembler::GetCompatibleReceiver(
    TNode<JSReceiver> receiver, TNode<HeapObject> signature,
    TNode<Context> context) {
  // Walk up the hidden prototype chain to find the compatible holder
  // for the {signature}, starting with the {receiver} itself.
  //
  // Be careful, these loops are hand-tuned for (close to) ideal CSA
  // code generation. Especially the sharing of the {var_template}
  // below is intentional (even though it reads a bit funny in the
  // first loop).
  TVARIABLE(HeapObject, var_holder, receiver);
  Label holder_loop(this, &var_holder), holder_found(this, &var_holder),
      holder_next(this, Label::kDeferred);
  Goto(&holder_loop);
  BIND(&holder_loop);
  {
    // Find the template to compare against the {signature}. We don't
    // bother checking that the template is a FunctionTemplateInfo here,
    // but instead do that as part of the template loop below. The only
    // thing we care about is that the template is actually a HeapObject.
    TNode<HeapObject> holder = var_holder.value();
    TVARIABLE(HeapObject, var_template, LoadMap(holder));
    Label template_map_loop(this, &var_template),
        template_loop(this, &var_template),
        template_from_closure(this, &var_template);
    Goto(&template_map_loop);
    BIND(&template_map_loop);
    {
      // Load the constructor field from the current map (in the
      // {var_template} variable), and see if that is a HeapObject.
      // If it's a Smi then it is non-instance prototype on some
      // initial map, which cannot be the case for API instances.
      TNode<Object> constructor = LoadObjectField(
          var_template.value(), Map::kConstructorOrBackPointerOffset);
      GotoIf(TaggedIsSmi(constructor), &holder_next);

      // Now there are three cases for {constructor} that we care
      // about here:
      //
      //  1. {constructor} is a JSFunction, and we can load the template
      //     from its SharedFunctionInfo::function_data field (which
      //     may not actually be a FunctionTemplateInfo).
      //  2. {constructor} is a Map, in which case it's not a constructor
      //     but a back-pointer and we follow that.
      //  3. {constructor} is a FunctionTemplateInfo (or some other
      //     HeapObject), in which case we can directly use that for
      //     the template loop below (non-FunctionTemplateInfo objects
      //     will be ruled out there).
      //
      var_template = CAST(constructor);
434
      TNode<Uint16T> template_type = LoadInstanceType(var_template.value());
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
      GotoIf(InstanceTypeEqual(template_type, JS_FUNCTION_TYPE),
             &template_from_closure);
      Branch(InstanceTypeEqual(template_type, MAP_TYPE), &template_map_loop,
             &template_loop);
    }

    BIND(&template_from_closure);
    {
      // The first case from above, where we load the template from the
      // SharedFunctionInfo of the closure. We only check that the
      // SharedFunctionInfo::function_data is a HeapObject and blindly
      // use that as a template, since a non-FunctionTemplateInfo objects
      // will be ruled out automatically by the template loop below.
      TNode<SharedFunctionInfo> template_shared =
          LoadObjectField<SharedFunctionInfo>(
              var_template.value(), JSFunction::kSharedFunctionInfoOffset);
      TNode<Object> template_data = LoadObjectField(
          template_shared, SharedFunctionInfo::kFunctionDataOffset);
      GotoIf(TaggedIsSmi(template_data), &holder_next);
      var_template = CAST(template_data);
      Goto(&template_loop);
    }

    BIND(&template_loop);
    {
      // This loop compares the template to the expected {signature},
      // following the chain of parent templates until it hits the
      // end, in which case we continue with the next holder (the
      // hidden prototype) if there's any.
      TNode<HeapObject> current = var_template.value();
465
      GotoIf(TaggedEqual(current, signature), &holder_found);
466 467 468 469 470 471 472 473 474 475 476 477 478

      GotoIfNot(IsFunctionTemplateInfoMap(LoadMap(current)), &holder_next);

      TNode<HeapObject> current_rare = LoadObjectField<HeapObject>(
          current, FunctionTemplateInfo::kFunctionTemplateRareDataOffset);
      GotoIf(IsUndefined(current_rare), &holder_next);
      var_template = LoadObjectField<HeapObject>(
          current_rare, FunctionTemplateRareData::kParentTemplateOffset);
      Goto(&template_loop);
    }

    BIND(&holder_next);
    {
479 480 481 482
      // Continue with the hidden prototype of the {holder} if it is a
      // JSGlobalProxy (the hidden prototype can either be null or a
      // JSObject in that case), or throw an illegal invocation exception,
      // since the receiver did not pass the {signature} check.
483 484
      TNode<Map> holder_map = LoadMap(holder);
      var_holder = LoadMapPrototype(holder_map);
485
      GotoIf(IsJSGlobalProxyMap(holder_map), &holder_loop);
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
      ThrowTypeError(context, MessageTemplate::kIllegalInvocation);
    }
  }

  BIND(&holder_found);
  return CAST(var_holder.value());
}

// This calls an API callback by passing a {FunctionTemplateInfo},
// does appropriate access and compatible receiver checks.
void CallOrConstructBuiltinsAssembler::CallFunctionTemplate(
    CallFunctionTemplateMode mode,
    TNode<FunctionTemplateInfo> function_template_info, TNode<IntPtrT> argc,
    TNode<Context> context) {
  CodeStubArguments args(this, argc);
  Label throw_illegal_invocation(this, Label::kDeferred);

503 504
  // For API callbacks the receiver is always a JSReceiver (since
  // they are treated like sloppy mode functions). We might need
505
  // to perform access checks in the current {context}, depending
506 507 508 509 510 511 512 513
  // on whether the "needs access check" bit is set on the receiver
  // _and_ the {function_template_info} doesn't have the "accepts
  // any receiver" bit set.
  TNode<JSReceiver> receiver = CAST(args.GetReceiver());
  if (mode == CallFunctionTemplateMode::kCheckAccess ||
      mode == CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver) {
    TNode<Map> receiver_map = LoadMap(receiver);
    Label receiver_needs_access_check(this, Label::kDeferred),
514 515 516 517
        receiver_done(this);
    GotoIfNot(
        IsSetWord32<Map::IsAccessCheckNeededBit>(LoadMapBitField(receiver_map)),
        &receiver_done);
518
    TNode<IntPtrT> function_template_info_flags = LoadAndUntagObjectField(
519 520 521 522 523 524 525
        function_template_info, FunctionTemplateInfo::kFlagOffset);
    Branch(IsSetWord(function_template_info_flags,
                     1 << FunctionTemplateInfo::kAcceptAnyReceiver),
           &receiver_done, &receiver_needs_access_check);

    BIND(&receiver_needs_access_check);
    {
526
      CallRuntime(Runtime::kAccessCheck, context, receiver);
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
      Goto(&receiver_done);
    }

    BIND(&receiver_done);
  }

  // Figure out the API holder for the {receiver} depending on the
  // {mode} and the signature on the {function_template_info}.
  TNode<JSReceiver> holder;
  if (mode == CallFunctionTemplateMode::kCheckAccess) {
    // We did the access check (including the ToObject) above, so
    // {receiver} is a JSReceiver at this point, and we don't need
    // to perform any "compatible receiver check", so {holder} is
    // actually the {receiver}.
    holder = receiver;
  } else {
    // If the {function_template_info} doesn't specify any signature, we
    // just use the receiver as the holder for the API callback, otherwise
    // we need to look for a compatible holder in the receiver's hidden
    // prototype chain.
    TNode<HeapObject> signature = LoadObjectField<HeapObject>(
        function_template_info, FunctionTemplateInfo::kSignatureOffset);
    holder = Select<JSReceiver>(
        IsUndefined(signature),  // --
        [&]() { return receiver; },
        [&]() { return GetCompatibleReceiver(receiver, signature, context); });
  }

  // Perform the actual API callback invocation via CallApiCallback.
  TNode<CallHandlerInfo> call_handler_info = LoadObjectField<CallHandlerInfo>(
      function_template_info, FunctionTemplateInfo::kCallCodeOffset);
  TNode<Foreign> foreign = LoadObjectField<Foreign>(
      call_handler_info, CallHandlerInfo::kJsCallbackOffset);
  TNode<RawPtrT> callback =
      LoadObjectField<RawPtrT>(foreign, Foreign::kForeignAddressOffset);
  TNode<Object> call_data =
      LoadObjectField<Object>(call_handler_info, CallHandlerInfo::kDataOffset);
  TailCallStub(CodeFactory::CallApiCallback(isolate()), context, callback, argc,
               call_data, holder);
}

TF_BUILTIN(CallFunctionTemplate_CheckAccess, CallOrConstructBuiltinsAssembler) {
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
  TNode<FunctionTemplateInfo> function_template_info =
      CAST(Parameter(Descriptor::kFunctionTemplateInfo));
  TNode<IntPtrT> argc =
      UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount));
  CallFunctionTemplate(CallFunctionTemplateMode::kCheckAccess,
                       function_template_info, argc, context);
}

TF_BUILTIN(CallFunctionTemplate_CheckCompatibleReceiver,
           CallOrConstructBuiltinsAssembler) {
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
  TNode<FunctionTemplateInfo> function_template_info =
      CAST(Parameter(Descriptor::kFunctionTemplateInfo));
  TNode<IntPtrT> argc =
      UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount));
  CallFunctionTemplate(CallFunctionTemplateMode::kCheckCompatibleReceiver,
                       function_template_info, argc, context);
}

TF_BUILTIN(CallFunctionTemplate_CheckAccessAndCompatibleReceiver,
           CallOrConstructBuiltinsAssembler) {
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
  TNode<FunctionTemplateInfo> function_template_info =
      CAST(Parameter(Descriptor::kFunctionTemplateInfo));
  TNode<IntPtrT> argc =
      UncheckedCast<IntPtrT>(Parameter(Descriptor::kArgumentsCount));
  CallFunctionTemplate(
      CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver,
      function_template_info, argc, context);
}

601 602
}  // namespace internal
}  // namespace v8