• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/builtins/builtins-call-gen.h"
6 
7 #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 namespace v8 {
19 namespace internal {
20 
Generate_CallFunction_ReceiverIsNullOrUndefined(MacroAssembler * masm)21 void Builtins::Generate_CallFunction_ReceiverIsNullOrUndefined(
22     MacroAssembler* masm) {
23   Generate_CallFunction(masm, ConvertReceiverMode::kNullOrUndefined);
24 }
25 
Generate_CallFunction_ReceiverIsNotNullOrUndefined(MacroAssembler * masm)26 void Builtins::Generate_CallFunction_ReceiverIsNotNullOrUndefined(
27     MacroAssembler* masm) {
28   Generate_CallFunction(masm, ConvertReceiverMode::kNotNullOrUndefined);
29 }
30 
Generate_CallFunction_ReceiverIsAny(MacroAssembler * masm)31 void Builtins::Generate_CallFunction_ReceiverIsAny(MacroAssembler* masm) {
32   Generate_CallFunction(masm, ConvertReceiverMode::kAny);
33 }
34 
Generate_CallBoundFunction(MacroAssembler * masm)35 void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
36   Generate_CallBoundFunctionImpl(masm);
37 }
38 
Generate_Call_ReceiverIsNullOrUndefined(MacroAssembler * masm)39 void Builtins::Generate_Call_ReceiverIsNullOrUndefined(MacroAssembler* masm) {
40   Generate_Call(masm, ConvertReceiverMode::kNullOrUndefined);
41 }
42 
Generate_Call_ReceiverIsNotNullOrUndefined(MacroAssembler * masm)43 void Builtins::Generate_Call_ReceiverIsNotNullOrUndefined(
44     MacroAssembler* masm) {
45   Generate_Call(masm, ConvertReceiverMode::kNotNullOrUndefined);
46 }
47 
Generate_Call_ReceiverIsAny(MacroAssembler * masm)48 void Builtins::Generate_Call_ReceiverIsAny(MacroAssembler* masm) {
49   Generate_Call(masm, ConvertReceiverMode::kAny);
50 }
51 
Generate_CallVarargs(MacroAssembler * masm)52 void Builtins::Generate_CallVarargs(MacroAssembler* masm) {
53   Generate_CallOrConstructVarargs(masm, masm->isolate()->builtins()->Call());
54 }
55 
Generate_CallForwardVarargs(MacroAssembler * masm)56 void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm) {
57   Generate_CallOrConstructForwardVarargs(masm, CallOrConstructMode::kCall,
58                                          masm->isolate()->builtins()->Call());
59 }
60 
Generate_CallFunctionForwardVarargs(MacroAssembler * masm)61 void Builtins::Generate_CallFunctionForwardVarargs(MacroAssembler* masm) {
62   Generate_CallOrConstructForwardVarargs(
63       masm, CallOrConstructMode::kCall,
64       masm->isolate()->builtins()->CallFunction());
65 }
66 
TF_BUILTIN(Call_ReceiverIsNullOrUndefined_WithFeedback,CallOrConstructBuiltinsAssembler)67 TF_BUILTIN(Call_ReceiverIsNullOrUndefined_WithFeedback,
68            CallOrConstructBuiltinsAssembler) {
69   auto target = Parameter<Object>(Descriptor::kFunction);
70   auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
71   auto context = Parameter<Context>(Descriptor::kContext);
72   auto maybe_feedback_vector =
73       Parameter<HeapObject>(Descriptor::kMaybeFeedbackVector);
74   auto slot = UncheckedParameter<Int32T>(Descriptor::kSlot);
75   CollectCallFeedback(target, context, maybe_feedback_vector,
76                       Unsigned(ChangeInt32ToIntPtr(slot)));
77   TailCallBuiltin(Builtins::kCall_ReceiverIsNullOrUndefined, context, target,
78                   argc);
79 }
80 
TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_WithFeedback,CallOrConstructBuiltinsAssembler)81 TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_WithFeedback,
82            CallOrConstructBuiltinsAssembler) {
83   auto target = Parameter<Object>(Descriptor::kFunction);
84   auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
85   auto context = Parameter<Context>(Descriptor::kContext);
86   auto maybe_feedback_vector =
87       Parameter<HeapObject>(Descriptor::kMaybeFeedbackVector);
88   auto slot = UncheckedParameter<Int32T>(Descriptor::kSlot);
89   CollectCallFeedback(target, context, maybe_feedback_vector,
90                       Unsigned(ChangeInt32ToIntPtr(slot)));
91   TailCallBuiltin(Builtins::kCall_ReceiverIsNotNullOrUndefined, context, target,
92                   argc);
93 }
94 
TF_BUILTIN(Call_ReceiverIsAny_WithFeedback,CallOrConstructBuiltinsAssembler)95 TF_BUILTIN(Call_ReceiverIsAny_WithFeedback, CallOrConstructBuiltinsAssembler) {
96   auto target = Parameter<Object>(Descriptor::kFunction);
97   auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
98   auto context = Parameter<Context>(Descriptor::kContext);
99   auto maybe_feedback_vector =
100       Parameter<HeapObject>(Descriptor::kMaybeFeedbackVector);
101   auto slot = UncheckedParameter<Int32T>(Descriptor::kSlot);
102   CollectCallFeedback(target, context, maybe_feedback_vector,
103                       Unsigned(ChangeInt32ToIntPtr(slot)));
104   TailCallBuiltin(Builtins::kCall_ReceiverIsAny, context, target, argc);
105 }
106 
CallOrConstructWithArrayLike(TNode<Object> target,base::Optional<TNode<Object>> new_target,TNode<Object> arguments_list,TNode<Context> context)107 void CallOrConstructBuiltinsAssembler::CallOrConstructWithArrayLike(
108     TNode<Object> target, base::Optional<TNode<Object>> new_target,
109     TNode<Object> arguments_list, TNode<Context> context) {
110   Label if_done(this), if_arguments(this), if_array(this),
111       if_holey_array(this, Label::kDeferred),
112       if_runtime(this, Label::kDeferred);
113 
114   // Perform appropriate checks on {target} (and {new_target} first).
115   if (!new_target) {
116     // Check that {target} is Callable.
117     Label if_target_callable(this),
118         if_target_not_callable(this, Label::kDeferred);
119     GotoIf(TaggedIsSmi(target), &if_target_not_callable);
120     Branch(IsCallable(CAST(target)), &if_target_callable,
121            &if_target_not_callable);
122     BIND(&if_target_not_callable);
123     {
124       CallRuntime(Runtime::kThrowApplyNonFunction, context, target);
125       Unreachable();
126     }
127     BIND(&if_target_callable);
128   } else {
129     // Check that {target} is a Constructor.
130     Label if_target_constructor(this),
131         if_target_not_constructor(this, Label::kDeferred);
132     GotoIf(TaggedIsSmi(target), &if_target_not_constructor);
133     Branch(IsConstructor(CAST(target)), &if_target_constructor,
134            &if_target_not_constructor);
135     BIND(&if_target_not_constructor);
136     {
137       CallRuntime(Runtime::kThrowNotConstructor, context, target);
138       Unreachable();
139     }
140     BIND(&if_target_constructor);
141 
142     // Check that {new_target} is a Constructor.
143     Label if_new_target_constructor(this),
144         if_new_target_not_constructor(this, Label::kDeferred);
145     GotoIf(TaggedIsSmi(*new_target), &if_new_target_not_constructor);
146     Branch(IsConstructor(CAST(*new_target)), &if_new_target_constructor,
147            &if_new_target_not_constructor);
148     BIND(&if_new_target_not_constructor);
149     {
150       CallRuntime(Runtime::kThrowNotConstructor, context, *new_target);
151       Unreachable();
152     }
153     BIND(&if_new_target_constructor);
154   }
155 
156   GotoIf(TaggedIsSmi(arguments_list), &if_runtime);
157 
158   TNode<Map> arguments_list_map = LoadMap(CAST(arguments_list));
159   TNode<NativeContext> native_context = LoadNativeContext(context);
160 
161   // Check if {arguments_list} is an (unmodified) arguments object.
162   TNode<Map> sloppy_arguments_map = CAST(
163       LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX));
164   GotoIf(TaggedEqual(arguments_list_map, sloppy_arguments_map), &if_arguments);
165   TNode<Map> strict_arguments_map = CAST(
166       LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX));
167   GotoIf(TaggedEqual(arguments_list_map, strict_arguments_map), &if_arguments);
168 
169   // Check if {arguments_list} is a fast JSArray.
170   Branch(IsJSArrayMap(arguments_list_map), &if_array, &if_runtime);
171 
172   TVARIABLE(FixedArrayBase, var_elements);
173   TVARIABLE(Int32T, var_length);
174   BIND(&if_array);
175   {
176     TNode<Int32T> kind = LoadMapElementsKind(arguments_list_map);
177     GotoIf(
178         IsElementsKindGreaterThan(kind, LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND),
179         &if_runtime);
180 
181     TNode<JSObject> js_object = CAST(arguments_list);
182     // Try to extract the elements from a JSArray object.
183     var_elements = LoadElements(js_object);
184     var_length =
185         LoadAndUntagToWord32ObjectField(js_object, JSArray::kLengthOffset);
186 
187     // Holey arrays and double backing stores need special treatment.
188     STATIC_ASSERT(PACKED_SMI_ELEMENTS == 0);
189     STATIC_ASSERT(HOLEY_SMI_ELEMENTS == 1);
190     STATIC_ASSERT(PACKED_ELEMENTS == 2);
191     STATIC_ASSERT(HOLEY_ELEMENTS == 3);
192     STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS == 4);
193     STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == 5);
194     STATIC_ASSERT(LAST_FAST_ELEMENTS_KIND == HOLEY_DOUBLE_ELEMENTS);
195 
196     Branch(Word32And(kind, Int32Constant(1)), &if_holey_array, &if_done);
197   }
198 
199   BIND(&if_holey_array);
200   {
201     // For holey JSArrays we need to check that the array prototype chain
202     // protector is intact and our prototype is the Array.prototype actually.
203     GotoIfNot(IsPrototypeInitialArrayPrototype(context, arguments_list_map),
204               &if_runtime);
205     Branch(IsNoElementsProtectorCellInvalid(), &if_runtime, &if_done);
206   }
207 
208   BIND(&if_arguments);
209   {
210     TNode<JSArgumentsObject> js_arguments = CAST(arguments_list);
211     // Try to extract the elements from a JSArgumentsObject with standard map.
212     TNode<Object> length = LoadJSArgumentsObjectLength(context, js_arguments);
213     TNode<FixedArrayBase> elements = LoadElements(js_arguments);
214     TNode<Smi> elements_length = LoadFixedArrayBaseLength(elements);
215     GotoIfNot(TaggedEqual(length, elements_length), &if_runtime);
216     var_elements = elements;
217     var_length = SmiToInt32(CAST(length));
218     Goto(&if_done);
219   }
220 
221   BIND(&if_runtime);
222   {
223     // Ask the runtime to create the list (actually a FixedArray).
224     var_elements = CAST(CallRuntime(Runtime::kCreateListFromArrayLike, context,
225                                     arguments_list));
226     var_length = LoadAndUntagToWord32ObjectField(var_elements.value(),
227                                                  FixedArray::kLengthOffset);
228     Goto(&if_done);
229   }
230 
231   // Tail call to the appropriate builtin (depending on whether we have
232   // a {new_target} passed).
233   BIND(&if_done);
234   {
235     Label if_not_double(this), if_double(this);
236     TNode<Int32T> args_count = Int32Constant(0);  // args already on the stack
237 
238     TNode<Int32T> length = var_length.value();
239     {
240       Label normalize_done(this);
241       CSA_ASSERT(this, Int32LessThanOrEqual(
242                            length, Int32Constant(FixedArray::kMaxLength)));
243       GotoIfNot(Word32Equal(length, Int32Constant(0)), &normalize_done);
244       // Make sure we don't accidentally pass along the
245       // empty_fixed_double_array since the tailed-called stubs cannot handle
246       // the normalization yet.
247       var_elements = EmptyFixedArrayConstant();
248       Goto(&normalize_done);
249 
250       BIND(&normalize_done);
251     }
252 
253     TNode<FixedArrayBase> elements = var_elements.value();
254     Branch(IsFixedDoubleArray(elements), &if_double, &if_not_double);
255 
256     BIND(&if_not_double);
257     {
258       if (!new_target) {
259         Callable callable = CodeFactory::CallVarargs(isolate());
260         TailCallStub(callable, context, target, args_count, length, elements);
261       } else {
262         Callable callable = CodeFactory::ConstructVarargs(isolate());
263         TailCallStub(callable, context, target, *new_target, args_count, length,
264                      elements);
265       }
266     }
267 
268     BIND(&if_double);
269     {
270       // Kind is hardcoded here because CreateListFromArrayLike will only
271       // produce holey double arrays.
272       CallOrConstructDoubleVarargs(target, new_target, CAST(elements), length,
273                                    args_count, context,
274                                    Int32Constant(HOLEY_DOUBLE_ELEMENTS));
275     }
276   }
277 }
278 
279 // Takes a FixedArray of doubles and creates a new FixedArray with those doubles
280 // boxed as HeapNumbers, then tail calls CallVarargs/ConstructVarargs depending
281 // on whether {new_target} was passed.
CallOrConstructDoubleVarargs(TNode<Object> target,base::Optional<TNode<Object>> new_target,TNode<FixedDoubleArray> elements,TNode<Int32T> length,TNode<Int32T> args_count,TNode<Context> context,TNode<Int32T> kind)282 void CallOrConstructBuiltinsAssembler::CallOrConstructDoubleVarargs(
283     TNode<Object> target, base::Optional<TNode<Object>> new_target,
284     TNode<FixedDoubleArray> elements, TNode<Int32T> length,
285     TNode<Int32T> args_count, TNode<Context> context, TNode<Int32T> kind) {
286   const ElementsKind new_kind = PACKED_ELEMENTS;
287   const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
288   CSA_ASSERT(this, Int32LessThanOrEqual(length,
289                                         Int32Constant(FixedArray::kMaxLength)));
290   TNode<IntPtrT> intptr_length = ChangeInt32ToIntPtr(length);
291   CSA_ASSERT(this, WordNotEqual(intptr_length, IntPtrConstant(0)));
292 
293   // Allocate a new FixedArray of Objects.
294   TNode<FixedArray> new_elements = CAST(AllocateFixedArray(
295       new_kind, intptr_length, CodeStubAssembler::kAllowLargeObjectAllocation));
296   // CopyFixedArrayElements does not distinguish between holey and packed for
297   // its first argument, so we don't need to dispatch on {kind} here.
298   CopyFixedArrayElements(PACKED_DOUBLE_ELEMENTS, elements, new_kind,
299                          new_elements, intptr_length, intptr_length,
300                          barrier_mode);
301   if (!new_target) {
302     Callable callable = CodeFactory::CallVarargs(isolate());
303     TailCallStub(callable, context, target, args_count, length, new_elements);
304   } else {
305     Callable callable = CodeFactory::ConstructVarargs(isolate());
306     TailCallStub(callable, context, target, *new_target, args_count, length,
307                  new_elements);
308   }
309 }
310 
CallOrConstructWithSpread(TNode<Object> target,base::Optional<TNode<Object>> new_target,TNode<Object> spread,TNode<Int32T> args_count,TNode<Context> context)311 void CallOrConstructBuiltinsAssembler::CallOrConstructWithSpread(
312     TNode<Object> target, base::Optional<TNode<Object>> new_target,
313     TNode<Object> spread, TNode<Int32T> args_count, TNode<Context> context) {
314   Label if_smiorobject(this), if_double(this),
315       if_generic(this, Label::kDeferred);
316 
317   TVARIABLE(JSArray, var_js_array);
318   TVARIABLE(FixedArrayBase, var_elements);
319   TVARIABLE(Int32T, var_elements_kind);
320 
321   GotoIf(TaggedIsSmi(spread), &if_generic);
322   TNode<Map> spread_map = LoadMap(CAST(spread));
323   GotoIfNot(IsJSArrayMap(spread_map), &if_generic);
324   TNode<JSArray> spread_array = CAST(spread);
325 
326   // Check that we have the original Array.prototype.
327   GotoIfNot(IsPrototypeInitialArrayPrototype(context, spread_map), &if_generic);
328 
329   // Check that there are no elements on the Array.prototype chain.
330   GotoIf(IsNoElementsProtectorCellInvalid(), &if_generic);
331 
332   // Check that the Array.prototype hasn't been modified in a way that would
333   // affect iteration.
334   TNode<PropertyCell> protector_cell = ArrayIteratorProtectorConstant();
335   GotoIf(
336       TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
337                   SmiConstant(Protectors::kProtectorInvalid)),
338       &if_generic);
339   {
340     // The fast-path accesses the {spread} elements directly.
341     TNode<Int32T> spread_kind = LoadMapElementsKind(spread_map);
342     var_js_array = spread_array;
343     var_elements_kind = spread_kind;
344     var_elements = LoadElements(spread_array);
345 
346     // Check elements kind of {spread}.
347     GotoIf(IsElementsKindLessThanOrEqual(spread_kind, HOLEY_ELEMENTS),
348            &if_smiorobject);
349     GotoIf(IsElementsKindLessThanOrEqual(spread_kind, LAST_FAST_ELEMENTS_KIND),
350            &if_double);
351     Branch(IsElementsKindLessThanOrEqual(spread_kind,
352                                          LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND),
353            &if_smiorobject, &if_generic);
354   }
355 
356   BIND(&if_generic);
357   {
358     Label if_iterator_fn_not_callable(this, Label::kDeferred),
359         if_iterator_is_null_or_undefined(this, Label::kDeferred),
360         throw_spread_error(this, Label::kDeferred);
361     TVARIABLE(Smi, message_id);
362 
363     GotoIf(IsNullOrUndefined(spread), &if_iterator_is_null_or_undefined);
364 
365     TNode<Object> iterator_fn =
366         GetProperty(context, spread, IteratorSymbolConstant());
367     GotoIfNot(TaggedIsCallable(iterator_fn), &if_iterator_fn_not_callable);
368     TNode<JSArray> list =
369         CAST(CallBuiltin(Builtins::kIterableToListMayPreserveHoles, context,
370                          spread, iterator_fn));
371 
372     var_js_array = list;
373     var_elements = LoadElements(list);
374     var_elements_kind = LoadElementsKind(list);
375     Branch(Int32LessThan(var_elements_kind.value(),
376                          Int32Constant(PACKED_DOUBLE_ELEMENTS)),
377            &if_smiorobject, &if_double);
378 
379     BIND(&if_iterator_fn_not_callable);
380     message_id = SmiConstant(
381         static_cast<int>(MessageTemplate::kIteratorSymbolNonCallable)),
382     Goto(&throw_spread_error);
383 
384     BIND(&if_iterator_is_null_or_undefined);
385     message_id = SmiConstant(
386         static_cast<int>(MessageTemplate::kNotIterableNoSymbolLoad));
387     Goto(&throw_spread_error);
388 
389     BIND(&throw_spread_error);
390     CallRuntime(Runtime::kThrowSpreadArgError, context, message_id.value(),
391                 spread);
392     Unreachable();
393   }
394 
395   BIND(&if_smiorobject);
396   {
397     TNode<Int32T> length = LoadAndUntagToWord32ObjectField(
398         var_js_array.value(), JSArray::kLengthOffset);
399     TNode<FixedArrayBase> elements = var_elements.value();
400     CSA_ASSERT(this, Int32LessThanOrEqual(
401                          length, Int32Constant(FixedArray::kMaxLength)));
402 
403     if (!new_target) {
404       Callable callable = CodeFactory::CallVarargs(isolate());
405       TailCallStub(callable, context, target, args_count, length, elements);
406     } else {
407       Callable callable = CodeFactory::ConstructVarargs(isolate());
408       TailCallStub(callable, context, target, *new_target, args_count, length,
409                    elements);
410     }
411   }
412 
413   BIND(&if_double);
414   {
415     TNode<Int32T> length = LoadAndUntagToWord32ObjectField(
416         var_js_array.value(), JSArray::kLengthOffset);
417     GotoIf(Word32Equal(length, Int32Constant(0)), &if_smiorobject);
418     CallOrConstructDoubleVarargs(target, new_target, CAST(var_elements.value()),
419                                  length, args_count, context,
420                                  var_elements_kind.value());
421   }
422 }
423 
TF_BUILTIN(CallWithArrayLike,CallOrConstructBuiltinsAssembler)424 TF_BUILTIN(CallWithArrayLike, CallOrConstructBuiltinsAssembler) {
425   auto target = Parameter<Object>(Descriptor::kTarget);
426   base::Optional<TNode<Object>> new_target = base::nullopt;
427   auto arguments_list = Parameter<Object>(Descriptor::kArgumentsList);
428   auto context = Parameter<Context>(Descriptor::kContext);
429   CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
430 }
431 
TF_BUILTIN(CallWithArrayLike_WithFeedback,CallOrConstructBuiltinsAssembler)432 TF_BUILTIN(CallWithArrayLike_WithFeedback, CallOrConstructBuiltinsAssembler) {
433   auto target = Parameter<Object>(Descriptor::kTarget);
434   base::Optional<TNode<Object>> new_target = base::nullopt;
435   auto arguments_list = Parameter<Object>(Descriptor::kArgumentsList);
436   auto context = Parameter<Context>(Descriptor::kContext);
437   auto maybe_feedback_vector =
438       Parameter<HeapObject>(Descriptor::kMaybeFeedbackVector);
439   auto slot = UncheckedParameter<Int32T>(Descriptor::kSlot);
440   CollectCallFeedback(target, context, maybe_feedback_vector,
441                       Unsigned(ChangeInt32ToIntPtr(slot)));
442   CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
443 }
444 
TF_BUILTIN(CallWithSpread,CallOrConstructBuiltinsAssembler)445 TF_BUILTIN(CallWithSpread, CallOrConstructBuiltinsAssembler) {
446   auto target = Parameter<Object>(Descriptor::kTarget);
447   base::Optional<TNode<Object>> new_target = base::nullopt;
448   auto spread = Parameter<Object>(Descriptor::kSpread);
449   auto args_count = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
450   auto context = Parameter<Context>(Descriptor::kContext);
451   CallOrConstructWithSpread(target, new_target, spread, args_count, context);
452 }
453 
TF_BUILTIN(CallWithSpread_WithFeedback,CallOrConstructBuiltinsAssembler)454 TF_BUILTIN(CallWithSpread_WithFeedback, CallOrConstructBuiltinsAssembler) {
455   auto target = Parameter<Object>(Descriptor::kTarget);
456   base::Optional<TNode<Object>> new_target = base::nullopt;
457   auto spread = Parameter<Object>(Descriptor::kSpread);
458   auto args_count = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
459   auto context = Parameter<Context>(Descriptor::kContext);
460   auto maybe_feedback_vector =
461       Parameter<HeapObject>(Descriptor::kMaybeFeedbackVector);
462   auto slot = UncheckedParameter<Int32T>(Descriptor::kSlot);
463   CollectCallFeedback(target, context, maybe_feedback_vector,
464                       Unsigned(ChangeInt32ToIntPtr(slot)));
465   CallOrConstructWithSpread(target, new_target, spread, args_count, context);
466 }
467 
GetCompatibleReceiver(TNode<JSReceiver> receiver,TNode<HeapObject> signature,TNode<Context> context)468 TNode<JSReceiver> CallOrConstructBuiltinsAssembler::GetCompatibleReceiver(
469     TNode<JSReceiver> receiver, TNode<HeapObject> signature,
470     TNode<Context> context) {
471   // Walk up the hidden prototype chain to find the compatible holder
472   // for the {signature}, starting with the {receiver} itself.
473   //
474   // Be careful, these loops are hand-tuned for (close to) ideal CSA
475   // code generation. Especially the sharing of the {var_template}
476   // below is intentional (even though it reads a bit funny in the
477   // first loop).
478   TVARIABLE(HeapObject, var_holder, receiver);
479   Label holder_loop(this, &var_holder), holder_found(this, &var_holder),
480       holder_next(this, Label::kDeferred);
481   Goto(&holder_loop);
482   BIND(&holder_loop);
483   {
484     // Find the template to compare against the {signature}. We don't
485     // bother checking that the template is a FunctionTemplateInfo here,
486     // but instead do that as part of the template loop below. The only
487     // thing we care about is that the template is actually a HeapObject.
488     TNode<HeapObject> holder = var_holder.value();
489     TVARIABLE(HeapObject, var_template, LoadMap(holder));
490     Label template_map_loop(this, &var_template),
491         template_loop(this, &var_template),
492         template_from_closure(this, &var_template);
493     Goto(&template_map_loop);
494     BIND(&template_map_loop);
495     {
496       // Load the constructor field from the current map (in the
497       // {var_template} variable), and see if that is a HeapObject.
498       // If it's a Smi then it is non-instance prototype on some
499       // initial map, which cannot be the case for API instances.
500       TNode<Object> constructor =
501           LoadObjectField(var_template.value(),
502                           Map::kConstructorOrBackPointerOrNativeContextOffset);
503       GotoIf(TaggedIsSmi(constructor), &holder_next);
504 
505       // Now there are three cases for {constructor} that we care
506       // about here:
507       //
508       //  1. {constructor} is a JSFunction, and we can load the template
509       //     from its SharedFunctionInfo::function_data field (which
510       //     may not actually be a FunctionTemplateInfo).
511       //  2. {constructor} is a Map, in which case it's not a constructor
512       //     but a back-pointer and we follow that.
513       //  3. {constructor} is a FunctionTemplateInfo (or some other
514       //     HeapObject), in which case we can directly use that for
515       //     the template loop below (non-FunctionTemplateInfo objects
516       //     will be ruled out there).
517       //
518       var_template = CAST(constructor);
519       TNode<Uint16T> template_type = LoadInstanceType(var_template.value());
520       GotoIf(InstanceTypeEqual(template_type, JS_FUNCTION_TYPE),
521              &template_from_closure);
522       Branch(InstanceTypeEqual(template_type, MAP_TYPE), &template_map_loop,
523              &template_loop);
524     }
525 
526     BIND(&template_from_closure);
527     {
528       // The first case from above, where we load the template from the
529       // SharedFunctionInfo of the closure. We only check that the
530       // SharedFunctionInfo::function_data is a HeapObject and blindly
531       // use that as a template, since a non-FunctionTemplateInfo objects
532       // will be ruled out automatically by the template loop below.
533       TNode<SharedFunctionInfo> template_shared =
534           LoadObjectField<SharedFunctionInfo>(
535               var_template.value(), JSFunction::kSharedFunctionInfoOffset);
536       TNode<Object> template_data = LoadObjectField(
537           template_shared, SharedFunctionInfo::kFunctionDataOffset);
538       GotoIf(TaggedIsSmi(template_data), &holder_next);
539       var_template = CAST(template_data);
540       Goto(&template_loop);
541     }
542 
543     BIND(&template_loop);
544     {
545       // This loop compares the template to the expected {signature},
546       // following the chain of parent templates until it hits the
547       // end, in which case we continue with the next holder (the
548       // hidden prototype) if there's any.
549       TNode<HeapObject> current = var_template.value();
550       GotoIf(TaggedEqual(current, signature), &holder_found);
551 
552       GotoIfNot(IsFunctionTemplateInfoMap(LoadMap(current)), &holder_next);
553 
554       TNode<HeapObject> current_rare = LoadObjectField<HeapObject>(
555           current, FunctionTemplateInfo::kRareDataOffset);
556       GotoIf(IsUndefined(current_rare), &holder_next);
557       var_template = LoadObjectField<HeapObject>(
558           current_rare, FunctionTemplateRareData::kParentTemplateOffset);
559       Goto(&template_loop);
560     }
561 
562     BIND(&holder_next);
563     {
564       // Continue with the hidden prototype of the {holder} if it is a
565       // JSGlobalProxy (the hidden prototype can either be null or a
566       // JSObject in that case), or throw an illegal invocation exception,
567       // since the receiver did not pass the {signature} check.
568       TNode<Map> holder_map = LoadMap(holder);
569       var_holder = LoadMapPrototype(holder_map);
570       GotoIf(IsJSGlobalProxyMap(holder_map), &holder_loop);
571       ThrowTypeError(context, MessageTemplate::kIllegalInvocation);
572     }
573   }
574 
575   BIND(&holder_found);
576   return CAST(var_holder.value());
577 }
578 
579 // This calls an API callback by passing a {FunctionTemplateInfo},
580 // does appropriate access and compatible receiver checks.
CallFunctionTemplate(CallFunctionTemplateMode mode,TNode<FunctionTemplateInfo> function_template_info,TNode<IntPtrT> argc,TNode<Context> context)581 void CallOrConstructBuiltinsAssembler::CallFunctionTemplate(
582     CallFunctionTemplateMode mode,
583     TNode<FunctionTemplateInfo> function_template_info, TNode<IntPtrT> argc,
584     TNode<Context> context) {
585   CodeStubArguments args(this, argc);
586   Label throw_illegal_invocation(this, Label::kDeferred);
587 
588   // For API callbacks the receiver is always a JSReceiver (since
589   // they are treated like sloppy mode functions). We might need
590   // to perform access checks in the current {context}, depending
591   // on whether the "needs access check" bit is set on the receiver
592   // _and_ the {function_template_info} doesn't have the "accepts
593   // any receiver" bit set.
594   TNode<JSReceiver> receiver = CAST(args.GetReceiver());
595   if (mode == CallFunctionTemplateMode::kCheckAccess ||
596       mode == CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver) {
597     TNode<Map> receiver_map = LoadMap(receiver);
598     Label receiver_needs_access_check(this, Label::kDeferred),
599         receiver_done(this);
600     GotoIfNot(IsSetWord32<Map::Bits1::IsAccessCheckNeededBit>(
601                   LoadMapBitField(receiver_map)),
602               &receiver_done);
603     TNode<IntPtrT> function_template_info_flags = LoadAndUntagObjectField(
604         function_template_info, FunctionTemplateInfo::kFlagOffset);
605     Branch(IsSetWord(function_template_info_flags,
606                      1 << FunctionTemplateInfo::AcceptAnyReceiverBit::kShift),
607            &receiver_done, &receiver_needs_access_check);
608 
609     BIND(&receiver_needs_access_check);
610     {
611       CallRuntime(Runtime::kAccessCheck, context, receiver);
612       Goto(&receiver_done);
613     }
614 
615     BIND(&receiver_done);
616   }
617 
618   // Figure out the API holder for the {receiver} depending on the
619   // {mode} and the signature on the {function_template_info}.
620   TNode<JSReceiver> holder;
621   if (mode == CallFunctionTemplateMode::kCheckAccess) {
622     // We did the access check (including the ToObject) above, so
623     // {receiver} is a JSReceiver at this point, and we don't need
624     // to perform any "compatible receiver check", so {holder} is
625     // actually the {receiver}.
626     holder = receiver;
627   } else {
628     // If the {function_template_info} doesn't specify any signature, we
629     // just use the receiver as the holder for the API callback, otherwise
630     // we need to look for a compatible holder in the receiver's hidden
631     // prototype chain.
632     TNode<HeapObject> signature = LoadObjectField<HeapObject>(
633         function_template_info, FunctionTemplateInfo::kSignatureOffset);
634     holder = Select<JSReceiver>(
635         IsUndefined(signature),  // --
636         [&]() { return receiver; },
637         [&]() { return GetCompatibleReceiver(receiver, signature, context); });
638   }
639 
640   // Perform the actual API callback invocation via CallApiCallback.
641   TNode<CallHandlerInfo> call_handler_info = LoadObjectField<CallHandlerInfo>(
642       function_template_info, FunctionTemplateInfo::kCallCodeOffset);
643   TNode<Foreign> foreign = LoadObjectField<Foreign>(
644       call_handler_info, CallHandlerInfo::kJsCallbackOffset);
645   TNode<RawPtrT> callback = LoadForeignForeignAddressPtr(foreign);
646   TNode<Object> call_data =
647       LoadObjectField<Object>(call_handler_info, CallHandlerInfo::kDataOffset);
648   TailCallStub(CodeFactory::CallApiCallback(isolate()), context, callback, argc,
649                call_data, holder);
650 }
651 
TF_BUILTIN(CallFunctionTemplate_CheckAccess,CallOrConstructBuiltinsAssembler)652 TF_BUILTIN(CallFunctionTemplate_CheckAccess, CallOrConstructBuiltinsAssembler) {
653   auto context = Parameter<Context>(Descriptor::kContext);
654   auto function_template_info = UncheckedParameter<FunctionTemplateInfo>(
655       Descriptor::kFunctionTemplateInfo);
656   auto argc = UncheckedParameter<IntPtrT>(Descriptor::kArgumentsCount);
657   CallFunctionTemplate(CallFunctionTemplateMode::kCheckAccess,
658                        function_template_info, argc, context);
659 }
660 
TF_BUILTIN(CallFunctionTemplate_CheckCompatibleReceiver,CallOrConstructBuiltinsAssembler)661 TF_BUILTIN(CallFunctionTemplate_CheckCompatibleReceiver,
662            CallOrConstructBuiltinsAssembler) {
663   auto context = Parameter<Context>(Descriptor::kContext);
664   auto function_template_info = UncheckedParameter<FunctionTemplateInfo>(
665       Descriptor::kFunctionTemplateInfo);
666   auto argc = UncheckedParameter<IntPtrT>(Descriptor::kArgumentsCount);
667   CallFunctionTemplate(CallFunctionTemplateMode::kCheckCompatibleReceiver,
668                        function_template_info, argc, context);
669 }
670 
TF_BUILTIN(CallFunctionTemplate_CheckAccessAndCompatibleReceiver,CallOrConstructBuiltinsAssembler)671 TF_BUILTIN(CallFunctionTemplate_CheckAccessAndCompatibleReceiver,
672            CallOrConstructBuiltinsAssembler) {
673   auto context = Parameter<Context>(Descriptor::kContext);
674   auto function_template_info = UncheckedParameter<FunctionTemplateInfo>(
675       Descriptor::kFunctionTemplateInfo);
676   auto argc = UncheckedParameter<IntPtrT>(Descriptor::kArgumentsCount);
677   CallFunctionTemplate(
678       CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver,
679       function_template_info, argc, context);
680 }
681 
682 }  // namespace internal
683 }  // namespace v8
684