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