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 20namespace v8 { 21namespace internal { 22 23CodeKinds 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 33CodeKinds 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 62bool JSFunction::HasAttachedOptimizedCode() const { 63 CodeKinds result = GetAttachedCodeKinds(); 64 return (result & kOptimizedJSFunctionCodeKindsMask) != 0; 65} 66 67bool JSFunction::HasAvailableOptimizedCode() const { 68 CodeKinds result = GetAvailableCodeKinds(); 69 return (result & kOptimizedJSFunctionCodeKindsMask) != 0; 70} 71 72bool JSFunction::HasAttachedCodeKind(CodeKind kind) const { 73 CodeKinds result = GetAttachedCodeKinds(); 74 return (result & CodeKindToCodeKindFlag(kind)) != 0; 75} 76 77bool JSFunction::HasAvailableCodeKind(CodeKind kind) const { 78 CodeKinds result = GetAvailableCodeKinds(); 79 return (result & CodeKindToCodeKindFlag(kind)) != 0; 80} 81 82namespace { 83 84// Returns false if no highest tier exists (i.e. the function is not compiled), 85// otherwise returns true and sets highest_tier. 86V8_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 101base::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 133bool JSFunction::ActiveTierIsIgnition() const { 134 return GetActiveTier() == CodeKind::INTERPRETED_FUNCTION; 135} 136 137bool JSFunction::ActiveTierIsBaseline() const { 138 return GetActiveTier() == CodeKind::BASELINE; 139} 140 141bool JSFunction::ActiveTierIsMaglev() const { 142 return GetActiveTier() == CodeKind::MAGLEV; 143} 144 145bool JSFunction::ActiveTierIsTurbofan() const { 146 return GetActiveTier() == CodeKind::TURBOFAN; 147} 148 149bool 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 164namespace { 165 166constexpr 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 179void 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 214void JSFunction::SetInterruptBudget(Isolate* isolate) { 215 raw_feedback_cell().set_interrupt_budget( 216 TieringManager::InterruptBudgetFor(isolate, *this)); 217} 218 219// static 220Maybe<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 305MaybeHandle<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 337Maybe<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 374Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) { 375 Isolate* const isolate = function->GetIsolate(); 376 return isolate->factory()->function_native_code_string(); 377} 378 379// static 380MaybeHandle<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 402Maybe<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 419Handle<String> JSWrappedFunction::ToString(Handle<JSWrappedFunction> function) { 420 Isolate* const isolate = function->GetIsolate(); 421 return isolate->factory()->function_native_code_string(); 422} 423 424// static 425MaybeHandle<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 480Handle<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 489void 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 538void 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 552void 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 580void 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 620namespace { 621 622void 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 666void 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 706void JSFunction::SetInitialMap(Isolate* isolate, Handle<JSFunction> function, 707 Handle<Map> map, Handle<HeapObject> prototype) { 708 SetInitialMap(isolate, function, map, prototype, function); 709} 710 711void 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 726void 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 776namespace { 777 778#ifdef DEBUG 779bool 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 910bool 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 968MaybeHandle<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 1041namespace { 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 1055TYPED_ARRAYS(TYPED_ARRAY_CASE) 1056#undef TYPED_ARRAY_CASE 1057 1058int 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 1063int 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 1070Handle<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 1096int 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 1105std::unique_ptr<char[]> JSFunction::DebugNameCStr() { 1106 return shared().DebugNameCStr(); 1107} 1108 1109void JSFunction::PrintName(FILE* out) { 1110 PrintF(out, "%s", DebugNameCStr().get()); 1111} 1112 1113namespace { 1114 1115bool 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 1136Handle<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 1159bool 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 1182namespace { 1183 1184Handle<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 1197Handle<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 1257int 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 1301void 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 1329void 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