builtins-call-gen.cc 25.1 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/execution/protectors.h"
13
#include "src/objects/api-callbacks.h"
14
#include "src/objects/arguments.h"
15
#include "src/objects/property-cell.h"
16
#include "src/objects/templates.h"
17 18 19 20 21 22

namespace v8 {
namespace internal {

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

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

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

void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
36
  Generate_CallBoundFunctionImpl(masm);
37 38 39
}

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

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

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

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

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

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

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

74
  // Perform appropriate checks on {target} (and {new_target} first).
75
  if (!new_target) {
76 77 78 79
    // Check that {target} is Callable.
    Label if_target_callable(this),
        if_target_not_callable(this, Label::kDeferred);
    GotoIf(TaggedIsSmi(target), &if_target_not_callable);
80 81
    Branch(IsCallable(CAST(target)), &if_target_callable,
           &if_target_not_callable);
82 83 84 85 86 87
    BIND(&if_target_not_callable);
    {
      CallRuntime(Runtime::kThrowApplyNonFunction, context, target);
      Unreachable();
    }
    BIND(&if_target_callable);
88 89 90 91 92
  } 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);
93
    Branch(IsConstructor(CAST(target)), &if_target_constructor,
94 95 96 97 98 99 100 101 102 103 104
           &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);
105 106
    GotoIf(TaggedIsSmi(*new_target), &if_new_target_not_constructor);
    Branch(IsConstructor(CAST(*new_target)), &if_new_target_constructor,
107 108 109
           &if_new_target_not_constructor);
    BIND(&if_new_target_not_constructor);
    {
110
      CallRuntime(Runtime::kThrowNotConstructor, context, *new_target);
111 112 113
      Unreachable();
    }
    BIND(&if_new_target_constructor);
114 115
  }

116
  GotoIf(TaggedIsSmi(arguments_list), &if_runtime);
117 118

  TNode<Map> arguments_list_map = LoadMap(CAST(arguments_list));
119
  TNode<NativeContext> native_context = LoadNativeContext(context);
120 121

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

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

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

    // Holey arrays and double backing stores need special treatment.
143 144 145 146 147 148 149
    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);
150

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

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

  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.
163
    GotoIfNot(IsPrototypeInitialArrayPrototype(context, arguments_list_map),
164
              &if_runtime);
165
    Branch(IsNoElementsProtectorCellInvalid(), &if_runtime, &if_done);
166 167 168 169
  }

  BIND(&if_arguments);
  {
170
    TNode<JSArgumentsObject> js_arguments = CAST(arguments_list);
171 172
    // Try to extract the elements from a JSArgumentsObject with standard map.
    TNode<Object> length = LoadJSArgumentsObjectLength(context, js_arguments);
173 174
    TNode<FixedArrayBase> elements = LoadElements(js_arguments);
    TNode<Smi> elements_length = LoadFixedArrayBaseLength(elements);
175
    GotoIfNot(TaggedEqual(length, elements_length), &if_runtime);
176 177
    var_elements = elements;
    var_length = SmiToInt32(CAST(length));
178 179 180 181 182 183
    Goto(&if_done);
  }

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

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

198
    TNode<Int32T> length = var_length.value();
199 200
    {
      Label normalize_done(this);
201 202
      CSA_ASSERT(this, Int32LessThanOrEqual(
                           length, Int32Constant(FixedArray::kMaxLength)));
203 204 205 206 207 208 209 210 211 212 213
      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();
214 215 216
    Branch(IsFixedDoubleArray(elements), &if_double, &if_not_double);

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

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

239 240 241
// 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.
242
void CallOrConstructBuiltinsAssembler::CallOrConstructDoubleVarargs(
243
    TNode<Object> target, base::Optional<TNode<Object>> new_target,
244 245
    TNode<FixedDoubleArray> elements, TNode<Int32T> length,
    TNode<Int32T> args_count, TNode<Context> context, TNode<Int32T> kind) {
246
  const ElementsKind new_kind = PACKED_ELEMENTS;
247
  const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
248 249
  CSA_ASSERT(this, Int32LessThanOrEqual(length,
                                        Int32Constant(FixedArray::kMaxLength)));
250
  TNode<IntPtrT> intptr_length = ChangeInt32ToIntPtr(length);
251
  CSA_ASSERT(this, WordNotEqual(intptr_length, IntPtrConstant(0)));
252 253

  // Allocate a new FixedArray of Objects.
254 255
  TNode<FixedArray> new_elements = CAST(AllocateFixedArray(
      new_kind, intptr_length, CodeStubAssembler::kAllowLargeObjectAllocation));
256 257 258 259 260
  // 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);
261
  if (!new_target) {
262 263 264 265
    Callable callable = CodeFactory::CallVarargs(isolate());
    TailCallStub(callable, context, target, args_count, length, new_elements);
  } else {
    Callable callable = CodeFactory::ConstructVarargs(isolate());
266
    TailCallStub(callable, context, target, *new_target, args_count, length,
267
                 new_elements);
268 269 270 271
  }
}

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

277 278 279
  TVARIABLE(Int32T, var_length);
  TVARIABLE(FixedArrayBase, var_elements);
  TVARIABLE(Int32T, var_elements_kind);
280

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

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

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

292
  // Check that the Array.prototype hasn't been modified in a way that would
293
  // affect iteration.
294
  TNode<PropertyCell> protector_cell = ArrayIteratorProtectorConstant();
295 296
  GotoIf(
      TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
297
                  SmiConstant(Protectors::kProtectorInvalid)),
298
      &if_generic);
