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