v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
builtins-call-gen.cc
Go to the documentation of this file.
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
6
7#include <optional>
8
12#include "src/common/globals.h"
19
20namespace v8 {
21namespace internal {
22
24
25void Builtins::Generate_CallFunction_ReceiverIsNullOrUndefined(
26 MacroAssembler* masm) {
28}
29
30void Builtins::Generate_CallFunction_ReceiverIsNotNullOrUndefined(
31 MacroAssembler* masm) {
33}
34
35void Builtins::Generate_CallFunction_ReceiverIsAny(MacroAssembler* masm) {
37}
38
39void Builtins::Generate_CallBoundFunction(MacroAssembler* masm) {
41}
42
43void Builtins::Generate_Call_ReceiverIsNullOrUndefined(MacroAssembler* masm) {
45}
46
47void Builtins::Generate_Call_ReceiverIsNotNullOrUndefined(
48 MacroAssembler* masm) {
50}
51
52void Builtins::Generate_Call_ReceiverIsAny(MacroAssembler* masm) {
54}
55
56void Builtins::Generate_CallVarargs(MacroAssembler* masm) {
58}
59
60void Builtins::Generate_CallForwardVarargs(MacroAssembler* masm) {
63}
64
65void Builtins::Generate_CallFunctionForwardVarargs(MacroAssembler* masm) {
68}
69
70void Builtins::Generate_CallApiCallbackGeneric(MacroAssembler* masm) {
72}
73
74void Builtins::Generate_CallApiCallbackOptimizedNoProfiling(
75 MacroAssembler* masm) {
78}
79
80void Builtins::Generate_CallApiCallbackOptimized(MacroAssembler* masm) {
82}
83
84// TODO(cbruni): Try reusing code between builtin versions to avoid binary
85// overhead.
86TF_BUILTIN(Call_ReceiverIsNullOrUndefined_Baseline_Compact,
88 auto receiver = UndefinedConstant();
89 CallReceiver<Descriptor>(Builtin::kCall_ReceiverIsNullOrUndefined, receiver);
90}
91
92TF_BUILTIN(Call_ReceiverIsNullOrUndefined_Baseline,
94 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
95 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
96 auto receiver = UndefinedConstant();
97 CallReceiver<Descriptor>(Builtin::kCall_ReceiverIsNullOrUndefined, argc, slot,
98 receiver);
99}
100
101TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_Baseline_Compact,
103 CallReceiver<Descriptor>(Builtin::kCall_ReceiverIsNotNullOrUndefined);
104}
105
106TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_Baseline,
108 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
109 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
110 CallReceiver<Descriptor>(Builtin::kCall_ReceiverIsNotNullOrUndefined, argc,
111 slot);
112}
113
114TF_BUILTIN(Call_ReceiverIsAny_Baseline_Compact,
116 CallReceiver<Descriptor>(Builtin::kCall_ReceiverIsAny);
117}
118
119TF_BUILTIN(Call_ReceiverIsAny_Baseline, CallOrConstructBuiltinsAssembler) {
120 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
121 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
122 CallReceiver<Descriptor>(Builtin::kCall_ReceiverIsAny, argc, slot);
123}
124
125TF_BUILTIN(Call_ReceiverIsNullOrUndefined_WithFeedback,
127 auto target = Parameter<JSAny>(Descriptor::kFunction);
128 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
129 auto context = Parameter<Context>(Descriptor::kContext);
130 auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
131 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
132 auto receiver = Parameter<JSAny>(Descriptor::kReceiver);
133 CollectCallFeedback(
134 target, [=] { return receiver; }, context, feedback_vector, slot);
135 TailCallBuiltin(Builtin::kCall_ReceiverIsNullOrUndefined, context, target,
136 argc);
137}
138
139TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_WithFeedback,
141 auto target = Parameter<JSAny>(Descriptor::kFunction);
142 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
143 auto context = Parameter<Context>(Descriptor::kContext);
144 auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
145 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
146 auto receiver = Parameter<JSAny>(Descriptor::kReceiver);
147 CollectCallFeedback(
148 target, [=] { return receiver; }, context, feedback_vector, slot);
149 TailCallBuiltin(Builtin::kCall_ReceiverIsNotNullOrUndefined, context, target,
150 argc);
151}
152
153TF_BUILTIN(Call_ReceiverIsAny_WithFeedback, CallOrConstructBuiltinsAssembler) {
154 auto target = Parameter<JSAny>(Descriptor::kFunction);
155 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
156 auto context = Parameter<Context>(Descriptor::kContext);
157 auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
158 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
159 auto receiver = Parameter<JSAny>(Descriptor::kReceiver);
160 CollectCallFeedback(
161 target, [=] { return receiver; }, context, feedback_vector, slot);
162 TailCallBuiltin(Builtin::kCall_ReceiverIsAny, context, target, argc);
163}
164
166 TNode<JSAny> target, std::optional<TNode<Object>> new_target,
167 TNode<Object> arguments_list, TNode<Context> context) {
168 Label if_done(this), if_arguments(this), if_array(this),
169 if_holey_array(this, Label::kDeferred),
170 if_runtime(this, Label::kDeferred);
171
172 // Perform appropriate checks on {target} (and {new_target} first).
173 if (!new_target) {
174 // Check that {target} is Callable.
175 Label if_target_callable(this),
176 if_target_not_callable(this, Label::kDeferred);
177 GotoIf(TaggedIsSmi(target), &if_target_not_callable);
178 Branch(IsCallable(CAST(target)), &if_target_callable,
179 &if_target_not_callable);
180 BIND(&if_target_not_callable);
181 {
182 CallRuntime(Runtime::kThrowApplyNonFunction, context, target);
183 Unreachable();
184 }
185 BIND(&if_target_callable);
186 } else {
187 // Check that {target} is a Constructor.
188 Label if_target_constructor(this),
189 if_target_not_constructor(this, Label::kDeferred);
190 GotoIf(TaggedIsSmi(target), &if_target_not_constructor);
191 Branch(IsConstructor(CAST(target)), &if_target_constructor,
192 &if_target_not_constructor);
193 BIND(&if_target_not_constructor);
194 {
195 CallRuntime(Runtime::kThrowNotConstructor, context, target);
196 Unreachable();
197 }
198 BIND(&if_target_constructor);
199
200 // Check that {new_target} is a Constructor.
201 Label if_new_target_constructor(this),
202 if_new_target_not_constructor(this, Label::kDeferred);
203 GotoIf(TaggedIsSmi(*new_target), &if_new_target_not_constructor);
204 Branch(IsConstructor(CAST(*new_target)), &if_new_target_constructor,
205 &if_new_target_not_constructor);
206 BIND(&if_new_target_not_constructor);
207 {
208 CallRuntime(Runtime::kThrowNotConstructor, context, *new_target);
209 Unreachable();
210 }
211 BIND(&if_new_target_constructor);
212 }
213
214 GotoIf(TaggedIsSmi(arguments_list), &if_runtime);
215
216 TNode<Map> arguments_list_map = LoadMap(CAST(arguments_list));
218
219 // Check if {arguments_list} is an (unmodified) arguments object.
220 TNode<Map> sloppy_arguments_map = CAST(
221 LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX));
222 GotoIf(TaggedEqual(arguments_list_map, sloppy_arguments_map), &if_arguments);
223 TNode<Map> strict_arguments_map = CAST(
224 LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX));
225 GotoIf(TaggedEqual(arguments_list_map, strict_arguments_map), &if_arguments);
226
227 // Check if {arguments_list} is a fast JSArray.
228 Branch(IsJSArrayMap(arguments_list_map), &if_array, &if_runtime);
229
230 TVARIABLE(FixedArrayBase, var_elements);
231 TVARIABLE(Int32T, var_length);
232 BIND(&if_array);
233 {
234 TNode<Int32T> kind = LoadMapElementsKind(arguments_list_map);
235 GotoIf(
237 &if_runtime);
238
239 TNode<JSObject> js_object = CAST(arguments_list);
240 // Try to extract the elements from a JSArray object.
241 var_elements = LoadElements(js_object);
242 var_length =
243 LoadAndUntagToWord32ObjectField(js_object, JSArray::kLengthOffset);
244
245 // Holey arrays and double backing stores need special treatment.
246 static_assert(PACKED_SMI_ELEMENTS == 0);
247 static_assert(HOLEY_SMI_ELEMENTS == 1);
248 static_assert(PACKED_ELEMENTS == 2);
249 static_assert(HOLEY_ELEMENTS == 3);
250 static_assert(PACKED_DOUBLE_ELEMENTS == 4);
251 static_assert(HOLEY_DOUBLE_ELEMENTS == 5);
253
254 Branch(Word32And(kind, Int32Constant(1)), &if_holey_array, &if_done);
255 }
256
257 BIND(&if_holey_array);
258 {
259 // For holey JSArrays we need to check that the array prototype chain
260 // protector is intact and our prototype is the Array.prototype actually.
261 GotoIfNot(IsPrototypeInitialArrayPrototype(context, arguments_list_map),
262 &if_runtime);
263 Branch(IsNoElementsProtectorCellInvalid(), &if_runtime, &if_done);
264 }
265
266 BIND(&if_arguments);
267 {
268 TNode<JSArgumentsObject> js_arguments = CAST(arguments_list);
269 // Try to extract the elements from a JSArgumentsObject with standard map.
270 TNode<Object> length = LoadJSArgumentsObjectLength(context, js_arguments);
271 TNode<FixedArrayBase> elements = LoadElements(js_arguments);
272 TNode<Smi> elements_length = LoadFixedArrayBaseLength(elements);
273 GotoIfNot(TaggedEqual(length, elements_length), &if_runtime);
274 var_elements = elements;
275 var_length = SmiToInt32(CAST(length));
276 Goto(&if_done);
277 }
278
279 BIND(&if_runtime);
280 {
281 // Ask the runtime to create the list (actually a FixedArray).
282 var_elements = CAST(CallRuntime(Runtime::kCreateListFromArrayLike, context,
283 arguments_list));
284 var_length = LoadAndUntagToWord32ObjectField(var_elements.value(),
285 offsetof(FixedArray, length_));
286 Goto(&if_done);
287 }
288
289 // Tail call to the appropriate builtin (depending on whether we have
290 // a {new_target} passed).
291 BIND(&if_done);
292 {
293 Label if_not_double(this), if_double(this);
294 TNode<Int32T> args_count =
295 Int32Constant(i::JSParameterCount(0)); // args already on the stack
296
297 TNode<Int32T> length = var_length.value();
298 {
299 Label normalize_done(this);
300 CSA_DCHECK(this, Int32LessThanOrEqual(
302 GotoIfNot(Word32Equal(length, Int32Constant(0)), &normalize_done);
303 // Make sure we don't accidentally pass along the
304 // empty_fixed_double_array since the tailed-called stubs cannot handle
305 // the normalization yet.
306 var_elements = EmptyFixedArrayConstant();
307 Goto(&normalize_done);
308
309 BIND(&normalize_done);
310 }
311
312 TNode<FixedArrayBase> elements = var_elements.value();
313 Branch(IsFixedDoubleArray(elements), &if_double, &if_not_double);
314
315 BIND(&if_not_double);
316 {
317 if (!new_target) {
318 TailCallBuiltin(Builtin::kCallVarargs, context, target, args_count,
319 length, elements);
320 } else {
321 TailCallBuiltin(Builtin::kConstructVarargs, context, target,
322 *new_target, args_count, length, elements);
323 }
324 }
325
326 BIND(&if_double);
327 {
328 // Kind is hardcoded here because CreateListFromArrayLike will only
329 // produce holey double arrays.
330 CallOrConstructDoubleVarargs(target, new_target, CAST(elements), length,
331 args_count, context,
333 }
334 }
335}
336
337// Takes a FixedArray of doubles and creates a new FixedArray with those doubles
338// boxed as HeapNumbers, then tail calls CallVarargs/ConstructVarargs depending
339// on whether {new_target} was passed.
341 TNode<JSAny> target, std::optional<TNode<Object>> new_target,
342 TNode<FixedDoubleArray> elements, TNode<Int32T> length,
343 TNode<Int32T> args_count, TNode<Context> context, TNode<Int32T> kind) {
344 const ElementsKind new_kind = PACKED_ELEMENTS;
345 const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
346 CSA_DCHECK(this, Int32LessThanOrEqual(length,
348 TNode<IntPtrT> intptr_length = ChangeInt32ToIntPtr(length);
349 CSA_DCHECK(this, WordNotEqual(intptr_length, IntPtrConstant(0)));
350
351 // Allocate a new FixedArray of Objects.
352 TNode<FixedArray> new_elements =
353 CAST(AllocateFixedArray(new_kind, intptr_length));
354 // CopyFixedArrayElements does not distinguish between holey and packed for
355 // its first argument, so we don't need to dispatch on {kind} here.
357 new_elements, intptr_length, intptr_length,
358 barrier_mode);
359 if (!new_target) {
360 TailCallBuiltin(Builtin::kCallVarargs, context, target, args_count, length,
361 new_elements);
362 } else {
363 TailCallBuiltin(Builtin::kConstructVarargs, context, target, *new_target,
364 args_count, length, new_elements);
365 }
366}
367
369 TNode<JSAny> target, std::optional<TNode<Object>> new_target,
370 TNode<JSAny> spread, TNode<Int32T> args_count, TNode<Context> context) {
371 Label if_smiorobject(this), if_double(this),
372 if_generic(this, Label::kDeferred);
373
374 TVARIABLE(JSArray, var_js_array);
375 TVARIABLE(FixedArrayBase, var_elements);
376 TVARIABLE(Int32T, var_elements_kind);
377
378 GotoIf(TaggedIsSmi(spread), &if_generic);
379 TNode<Map> spread_map = LoadMap(CAST(spread));
380 GotoIfNot(IsJSArrayMap(spread_map), &if_generic);
381 TNode<JSArray> spread_array = CAST(spread);
382
383 // Check that we have the original Array.prototype.
384 GotoIfNot(IsPrototypeInitialArrayPrototype(context, spread_map), &if_generic);
385
386 // Check that there are no elements on the Array.prototype chain.
388
389 // Check that the Array.prototype hasn't been modified in a way that would
390 // affect iteration.
391 TNode<PropertyCell> protector_cell = ArrayIteratorProtectorConstant();
392 GotoIf(
393 TaggedEqual(LoadObjectField(protector_cell, PropertyCell::kValueOffset),
395 &if_generic);
396 {
397 // The fast-path accesses the {spread} elements directly.
398 TNode<Int32T> spread_kind = LoadMapElementsKind(spread_map);
399 var_js_array = spread_array;
400 var_elements_kind = spread_kind;
401 var_elements = LoadElements(spread_array);
402
403 // Check elements kind of {spread}.
405 &if_smiorobject);
407 &if_double);
410 &if_smiorobject, &if_generic);
411 }
412
413 BIND(&if_generic);
414 {
415 Label if_iterator_fn_not_callable(this, Label::kDeferred),
416 if_iterator_is_null_or_undefined(this, Label::kDeferred),
417 throw_spread_error(this, Label::kDeferred);
418 TVARIABLE(Smi, message_id);
419
420 GotoIf(IsNullOrUndefined(spread), &if_iterator_is_null_or_undefined);
421
422 TNode<Object> iterator_fn =
423 GetProperty(context, spread, IteratorSymbolConstant());
424 GotoIfNot(TaggedIsCallable(iterator_fn), &if_iterator_fn_not_callable);
425 TNode<JSArray> list =
426 CAST(CallBuiltin(Builtin::kIterableToListMayPreserveHoles, context,
427 spread, iterator_fn));
428
429 var_js_array = list;
430 var_elements = LoadElements(list);
431 var_elements_kind = LoadElementsKind(list);
432 Branch(Int32LessThan(var_elements_kind.value(),
434 &if_smiorobject, &if_double);
435
436 BIND(&if_iterator_fn_not_callable);
437 message_id = SmiConstant(
438 static_cast<int>(MessageTemplate::kSpreadIteratorSymbolNonCallable)),
439 Goto(&throw_spread_error);
440
441 BIND(&if_iterator_is_null_or_undefined);
442 message_id = SmiConstant(
443 static_cast<int>(MessageTemplate::kNotIterableNoSymbolLoad));
444 Goto(&throw_spread_error);
445
446 BIND(&throw_spread_error);
447 CallRuntime(Runtime::kThrowSpreadArgError, context, message_id.value(),
448 spread);
449 Unreachable();
450 }
451
452 BIND(&if_smiorobject);
453 {
455 var_js_array.value(), JSArray::kLengthOffset);
456 TNode<FixedArrayBase> elements = var_elements.value();
457 CSA_DCHECK(this, Int32LessThanOrEqual(
459
460 if (!new_target) {
461 TailCallBuiltin(Builtin::kCallVarargs, context, target, args_count,
462 length, elements);
463 } else {
464 TailCallBuiltin(Builtin::kConstructVarargs, context, target, *new_target,
465 args_count, length, elements);
466 }
467 }
468
469 BIND(&if_double);
470 {
472 var_js_array.value(), JSArray::kLengthOffset);
473 GotoIf(Word32Equal(length, Int32Constant(0)), &if_smiorobject);
474 CallOrConstructDoubleVarargs(target, new_target, CAST(var_elements.value()),
475 length, args_count, context,
476 var_elements_kind.value());
477 }
478}
479
480template <class Descriptor>
482 Builtin id, std::optional<TNode<JSAny>> receiver) {
483 static_assert(
484 std::is_same_v<Descriptor, CallTrampoline_Baseline_CompactDescriptor>,
485 "Incompatible Descriptor");
486 auto bitfield = UncheckedParameter<Word32T>(Descriptor::kBitField);
487 TNode<Int32T> argc =
490 bitfield));
493 bitfield));
494 CallReceiver<Descriptor>(id, argc, slot, receiver);
495}
496
497template <class Descriptor>
500 std::optional<TNode<JSAny>> maybe_receiver) {
501 auto target = Parameter<JSAny>(Descriptor::kFunction);
502 auto context = LoadContextFromBaseline();
503 auto feedback_vector = LoadFeedbackVectorFromBaseline();
504 LazyNode<JSAny> receiver = [=, this] {
505 if (maybe_receiver) {
506 return *maybe_receiver;
507 } else {
508 CodeStubArguments args(this, argc);
509 return args.GetReceiver();
510 }
511 };
512
513 CollectCallFeedback(target, receiver, context, feedback_vector, slot);
514 TailCallBuiltin(id, context, target, argc);
515}
516
518 auto target = Parameter<JSAny>(Descriptor::kTarget);
519 std::optional<TNode<Object>> new_target = std::nullopt;
520 auto arguments_list = Parameter<Object>(Descriptor::kArgumentsList);
521 auto context = Parameter<Context>(Descriptor::kContext);
522 CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
523}
524
525// TODO(ishell): not used, consider removing.
526TF_BUILTIN(CallWithArrayLike_WithFeedback, CallOrConstructBuiltinsAssembler) {
527 auto target = Parameter<JSAny>(Descriptor::kTarget);
528 std::optional<TNode<Object>> new_target = std::nullopt;
529 auto arguments_list = Parameter<Object>(Descriptor::kArgumentsList);
530 auto context = Parameter<Context>(Descriptor::kContext);
531 auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
532 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
533 auto receiver = Parameter<JSAny>(Descriptor::kReceiver);
534 CollectCallFeedback(
535 target, [=] { return receiver; }, context, feedback_vector, slot);
536 CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
537}
538
540 auto target = Parameter<JSAny>(Descriptor::kTarget);
541 std::optional<TNode<Object>> new_target = std::nullopt;
542 auto spread = Parameter<JSAny>(Descriptor::kSpread);
543 auto args_count = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
544 auto context = Parameter<Context>(Descriptor::kContext);
545 CallOrConstructWithSpread(target, new_target, spread, args_count, context);
546}
547
549 auto target = Parameter<JSAny>(Descriptor::kTarget);
550 std::optional<TNode<Object>> new_target = std::nullopt;
551 auto spread = Parameter<JSAny>(Descriptor::kSpread);
552 auto args_count = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
553 auto context = LoadContextFromBaseline();
554 auto feedback_vector = LoadFeedbackVectorFromBaseline();
555 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
556 CodeStubArguments args(this, args_count);
557 CollectCallFeedback(
558 target, [=] { return args.GetReceiver(); }, context, feedback_vector,
559 slot);
560 CallOrConstructWithSpread(target, new_target, spread, args_count, context);
561}
562
563TF_BUILTIN(CallWithSpread_WithFeedback, CallOrConstructBuiltinsAssembler) {
564 auto target = Parameter<JSAny>(Descriptor::kTarget);
565 std::optional<TNode<Object>> new_target = std::nullopt;
566 auto spread = Parameter<JSAny>(Descriptor::kSpread);
567 auto args_count = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
568 auto context = Parameter<Context>(Descriptor::kContext);
569 auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
570 auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
571 auto receiver = Parameter<JSAny>(Descriptor::kReceiver);
572 CollectCallFeedback(
573 target, [=] { return receiver; }, context, feedback_vector, slot);
574 CallOrConstructWithSpread(target, new_target, spread, args_count, context);
575}
576
579 TNode<Context> context) {
580 // Walk up the hidden prototype chain to find the compatible holder
581 // for the {signature}, starting with the {receiver} itself.
582 //
583 // Be careful, these loops are hand-tuned for (close to) ideal CSA
584 // code generation. Especially the sharing of the {var_template}
585 // below is intentional (even though it reads a bit funny in the
586 // first loop).
587 TVARIABLE(HeapObject, var_holder, receiver);
588 Label holder_loop(this, &var_holder), holder_found(this, &var_holder),
589 holder_next(this, Label::kDeferred);
590 Goto(&holder_loop);
591 BIND(&holder_loop);
592 {
593 // Find the template to compare against the {signature}. We don't
594 // bother checking that the template is a FunctionTemplateInfo here,
595 // but instead do that as part of the template loop below. The only
596 // thing we care about is that the template is actually a HeapObject.
597 TNode<HeapObject> holder = var_holder.value();
598 TVARIABLE(HeapObject, var_template, LoadMap(holder));
599 Label template_map_loop(this, &var_template),
600 template_loop(this, &var_template),
601 template_from_closure(this, &var_template);
602 Goto(&template_map_loop);
603 BIND(&template_map_loop);
604 {
605 // Load the constructor field from the current map (in the
606 // {var_template} variable), and see if that is a HeapObject.
607 // If it's a Smi then it is non-instance prototype on some
608 // initial map, which cannot be the case for API instances.
609 TNode<Object> constructor =
610 LoadObjectField(var_template.value(),
611 Map::kConstructorOrBackPointerOrNativeContextOffset);
612 GotoIf(TaggedIsSmi(constructor), &holder_next);
613
614 // Now there are three cases for {constructor} that we care
615 // about here:
616 //
617 // 1. {constructor} is a JSFunction, and we can load the template
618 // from its SharedFunctionInfo::function_data field (which
619 // may not actually be a FunctionTemplateInfo).
620 // 2. {constructor} is a Map, in which case it's not a constructor
621 // but a back-pointer and we follow that.
622 // 3. {constructor} is a FunctionTemplateInfo (or some other
623 // HeapObject), in which case we can directly use that for
624 // the template loop below (non-FunctionTemplateInfo objects
625 // will be ruled out there).
626 //
627 var_template = CAST(constructor);
628 TNode<Uint16T> template_type = LoadInstanceType(var_template.value());
629 GotoIf(IsJSFunctionInstanceType(template_type), &template_from_closure);
630 Branch(InstanceTypeEqual(template_type, MAP_TYPE), &template_map_loop,
631 &template_loop);
632 }
633
634 BIND(&template_from_closure);
635 {
636 // The first case from above, where we load the template from the
637 // SharedFunctionInfo of the closure. We only check that the
638 // SharedFunctionInfo::function_data is a HeapObject and blindly
639 // use that as a template, since a non-FunctionTemplateInfo objects
640 // will be ruled out automatically by the template loop below.
641 TNode<SharedFunctionInfo> template_shared =
643 var_template.value(), JSFunction::kSharedFunctionInfoOffset);
644 TNode<Object> template_data =
645 LoadSharedFunctionInfoUntrustedFunctionData(template_shared);
646 GotoIf(TaggedIsSmi(template_data), &holder_next);
647 var_template = CAST(template_data);
648 Goto(&template_loop);
649 }
650
651 BIND(&template_loop);
652 {
653 // This loop compares the template to the expected {signature},
654 // following the chain of parent templates until it hits the
655 // end, in which case we continue with the next holder (the
656 // hidden prototype) if there's any.
657 TNode<HeapObject> current = var_template.value();
658 GotoIf(TaggedEqual(current, signature), &holder_found);
659
660 GotoIfNot(IsFunctionTemplateInfoMap(LoadMap(current)), &holder_next);
661
663 current, FunctionTemplateInfo::kRareDataOffset);
664 GotoIf(IsUndefined(current_rare), &holder_next);
665 var_template = LoadObjectField<HeapObject>(
666 current_rare, FunctionTemplateRareData::kParentTemplateOffset);
667 Goto(&template_loop);
668 }
669
670 BIND(&holder_next);
671 {
672 // Continue with the hidden prototype of the {holder} if it is a
673 // JSGlobalProxy (the hidden prototype can either be null or a
674 // JSObject in that case), or throw an illegal invocation exception,
675 // since the receiver did not pass the {signature} check.
676 TNode<Map> holder_map = LoadMap(holder);
677 var_holder = LoadMapPrototype(holder_map);
678 GotoIf(IsJSGlobalProxyMap(holder_map), &holder_loop);
679 ThrowTypeError(context, MessageTemplate::kIllegalInvocation);
680 }
681 }
682
683 BIND(&holder_found);
684 return CAST(var_holder.value());
685}
686
687// static
700
701// This calls an API callback by passing a {FunctionTemplateInfo},
702// does appropriate access and compatible receiver checks.
705 TNode<FunctionTemplateInfo> function_template_info, TNode<Int32T> argc,
706 TNode<Context> context, TNode<Object> topmost_script_having_context) {
707 CodeStubArguments args(this, argc);
708 Label throw_illegal_invocation(this, Label::kDeferred);
709
710 // For API callbacks the receiver is always a JSReceiver (since
711 // they are treated like sloppy mode functions). We might need
712 // to perform access checks in the current {context}, depending
713 // on whether the "needs access check" bit is set on the receiver
714 // _and_ the {function_template_info} doesn't have the "accepts
715 // any receiver" bit set.
716 TNode<JSReceiver> receiver = CAST(args.GetReceiver());
717 if (IsAccessCheckRequired(mode)) {
718 TNode<Map> receiver_map = LoadMap(receiver);
719 Label receiver_needs_access_check(this, Label::kDeferred),
720 receiver_done(this);
722 LoadMapBitField(receiver_map)),
723 &receiver_done);
724 TNode<Uint32T> function_template_info_flags = LoadObjectField<Uint32T>(
725 function_template_info, FunctionTemplateInfo::kFlagOffset);
727 function_template_info_flags),
728 &receiver_done, &receiver_needs_access_check);
729
730 BIND(&receiver_needs_access_check);
731 {
732 CallRuntime(Runtime::kAccessCheck, context, receiver);
733 Goto(&receiver_done);
734 }
735
736 BIND(&receiver_done);
737 }
738
739 // Figure out the API holder for the {receiver} depending on the
740 // {mode} and the signature on the {function_template_info}.
741 TNode<JSReceiver> holder;
742 switch (mode) {
744 // We did the access check (including the ToObject) above, so
745 // {receiver} is a JSReceiver at this point, and we don't need
746 // to perform any "compatible receiver check", so {holder} is
747 // actually the {receiver}.
748 holder = receiver;
749 break;
750
753 // The {function_template_info} has a signature, so look for a compatible
754 // holder in the receiver's hidden prototype chain.
756 function_template_info, FunctionTemplateInfo::kSignatureOffset);
757 CSA_DCHECK(this, Word32BinaryNot(IsUndefined(signature)));
758 // TODO(ishell, http://crbug.com/326505377): rename to
759 // CheckCompatibleReceiverOrThrow().
760 holder = GetCompatibleReceiver(receiver, signature, context);
761 break;
762 }
764 // If the {function_template_info} doesn't specify any signature, we
765 // just use the receiver as the holder for the API callback, otherwise
766 // we need to look for a compatible holder in the receiver's hidden
767 // prototype chain.
769 function_template_info, FunctionTemplateInfo::kSignatureOffset);
770 holder = Select<JSReceiver>(
771 IsUndefined(signature), // --
772 [&]() { return receiver; },
773 [&]() {
774 return GetCompatibleReceiver(receiver, signature, context);
775 });
776 break;
777 }
778 }
779
780 TNode<Object> callback_data = LoadObjectField(
781 function_template_info, FunctionTemplateInfo::kCallbackDataOffset);
782 // If the function doesn't have an associated C++ code to execute, just
783 // return the receiver as would an empty function do (see
784 // HandleApiCallHelper).
785 {
786 Label if_continue(this);
787 GotoIfNot(IsTheHole(callback_data), &if_continue);
788 args.PopAndReturn(receiver);
789
790 Bind(&if_continue);
791 }
792
793 // Perform the actual API callback invocation via CallApiCallback.
794 switch (mode) {
796 TailCallBuiltin(Builtin::kCallApiCallbackGeneric, context,
797 TruncateIntPtrToInt32(args.GetLengthWithoutReceiver()),
798 topmost_script_having_context, function_template_info);
799 break;
800
804 TNode<RawPtrT> callback_address =
805 LoadFunctionTemplateInfoJsCallbackPtr(function_template_info);
806 TailCallBuiltin(Builtin::kCallApiCallbackOptimized, context,
807 callback_address,
808 TruncateIntPtrToInt32(args.GetLengthWithoutReceiver()),
809 function_template_info);
810 break;
811 }
812 }
813}
814
815TF_BUILTIN(CallFunctionTemplate_Generic, CallOrConstructBuiltinsAssembler) {
816 auto context = Parameter<Context>(Descriptor::kContext);
817 auto function_template_info = UncheckedParameter<FunctionTemplateInfo>(
818 Descriptor::kFunctionTemplateInfo);
819 auto argc = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
820 // This builtin is called from IC where the topmost script-having context is
821 // known precisely and from Builtin::kHandleApiCallOrConstruct where the
822 // caller context is not guranteed to be known.
823 auto topmost_script_having_context =
824 Parameter<Object>(Descriptor::kTopmostScriptHavingContext);
825 CallFunctionTemplate(CallFunctionTemplateMode::kGeneric,
826 function_template_info, argc, context,
827 topmost_script_having_context);
828}
829
830TF_BUILTIN(CallFunctionTemplate_CheckAccess, CallOrConstructBuiltinsAssembler) {
831 auto context = Parameter<Context>(Descriptor::kContext);
832 auto function_template_info = UncheckedParameter<FunctionTemplateInfo>(
833 Descriptor::kFunctionTemplateInfo);
834 auto argc = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
835 // This builtin is called from optimized code where the topmost script-having
836 // context is always equal to the current context because we don't inline
837 // calls cross context.
838 auto topmost_script_having_context = context;
839 CallFunctionTemplate(CallFunctionTemplateMode::kCheckAccess,
840 function_template_info, argc, context,
841 topmost_script_having_context);
842}
843
844TF_BUILTIN(CallFunctionTemplate_CheckCompatibleReceiver,
846 auto context = Parameter<Context>(Descriptor::kContext);
847 auto function_template_info = UncheckedParameter<FunctionTemplateInfo>(
848 Descriptor::kFunctionTemplateInfo);
849 auto argc = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
850 // This builtin is called from optimized code where the topmost script-having
851 // context is always equal to the current context because we don't inline
852 // calls cross context.
853 auto topmost_script_having_context = context;
854 CallFunctionTemplate(CallFunctionTemplateMode::kCheckCompatibleReceiver,
855 function_template_info, argc, context,
856 topmost_script_having_context);
857}
858
859TF_BUILTIN(CallFunctionTemplate_CheckAccessAndCompatibleReceiver,
861 auto context = Parameter<Context>(Descriptor::kContext);
862 auto function_template_info = UncheckedParameter<FunctionTemplateInfo>(
863 Descriptor::kFunctionTemplateInfo);
864 auto argc = UncheckedParameter<Int32T>(Descriptor::kArgumentsCount);
865 // This builtin is called from optimized code where the topmost script-having
866 // context is always equal to the current context because we don't inline
867 // calls cross context.
868 auto topmost_script_having_context = context;
869 CallFunctionTemplate(
870 CallFunctionTemplateMode::kCheckAccessAndCompatibleReceiver,
871 function_template_info, argc, context, topmost_script_having_context);
872}
873
875 auto target = Parameter<JSAny>(Descriptor::kTarget);
876 auto new_target = Parameter<Object>(Descriptor::kNewTarget);
877 auto context = Parameter<Context>(Descriptor::kContext);
878 auto argc = UncheckedParameter<Int32T>(Descriptor::kActualArgumentsCount);
879#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
880 auto dispatch_handle =
881 UncheckedParameter<JSDispatchHandleT>(Descriptor::kDispatchHandle);
882#else
883 auto dispatch_handle = InvalidDispatchHandleConstant();
884#endif
885
886 Label if_call(this), if_construct(this);
887 Branch(IsUndefined(new_target), &if_call, &if_construct);
888
889 BIND(&if_call);
890 {
892 LoadJSFunctionSharedFunctionInfo(CAST(target));
893 TNode<FunctionTemplateInfo> function_template_info =
894 CAST(LoadSharedFunctionInfoUntrustedFunctionData(shared));
895
896 // The topmost script-having context is not guaranteed to be equal to
897 // current context at this point. For example, if target function was
898 // called via Function.prototype.call or other similar builtins, or if it
899 // was called directly from C++ via Execution::Call*(). So we pass
900 // kNoContext in order to ensure that Isolate::GetIncumbentContext()
901 // does the right thing (by taking a slow path).
902 TNode<Object> topmost_script_having_context = NoContextConstant();
903
904 // Tail call to the stub while leaving all the incoming JS arguments on
905 // the stack.
906 TailCallBuiltin(Builtin::kCallFunctionTemplate_Generic, context,
907 function_template_info, argc,
908 topmost_script_having_context);
909 }
910 BIND(&if_construct);
911 {
912 // Tail call to the stub while leaving all the incoming JS arguments on
913 // the stack.
914 TailCallJSBuiltin(Builtin::kHandleApiConstruct, context, target, new_target,
915 argc, dispatch_handle);
916 }
917}
918
920
921} // namespace internal
922} // namespace v8
#define BIND(label)
#define TVARIABLE(...)
#define CSA_DCHECK(csa,...)
#define TF_BUILTIN(Name, AssemblerBase)
Builtins::Kind kind
Definition builtins.cc:40
static void Generate_CallOrConstructForwardVarargs(MacroAssembler *masm, CallOrConstructMode mode, Builtin target_builtin)
static constexpr Builtin CallFunction(ConvertReceiverMode=ConvertReceiverMode::kAny)
static void Generate_Call(MacroAssembler *masm, ConvertReceiverMode mode)
static void Generate_CallFunction(MacroAssembler *masm, ConvertReceiverMode mode)
static void Generate_CallOrConstructVarargs(MacroAssembler *masm, Builtin target_builtin)
static void Generate_CallApiCallbackImpl(MacroAssembler *masm, CallApiCallbackMode mode)
static constexpr Builtin Call(ConvertReceiverMode=ConvertReceiverMode::kAny)
static void Generate_CallBoundFunctionImpl(MacroAssembler *masm)
void CallOrConstructDoubleVarargs(TNode< JSAny > target, std::optional< TNode< Object > > new_target, TNode< FixedDoubleArray > elements, TNode< Int32T > length, TNode< Int32T > args_count, TNode< Context > context, TNode< Int32T > kind)
void CallFunctionTemplate(CallFunctionTemplateMode mode, TNode< FunctionTemplateInfo > function_template_info, TNode< Int32T > argc, TNode< Context > context, TNode< Object > maybe_incumbent_context)
TNode< JSReceiver > GetCompatibleReceiver(TNode< JSReceiver > receiver, TNode< HeapObject > signature, TNode< Context > context)
static constexpr bool IsAccessCheckRequired(CallFunctionTemplateMode mode)
void CallReceiver(Builtin id, std::optional< TNode< JSAny > >=std::nullopt)
void CallOrConstructWithArrayLike(TNode< JSAny > target, std::optional< TNode< Object > > new_target, TNode< Object > arguments_list, TNode< Context > context)
void CallOrConstructWithSpread(TNode< JSAny > target, std::optional< TNode< Object > > new_target, TNode< JSAny > spread, TNode< Int32T > args_count, TNode< Context > context)
TNode< BoolT > IsNullOrUndefined(TNode< Object > object)
TNode< FixedArrayBase > AllocateFixedArray(ElementsKind kind, TNode< TIndex > capacity, AllocationFlags flags=AllocationFlag::kNone, std::optional< TNode< Map > > fixed_array_map=std::nullopt)
TNode< BoolT > IsConstructor(TNode< HeapObject > object)
TNode< Int32T > TruncateIntPtrToInt32(TNode< IntPtrT > value)
TNode< Smi > LoadFixedArrayBaseLength(TNode< FixedArrayBase > array)
TNode< BoolT > IsPrototypeInitialArrayPrototype(TNode< Context > context, TNode< Map > map)
TNode< BoolT > InstanceTypeEqual(TNode< Int32T > instance_type, int type)
void ThrowTypeError(TNode< Context > context, MessageTemplate message, char const *arg0=nullptr, char const *arg1=nullptr)
TNode< JSAny > GetProperty(TNode< Context > context, TNode< JSAny > receiver, Handle< Name > name)
TNode< BoolT > TaggedEqual(TNode< AnyTaggedT > a, TNode< AnyTaggedT > b)
TNode< FixedArrayBase > LoadElements(TNode< JSObject > object)
TNode< T > LoadObjectField(TNode< HeapObject > object, int offset)
TNode< BoolT > TaggedIsCallable(TNode< Object > object)
TNode< JSPrototype > LoadMapPrototype(TNode< Map > map)
TNode< BoolT > IsSetWord32(TNode< Word32T > word32)
void CopyFixedArrayElements(ElementsKind kind, TNode< FixedArrayBase > from_array, TNode< FixedArrayBase > to_array, TNode< TIndex > length, WriteBarrierMode barrier_mode=UPDATE_WRITE_BARRIER)
TNode< BoolT > IsJSGlobalProxyMap(TNode< Map > map)
TNode< BoolT > IsElementsKindLessThanOrEqual(TNode< Int32T > target_kind, ElementsKind reference_kind)
TNode< Uint32T > DecodeWord32(TNode< Word32T > word32)
TNode< Int32T > LoadElementsKind(TNode< HeapObject > object)
TNode< Int32T > LoadAndUntagToWord32ObjectField(TNode< HeapObject > object, int offset)
std::function< TNode< T >()> LazyNode
TNode< BoolT > IsElementsKindGreaterThan(TNode< Int32T > target_kind, ElementsKind reference_kind)
TNode< RawPtrT > LoadFunctionTemplateInfoJsCallbackPtr(TNode< FunctionTemplateInfo > object)
TNode< BoolT > IsJSArrayMap(TNode< Map > map)
TNode< Uint16T > LoadInstanceType(TNode< HeapObject > object)
TNode< NativeContext > LoadNativeContext(TNode< Context > context)
TNode< T > Select(TNode< BoolT > condition, const NodeGenerator< T > &true_body, const NodeGenerator< T > &false_body, BranchHint branch_hint=BranchHint::kNone)
TNode< BoolT > TaggedIsSmi(TNode< MaybeObject > a)
TNode< Int32T > LoadMapBitField(TNode< Map > map)
TNode< Map > LoadMap(TNode< HeapObject > object)
TNode< Int32T > SmiToInt32(TNode< Smi > value)
TNode< Int32T > LoadMapElementsKind(TNode< Map > map)
TNode< Object > LoadJSArgumentsObjectLength(TNode< Context > context, TNode< JSArgumentsObject > array)
TNode< BoolT > IsCallable(TNode< HeapObject > object)
TNode< BoolT > IsJSFunctionInstanceType(TNode< Int32T > instance_type)
TNode< FeedbackVector > LoadFeedbackVectorFromBaseline()
static constexpr int kMaxLength
static const int kProtectorInvalid
Definition protectors.h:16
TNode< Int32T > Signed(TNode< Word32T > x)
TNode< IntPtrT > IntPtrConstant(intptr_t value)
TNode< UintPtrT > ChangeUint32ToWord(TNode< Word32T > value)
void GotoIfNot(TNode< IntegralT > condition, Label *false_label, GotoHint goto_hint=GotoHint::kNone)
TNode< Int32T > Word32And(TNode< Int32T > left, TNode< Int32T > right)
TNode< Smi > SmiConstant(Tagged< Smi > value)
void GotoIf(TNode< IntegralT > condition, Label *true_label, GotoHint goto_hint=GotoHint::kNone)
TNode< IntPtrT > ChangeInt32ToIntPtr(TNode< Word32T > value)
void TailCallBuiltin(Builtin id, TNode< Object > context, TArgs... args)
TNode< Int32T > Int32Constant(int32_t value)
TNode< BoolT > WordNotEqual(TNode< WordT > left, TNode< WordT > right)
TNode< BoolT > Word32Equal(TNode< Word32T > left, TNode< Word32T > right)
TNode< T > CallRuntime(Runtime::FunctionId function, TNode< Object > context, TArgs... args)
TNode< T > CallBuiltin(Builtin id, TNode< Object > context, TArgs... args)
void Branch(TNode< IntegralT > condition, Label *true_label, Label *false_label, BranchHint branch_hint=BranchHint::kNone)
TNode< T > Parameter(int value, const SourceLocation &loc=SourceLocation::Current())
#define CAST(x)
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
DirectHandle< Object > new_target
Definition execution.cc:75
TNode< Context > context
TNode< Object > receiver
const int length_
Definition mul-fft.cc:473
@ UPDATE_WRITE_BARRIER
Definition objects.h:55
@ LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND
DONT_OVERRIDE DISABLE_ALLOCATION_SITES HOLEY_ELEMENTS
DONT_OVERRIDE DISABLE_ALLOCATION_SITES DISABLE_ALLOCATION_SITES HOLEY_DOUBLE_ELEMENTS
constexpr int JSParameterCount(int param_count_without_receiver)
Definition globals.h:2782
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877