299 300 301 302 303 304 305 306 307
  {
    // 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}.
308
    GotoIf(IsElementsKindLessThanOrEqual(spread_kind, HOLEY_ELEMENTS),
309
           &if_smiorobject);
310 311
    GotoIf(IsElementsKindLessThanOrEqual(spread_kind, LAST_FAST_ELEMENTS_KIND),
           &if_double);
312 313 314
    Branch(IsElementsKindLessThanOrEqual(spread_kind,
                                         LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND),
           &if_smiorobject, &if_generic);
315
  }
316

317
  BIND(&if_generic);
318
  {
319 320 321 322 323
    Label if_iterator_fn_not_callable(this, Label::kDeferred),
        if_iterator_is_null_or_undefined(this, Label::kDeferred);

    GotoIf(IsNullOrUndefined(spread), &if_iterator_is_null_or_undefined);

324 325 326
    TNode<Object> iterator_fn =
        GetProperty(context, spread, IteratorSymbolConstant());
    GotoIfNot(TaggedIsCallable(iterator_fn), &if_iterator_fn_not_callable);
327 328 329
    TNode<JSArray> list =
        CAST(CallBuiltin(Builtins::kIterableToListMayPreserveHoles, context,
                         spread, iterator_fn));
330
    var_length = LoadAndUntagToWord32ObjectField(list, JSArray::kLengthOffset);
331

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

    BIND(&if_iterator_fn_not_callable);
    ThrowTypeError(context, MessageTemplate::kIteratorSymbolNonCallable);
340 341 342 343

    BIND(&if_iterator_is_null_or_undefined);
    CallRuntime(Runtime::kThrowSpreadArgIsNullOrUndefined, context, spread);
    Unreachable();
344 345
  }

346
  BIND(&if_smiorobject);
347
  {
348 349
    TNode<FixedArrayBase> elements = var_elements.value();
    TNode<Int32T> length = var_length.value();
350 351
    CSA_ASSERT(this, Int32LessThanOrEqual(
                         length, Int32Constant(FixedArray::kMaxLength)));
352

353
    if (!new_target) {
354
      Callable callable = CodeFactory::CallVarargs(isolate());
355
      TailCallStub(callable, context, target, args_count, length, elements);
356 357
    } else {
      Callable callable = CodeFactory::ConstructVarargs(isolate());
358
      TailCallStub(callable, context, target, *new_target, args_count, length,
359
                   elements);
360
    }
361
  }
362

363 364
  BIND(&if_double);
  {
365
    GotoIf(Word32Equal(var_length.value(), Int32Constant(0)), &if_smiorobject);
366 367 368
    CallOrConstructDoubleVarargs(target, new_target, CAST(var_elements.value()),
                                 var_length.value(), args_count, context,
                                 var_elements_kind.value());
369 370 371 372
  }
}

TF_BUILTIN(CallWithArrayLike, CallOrConstructBuiltinsAssembler) {
373
  TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
374
  base::Optional<TNode<Object>> new_target = base::nullopt;
375 376
  TNode<Object> arguments_list = CAST(Parameter(Descriptor::kArgumentsList));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
377
  CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
378 379
}

380
TF_BUILTIN(CallWithSpread, CallOrConstructBuiltinsAssembler) {
381
  TNode<Object> target = CAST(Parameter(Descriptor::kTarget));
382
  base::Optional<TNode<Object>> new_target = base::nullopt;
383 384 385 386
  TNode<Object> spread = CAST(Parameter(Descriptor::kSpread));
  TNode<Int32T> args_count =
      UncheckedCast<Int32T>(Parameter(Descriptor::kArgumentsCount));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
387 388 389
  CallOrConstructWithSpread(target, new_target, spread, args_count, context);
}

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
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.
422 423 424
      TNode<Object> constructor =
          LoadObjectField(var_template.value(),
                          Map::kConstructorOrBackPointerOrNativeContextOffset);
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
      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);
441
      TNode<Uint16T> template_type = LoadInstanceType(var_template.value());
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
      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();
472
      GotoIf(TaggedEqual(current, signature), &holder_found);
473 474 475 476

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

      TNode<HeapObject> current_rare = LoadObjectField<HeapObject>(
477
          current, FunctionTemplateInfo::kRareDataOffset);
478 479 480 481 482 483 484 485
      GotoIf(IsUndefined(current_rare), &holder_next);
      var_template = LoadObjectField<HeapObject>(
          current_rare, FunctionTemplateRareData::kParentTemplateOffset);
      Goto(&template_loop);
    }

    BIND(&holder_next);
    {
486 487 488 489
      // 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.
490 491
      TNode<Map> holder_map = LoadMap(holder);
      var_holder = LoadMapPrototype(holder_map);
492
      GotoIf(IsJSGlobalProxyMap(holder_map), &holder_loop);
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
      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);

510 511
  // For API callbacks the receiver is always a JSReceiver (since
  // they are treated like sloppy mode functions). We might need
512
  // to perform access checks in the current {context}, depending
513 514 515 516 517 518 519 520
  // 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),
521
        receiver_done(this);
522 523 524
    GotoIfNot(IsSetWord32<Map::Bits1::IsAccessCheckNeededBit>(
                  LoadMapBitField(receiver_map)),
              &receiver_done);
525
    TNode<IntPtrT> function_template_info_flags = LoadAndUntagObjectField(
526 527 528 529 530 531 532
        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);
    {
533
      CallRuntime(Runtime::kAccessCheck, context, receiver);
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 601 602 603 604 605 606 607
      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);
}

608 609
}  // namespace internal
}  // namespace v8