1 // Copyright 2020 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/objects/js-function.h"
6
7 #include "src/codegen/compiler.h"
8 #include "src/diagnostics/code-tracer.h"
9 #include "src/execution/isolate.h"
10 #include "src/execution/tiering-manager.h"
11 #include "src/heap/heap-inl.h"
12 #include "src/ic/ic.h"
13 #include "src/init/bootstrapper.h"
14 #include "src/objects/feedback-cell-inl.h"
15 #include "src/strings/string-builder-inl.h"
16
17 // Has to be the last include (doesn't have include guards):
18 #include "src/objects/object-macros.h"
19
20 namespace v8 {
21 namespace internal {
22
GetAttachedCodeKinds() const23 CodeKinds JSFunction::GetAttachedCodeKinds() const {
24 const CodeKind kind = code().kind();
25 if (!CodeKindIsJSFunction(kind)) return {};
26 if (CodeKindIsOptimizedJSFunction(kind) &&
27 code().marked_for_deoptimization()) {
28 return {};
29 }
30 return CodeKindToCodeKindFlag(kind);
31 }
32
GetAvailableCodeKinds() const33 CodeKinds JSFunction::GetAvailableCodeKinds() const {
34 CodeKinds result = GetAttachedCodeKinds();
35
36 if ((result & CodeKindFlag::INTERPRETED_FUNCTION) == 0) {
37 // The SharedFunctionInfo could have attached bytecode.
38 if (shared().HasBytecodeArray()) {
39 result |= CodeKindFlag::INTERPRETED_FUNCTION;
40 }
41 }
42
43 if ((result & CodeKindFlag::BASELINE) == 0) {
44 // The SharedFunctionInfo could have attached baseline code.
45 if (shared().HasBaselineCode()) {
46 result |= CodeKindFlag::BASELINE;
47 }
48 }
49
50 // Check the optimized code cache.
51 if (has_feedback_vector() && feedback_vector().has_optimized_code() &&
52 !feedback_vector().optimized_code().marked_for_deoptimization()) {
53 CodeT code = feedback_vector().optimized_code();
54 DCHECK(CodeKindIsOptimizedJSFunction(code.kind()));
55 result |= CodeKindToCodeKindFlag(code.kind());
56 }
57
58 DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
59 return result;
60 }
61
HasAttachedOptimizedCode() const62 bool JSFunction::HasAttachedOptimizedCode() const {
63 CodeKinds result = GetAttachedCodeKinds();
64 return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
65 }
66
HasAvailableOptimizedCode() const67 bool JSFunction::HasAvailableOptimizedCode() const {
68 CodeKinds result = GetAvailableCodeKinds();
69 return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
70 }
71
HasAttachedCodeKind(CodeKind kind) const72 bool JSFunction::HasAttachedCodeKind(CodeKind kind) const {
73 CodeKinds result = GetAttachedCodeKinds();
74 return (result & CodeKindToCodeKindFlag(kind)) != 0;
75 }
76
HasAvailableCodeKind(CodeKind kind) const77 bool JSFunction::HasAvailableCodeKind(CodeKind kind) const {
78 CodeKinds result = GetAvailableCodeKinds();
79 return (result & CodeKindToCodeKindFlag(kind)) != 0;
80 }
81
82 namespace {
83
84 // Returns false if no highest tier exists (i.e. the function is not compiled),
85 // otherwise returns true and sets highest_tier.
HighestTierOf(CodeKinds kinds,CodeKind * highest_tier)86 V8_WARN_UNUSED_RESULT bool HighestTierOf(CodeKinds kinds,
87 CodeKind* highest_tier) {
88 DCHECK_EQ((kinds & ~kJSFunctionCodeKindsMask), 0);
89 // Higher tiers > lower tiers.
90 STATIC_ASSERT(CodeKind::TURBOFAN > CodeKind::INTERPRETED_FUNCTION);
91 if (kinds == 0) return false;
92 const int highest_tier_log2 =
93 31 - base::bits::CountLeadingZeros(static_cast<uint32_t>(kinds));
94 DCHECK(CodeKindIsJSFunction(static_cast<CodeKind>(highest_tier_log2)));
95 *highest_tier = static_cast<CodeKind>(highest_tier_log2);
96 return true;
97 }
98
99 } // namespace
100
GetActiveTier() const101 base::Optional<CodeKind> JSFunction::GetActiveTier() const {
102 #if V8_ENABLE_WEBASSEMBLY
103 // Asm/Wasm functions are currently not supported. For simplicity, this
104 // includes invalid asm.js functions whose code hasn't yet been updated to
105 // CompileLazy but is still the InstantiateAsmJs builtin.
106 if (shared().HasAsmWasmData() ||
107 code().builtin_id() == Builtin::kInstantiateAsmJs) {
108 return {};
109 }
110 #endif // V8_ENABLE_WEBASSEMBLY
111
112 CodeKind highest_tier;
113 if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return {};
114
115 #ifdef DEBUG
116 CHECK(highest_tier == CodeKind::TURBOFAN ||
117 highest_tier == CodeKind::BASELINE ||
118 highest_tier == CodeKind::MAGLEV ||
119 highest_tier == CodeKind::INTERPRETED_FUNCTION);
120
121 if (highest_tier == CodeKind::INTERPRETED_FUNCTION) {
122 CHECK(code().is_interpreter_trampoline_builtin() ||
123 (CodeKindIsOptimizedJSFunction(code().kind()) &&
124 code().marked_for_deoptimization()) ||
125 (code().builtin_id() == Builtin::kCompileLazy &&
126 shared().HasBytecodeArray() && !shared().HasBaselineCode()));
127 }
128 #endif // DEBUG
129
130 return highest_tier;
131 }
132
ActiveTierIsIgnition() const133 bool JSFunction::ActiveTierIsIgnition() const {
134 return GetActiveTier() == CodeKind::INTERPRETED_FUNCTION;
135 }
136
ActiveTierIsBaseline() const137 bool JSFunction::ActiveTierIsBaseline() const {
138 return GetActiveTier() == CodeKind::BASELINE;
139 }
140
ActiveTierIsMaglev() const141 bool JSFunction::ActiveTierIsMaglev() const {
142 return GetActiveTier() == CodeKind::MAGLEV;
143 }
144
ActiveTierIsTurbofan() const145 bool JSFunction::ActiveTierIsTurbofan() const {
146 return GetActiveTier() == CodeKind::TURBOFAN;
147 }
148
CanDiscardCompiled() const149 bool JSFunction::CanDiscardCompiled() const {
150 // Essentially, what we are asking here is, has this function been compiled
151 // from JS code? We can currently tell only indirectly, by looking at
152 // available code kinds. If any JS code kind exists, we can discard.
153 //
154 // Attached optimized code that is marked for deoptimization will not show up
155 // in the list of available code kinds, thus we must check for it manually.
156 //
157 // Note that when the function has not yet been compiled we also return
158 // false; that's fine, since nothing must be discarded in that case.
159 if (CodeKindIsOptimizedJSFunction(code().kind())) return true;
160 CodeKinds result = GetAvailableCodeKinds();
161 return (result & kJSFunctionCodeKindsMask) != 0;
162 }
163
164 namespace {
165
TieringStateFor(CodeKind target_kind,ConcurrencyMode mode)166 constexpr TieringState TieringStateFor(CodeKind target_kind,
167 ConcurrencyMode mode) {
168 DCHECK(target_kind == CodeKind::MAGLEV || target_kind == CodeKind::TURBOFAN);
169 return target_kind == CodeKind::MAGLEV
170 ? (IsConcurrent(mode) ? TieringState::kRequestMaglev_Concurrent
171 : TieringState::kRequestMaglev_Synchronous)
172 : (IsConcurrent(mode)
173 ? TieringState::kRequestTurbofan_Concurrent
174 : TieringState::kRequestTurbofan_Synchronous);
175 }
176
177 } // namespace
178
MarkForOptimization(Isolate * isolate,CodeKind target_kind,ConcurrencyMode mode)179 void JSFunction::MarkForOptimization(Isolate* isolate, CodeKind target_kind,
180 ConcurrencyMode mode) {
181 if (!isolate->concurrent_recompilation_enabled() ||
182 isolate->bootstrapper()->IsActive()) {
183 mode = ConcurrencyMode::kSynchronous;
184 }
185
186 DCHECK(CodeKindIsOptimizedJSFunction(target_kind));
187 DCHECK(!is_compiled() || ActiveTierIsIgnition() || ActiveTierIsBaseline() ||
188 ActiveTierIsMaglev());
189 DCHECK(!ActiveTierIsTurbofan());
190 DCHECK(shared().HasBytecodeArray());
191 DCHECK(shared().allows_lazy_compilation() ||
192 !shared().optimization_disabled());
193
194 if (IsConcurrent(mode)) {
195 if (IsInProgress(tiering_state())) {
196 if (FLAG_trace_concurrent_recompilation) {
197 PrintF(" ** Not marking ");
198 ShortPrint();
199 PrintF(" -- already in optimization queue.\n");
200 }
201 return;
202 }
203 if (FLAG_trace_concurrent_recompilation) {
204 PrintF(" ** Marking ");
205 ShortPrint();
206 PrintF(" for concurrent %s recompilation.\n",
207 CodeKindToString(target_kind));
208 }
209 }
210
211 set_tiering_state(TieringStateFor(target_kind, mode));
212 }
213
SetInterruptBudget(Isolate * isolate)214 void JSFunction::SetInterruptBudget(Isolate* isolate) {
215 raw_feedback_cell().set_interrupt_budget(
216 TieringManager::InterruptBudgetFor(isolate, *this));
217 }
218
219 // static
CopyNameAndLength(Isolate * isolate,Handle<JSFunctionOrBoundFunctionOrWrappedFunction> function,Handle<JSReceiver> target,Handle<String> prefix,int arg_count)220 Maybe<bool> JSFunctionOrBoundFunctionOrWrappedFunction::CopyNameAndLength(
221 Isolate* isolate,
222 Handle<JSFunctionOrBoundFunctionOrWrappedFunction> function,
223 Handle<JSReceiver> target, Handle<String> prefix, int arg_count) {
224 // Setup the "length" property based on the "length" of the {target}.
225 // If the targets length is the default JSFunction accessor, we can keep the
226 // accessor that's installed by default on the
227 // JSBoundFunction/JSWrappedFunction. It lazily computes the value from the
228 // underlying internal length.
229 Handle<AccessorInfo> function_length_accessor =
230 isolate->factory()->function_length_accessor();
231 LookupIterator length_lookup(isolate, target,
232 isolate->factory()->length_string(), target,
233 LookupIterator::OWN);
234 if (!target->IsJSFunction() ||
235 length_lookup.state() != LookupIterator::ACCESSOR ||
236 !length_lookup.GetAccessors().is_identical_to(function_length_accessor)) {
237 Handle<Object> length(Smi::zero(), isolate);
238 Maybe<PropertyAttributes> attributes =
239 JSReceiver::GetPropertyAttributes(&length_lookup);
240 if (attributes.IsNothing()) return Nothing<bool>();
241 if (attributes.FromJust() != ABSENT) {
242 Handle<Object> target_length;
243 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_length,
244 Object::GetProperty(&length_lookup),
245 Nothing<bool>());
246 if (target_length->IsNumber()) {
247 length = isolate->factory()->NewNumber(std::max(
248 0.0, DoubleToInteger(target_length->Number()) - arg_count));
249 }
250 }
251 LookupIterator it(isolate, function, isolate->factory()->length_string(),
252 function);
253 DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
254 RETURN_ON_EXCEPTION_VALUE(isolate,
255 JSObject::DefineOwnPropertyIgnoreAttributes(
256 &it, length, it.property_attributes()),
257 Nothing<bool>());
258 }
259
260 // Setup the "name" property based on the "name" of the {target}.
261 // If the target's name is the default JSFunction accessor, we can keep the
262 // accessor that's installed by default on the
263 // JSBoundFunction/JSWrappedFunction. It lazily computes the value from the
264 // underlying internal name.
265 Handle<AccessorInfo> function_name_accessor =
266 isolate->factory()->function_name_accessor();
267 LookupIterator name_lookup(isolate, target, isolate->factory()->name_string(),
268 target);
269 if (!target->IsJSFunction() ||
270 name_lookup.state() != LookupIterator::ACCESSOR ||
271 !name_lookup.GetAccessors().is_identical_to(function_name_accessor) ||
272 (name_lookup.IsFound() && !name_lookup.HolderIsReceiver())) {
273 Handle<Object> target_name;
274 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_name,
275 Object::GetProperty(&name_lookup),
276 Nothing<bool>());
277 Handle<String> name;
278 if (target_name->IsString()) {
279 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
280 isolate, name,
281 Name::ToFunctionName(isolate, Handle<String>::cast(target_name)),
282 Nothing<bool>());
283 if (!prefix.is_null()) {
284 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
285 isolate, name, isolate->factory()->NewConsString(prefix, name),
286 Nothing<bool>());
287 }
288 } else if (prefix.is_null()) {
289 name = isolate->factory()->empty_string();
290 } else {
291 name = prefix;
292 }
293 LookupIterator it(isolate, function, isolate->factory()->name_string());
294 DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
295 RETURN_ON_EXCEPTION_VALUE(isolate,
296 JSObject::DefineOwnPropertyIgnoreAttributes(
297 &it, name, it.property_attributes()),
298 Nothing<bool>());
299 }
300
301 return Just(true);
302 }
303
304 // static
GetName(Isolate * isolate,Handle<JSBoundFunction> function)305 MaybeHandle<String> JSBoundFunction::GetName(Isolate* isolate,
306 Handle<JSBoundFunction> function) {
307 Handle<String> prefix = isolate->factory()->bound__string();
308 Handle<String> target_name = prefix;
309 Factory* factory = isolate->factory();
310 // Concatenate the "bound " up to the last non-bound target.
311 while (function->bound_target_function().IsJSBoundFunction()) {
312 ASSIGN_RETURN_ON_EXCEPTION(isolate, target_name,
313 factory->NewConsString(prefix, target_name),
314 String);
315 function = handle(JSBoundFunction::cast(function->bound_target_function()),
316 isolate);
317 }
318 if (function->bound_target_function().IsJSWrappedFunction()) {
319 Handle<JSWrappedFunction> target(
320 JSWrappedFunction::cast(function->bound_target_function()), isolate);
321 Handle<String> name;
322 ASSIGN_RETURN_ON_EXCEPTION(
323 isolate, name, JSWrappedFunction::GetName(isolate, target), String);
324 return factory->NewConsString(target_name, name);
325 }
326 if (function->bound_target_function().IsJSFunction()) {
327 Handle<JSFunction> target(
328 JSFunction::cast(function->bound_target_function()), isolate);
329 Handle<String> name = JSFunction::GetName(isolate, target);
330 return factory->NewConsString(target_name, name);
331 }
332 // This will omit the proper target name for bound JSProxies.
333 return target_name;
334 }
335
336 // static
GetLength(Isolate * isolate,Handle<JSBoundFunction> function)337 Maybe<int> JSBoundFunction::GetLength(Isolate* isolate,
338 Handle<JSBoundFunction> function) {
339 int nof_bound_arguments = function->bound_arguments().length();
340 while (function->bound_target_function().IsJSBoundFunction()) {
341 function = handle(JSBoundFunction::cast(function->bound_target_function()),
342 isolate);
343 // Make sure we never overflow {nof_bound_arguments}, the number of
344 // arguments of a function is strictly limited by the max length of an
345 // JSAarray, Smi::kMaxValue is thus a reasonably good overestimate.
346 int length = function->bound_arguments().length();
347 if (V8_LIKELY(Smi::kMaxValue - nof_bound_arguments > length)) {
348 nof_bound_arguments += length;
349 } else {
350 nof_bound_arguments = Smi::kMaxValue;
351 }
352 }
353 if (function->bound_target_function().IsJSWrappedFunction()) {
354 Handle<JSWrappedFunction> target(
355 JSWrappedFunction::cast(function->bound_target_function()), isolate);
356 int target_length = 0;
357 MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(
358 isolate, target_length, JSWrappedFunction::GetLength(isolate, target),
359 Nothing<int>());
360 int length = std::max(0, target_length - nof_bound_arguments);
361 return Just(length);
362 }
363 // All non JSFunction targets get a direct property and don't use this
364 // accessor.
365 Handle<JSFunction> target(JSFunction::cast(function->bound_target_function()),
366 isolate);
367 int target_length = target->length();
368
369 int length = std::max(0, target_length - nof_bound_arguments);
370 return Just(length);
371 }
372
373 // static
ToString(Handle<JSBoundFunction> function)374 Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) {
375 Isolate* const isolate = function->GetIsolate();
376 return isolate->factory()->function_native_code_string();
377 }
378
379 // static
GetName(Isolate * isolate,Handle<JSWrappedFunction> function)380 MaybeHandle<String> JSWrappedFunction::GetName(
381 Isolate* isolate, Handle<JSWrappedFunction> function) {
382 STACK_CHECK(isolate, MaybeHandle<String>());
383 Factory* factory = isolate->factory();
384 Handle<String> target_name = factory->empty_string();
385 Handle<JSReceiver> target =
386 handle(function->wrapped_target_function(), isolate);
387 if (target->IsJSBoundFunction()) {
388 return JSBoundFunction::GetName(
389 isolate,
390 handle(JSBoundFunction::cast(function->wrapped_target_function()),
391 isolate));
392 } else if (target->IsJSFunction()) {
393 return JSFunction::GetName(
394 isolate,
395 handle(JSFunction::cast(function->wrapped_target_function()), isolate));
396 }
397 // This will omit the proper target name for bound JSProxies.
398 return target_name;
399 }
400
401 // static
GetLength(Isolate * isolate,Handle<JSWrappedFunction> function)402 Maybe<int> JSWrappedFunction::GetLength(Isolate* isolate,
403 Handle<JSWrappedFunction> function) {
404 STACK_CHECK(isolate, Nothing<int>());
405 Handle<JSReceiver> target =
406 handle(function->wrapped_target_function(), isolate);
407 if (target->IsJSBoundFunction()) {
408 return JSBoundFunction::GetLength(
409 isolate,
410 handle(JSBoundFunction::cast(function->wrapped_target_function()),
411 isolate));
412 }
413 // All non JSFunction targets get a direct property and don't use this
414 // accessor.
415 return Just(Handle<JSFunction>::cast(target)->length());
416 }
417
418 // static
ToString(Handle<JSWrappedFunction> function)419 Handle<String> JSWrappedFunction::ToString(Handle<JSWrappedFunction> function) {
420 Isolate* const isolate = function->GetIsolate();
421 return isolate->factory()->function_native_code_string();
422 }
423
424 // static
Create(Isolate * isolate,Handle<NativeContext> creation_context,Handle<JSReceiver> value)425 MaybeHandle<Object> JSWrappedFunction::Create(
426 Isolate* isolate, Handle<NativeContext> creation_context,
427 Handle<JSReceiver> value) {
428 // The value must be a callable according to the specification.
429 DCHECK(value->IsCallable());
430 // The intermediate wrapped functions are not user-visible. And calling a
431 // wrapped function won't cause a side effect in the creation realm.
432 // Unwrap here to avoid nested unwrapping at the call site.
433 if (value->IsJSWrappedFunction()) {
434 Handle<JSWrappedFunction> target_wrapped =
435 Handle<JSWrappedFunction>::cast(value);
436 value =
437 Handle<JSReceiver>(target_wrapped->wrapped_target_function(), isolate);
438 }
439
440 // 1. Let internalSlotsList be the internal slots listed in Table 2, plus
441 // [[Prototype]] and [[Extensible]].
442 // 2. Let wrapped be ! MakeBasicObject(internalSlotsList).
443 // 3. Set wrapped.[[Prototype]] to
444 // callerRealm.[[Intrinsics]].[[%Function.prototype%]].
445 // 4. Set wrapped.[[Call]] as described in 2.1.
446 // 5. Set wrapped.[[WrappedTargetFunction]] to Target.
447 // 6. Set wrapped.[[Realm]] to callerRealm.
448 Handle<JSWrappedFunction> wrapped =
449 isolate->factory()->NewJSWrappedFunction(creation_context, value);
450
451 // 7. Let result be CopyNameAndLength(wrapped, Target, "wrapped").
452 Maybe<bool> is_abrupt =
453 JSFunctionOrBoundFunctionOrWrappedFunction::CopyNameAndLength(
454 isolate, wrapped, value, Handle<String>(), 0);
455
456 // 8. If result is an Abrupt Completion, throw a TypeError exception.
457 if (is_abrupt.IsNothing()) {
458 DCHECK(isolate->has_pending_exception());
459 isolate->clear_pending_exception();
460 // TODO(v8:11989): provide a non-observable inspection on the
461 // pending_exception to the newly created TypeError.
462 // https://github.com/tc39/proposal-shadowrealm/issues/353
463
464 // The TypeError thrown is created with creation Realm's TypeError
465 // constructor instead of the executing Realm's.
466 THROW_NEW_ERROR_RETURN_VALUE(
467 isolate,
468 NewError(Handle<JSFunction>(creation_context->type_error_function(),
469 isolate),
470 MessageTemplate::kCannotWrap),
471 {});
472 }
473 DCHECK(is_abrupt.FromJust());
474
475 // 9. Return wrapped.
476 return wrapped;
477 }
478
479 // static
GetName(Isolate * isolate,Handle<JSFunction> function)480 Handle<String> JSFunction::GetName(Isolate* isolate,
481 Handle<JSFunction> function) {
482 if (function->shared().name_should_print_as_anonymous()) {
483 return isolate->factory()->anonymous_string();
484 }
485 return handle(function->shared().Name(), isolate);
486 }
487
488 // static
EnsureClosureFeedbackCellArray(Handle<JSFunction> function,bool reset_budget_for_feedback_allocation)489 void JSFunction::EnsureClosureFeedbackCellArray(
490 Handle<JSFunction> function, bool reset_budget_for_feedback_allocation) {
491 Isolate* const isolate = function->GetIsolate();
492 DCHECK(function->shared().is_compiled());
493 DCHECK(function->shared().HasFeedbackMetadata());
494 #if V8_ENABLE_WEBASSEMBLY
495 if (function->shared().HasAsmWasmData()) return;
496 #endif // V8_ENABLE_WEBASSEMBLY
497
498 Handle<SharedFunctionInfo> shared(function->shared(), isolate);
499 DCHECK(function->shared().HasBytecodeArray());
500
501 const bool has_closure_feedback_cell_array =
502 (function->has_closure_feedback_cell_array() ||
503 function->has_feedback_vector());
504 // Initialize the interrupt budget to the feedback vector allocation budget
505 // when initializing the feedback cell for the first time or after a bytecode
506 // flush. We retain the closure feedback cell array on bytecode flush, so
507 // reset_budget_for_feedback_allocation is used to reset the budget in these
508 // cases.
509 if (reset_budget_for_feedback_allocation ||
510 !has_closure_feedback_cell_array) {
511 function->SetInterruptBudget(isolate);
512 }
513
514 if (has_closure_feedback_cell_array) {
515 return;
516 }
517
518 Handle<HeapObject> feedback_cell_array =
519 ClosureFeedbackCellArray::New(isolate, shared);
520 // Many closure cell is used as a way to specify that there is no
521 // feedback cell for this function and a new feedback cell has to be
522 // allocated for this funciton. For ex: for eval functions, we have to create
523 // a feedback cell and cache it along with the code. It is safe to use
524 // many_closure_cell to indicate this because in regular cases, it should
525 // already have a feedback_vector / feedback cell array allocated.
526 if (function->raw_feedback_cell() == isolate->heap()->many_closures_cell()) {
527 Handle<FeedbackCell> feedback_cell =
528 isolate->factory()->NewOneClosureCell(feedback_cell_array);
529 function->set_raw_feedback_cell(*feedback_cell, kReleaseStore);
530 function->SetInterruptBudget(isolate);
531 } else {
532 function->raw_feedback_cell().set_value(*feedback_cell_array,
533 kReleaseStore);
534 }
535 }
536
537 // static
EnsureFeedbackVector(Isolate * isolate,Handle<JSFunction> function,IsCompiledScope * compiled_scope)538 void JSFunction::EnsureFeedbackVector(Isolate* isolate,
539 Handle<JSFunction> function,
540 IsCompiledScope* compiled_scope) {
541 DCHECK(compiled_scope->is_compiled());
542 DCHECK(function->shared().HasFeedbackMetadata());
543 if (function->has_feedback_vector()) return;
544 #if V8_ENABLE_WEBASSEMBLY
545 if (function->shared().HasAsmWasmData()) return;
546 #endif // V8_ENABLE_WEBASSEMBLY
547
548 CreateAndAttachFeedbackVector(isolate, function, compiled_scope);
549 }
550
551 // static
CreateAndAttachFeedbackVector(Isolate * isolate,Handle<JSFunction> function,IsCompiledScope * compiled_scope)552 void JSFunction::CreateAndAttachFeedbackVector(
553 Isolate* isolate, Handle<JSFunction> function,
554 IsCompiledScope* compiled_scope) {
555 DCHECK(compiled_scope->is_compiled());
556 DCHECK(function->shared().HasFeedbackMetadata());
557 DCHECK(!function->has_feedback_vector());
558 #if V8_ENABLE_WEBASSEMBLY
559 DCHECK(!function->shared().HasAsmWasmData());
560 #endif // V8_ENABLE_WEBASSEMBLY
561
562 Handle<SharedFunctionInfo> shared(function->shared(), isolate);
563 DCHECK(function->shared().HasBytecodeArray());
564
565 EnsureClosureFeedbackCellArray(function, false);
566 Handle<ClosureFeedbackCellArray> closure_feedback_cell_array =
567 handle(function->closure_feedback_cell_array(), isolate);
568 Handle<HeapObject> feedback_vector = FeedbackVector::New(
569 isolate, shared, closure_feedback_cell_array, compiled_scope);
570 // EnsureClosureFeedbackCellArray should handle the special case where we need
571 // to allocate a new feedback cell. Please look at comment in that function
572 // for more details.
573 DCHECK(function->raw_feedback_cell() !=
574 isolate->heap()->many_closures_cell());
575 function->raw_feedback_cell().set_value(*feedback_vector, kReleaseStore);
576 function->SetInterruptBudget(isolate);
577 }
578
579 // static
InitializeFeedbackCell(Handle<JSFunction> function,IsCompiledScope * is_compiled_scope,bool reset_budget_for_feedback_allocation)580 void JSFunction::InitializeFeedbackCell(
581 Handle<JSFunction> function, IsCompiledScope* is_compiled_scope,
582 bool reset_budget_for_feedback_allocation) {
583 Isolate* const isolate = function->GetIsolate();
584 #if V8_ENABLE_WEBASSEMBLY
585 // The following checks ensure that the feedback vectors are compatible with
586 // the feedback metadata. For Asm / Wasm functions we never allocate / use
587 // feedback vectors, so a mismatch between the metadata and feedback vector is
588 // harmless. The checks could fail for functions that has has_asm_wasm_broken
589 // set at runtime (for ex: failed instantiation).
590 if (function->shared().HasAsmWasmData()) return;
591 #endif // V8_ENABLE_WEBASSEMBLY
592
593 if (function->has_feedback_vector()) {
594 CHECK_EQ(function->feedback_vector().length(),
595 function->feedback_vector().metadata().slot_count());
596 return;
597 }
598
599 if (function->has_closure_feedback_cell_array()) {
600 CHECK_EQ(
601 function->closure_feedback_cell_array().length(),
602 function->shared().feedback_metadata().create_closure_slot_count());
603 }
604
605 const bool needs_feedback_vector =
606 !FLAG_lazy_feedback_allocation || FLAG_always_opt ||
607 // We also need a feedback vector for certain log events, collecting type
608 // profile and more precise code coverage.
609 FLAG_log_function_events || !isolate->is_best_effort_code_coverage() ||
610 isolate->is_collecting_type_profile();
611
612 if (needs_feedback_vector) {
613 CreateAndAttachFeedbackVector(isolate, function, is_compiled_scope);
614 } else {
615 EnsureClosureFeedbackCellArray(function,
616 reset_budget_for_feedback_allocation);
617 }
618 }
619
620 namespace {
621
SetInstancePrototype(Isolate * isolate,Handle<JSFunction> function,Handle<JSReceiver> value)622 void SetInstancePrototype(Isolate* isolate, Handle<JSFunction> function,
623 Handle<JSReceiver> value) {
624 // Now some logic for the maps of the objects that are created by using this
625 // function as a constructor.
626 if (function->has_initial_map()) {
627 // If the function has allocated the initial map replace it with a
628 // copy containing the new prototype. Also complete any in-object
629 // slack tracking that is in progress at this point because it is
630 // still tracking the old copy.
631 function->CompleteInobjectSlackTrackingIfActive();
632
633 Handle<Map> initial_map(function->initial_map(), isolate);
634
635 if (!isolate->bootstrapper()->IsActive() &&
636 initial_map->instance_type() == JS_OBJECT_TYPE) {
637 // Put the value in the initial map field until an initial map is needed.
638 // At that point, a new initial map is created and the prototype is put
639 // into the initial map where it belongs.
640 function->set_prototype_or_initial_map(*value, kReleaseStore);
641 } else {
642 Handle<Map> new_map =
643 Map::Copy(isolate, initial_map, "SetInstancePrototype");
644 JSFunction::SetInitialMap(isolate, function, new_map, value);
645 DCHECK_IMPLIES(!isolate->bootstrapper()->IsActive(),
646 *function != function->native_context().array_function());
647 }
648
649 // Deoptimize all code that embeds the previous initial map.
650 initial_map->dependent_code().DeoptimizeDependentCodeGroup(
651 isolate, DependentCode::kInitialMapChangedGroup);
652 } else {
653 // Put the value in the initial map field until an initial map is
654 // needed. At that point, a new initial map is created and the
655 // prototype is put into the initial map where it belongs.
656 function->set_prototype_or_initial_map(*value, kReleaseStore);
657 if (value->IsJSObject()) {
658 // Optimize as prototype to detach it from its transition tree.
659 JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
660 }
661 }
662 }
663
664 } // anonymous namespace
665
SetPrototype(Handle<JSFunction> function,Handle<Object> value)666 void JSFunction::SetPrototype(Handle<JSFunction> function,
667 Handle<Object> value) {
668 DCHECK(function->IsConstructor() ||
669 IsGeneratorFunction(function->shared().kind()));
670 Isolate* isolate = function->GetIsolate();
671 Handle<JSReceiver> construct_prototype;
672
673 // If the value is not a JSReceiver, store the value in the map's
674 // constructor field so it can be accessed. Also, set the prototype
675 // used for constructing objects to the original object prototype.
676 // See ECMA-262 13.2.2.
677 if (!value->IsJSReceiver()) {
678 // Copy the map so this does not affect unrelated functions.
679 // Remove map transitions because they point to maps with a
680 // different prototype.
681 Handle<Map> new_map =
682 Map::Copy(isolate, handle(function->map(), isolate), "SetPrototype");
683
684 new_map->SetConstructor(*value);
685 new_map->set_has_non_instance_prototype(true);
686 JSObject::MigrateToMap(isolate, function, new_map);
687
688 FunctionKind kind = function->shared().kind();
689 Handle<Context> native_context(function->native_context(), isolate);
690
691 construct_prototype = Handle<JSReceiver>(
692 IsGeneratorFunction(kind)
693 ? IsAsyncFunction(kind)
694 ? native_context->initial_async_generator_prototype()
695 : native_context->initial_generator_prototype()
696 : native_context->initial_object_prototype(),
697 isolate);
698 } else {
699 construct_prototype = Handle<JSReceiver>::cast(value);
700 function->map().set_has_non_instance_prototype(false);
701 }
702
703 SetInstancePrototype(isolate, function, construct_prototype);
704 }
705
SetInitialMap(Isolate * isolate,Handle<JSFunction> function,Handle<Map> map,Handle<HeapObject> prototype)706 void JSFunction::SetInitialMap(Isolate* isolate, Handle<JSFunction> function,
707 Handle<Map> map, Handle<HeapObject> prototype) {
708 SetInitialMap(isolate, function, map, prototype, function);
709 }
710
SetInitialMap(Isolate * isolate,Handle<JSFunction> function,Handle<Map> map,Handle<HeapObject> prototype,Handle<JSFunction> constructor)711 void JSFunction::SetInitialMap(Isolate* isolate, Handle<JSFunction> function,
712 Handle<Map> map, Handle<HeapObject> prototype,
713 Handle<JSFunction> constructor) {
714 if (map->prototype() != *prototype) {
715 Map::SetPrototype(isolate, map, prototype);
716 }
717 map->SetConstructor(*constructor);
718 function->set_prototype_or_initial_map(*map, kReleaseStore);
719 if (FLAG_log_maps) {
720 LOG(isolate, MapEvent("InitialMap", Handle<Map>(), map, "",
721 SharedFunctionInfo::DebugName(
722 handle(function->shared(), isolate))));
723 }
724 }
725
EnsureHasInitialMap(Handle<JSFunction> function)726 void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
727 DCHECK(function->has_prototype_slot());
728 DCHECK(function->IsConstructor() ||
729 IsResumableFunction(function->shared().kind()));
730 if (function->has_initial_map()) return;
731 Isolate* isolate = function->GetIsolate();
732
733 int expected_nof_properties =
734 CalculateExpectedNofProperties(isolate, function);
735
736 // {CalculateExpectedNofProperties} can have had the side effect of creating
737 // the initial map (e.g. it could have triggered an optimized compilation
738 // whose dependency installation reentered {EnsureHasInitialMap}).
739 if (function->has_initial_map()) return;
740
741 // Create a new map with the size and number of in-object properties suggested
742 // by the function.
743 InstanceType instance_type;
744 if (IsResumableFunction(function->shared().kind())) {
745 instance_type = IsAsyncGeneratorFunction(function->shared().kind())
746 ? JS_ASYNC_GENERATOR_OBJECT_TYPE
747 : JS_GENERATOR_OBJECT_TYPE;
748 } else {
749 instance_type = JS_OBJECT_TYPE;
750 }
751
752 int instance_size;
753 int inobject_properties;
754 CalculateInstanceSizeHelper(instance_type, false, 0, expected_nof_properties,
755 &instance_size, &inobject_properties);
756
757 Handle<Map> map = isolate->factory()->NewMap(instance_type, instance_size,
758 TERMINAL_FAST_ELEMENTS_KIND,
759 inobject_properties);
760
761 // Fetch or allocate prototype.
762 Handle<HeapObject> prototype;
763 if (function->has_instance_prototype()) {
764 prototype = handle(function->instance_prototype(), isolate);
765 } else {
766 prototype = isolate->factory()->NewFunctionPrototype(function);
767 }
768 DCHECK(map->has_fast_object_elements());
769
770 // Finally link initial map and constructor function.
771 DCHECK(prototype->IsJSReceiver());
772 JSFunction::SetInitialMap(isolate, function, map, prototype);
773 map->StartInobjectSlackTracking();
774 }
775
776 namespace {
777
778 #ifdef DEBUG
CanSubclassHaveInobjectProperties(InstanceType instance_type)779 bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
780 switch (instance_type) {
781 case JS_API_OBJECT_TYPE:
782 case JS_ARRAY_BUFFER_TYPE:
783 case JS_ARRAY_ITERATOR_PROTOTYPE_TYPE:
784 case JS_ARRAY_TYPE:
785 case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
786 case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
787 case JS_DATA_VIEW_TYPE:
788 case JS_DATE_TYPE:
789 case JS_GENERATOR_OBJECT_TYPE:
790 case JS_FUNCTION_TYPE:
791 case JS_CLASS_CONSTRUCTOR_TYPE:
792 case JS_PROMISE_CONSTRUCTOR_TYPE:
793 case JS_REG_EXP_CONSTRUCTOR_TYPE:
794 case JS_ARRAY_CONSTRUCTOR_TYPE:
795 #define TYPED_ARRAY_CONSTRUCTORS_SWITCH(Type, type, TYPE, Ctype) \
796 case TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE:
797 TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTORS_SWITCH)
798 #undef TYPED_ARRAY_CONSTRUCTORS_SWITCH
799 case JS_ITERATOR_PROTOTYPE_TYPE:
800 case JS_MAP_ITERATOR_PROTOTYPE_TYPE:
801 case JS_OBJECT_PROTOTYPE_TYPE:
802 case JS_PROMISE_PROTOTYPE_TYPE:
803 case JS_REG_EXP_PROTOTYPE_TYPE:
804 case JS_SET_ITERATOR_PROTOTYPE_TYPE:
805 case JS_SET_PROTOTYPE_TYPE:
806 case JS_STRING_ITERATOR_PROTOTYPE_TYPE:
807 case JS_TYPED_ARRAY_PROTOTYPE_TYPE:
808 #ifdef V8_INTL_SUPPORT
809 case JS_COLLATOR_TYPE:
810 case JS_DATE_TIME_FORMAT_TYPE:
811 case JS_DISPLAY_NAMES_TYPE:
812 case JS_LIST_FORMAT_TYPE:
813 case JS_LOCALE_TYPE:
814 case JS_NUMBER_FORMAT_TYPE:
815 case JS_PLURAL_RULES_TYPE:
816 case JS_RELATIVE_TIME_FORMAT_TYPE:
817 case JS_SEGMENT_ITERATOR_TYPE:
818 case JS_SEGMENTER_TYPE:
819 case JS_SEGMENTS_TYPE:
820 case JS_V8_BREAK_ITERATOR_TYPE:
821 #endif
822 case JS_ASYNC_FUNCTION_OBJECT_TYPE:
823 case JS_ASYNC_GENERATOR_OBJECT_TYPE:
824 case JS_MAP_TYPE:
825 case JS_MESSAGE_OBJECT_TYPE:
826 case JS_OBJECT_TYPE:
827 case JS_ERROR_TYPE:
828 case JS_FINALIZATION_REGISTRY_TYPE:
829 case JS_ARGUMENTS_OBJECT_TYPE:
830 case JS_PROMISE_TYPE:
831 case JS_REG_EXP_TYPE:
832 case JS_SET_TYPE:
833 case JS_SHADOW_REALM_TYPE:
834 case JS_SPECIAL_API_OBJECT_TYPE:
835 case JS_TYPED_ARRAY_TYPE:
836 case JS_PRIMITIVE_WRAPPER_TYPE:
837 case JS_TEMPORAL_CALENDAR_TYPE:
838 case JS_TEMPORAL_DURATION_TYPE:
839 case JS_TEMPORAL_INSTANT_TYPE:
840 case JS_TEMPORAL_PLAIN_DATE_TYPE:
841 case JS_TEMPORAL_PLAIN_DATE_TIME_TYPE:
842 case JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE:
843 case JS_TEMPORAL_PLAIN_TIME_TYPE:
844 case JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE:
845 case JS_TEMPORAL_TIME_ZONE_TYPE:
846 case JS_TEMPORAL_ZONED_DATE_TIME_TYPE:
847 case JS_WEAK_MAP_TYPE:
848 case JS_WEAK_REF_TYPE:
849 case JS_WEAK_SET_TYPE:
850 #if V8_ENABLE_WEBASSEMBLY
851 case WASM_GLOBAL_OBJECT_TYPE:
852 case WASM_INSTANCE_OBJECT_TYPE:
853 case WASM_MEMORY_OBJECT_TYPE:
854 case WASM_MODULE_OBJECT_TYPE:
855 case WASM_TABLE_OBJECT_TYPE:
856 case WASM_VALUE_OBJECT_TYPE:
857 #endif // V8_ENABLE_WEBASSEMBLY
858 return true;
859
860 case BIGINT_TYPE:
861 case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
862 case BYTECODE_ARRAY_TYPE:
863 case BYTE_ARRAY_TYPE:
864 case CELL_TYPE:
865 case CODE_TYPE:
866 case FILLER_TYPE:
867 case FIXED_ARRAY_TYPE:
868 case SCRIPT_CONTEXT_TABLE_TYPE:
869 case FIXED_DOUBLE_ARRAY_TYPE:
870 case FEEDBACK_METADATA_TYPE:
871 case FOREIGN_TYPE:
872 case FREE_SPACE_TYPE:
873 case HASH_TABLE_TYPE:
874 case ORDERED_HASH_MAP_TYPE:
875 case ORDERED_HASH_SET_TYPE:
876 case ORDERED_NAME_DICTIONARY_TYPE:
877 case NAME_DICTIONARY_TYPE:
878 case GLOBAL_DICTIONARY_TYPE:
879 case NUMBER_DICTIONARY_TYPE:
880 case SIMPLE_NUMBER_DICTIONARY_TYPE:
881 case HEAP_NUMBER_TYPE:
882 case JS_BOUND_FUNCTION_TYPE:
883 case JS_GLOBAL_OBJECT_TYPE:
884 case JS_GLOBAL_PROXY_TYPE:
885 case JS_PROXY_TYPE:
886 case JS_WRAPPED_FUNCTION_TYPE:
887 case MAP_TYPE:
888 case ODDBALL_TYPE:
889 case PROPERTY_CELL_TYPE:
890 case SHARED_FUNCTION_INFO_TYPE:
891 case SYMBOL_TYPE:
892 case ALLOCATION_SITE_TYPE:
893
894 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
895 case FIXED_##TYPE##_ARRAY_TYPE:
896 #undef TYPED_ARRAY_CASE
897
898 #define MAKE_STRUCT_CASE(TYPE, Name, name) case TYPE:
899 STRUCT_LIST(MAKE_STRUCT_CASE)
900 #undef MAKE_STRUCT_CASE
901 // We must not end up here for these instance types at all.
902 UNREACHABLE();
903 // Fall through.
904 default:
905 return false;
906 }
907 }
908 #endif // DEBUG
909
FastInitializeDerivedMap(Isolate * isolate,Handle<JSFunction> new_target,Handle<JSFunction> constructor,Handle<Map> constructor_initial_map)910 bool FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
911 Handle<JSFunction> constructor,
912 Handle<Map> constructor_initial_map) {
913 // Use the default intrinsic prototype instead.
914 if (!new_target->has_prototype_slot()) return false;
915 // Check that |function|'s initial map still in sync with the |constructor|,
916 // otherwise we must create a new initial map for |function|.
917 if (new_target->has_initial_map() &&
918 new_target->initial_map().GetConstructor() == *constructor) {
919 DCHECK(new_target->instance_prototype().IsJSReceiver());
920 return true;
921 }
922 InstanceType instance_type = constructor_initial_map->instance_type();
923 DCHECK(CanSubclassHaveInobjectProperties(instance_type));
924 // Create a new map with the size and number of in-object properties
925 // suggested by |function|.
926
927 // Link initial map and constructor function if the new.target is actually a
928 // subclass constructor.
929 if (!IsDerivedConstructor(new_target->shared().kind())) return false;
930
931 int instance_size;
932 int in_object_properties;
933 int embedder_fields =
934 JSObject::GetEmbedderFieldCount(*constructor_initial_map);
935 // Constructor expects certain number of in-object properties to be in the
936 // object. However, CalculateExpectedNofProperties() may return smaller value
937 // if 1) the constructor is not in the prototype chain of new_target, or
938 // 2) the prototype chain is modified during iteration, or 3) compilation
939 // failure occur during prototype chain iteration.
940 // So we take the maximum of two values.
941 int expected_nof_properties = std::max(
942 static_cast<int>(constructor->shared().expected_nof_properties()),
943 JSFunction::CalculateExpectedNofProperties(isolate, new_target));
944 JSFunction::CalculateInstanceSizeHelper(
945 instance_type, constructor_initial_map->has_prototype_slot(),
946 embedder_fields, expected_nof_properties, &instance_size,
947 &in_object_properties);
948
949 int pre_allocated = constructor_initial_map->GetInObjectProperties() -
950 constructor_initial_map->UnusedPropertyFields();
951 CHECK_LE(constructor_initial_map->UsedInstanceSize(), instance_size);
952 int unused_property_fields = in_object_properties - pre_allocated;
953 Handle<Map> map =
954 Map::CopyInitialMap(isolate, constructor_initial_map, instance_size,
955 in_object_properties, unused_property_fields);
956 map->set_new_target_is_base(false);
957 Handle<HeapObject> prototype(new_target->instance_prototype(), isolate);
958 JSFunction::SetInitialMap(isolate, new_target, map, prototype, constructor);
959 DCHECK(new_target->instance_prototype().IsJSReceiver());
960 map->set_construction_counter(Map::kNoSlackTracking);
961 map->StartInobjectSlackTracking();
962 return true;
963 }
964
965 } // namespace
966
967 // static
GetDerivedMap(Isolate * isolate,Handle<JSFunction> constructor,Handle<JSReceiver> new_target)968 MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
969 Handle<JSFunction> constructor,
970 Handle<JSReceiver> new_target) {
971 EnsureHasInitialMap(constructor);
972
973 Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
974 if (*new_target == *constructor) return constructor_initial_map;
975
976 Handle<Map> result_map;
977 // Fast case, new.target is a subclass of constructor. The map is cacheable
978 // (and may already have been cached). new.target.prototype is guaranteed to
979 // be a JSReceiver.
980 if (new_target->IsJSFunction()) {
981 Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
982 if (FastInitializeDerivedMap(isolate, function, constructor,
983 constructor_initial_map)) {
984 return handle(function->initial_map(), isolate);
985 }
986 }
987
988 // Slow path, new.target is either a proxy or can't cache the map.
989 // new.target.prototype is not guaranteed to be a JSReceiver, and may need to
990 // fall back to the intrinsicDefaultProto.
991 Handle<Object> prototype;
992 if (new_target->IsJSFunction()) {
993 Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
994 if (function->has_prototype_slot()) {
995 // Make sure the new.target.prototype is cached.
996 EnsureHasInitialMap(function);
997 prototype = handle(function->prototype(), isolate);
998 } else {
999 // No prototype property, use the intrinsict default proto further down.
1000 prototype = isolate->factory()->undefined_value();
1001 }
1002 } else {
1003 Handle<String> prototype_string = isolate->factory()->prototype_string();
1004 ASSIGN_RETURN_ON_EXCEPTION(
1005 isolate, prototype,
1006 JSReceiver::GetProperty(isolate, new_target, prototype_string), Map);
1007 // The above prototype lookup might change the constructor and its
1008 // prototype, hence we have to reload the initial map.
1009 EnsureHasInitialMap(constructor);
1010 constructor_initial_map = handle(constructor->initial_map(), isolate);
1011 }
1012
1013 // If prototype is not a JSReceiver, fetch the intrinsicDefaultProto from the
1014 // correct realm. Rather than directly fetching the .prototype, we fetch the
1015 // constructor that points to the .prototype. This relies on
1016 // constructor.prototype being FROZEN for those constructors.
1017 if (!prototype->IsJSReceiver()) {
1018 Handle<Context> context;
1019 ASSIGN_RETURN_ON_EXCEPTION(isolate, context,
1020 JSReceiver::GetFunctionRealm(new_target), Map);
1021 DCHECK(context->IsNativeContext());
1022 Handle<Object> maybe_index = JSReceiver::GetDataProperty(
1023 isolate, constructor,
1024 isolate->factory()->native_context_index_symbol());
1025 int index = maybe_index->IsSmi() ? Smi::ToInt(*maybe_index)
1026 : Context::OBJECT_FUNCTION_INDEX;
1027 Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index)),
1028 isolate);
1029 prototype = handle(realm_constructor->prototype(), isolate);
1030 }
1031
1032 Handle<Map> map = Map::CopyInitialMap(isolate, constructor_initial_map);
1033 map->set_new_target_is_base(false);
1034 CHECK(prototype->IsJSReceiver());
1035 if (map->prototype() != *prototype)
1036 Map::SetPrototype(isolate, map, Handle<HeapObject>::cast(prototype));
1037 map->SetConstructor(*constructor);
1038 return map;
1039 }
1040
1041 namespace {
1042
1043 // Assert that the computations in TypedArrayElementsKindToConstructorIndex and
1044 // TypedArrayElementsKindToRabGsabCtorIndex are sound.
1045 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
1046 STATIC_ASSERT(Context::TYPE##_ARRAY_FUN_INDEX == \
1047 Context::FIRST_FIXED_TYPED_ARRAY_FUN_INDEX + \
1048 ElementsKind::TYPE##_ELEMENTS - \
1049 ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND); \
1050 STATIC_ASSERT(Context::RAB_GSAB_##TYPE##_ARRAY_MAP_INDEX == \
1051 Context::FIRST_RAB_GSAB_TYPED_ARRAY_MAP_INDEX + \
1052 ElementsKind::TYPE##_ELEMENTS - \
1053 ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
1054
TYPED_ARRAYS(TYPED_ARRAY_CASE)1055 TYPED_ARRAYS(TYPED_ARRAY_CASE)
1056 #undef TYPED_ARRAY_CASE
1057
1058 int TypedArrayElementsKindToConstructorIndex(ElementsKind elements_kind) {
1059 return Context::FIRST_FIXED_TYPED_ARRAY_FUN_INDEX + elements_kind -
1060 ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND;
1061 }
1062
TypedArrayElementsKindToRabGsabCtorIndex(ElementsKind elements_kind)1063 int TypedArrayElementsKindToRabGsabCtorIndex(ElementsKind elements_kind) {
1064 return Context::FIRST_RAB_GSAB_TYPED_ARRAY_MAP_INDEX + elements_kind -
1065 ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND;
1066 }
1067
1068 } // namespace
1069
GetDerivedRabGsabMap(Isolate * isolate,Handle<JSFunction> constructor,Handle<JSReceiver> new_target)1070 Handle<Map> JSFunction::GetDerivedRabGsabMap(Isolate* isolate,
1071 Handle<JSFunction> constructor,
1072 Handle<JSReceiver> new_target) {
1073 Handle<Map> map =
1074 GetDerivedMap(isolate, constructor, new_target).ToHandleChecked();
1075 {
1076 DisallowHeapAllocation no_alloc;
1077 NativeContext context = isolate->context().native_context();
1078 int ctor_index =
1079 TypedArrayElementsKindToConstructorIndex(map->elements_kind());
1080 if (*new_target == context.get(ctor_index)) {
1081 ctor_index =
1082 TypedArrayElementsKindToRabGsabCtorIndex(map->elements_kind());
1083 return handle(Map::cast(context.get(ctor_index)), isolate);
1084 }
1085 }
1086
1087 // This only happens when subclassing TypedArrays. Create a new map with the
1088 // corresponding RAB / GSAB ElementsKind. Note: the map is not cached and
1089 // reused -> every array gets a unique map, making ICs slow.
1090 Handle<Map> rab_gsab_map = Map::Copy(isolate, map, "RAB / GSAB");
1091 rab_gsab_map->set_elements_kind(
1092 GetCorrespondingRabGsabElementsKind(map->elements_kind()));
1093 return rab_gsab_map;
1094 }
1095
ComputeInstanceSizeWithMinSlack(Isolate * isolate)1096 int JSFunction::ComputeInstanceSizeWithMinSlack(Isolate* isolate) {
1097 CHECK(has_initial_map());
1098 if (initial_map().IsInobjectSlackTrackingInProgress()) {
1099 int slack = initial_map().ComputeMinObjectSlack(isolate);
1100 return initial_map().InstanceSizeFromSlack(slack);
1101 }
1102 return initial_map().instance_size();
1103 }
1104
DebugNameCStr()1105 std::unique_ptr<char[]> JSFunction::DebugNameCStr() {
1106 return shared().DebugNameCStr();
1107 }
1108
PrintName(FILE * out)1109 void JSFunction::PrintName(FILE* out) {
1110 PrintF(out, "%s", DebugNameCStr().get());
1111 }
1112
1113 namespace {
1114
UseFastFunctionNameLookup(Isolate * isolate,Map map)1115 bool UseFastFunctionNameLookup(Isolate* isolate, Map map) {
1116 DCHECK(map.IsJSFunctionMap());
1117 if (map.NumberOfOwnDescriptors() <
1118 JSFunction::kMinDescriptorsForFastBindAndWrap) {
1119 return false;
1120 }
1121 DCHECK(!map.is_dictionary_map());
1122 HeapObject value;
1123 ReadOnlyRoots roots(isolate);
1124 auto descriptors = map.instance_descriptors(isolate);
1125 InternalIndex kNameIndex{JSFunction::kNameDescriptorIndex};
1126 if (descriptors.GetKey(kNameIndex) != roots.name_string() ||
1127 !descriptors.GetValue(kNameIndex)
1128 .GetHeapObjectIfStrong(isolate, &value)) {
1129 return false;
1130 }
1131 return value.IsAccessorInfo();
1132 }
1133
1134 } // namespace
1135
GetDebugName(Handle<JSFunction> function)1136 Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
1137 // Below we use the same fast-path that we already established for
1138 // Function.prototype.bind(), where we avoid a slow "name" property
1139 // lookup if the DescriptorArray for the |function| still has the
1140 // "name" property at the original spot and that property is still
1141 // implemented via an AccessorInfo (which effectively means that
1142 // it must be the FunctionNameGetter).
1143 Isolate* isolate = function->GetIsolate();
1144 if (!UseFastFunctionNameLookup(isolate, function->map())) {
1145 // Normally there should be an else case for the fast-path check
1146 // above, which should invoke JSFunction::GetName(), since that's
1147 // what the FunctionNameGetter does, however GetDataProperty() has
1148 // never invoked accessors and thus always returned undefined for
1149 // JSFunction where the "name" property is untouched, so we retain
1150 // that exact behavior and go with SharedFunctionInfo::DebugName()
1151 // in case of the fast-path.
1152 Handle<Object> name =
1153 GetDataProperty(isolate, function, isolate->factory()->name_string());
1154 if (name->IsString()) return Handle<String>::cast(name);
1155 }
1156 return SharedFunctionInfo::DebugName(handle(function->shared(), isolate));
1157 }
1158
SetName(Handle<JSFunction> function,Handle<Name> name,Handle<String> prefix)1159 bool JSFunction::SetName(Handle<JSFunction> function, Handle<Name> name,
1160 Handle<String> prefix) {
1161 Isolate* isolate = function->GetIsolate();
1162 Handle<String> function_name;
1163 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, function_name,
1164 Name::ToFunctionName(isolate, name), false);
1165 if (prefix->length() > 0) {
1166 IncrementalStringBuilder builder(isolate);
1167 builder.AppendString(prefix);
1168 builder.AppendCharacter(' ');
1169 builder.AppendString(function_name);
1170 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, function_name, builder.Finish(),
1171 false);
1172 }
1173 RETURN_ON_EXCEPTION_VALUE(
1174 isolate,
1175 JSObject::DefinePropertyOrElementIgnoreAttributes(
1176 function, isolate->factory()->name_string(), function_name,
1177 static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY)),
1178 false);
1179 return true;
1180 }
1181
1182 namespace {
1183
NativeCodeFunctionSourceString(Handle<SharedFunctionInfo> shared_info)1184 Handle<String> NativeCodeFunctionSourceString(
1185 Handle<SharedFunctionInfo> shared_info) {
1186 Isolate* const isolate = shared_info->GetIsolate();
1187 IncrementalStringBuilder builder(isolate);
1188 builder.AppendCStringLiteral("function ");
1189 builder.AppendString(handle(shared_info->Name(), isolate));
1190 builder.AppendCStringLiteral("() { [native code] }");
1191 return builder.Finish().ToHandleChecked();
1192 }
1193
1194 } // namespace
1195
1196 // static
ToString(Handle<JSFunction> function)1197 Handle<String> JSFunction::ToString(Handle<JSFunction> function) {
1198 Isolate* const isolate = function->GetIsolate();
1199 Handle<SharedFunctionInfo> shared_info(function->shared(), isolate);
1200
1201 // Check if {function} should hide its source code.
1202 if (!shared_info->IsUserJavaScript()) {
1203 return NativeCodeFunctionSourceString(shared_info);
1204 }
1205
1206 // Check if we should print {function} as a class.
1207 Handle<Object> maybe_class_positions = JSReceiver::GetDataProperty(
1208 isolate, function, isolate->factory()->class_positions_symbol());
1209 if (maybe_class_positions->IsClassPositions()) {
1210 ClassPositions class_positions =
1211 ClassPositions::cast(*maybe_class_positions);
1212 int start_position = class_positions.start();
1213 int end_position = class_positions.end();
1214 Handle<String> script_source(
1215 String::cast(Script::cast(shared_info->script()).source()), isolate);
1216 return isolate->factory()->NewSubString(script_source, start_position,
1217 end_position);
1218 }
1219
1220 // Check if we have source code for the {function}.
1221 if (!shared_info->HasSourceCode()) {
1222 return NativeCodeFunctionSourceString(shared_info);
1223 }
1224
1225 // If this function was compiled from asm.js, use the recorded offset
1226 // information.
1227 #if V8_ENABLE_WEBASSEMBLY
1228 if (shared_info->HasWasmExportedFunctionData()) {
1229 Handle<WasmExportedFunctionData> function_data(
1230 shared_info->wasm_exported_function_data(), isolate);
1231 const wasm::WasmModule* module = function_data->instance().module();
1232 if (is_asmjs_module(module)) {
1233 std::pair<int, int> offsets =
1234 module->asm_js_offset_information->GetFunctionOffsets(
1235 declared_function_index(module, function_data->function_index()));
1236 Handle<String> source(
1237 String::cast(Script::cast(shared_info->script()).source()), isolate);
1238 return isolate->factory()->NewSubString(source, offsets.first,
1239 offsets.second);
1240 }
1241 }
1242 #endif // V8_ENABLE_WEBASSEMBLY
1243
1244 if (shared_info->function_token_position() == kNoSourcePosition) {
1245 // If the function token position isn't valid, return [native code] to
1246 // ensure calling eval on the returned source code throws rather than
1247 // giving inconsistent call behaviour.
1248 isolate->CountUsage(
1249 v8::Isolate::UseCounterFeature::kFunctionTokenOffsetTooLongForToString);
1250 return NativeCodeFunctionSourceString(shared_info);
1251 }
1252 return Handle<String>::cast(
1253 SharedFunctionInfo::GetSourceCodeHarmony(shared_info));
1254 }
1255
1256 // static
CalculateExpectedNofProperties(Isolate * isolate,Handle<JSFunction> function)1257 int JSFunction::CalculateExpectedNofProperties(Isolate* isolate,
1258 Handle<JSFunction> function) {
1259 int expected_nof_properties = 0;
1260 for (PrototypeIterator iter(isolate, function, kStartAtReceiver);
1261 !iter.IsAtEnd(); iter.Advance()) {
1262 Handle<JSReceiver> current =
1263 PrototypeIterator::GetCurrent<JSReceiver>(iter);
1264 if (!current->IsJSFunction()) break;
1265 Handle<JSFunction> func = Handle<JSFunction>::cast(current);
1266 // The super constructor should be compiled for the number of expected
1267 // properties to be available.
1268 Handle<SharedFunctionInfo> shared(func->shared(), isolate);
1269 IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate));
1270 if (is_compiled_scope.is_compiled() ||
1271 Compiler::Compile(isolate, func, Compiler::CLEAR_EXCEPTION,
1272 &is_compiled_scope)) {
1273 DCHECK(shared->is_compiled());
1274 int count = shared->expected_nof_properties();
1275 // Check that the estimate is sensible.
1276 if (expected_nof_properties <= JSObject::kMaxInObjectProperties - count) {
1277 expected_nof_properties += count;
1278 } else {
1279 return JSObject::kMaxInObjectProperties;
1280 }
1281 } else {
1282 // In case there was a compilation error proceed iterating in case there
1283 // will be a builtin function in the prototype chain that requires
1284 // certain number of in-object properties.
1285 continue;
1286 }
1287 }
1288 // Inobject slack tracking will reclaim redundant inobject space
1289 // later, so we can afford to adjust the estimate generously,
1290 // meaning we over-allocate by at least 8 slots in the beginning.
1291 if (expected_nof_properties > 0) {
1292 expected_nof_properties += 8;
1293 if (expected_nof_properties > JSObject::kMaxInObjectProperties) {
1294 expected_nof_properties = JSObject::kMaxInObjectProperties;
1295 }
1296 }
1297 return expected_nof_properties;
1298 }
1299
1300 // static
CalculateInstanceSizeHelper(InstanceType instance_type,bool has_prototype_slot,int requested_embedder_fields,int requested_in_object_properties,int * instance_size,int * in_object_properties)1301 void JSFunction::CalculateInstanceSizeHelper(InstanceType instance_type,
1302 bool has_prototype_slot,
1303 int requested_embedder_fields,
1304 int requested_in_object_properties,
1305 int* instance_size,
1306 int* in_object_properties) {
1307 DCHECK_LE(static_cast<unsigned>(requested_embedder_fields),
1308 JSObject::kMaxEmbedderFields);
1309 int header_size = JSObject::GetHeaderSize(instance_type, has_prototype_slot);
1310 requested_embedder_fields *= kEmbedderDataSlotSizeInTaggedSlots;
1311
1312 int max_nof_fields =
1313 (JSObject::kMaxInstanceSize - header_size) >> kTaggedSizeLog2;
1314 CHECK_LE(max_nof_fields, JSObject::kMaxInObjectProperties);
1315 CHECK_LE(static_cast<unsigned>(requested_embedder_fields),
1316 static_cast<unsigned>(max_nof_fields));
1317 *in_object_properties = std::min(requested_in_object_properties,
1318 max_nof_fields - requested_embedder_fields);
1319 *instance_size =
1320 header_size +
1321 ((requested_embedder_fields + *in_object_properties) << kTaggedSizeLog2);
1322 CHECK_EQ(*in_object_properties,
1323 ((*instance_size - header_size) >> kTaggedSizeLog2) -
1324 requested_embedder_fields);
1325 CHECK_LE(static_cast<unsigned>(*instance_size),
1326 static_cast<unsigned>(JSObject::kMaxInstanceSize));
1327 }
1328
ClearTypeFeedbackInfo()1329 void JSFunction::ClearTypeFeedbackInfo() {
1330 ResetIfCodeFlushed();
1331 if (has_feedback_vector()) {
1332 FeedbackVector vector = feedback_vector();
1333 Isolate* isolate = GetIsolate();
1334 if (vector.ClearSlots(isolate)) {
1335 IC::OnFeedbackChanged(isolate, vector, FeedbackSlot::Invalid(),
1336 "ClearTypeFeedbackInfo");
1337 }
1338 }
1339 }
1340
1341 } // namespace internal
1342 } // namespace v8
1343
1344 #include "src/objects/object-macros-undef.h"
1345