blob: abd376a6da45245e83f9053c9c96c06a753b013b [file] [log] [blame]
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/objects/js-function.h"
#include "src/base/optional.h"
#include "src/baseline/baseline-batch-compiler.h"
#include "src/codegen/compiler.h"
#include "src/common/globals.h"
#include "src/diagnostics/code-tracer.h"
#include "src/execution/frames-inl.h"
#include "src/execution/isolate.h"
#include "src/execution/tiering-manager.h"
#include "src/heap/heap-inl.h"
#include "src/ic/ic.h"
#include "src/init/bootstrapper.h"
#include "src/objects/feedback-cell-inl.h"
#include "src/strings/string-builder-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
CodeKinds JSFunction::GetAttachedCodeKinds(IsolateForSandbox isolate) const {
const CodeKind kind = code(isolate)->kind();
if (!CodeKindIsJSFunction(kind)) return {};
if (CodeKindIsOptimizedJSFunction(kind) &&
code(isolate)->marked_for_deoptimization()) {
return {};
}
return CodeKindToCodeKindFlag(kind);
}
CodeKinds JSFunction::GetAvailableCodeKinds(IsolateForSandbox isolate) const {
CodeKinds result = GetAttachedCodeKinds(isolate);
if ((result & CodeKindFlag::INTERPRETED_FUNCTION) == 0) {
// The SharedFunctionInfo could have attached bytecode.
if (shared()->HasBytecodeArray()) {
result |= CodeKindFlag::INTERPRETED_FUNCTION;
}
}
if ((result & CodeKindFlag::BASELINE) == 0) {
// The SharedFunctionInfo could have attached baseline code.
if (shared()->HasBaselineCode()) {
result |= CodeKindFlag::BASELINE;
}
}
// Check the optimized code cache.
if (has_feedback_vector() && feedback_vector()->has_optimized_code() &&
!feedback_vector()
->optimized_code(isolate)
->marked_for_deoptimization()) {
Tagged<Code> code = feedback_vector()->optimized_code(isolate);
DCHECK(CodeKindIsOptimizedJSFunction(code->kind()));
result |= CodeKindToCodeKindFlag(code->kind());
}
DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
return result;
}
bool JSFunction::HasAttachedOptimizedCode(IsolateForSandbox isolate) const {
CodeKinds result = GetAttachedCodeKinds(isolate);
return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
}
bool JSFunction::HasAvailableHigherTierCodeThan(IsolateForSandbox isolate,
CodeKind kind) const {
return HasAvailableHigherTierCodeThanWithFilter(isolate, kind,
kJSFunctionCodeKindsMask);
}
bool JSFunction::HasAvailableHigherTierCodeThanWithFilter(
IsolateForSandbox isolate, CodeKind kind, CodeKinds filter_mask) const {
const int kind_as_int_flag = static_cast<int>(CodeKindToCodeKindFlag(kind));
DCHECK(base::bits::IsPowerOfTwo(kind_as_int_flag));
// Smear right - any higher present bit means we have a higher tier available.
const int mask = kind_as_int_flag | (kind_as_int_flag - 1);
const CodeKinds masked_available_kinds =
GetAvailableCodeKinds(isolate) & filter_mask;
return (masked_available_kinds & static_cast<CodeKinds>(~mask)) != 0;
}
bool JSFunction::HasAvailableOptimizedCode(IsolateForSandbox isolate) const {
CodeKinds result = GetAvailableCodeKinds(isolate);
return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
}
bool JSFunction::HasAttachedCodeKind(IsolateForSandbox isolate,
CodeKind kind) const {
CodeKinds result = GetAttachedCodeKinds(isolate);
return (result & CodeKindToCodeKindFlag(kind)) != 0;
}
bool JSFunction::HasAvailableCodeKind(IsolateForSandbox isolate,
CodeKind kind) const {
CodeKinds result = GetAvailableCodeKinds(isolate);
return (result & CodeKindToCodeKindFlag(kind)) != 0;
}
namespace {
// Returns false if no highest tier exists (i.e. the function is not compiled),
// otherwise returns true and sets highest_tier.
V8_WARN_UNUSED_RESULT bool HighestTierOf(CodeKinds kinds,
CodeKind* highest_tier) {
DCHECK_EQ((kinds & ~kJSFunctionCodeKindsMask), 0);
// Higher tiers > lower tiers.
static_assert(CodeKind::TURBOFAN > CodeKind::INTERPRETED_FUNCTION);
if (kinds == 0) return false;
const int highest_tier_log2 =
31 - base::bits::CountLeadingZeros(static_cast<uint32_t>(kinds));
DCHECK(CodeKindIsJSFunction(static_cast<CodeKind>(highest_tier_log2)));
*highest_tier = static_cast<CodeKind>(highest_tier_log2);
return true;
}
} // namespace
base::Optional<CodeKind> JSFunction::GetActiveTier(
IsolateForSandbox isolate) const {
#if V8_ENABLE_WEBASSEMBLY
// Asm/Wasm functions are currently not supported. For simplicity, this
// includes invalid asm.js functions whose code hasn't yet been updated to
// CompileLazy but is still the InstantiateAsmJs builtin.
if (shared()->HasAsmWasmData() ||
code(isolate)->builtin_id() == Builtin::kInstantiateAsmJs) {
return {};
}
#endif // V8_ENABLE_WEBASSEMBLY
CodeKind highest_tier;
if (!HighestTierOf(GetAvailableCodeKinds(isolate), &highest_tier)) return {};
#ifdef DEBUG
CHECK(highest_tier == CodeKind::TURBOFAN ||
highest_tier == CodeKind::BASELINE ||
highest_tier == CodeKind::MAGLEV ||
highest_tier == CodeKind::INTERPRETED_FUNCTION);
if (highest_tier == CodeKind::INTERPRETED_FUNCTION) {
CHECK(code(isolate)->is_interpreter_trampoline_builtin() ||
(CodeKindIsOptimizedJSFunction(code(isolate)->kind()) &&
code(isolate)->marked_for_deoptimization()) ||
(code(isolate)->builtin_id() == Builtin::kCompileLazy &&
shared()->HasBytecodeArray() && !shared()->HasBaselineCode()));
}
#endif // DEBUG
return highest_tier;
}
bool JSFunction::ActiveTierIsIgnition(IsolateForSandbox isolate) const {
return GetActiveTier(isolate) == CodeKind::INTERPRETED_FUNCTION;
}
bool JSFunction::ActiveTierIsBaseline(IsolateForSandbox isolate) const {
return GetActiveTier(isolate) == CodeKind::BASELINE;
}
bool JSFunction::ActiveTierIsMaglev(IsolateForSandbox isolate) const {
return GetActiveTier(isolate) == CodeKind::MAGLEV;
}
bool JSFunction::ActiveTierIsTurbofan(IsolateForSandbox isolate) const {
return GetActiveTier(isolate) == CodeKind::TURBOFAN;
}
bool JSFunction::CanDiscardCompiled(IsolateForSandbox isolate) const {
// Essentially, what we are asking here is, has this function been compiled
// from JS code? We can currently tell only indirectly, by looking at
// available code kinds. If any JS code kind exists, we can discard.
//
// Attached optimized code that is marked for deoptimization will not show up
// in the list of available code kinds, thus we must check for it manually.
//
// Note that when the function has not yet been compiled we also return
// false; that's fine, since nothing must be discarded in that case.
if (CodeKindIsOptimizedJSFunction(code(isolate)->kind())) return true;
CodeKinds result = GetAvailableCodeKinds(isolate);
return (result & kJSFunctionCodeKindsMask) != 0;
}
namespace {
constexpr TieringState TieringStateFor(CodeKind target_kind,
ConcurrencyMode mode) {
DCHECK(target_kind == CodeKind::MAGLEV || target_kind == CodeKind::TURBOFAN);
return target_kind == CodeKind::MAGLEV
? (IsConcurrent(mode) ? TieringState::kRequestMaglev_Concurrent
: TieringState::kRequestMaglev_Synchronous)
: (IsConcurrent(mode)
? TieringState::kRequestTurbofan_Concurrent
: TieringState::kRequestTurbofan_Synchronous);
}
} // namespace
void JSFunction::MarkForOptimization(Isolate* isolate, CodeKind target_kind,
ConcurrencyMode mode) {
if (!isolate->concurrent_recompilation_enabled() ||
isolate->bootstrapper()->IsActive()) {
mode = ConcurrencyMode::kSynchronous;
}
DCHECK(CodeKindIsOptimizedJSFunction(target_kind));
DCHECK(!is_compiled(isolate) || ActiveTierIsIgnition(isolate) ||
ActiveTierIsBaseline(isolate) || ActiveTierIsMaglev(isolate));
DCHECK(!ActiveTierIsTurbofan(isolate));
DCHECK(shared()->HasBytecodeArray());
DCHECK(shared()->allows_lazy_compilation() ||
!shared()->optimization_disabled());
if (IsConcurrent(mode)) {
if (IsInProgress(tiering_state())) {
if (v8_flags.trace_concurrent_recompilation) {
PrintF(" ** Not marking ");
ShortPrint(*this);
PrintF(" -- already in optimization queue.\n");
}
return;
}
if (v8_flags.trace_concurrent_recompilation) {
PrintF(" ** Marking ");
ShortPrint(*this);
PrintF(" for concurrent %s recompilation.\n",
CodeKindToString(target_kind));
}
}
set_tiering_state(isolate, TieringStateFor(target_kind, mode));
}
void JSFunction::SetInterruptBudget(
Isolate* isolate, base::Optional<CodeKind> override_active_tier) {
raw_feedback_cell()->set_interrupt_budget(
TieringManager::InterruptBudgetFor(isolate, *this, override_active_tier));
}
// static
Maybe<bool> JSFunctionOrBoundFunctionOrWrappedFunction::CopyNameAndLength(
Isolate* isolate,
Handle<JSFunctionOrBoundFunctionOrWrappedFunction> function,
Handle<JSReceiver> target, Handle<String> prefix, int arg_count) {
// Setup the "length" property based on the "length" of the {target}.
// If the targets length is the default JSFunction accessor, we can keep the
// accessor that's installed by default on the
// JSBoundFunction/JSWrappedFunction. It lazily computes the value from the
// underlying internal length.
Handle<AccessorInfo> function_length_accessor =
isolate->factory()->function_length_accessor();
LookupIterator length_lookup(isolate, target,
isolate->factory()->length_string(), target,
LookupIterator::OWN);
if (!IsJSFunction(*target) ||
length_lookup.state() != LookupIterator::ACCESSOR ||
!length_lookup.GetAccessors().is_identical_to(function_length_accessor)) {
Handle<Object> length(Smi::zero(), isolate);
Maybe<PropertyAttributes> attributes =
JSReceiver::GetPropertyAttributes(&length_lookup);
if (attributes.IsNothing()) return Nothing<bool>();
if (attributes.FromJust() != ABSENT) {
Handle<Object> target_length;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_length,
Object::GetProperty(&length_lookup),
Nothing<bool>());
if (IsNumber(*target_length)) {
length = isolate->factory()->NewNumber(std::max(
0.0, DoubleToInteger(Object::Number(*target_length)) - arg_count));
}
}
LookupIterator it(isolate, function, isolate->factory()->length_string(),
function);
DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
RETURN_ON_EXCEPTION_VALUE(isolate,
JSObject::DefineOwnPropertyIgnoreAttributes(
&it, length, it.property_attributes()),
Nothing<bool>());
}
// Setup the "name" property based on the "name" of the {target}.
// If the target's name is the default JSFunction accessor, we can keep the
// accessor that's installed by default on the
// JSBoundFunction/JSWrappedFunction. It lazily computes the value from the
// underlying internal name.
Handle<AccessorInfo> function_name_accessor =
isolate->factory()->function_name_accessor();
LookupIterator name_lookup(isolate, target, isolate->factory()->name_string(),
target);
if (!IsJSFunction(*target) ||
name_lookup.state() != LookupIterator::ACCESSOR ||
!name_lookup.GetAccessors().is_identical_to(function_name_accessor) ||
(name_lookup.IsFound() && !name_lookup.HolderIsReceiver())) {
Handle<Object> target_name;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, target_name,
Object::GetProperty(&name_lookup),
Nothing<bool>());
Handle<String> name;
if (IsString(*target_name)) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, name,
Name::ToFunctionName(isolate, Handle<String>::cast(target_name)),
Nothing<bool>());
if (!prefix.is_null()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, name, isolate->factory()->NewConsString(prefix, name),
Nothing<bool>());
}
} else if (prefix.is_null()) {
name = isolate->factory()->empty_string();
} else {
name = prefix;
}
LookupIterator it(isolate, function, isolate->factory()->name_string());
DCHECK_EQ(LookupIterator::ACCESSOR, it.state());
RETURN_ON_EXCEPTION_VALUE(isolate,
JSObject::DefineOwnPropertyIgnoreAttributes(
&it, name, it.property_attributes()),
Nothing<bool>());
}
return Just(true);
}
// static
MaybeHandle<String> JSBoundFunction::GetName(Isolate* isolate,
Handle<JSBoundFunction> function) {
Handle<String> prefix = isolate->factory()->bound__string();
Handle<String> target_name = prefix;
Factory* factory = isolate->factory();
// Concatenate the "bound " up to the last non-bound target.
while (IsJSBoundFunction(function->bound_target_function())) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, target_name,
factory->NewConsString(prefix, target_name),
String);
function = handle(JSBoundFunction::cast(function->bound_target_function()),
isolate);
}
if (IsJSWrappedFunction(function->bound_target_function())) {
Handle<JSWrappedFunction> target(
JSWrappedFunction::cast(function->bound_target_function()), isolate);
Handle<String> name;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, name, JSWrappedFunction::GetName(isolate, target), String);
return factory->NewConsString(target_name, name);
}
if (IsJSFunction(function->bound_target_function())) {
Handle<JSFunction> target(
JSFunction::cast(function->bound_target_function()), isolate);
Handle<String> name = JSFunction::GetName(isolate, target);
return factory->NewConsString(target_name, name);
}
// This will omit the proper target name for bound JSProxies.
return target_name;
}
// static
Maybe<int> JSBoundFunction::GetLength(Isolate* isolate,
Handle<JSBoundFunction> function) {
int nof_bound_arguments = function->bound_arguments()->length();
while (IsJSBoundFunction(function->bound_target_function())) {
function = handle(JSBoundFunction::cast(function->bound_target_function()),
isolate);
// Make sure we never overflow {nof_bound_arguments}, the number of
// arguments of a function is strictly limited by the max length of an
// JSAarray, Smi::kMaxValue is thus a reasonably good overestimate.
int length = function->bound_arguments()->length();
if (V8_LIKELY(Smi::kMaxValue - nof_bound_arguments > length)) {
nof_bound_arguments += length;
} else {
nof_bound_arguments = Smi::kMaxValue;
}
}
if (IsJSWrappedFunction(function->bound_target_function())) {
Handle<JSWrappedFunction> target(
JSWrappedFunction::cast(function->bound_target_function()), isolate);
int target_length = 0;
MAYBE_ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, target_length, JSWrappedFunction::GetLength(isolate, target),
Nothing<int>());
int length = std::max(0, target_length - nof_bound_arguments);
return Just(length);
}
// All non JSFunction targets get a direct property and don't use this
// accessor.
Handle<JSFunction> target(JSFunction::cast(function->bound_target_function()),
isolate);
int target_length = target->length();
int length = std::max(0, target_length - nof_bound_arguments);
return Just(length);
}
// static
Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) {
Isolate* const isolate = function->GetIsolate();
return isolate->factory()->function_native_code_string();
}
// static
MaybeHandle<String> JSWrappedFunction::GetName(
Isolate* isolate, Handle<JSWrappedFunction> function) {
STACK_CHECK(isolate, MaybeHandle<String>());
Factory* factory = isolate->factory();
Handle<String> target_name = factory->empty_string();
Handle<JSReceiver> target =
handle(function->wrapped_target_function(), isolate);
if (IsJSBoundFunction(*target)) {
return JSBoundFunction::GetName(
isolate,
handle(JSBoundFunction::cast(function->wrapped_target_function()),
isolate));
} else if (IsJSFunction(*target)) {
return JSFunction::GetName(
isolate,
handle(JSFunction::cast(function->wrapped_target_function()), isolate));
}
// This will omit the proper target name for bound JSProxies.
return target_name;
}
// static
Maybe<int> JSWrappedFunction::GetLength(Isolate* isolate,
Handle<JSWrappedFunction> function) {
STACK_CHECK(isolate, Nothing<int>());
Handle<JSReceiver> target =
handle(function->wrapped_target_function(), isolate);
if (IsJSBoundFunction(*target)) {
return JSBoundFunction::GetLength(
isolate,
handle(JSBoundFunction::cast(function->wrapped_target_function()),
isolate));
}
// All non JSFunction targets get a direct property and don't use this
// accessor.
return Just(Handle<JSFunction>::cast(target)->length());
}
// static
Handle<String> JSWrappedFunction::ToString(Handle<JSWrappedFunction> function) {
Isolate* const isolate = function->GetIsolate();
return isolate->factory()->function_native_code_string();
}
// static
MaybeHandle<Object> JSWrappedFunction::Create(
Isolate* isolate, Handle<NativeContext> creation_context,
Handle<JSReceiver> value) {
// The value must be a callable according to the specification.
DCHECK(IsCallable(*value));
// The intermediate wrapped functions are not user-visible. And calling a
// wrapped function won't cause a side effect in the creation realm.
// Unwrap here to avoid nested unwrapping at the call site.
if (IsJSWrappedFunction(*value)) {
Handle<JSWrappedFunction> target_wrapped =
Handle<JSWrappedFunction>::cast(value);
value =
Handle<JSReceiver>(target_wrapped->wrapped_target_function(), isolate);
}
// 1. Let internalSlotsList be the internal slots listed in Table 2, plus
// [[Prototype]] and [[Extensible]].
// 2. Let wrapped be ! MakeBasicObject(internalSlotsList).
// 3. Set wrapped.[[Prototype]] to
// callerRealm.[[Intrinsics]].[[%Function.prototype%]].
// 4. Set wrapped.[[Call]] as described in 2.1.
// 5. Set wrapped.[[WrappedTargetFunction]] to Target.
// 6. Set wrapped.[[Realm]] to callerRealm.
Handle<JSWrappedFunction> wrapped =
isolate->factory()->NewJSWrappedFunction(creation_context, value);
// 7. Let result be CopyNameAndLength(wrapped, Target, "wrapped").
Maybe<bool> is_abrupt =
JSFunctionOrBoundFunctionOrWrappedFunction::CopyNameAndLength(
isolate, wrapped, value, Handle<String>(), 0);
// 8. If result is an Abrupt Completion, throw a TypeError exception.
if (is_abrupt.IsNothing()) {
DCHECK(isolate->has_exception());
Handle<Object> exception = Handle<Object>(isolate->exception(), isolate);
isolate->clear_exception();
// The TypeError thrown is created with creation Realm's TypeError
// constructor instead of the executing Realm's.
Handle<JSFunction> type_error_function =
Handle<JSFunction>(creation_context->type_error_function(), isolate);
Handle<String> string = Object::NoSideEffectsToString(isolate, exception);
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewError(type_error_function, MessageTemplate::kCannotWrap, string),
{});
}
DCHECK(is_abrupt.FromJust());
// 9. Return wrapped.
return wrapped;
}
// static
Handle<String> JSFunction::GetName(Isolate* isolate,
Handle<JSFunction> function) {
if (function->shared()->name_should_print_as_anonymous()) {
return isolate->factory()->anonymous_string();
}
return handle(function->shared()->Name(), isolate);
}
// static
void JSFunction::EnsureClosureFeedbackCellArray(
Handle<JSFunction> function, bool reset_budget_for_feedback_allocation) {
Isolate* const isolate = function->GetIsolate();
DCHECK(function->shared()->is_compiled());
DCHECK(function->shared()->HasFeedbackMetadata());
#if V8_ENABLE_WEBASSEMBLY
if (function->shared()->HasAsmWasmData()) return;
#endif // V8_ENABLE_WEBASSEMBLY
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
DCHECK(function->shared()->HasBytecodeArray());
const bool has_closure_feedback_cell_array =
(function->has_closure_feedback_cell_array() ||
function->has_feedback_vector());
// Initialize the interrupt budget to the feedback vector allocation budget
// when initializing the feedback cell for the first time or after a bytecode
// flush. We retain the closure feedback cell array on bytecode flush, so
// reset_budget_for_feedback_allocation is used to reset the budget in these
// cases.
if (reset_budget_for_feedback_allocation ||
!has_closure_feedback_cell_array) {
function->SetInterruptBudget(isolate);
}
if (has_closure_feedback_cell_array) {
return;
}
Handle<HeapObject> feedback_cell_array =
ClosureFeedbackCellArray::New(isolate, shared);
// Many closure cell is used as a way to specify that there is no
// feedback cell for this function and a new feedback cell has to be
// allocated for this function. For ex: for eval functions, we have to create
// a feedback cell and cache it along with the code. It is safe to use
// many_closure_cell to indicate this because in regular cases, it should
// already have a feedback_vector / feedback cell array allocated.
if (function->raw_feedback_cell() == isolate->heap()->many_closures_cell()) {
Handle<FeedbackCell> feedback_cell =
isolate->factory()->NewOneClosureCell(feedback_cell_array);
function->set_raw_feedback_cell(*feedback_cell, kReleaseStore);
function->SetInterruptBudget(isolate);
} else {
function->raw_feedback_cell()->set_value(*feedback_cell_array,
kReleaseStore);
}
}
// static
void JSFunction::EnsureFeedbackVector(Isolate* isolate,
Handle<JSFunction> function,
IsCompiledScope* compiled_scope) {
CHECK(compiled_scope->is_compiled());
DCHECK(function->shared()->HasFeedbackMetadata());
if (function->has_feedback_vector()) return;
#if V8_ENABLE_WEBASSEMBLY
if (function->shared()->HasAsmWasmData()) return;
#endif // V8_ENABLE_WEBASSEMBLY
CreateAndAttachFeedbackVector(isolate, function, compiled_scope);
}
// static
void JSFunction::CreateAndAttachFeedbackVector(
Isolate* isolate, Handle<JSFunction> function,
IsCompiledScope* compiled_scope) {
CHECK(compiled_scope->is_compiled());
DCHECK(function->shared()->HasFeedbackMetadata());
DCHECK(!function->has_feedback_vector());
#if V8_ENABLE_WEBASSEMBLY
DCHECK(!function->shared()->HasAsmWasmData());
#endif // V8_ENABLE_WEBASSEMBLY
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
DCHECK(function->shared()->HasBytecodeArray());
EnsureClosureFeedbackCellArray(function, false);
Handle<ClosureFeedbackCellArray> closure_feedback_cell_array =
handle(function->closure_feedback_cell_array(), isolate);
Handle<FeedbackVector> feedback_vector = FeedbackVector::New(
isolate, shared, closure_feedback_cell_array,
handle(function->raw_feedback_cell(isolate), isolate), compiled_scope);
USE(feedback_vector);
// EnsureClosureFeedbackCellArray should handle the special case where we need
// to allocate a new feedback cell. Please look at comment in that function
// for more details.
DCHECK(function->raw_feedback_cell() !=
isolate->heap()->many_closures_cell());
DCHECK_EQ(function->raw_feedback_cell()->value(), *feedback_vector);
function->SetInterruptBudget(isolate);
DCHECK_EQ(v8_flags.log_function_events,
feedback_vector->log_next_execution());
}
// static
void JSFunction::InitializeFeedbackCell(
Handle<JSFunction> function, IsCompiledScope* is_compiled_scope,
bool reset_budget_for_feedback_allocation) {
Isolate* const isolate = function->GetIsolate();
#if V8_ENABLE_WEBASSEMBLY
// The following checks ensure that the feedback vectors are compatible with
// the feedback metadata. For Asm / Wasm functions we never allocate / use
// feedback vectors, so a mismatch between the metadata and feedback vector is
// harmless. The checks could fail for functions that has has_asm_wasm_broken
// set at runtime (for ex: failed instantiation).
if (function->shared()->HasAsmWasmData()) return;
#endif // V8_ENABLE_WEBASSEMBLY
if (function->has_feedback_vector()) {
CHECK_EQ(function->feedback_vector()->length(),
function->feedback_vector()->metadata()->slot_count());
return;
}
if (function->has_closure_feedback_cell_array()) {
CHECK_EQ(
function->closure_feedback_cell_array()->length(),
function->shared()->feedback_metadata()->create_closure_slot_count());
}
const bool needs_feedback_vector =
!v8_flags.lazy_feedback_allocation || v8_flags.always_turbofan ||
// We also need a feedback vector for certain log events, collecting type
// profile and more precise code coverage.
v8_flags.log_function_events ||
!isolate->is_best_effort_code_coverage() ||
function->shared()->sparkplug_compiled();
if (needs_feedback_vector) {
CreateAndAttachFeedbackVector(isolate, function, is_compiled_scope);
} else {
EnsureClosureFeedbackCellArray(function,
reset_budget_for_feedback_allocation);
}
#ifdef V8_ENABLE_SPARKPLUG
// TODO(jgruber): Unduplicate these conditions from tiering-manager.cc.
if (function->shared()->sparkplug_compiled() &&
CanCompileWithBaseline(isolate, function->shared()) &&
function->ActiveTierIsIgnition(isolate)) {
if (v8_flags.baseline_batch_compilation) {
isolate->baseline_batch_compiler()->EnqueueFunction(function);
} else {
IsCompiledScope is_compiled_scope(
function->shared()->is_compiled_scope(isolate));
Compiler::CompileBaseline(isolate, function, Compiler::CLEAR_EXCEPTION,
&is_compiled_scope);
}
}
#endif // V8_ENABLE_SPARKPLUG
if (v8_flags.profile_guided_optimization &&
v8_flags.profile_guided_optimization_for_empty_feedback_vector &&
function->has_feedback_vector() &&
function->feedback_vector()->length() == 0) {
if (function->shared()->cached_tiering_decision() ==
CachedTieringDecision::kEarlyMaglev) {
function->MarkForOptimization(isolate, CodeKind::MAGLEV,
ConcurrencyMode::kConcurrent);
} else if (function->shared()->cached_tiering_decision() ==
CachedTieringDecision::kEarlyTurbofan) {
function->MarkForOptimization(isolate, CodeKind::TURBOFAN,
ConcurrencyMode::kConcurrent);
}
}
}
namespace {
void SetInstancePrototype(Isolate* isolate, Handle<JSFunction> function,
Handle<JSReceiver> value) {
// Now some logic for the maps of the objects that are created by using this
// function as a constructor.
if (function->has_initial_map()) {
// If the function has allocated the initial map replace it with a
// copy containing the new prototype. Also complete any in-object
// slack tracking that is in progress at this point because it is
// still tracking the old copy.
function->CompleteInobjectSlackTrackingIfActive();
Handle<Map> initial_map(function->initial_map(), isolate);
if (!isolate->bootstrapper()->IsActive() &&
initial_map->instance_type() == JS_OBJECT_TYPE) {
// Put the value in the initial map field until an initial map is needed.
// At that point, a new initial map is created and the prototype is put
// into the initial map where it belongs.
function->set_prototype_or_initial_map(*value, kReleaseStore);
if (IsJSObjectThatCanBeTrackedAsPrototype(*value)) {
// Optimize as prototype to detach it from its transition tree.
JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}
} else {
Handle<Map> new_map =
Map::Copy(isolate, initial_map, "SetInstancePrototype");
JSFunction::SetInitialMap(isolate, function, new_map, value);
DCHECK_IMPLIES(!isolate->bootstrapper()->IsActive(),
*function != function->native_context()->array_function());
}
// Deoptimize all code that embeds the previous initial map.
DependentCode::DeoptimizeDependencyGroups(
isolate, *initial_map, DependentCode::kInitialMapChangedGroup);
} else {
// Put the value in the initial map field until an initial map is
// needed. At that point, a new initial map is created and the
// prototype is put into the initial map where it belongs.
function->set_prototype_or_initial_map(*value, kReleaseStore);
if (IsJSObjectThatCanBeTrackedAsPrototype(*value)) {
// Optimize as prototype to detach it from its transition tree.
JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}
}
}
} // anonymous namespace
void JSFunction::SetPrototype(Handle<JSFunction> function,
Handle<Object> value) {
DCHECK(IsConstructor(*function) ||
IsGeneratorFunction(function->shared()->kind()));
Isolate* isolate = function->GetIsolate();
Handle<JSReceiver> construct_prototype;
// If the value is not a JSReceiver, store the value in the map's
// constructor field so it can be accessed. Also, set the prototype
// used for constructing objects to the original object prototype.
// See ECMA-262 13.2.2.
if (!IsJSReceiver(*value)) {
// Copy the map so this does not affect unrelated functions.
// Remove map transitions because they point to maps with a
// different prototype.
Handle<Map> new_map =
Map::Copy(isolate, handle(function->map(), isolate), "SetPrototype");
// Create a new {constructor, non-instance_prototype} tuple and store it
// in Map::constructor field.
Handle<Object> constructor(new_map->GetConstructor(), isolate);
Handle<Tuple2> non_instance_prototype_constructor_tuple =
isolate->factory()->NewTuple2(constructor, value, AllocationType::kOld);
new_map->set_has_non_instance_prototype(true);
new_map->SetConstructor(*non_instance_prototype_constructor_tuple);
JSObject::MigrateToMap(isolate, function, new_map);
FunctionKind kind = function->shared()->kind();
Handle<Context> native_context(function->native_context(), isolate);
construct_prototype = Handle<JSReceiver>(
IsGeneratorFunction(kind)
? IsAsyncFunction(kind)
? native_context->initial_async_generator_prototype()
: native_context->initial_generator_prototype()
: native_context->initial_object_prototype(),
isolate);
} else {
construct_prototype = Handle<JSReceiver>::cast(value);
function->map()->set_has_non_instance_prototype(false);
}
SetInstancePrototype(isolate, function, construct_prototype);
}
void JSFunction::SetInitialMap(Isolate* isolate, Handle<JSFunction> function,
Handle<Map> map, Handle<HeapObject> prototype) {
SetInitialMap(isolate, function, map, prototype, function);
}
void JSFunction::SetInitialMap(Isolate* isolate, Handle<JSFunction> function,
Handle<Map> map, Handle<HeapObject> prototype,
Handle<JSFunction> constructor) {
if (map->prototype() != *prototype) {
Map::SetPrototype(isolate, map, prototype);
}
map->SetConstructor(*constructor);
function->set_prototype_or_initial_map(*map, kReleaseStore);
if (v8_flags.log_maps) {
LOG(isolate, MapEvent("InitialMap", Handle<Map>(), map, "",
SharedFunctionInfo::DebugName(
isolate, handle(function->shared(), isolate))));
}
}
void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
DCHECK(function->has_prototype_slot());
DCHECK(IsConstructor(*function) ||
IsResumableFunction(function->shared()->kind()));
if (function->has_initial_map()) return;
Isolate* isolate = function->GetIsolate();
int expected_nof_properties =
CalculateExpectedNofProperties(isolate, function);
// {CalculateExpectedNofProperties} can have had the side effect of creating
// the initial map (e.g. it could have triggered an optimized compilation
// whose dependency installation reentered {EnsureHasInitialMap}).
if (function->has_initial_map()) return;
// Create a new map with the size and number of in-object properties suggested
// by the function.
InstanceType instance_type;
if (IsResumableFunction(function->shared()->kind())) {
instance_type = IsAsyncGeneratorFunction(function->shared()->kind())
? JS_ASYNC_GENERATOR_OBJECT_TYPE
: JS_GENERATOR_OBJECT_TYPE;
} else {
instance_type = JS_OBJECT_TYPE;
}
int instance_size;
int inobject_properties;
CalculateInstanceSizeHelper(instance_type, false, 0, expected_nof_properties,
&instance_size, &inobject_properties);
Handle<NativeContext> creation_context(function->native_context(), isolate);
Handle<Map> map = isolate->factory()->NewContextfulMap(
creation_context, instance_type, instance_size,
TERMINAL_FAST_ELEMENTS_KIND, inobject_properties);
// Fetch or allocate prototype.
Handle<HeapObject> prototype;
if (function->has_instance_prototype()) {
prototype = handle(function->instance_prototype(), isolate);
map->set_prototype(*prototype);
} else {
prototype = isolate->factory()->NewFunctionPrototype(function);
Map::SetPrototype(isolate, map, prototype);
}
DCHECK(map->has_fast_object_elements());
// Finally link initial map and constructor function.
DCHECK(IsJSReceiver(*prototype));
JSFunction::SetInitialMap(isolate, function, map, prototype);
map->StartInobjectSlackTracking();
}
namespace {
#ifdef DEBUG
bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
switch (instance_type) {
case JS_API_OBJECT_TYPE:
case JS_ARRAY_BUFFER_TYPE:
case JS_ARRAY_ITERATOR_PROTOTYPE_TYPE:
case JS_ARRAY_TYPE:
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_DATA_VIEW_TYPE:
case JS_RAB_GSAB_DATA_VIEW_TYPE:
case JS_DATE_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
case JS_FUNCTION_TYPE:
case JS_CLASS_CONSTRUCTOR_TYPE:
case JS_PROMISE_CONSTRUCTOR_TYPE:
case JS_REG_EXP_CONSTRUCTOR_TYPE:
case JS_ARRAY_CONSTRUCTOR_TYPE:
#define TYPED_ARRAY_CONSTRUCTORS_SWITCH(Type, type, TYPE, Ctype) \
case TYPE##_TYPED_ARRAY_CONSTRUCTOR_TYPE:
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTORS_SWITCH)
#undef TYPED_ARRAY_CONSTRUCTORS_SWITCH
case JS_ITERATOR_PROTOTYPE_TYPE:
case JS_MAP_ITERATOR_PROTOTYPE_TYPE:
case JS_OBJECT_PROTOTYPE_TYPE:
case JS_PROMISE_PROTOTYPE_TYPE:
case JS_REG_EXP_PROTOTYPE_TYPE:
case JS_SET_ITERATOR_PROTOTYPE_TYPE:
case JS_SET_PROTOTYPE_TYPE:
case JS_STRING_ITERATOR_PROTOTYPE_TYPE:
case JS_TYPED_ARRAY_PROTOTYPE_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_COLLATOR_TYPE:
case JS_DATE_TIME_FORMAT_TYPE:
case JS_DISPLAY_NAMES_TYPE:
case JS_DURATION_FORMAT_TYPE:
case JS_LIST_FORMAT_TYPE:
case JS_LOCALE_TYPE:
case JS_NUMBER_FORMAT_TYPE:
case JS_PLURAL_RULES_TYPE:
case JS_RELATIVE_TIME_FORMAT_TYPE:
case JS_SEGMENT_ITERATOR_TYPE:
case JS_SEGMENTER_TYPE:
case JS_SEGMENTS_TYPE:
case JS_V8_BREAK_ITERATOR_TYPE:
#endif
case JS_ASYNC_FUNCTION_OBJECT_TYPE:
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
case JS_MAP_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
case JS_OBJECT_TYPE:
case JS_ERROR_TYPE:
case JS_FINALIZATION_REGISTRY_TYPE:
case JS_ARGUMENTS_OBJECT_TYPE:
case JS_PROMISE_TYPE:
case JS_REG_EXP_TYPE:
case JS_SET_TYPE:
case JS_SHADOW_REALM_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE:
case JS_TYPED_ARRAY_TYPE:
case JS_PRIMITIVE_WRAPPER_TYPE:
case JS_TEMPORAL_CALENDAR_TYPE:
case JS_TEMPORAL_DURATION_TYPE:
case JS_TEMPORAL_INSTANT_TYPE:
case JS_TEMPORAL_PLAIN_DATE_TYPE:
case JS_TEMPORAL_PLAIN_DATE_TIME_TYPE:
case JS_TEMPORAL_PLAIN_MONTH_DAY_TYPE:
case JS_TEMPORAL_PLAIN_TIME_TYPE:
case JS_TEMPORAL_PLAIN_YEAR_MONTH_TYPE:
case JS_TEMPORAL_TIME_ZONE_TYPE:
case JS_TEMPORAL_ZONED_DATE_TIME_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_WEAK_REF_TYPE:
case JS_WEAK_SET_TYPE:
#if V8_ENABLE_WEBASSEMBLY
case WASM_GLOBAL_OBJECT_TYPE:
case WASM_INSTANCE_OBJECT_TYPE:
case WASM_MEMORY_OBJECT_TYPE:
case WASM_MODULE_OBJECT_TYPE:
case WASM_TABLE_OBJECT_TYPE:
case WASM_VALUE_OBJECT_TYPE:
#endif // V8_ENABLE_WEBASSEMBLY
return true;
case BIGINT_TYPE:
case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
case BYTECODE_ARRAY_TYPE:
case BYTE_ARRAY_TYPE:
case CELL_TYPE:
case INSTRUCTION_STREAM_TYPE:
case FILLER_TYPE:
case FIXED_ARRAY_TYPE:
case SCRIPT_CONTEXT_TABLE_TYPE:
case FIXED_DOUBLE_ARRAY_TYPE:
case FEEDBACK_METADATA_TYPE:
case FOREIGN_TYPE:
case FREE_SPACE_TYPE:
case HASH_TABLE_TYPE:
case ORDERED_HASH_MAP_TYPE:
case ORDERED_HASH_SET_TYPE:
case ORDERED_NAME_DICTIONARY_TYPE:
case NAME_DICTIONARY_TYPE:
case GLOBAL_DICTIONARY_TYPE:
case NUMBER_DICTIONARY_TYPE:
case SIMPLE_NUMBER_DICTIONARY_TYPE:
case HEAP_NUMBER_TYPE:
case JS_BOUND_FUNCTION_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_PROXY_TYPE:
case JS_WRAPPED_FUNCTION_TYPE:
case MAP_TYPE:
case ODDBALL_TYPE:
case PROPERTY_CELL_TYPE:
case CONST_TRACKING_LET_CELL_TYPE:
case SHARED_FUNCTION_INFO_TYPE:
case SYMBOL_TYPE:
case ALLOCATION_SITE_TYPE:
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case FIXED_##TYPE##_ARRAY_TYPE:
#undef TYPED_ARRAY_CASE
#define MAKE_STRUCT_CASE(TYPE, Name, name) case TYPE:
STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
// We must not end up here for these instance types at all.
UNREACHABLE();
default:
if (InstanceTypeChecker::IsJSApiObject(instance_type)) return true;
return false;
}
}
#endif // DEBUG
bool FastInitializeDerivedMap(Isolate* isolate, Handle<JSFunction> new_target,
Handle<JSFunction> constructor,
Handle<Map> constructor_initial_map) {
// Use the default intrinsic prototype instead.
if (!new_target->has_prototype_slot()) return false;
// Check that |function|'s initial map still in sync with the |constructor|,
// otherwise we must create a new initial map for |function|.
if (new_target->has_initial_map() &&
new_target->initial_map()->GetConstructor() == *constructor) {
DCHECK(IsJSReceiver(new_target->instance_prototype()));
return true;
}
InstanceType instance_type = constructor_initial_map->instance_type();
DCHECK(CanSubclassHaveInobjectProperties(instance_type));
// Create a new map with the size and number of in-object properties
// suggested by |function|.
// Link initial map and constructor function if the new.target is actually a
// subclass constructor.
if (!IsDerivedConstructor(new_target->shared()->kind())) return false;
int instance_size;
int in_object_properties;
int embedder_fields =
JSObject::GetEmbedderFieldCount(*constructor_initial_map);
// Constructor expects certain number of in-object properties to be in the
// object. However, CalculateExpectedNofProperties() may return smaller value
// if 1) the constructor is not in the prototype chain of new_target, or
// 2) the prototype chain is modified during iteration, or 3) compilation
// failure occur during prototype chain iteration.
// So we take the maximum of two values.
int expected_nof_properties = std::max(
static_cast<int>(constructor->shared()->expected_nof_properties()),
JSFunction::CalculateExpectedNofProperties(isolate, new_target));
JSFunction::CalculateInstanceSizeHelper(
instance_type, constructor_initial_map->has_prototype_slot(),
embedder_fields, expected_nof_properties, &instance_size,
&in_object_properties);
int pre_allocated = constructor_initial_map->GetInObjectProperties() -
constructor_initial_map->UnusedPropertyFields();
CHECK_LE(constructor_initial_map->UsedInstanceSize(), instance_size);
int unused_property_fields = in_object_properties - pre_allocated;
Handle<Map> map =
Map::CopyInitialMap(isolate, constructor_initial_map, instance_size,
in_object_properties, unused_property_fields);
map->set_new_target_is_base(false);
Handle<HeapObject> prototype(new_target->instance_prototype(), isolate);
JSFunction::SetInitialMap(isolate, new_target, map, prototype, constructor);
DCHECK(IsJSReceiver(new_target->instance_prototype()));
map->set_construction_counter(Map::kNoSlackTracking);
map->StartInobjectSlackTracking();
return true;
}
} // namespace
// static
MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
Handle<JSFunction> constructor,
Handle<JSReceiver> new_target) {
EnsureHasInitialMap(constructor);
Handle<Map> constructor_initial_map(constructor->initial_map(), isolate);
if (*new_target == *constructor) return constructor_initial_map;
Handle<Map> result_map;
// Fast case, new.target is a subclass of constructor. The map is cacheable
// (and may already have been cached). new.target.prototype is guaranteed to
// be a JSReceiver.
InstanceType new_target_instance_type = new_target->map()->instance_type();
if (InstanceTypeChecker::IsJSFunction(new_target_instance_type)) {
Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
if (FastInitializeDerivedMap(isolate, function, constructor,
constructor_initial_map)) {
return handle(function->initial_map(), isolate);
}
}
// Slow path, new.target is either a proxy object or can't cache the map.
// new.target.prototype is not guaranteed to be a JSReceiver, and may need to
// fall back to the intrinsicDefaultProto.
Handle<Object> prototype;
if (InstanceTypeChecker::IsJSFunction(new_target_instance_type) &&
Handle<JSFunction>::cast(new_target)->has_prototype_slot()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
// Make sure the new.target.prototype is cached.
EnsureHasInitialMap(function);
prototype = handle(function->prototype(), isolate);
} else {
// The new.target is a constructor but it's not a JSFunction with
// a prototype slot, so get the prototype property.
Handle<String> prototype_string = isolate->factory()->prototype_string();
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
JSReceiver::GetProperty(isolate, new_target, prototype_string), Map);
// The above prototype lookup might change the constructor and its
// prototype, hence we have to reload the initial map.
EnsureHasInitialMap(constructor);
constructor_initial_map = handle(constructor->initial_map(), isolate);
}
// If prototype is not a JSReceiver, fetch the intrinsicDefaultProto from the
// correct realm. Rather than directly fetching the .prototype, we fetch the
// constructor that points to the .prototype. This relies on
// constructor.prototype being FROZEN for those constructors.
if (!IsJSReceiver(*prototype)) {
Handle<NativeContext> native_context;
ASSIGN_RETURN_ON_EXCEPTION(isolate, native_context,
JSReceiver::GetFunctionRealm(new_target), Map);
Handle<Object> maybe_index = JSReceiver::GetDataProperty(
isolate, constructor,
isolate->factory()->native_context_index_symbol());
int index = IsSmi(*maybe_index) ? Smi::ToInt(*maybe_index)
: Context::OBJECT_FUNCTION_INDEX;
Handle<JSFunction> realm_constructor(
JSFunction::cast(native_context->get(index)), isolate);
prototype = handle(realm_constructor->prototype(), isolate);
}
DCHECK_EQ(constructor_initial_map->constructor_or_back_pointer(),
*constructor);
return Map::GetDerivedMap(isolate, constructor_initial_map,
Handle<JSReceiver>::cast(prototype));
}
namespace {
// Assert that the computations in TypedArrayElementsKindToConstructorIndex and
// TypedArrayElementsKindToRabGsabCtorIndex are sound.
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
static_assert(Context::TYPE##_ARRAY_FUN_INDEX == \
Context::FIRST_FIXED_TYPED_ARRAY_FUN_INDEX + \
ElementsKind::TYPE##_ELEMENTS - \
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND); \
static_assert(Context::RAB_GSAB_##TYPE##_ARRAY_MAP_INDEX == \
Context::FIRST_RAB_GSAB_TYPED_ARRAY_MAP_INDEX + \
ElementsKind::TYPE##_ELEMENTS - \
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND);
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
int TypedArrayElementsKindToConstructorIndex(ElementsKind elements_kind) {
return Context::FIRST_FIXED_TYPED_ARRAY_FUN_INDEX + elements_kind -
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND;
}
int TypedArrayElementsKindToRabGsabCtorIndex(ElementsKind elements_kind) {
return Context::FIRST_RAB_GSAB_TYPED_ARRAY_MAP_INDEX + elements_kind -
ElementsKind::FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND;
}
} // namespace
MaybeHandle<Map> JSFunction::GetDerivedRabGsabTypedArrayMap(
Isolate* isolate, Handle<JSFunction> constructor,
Handle<JSReceiver> new_target) {
MaybeHandle<Map> maybe_map = GetDerivedMap(isolate, constructor, new_target);
Handle<Map> map;
if (!maybe_map.ToHandle(&map)) {
return MaybeHandle<Map>();
}
{
DisallowHeapAllocation no_alloc;
Tagged<NativeContext> context = isolate->context()->native_context();
int ctor_index =
TypedArrayElementsKindToConstructorIndex(map->elements_kind());
if (*new_target == context->get(ctor_index)) {
ctor_index =
TypedArrayElementsKindToRabGsabCtorIndex(map->elements_kind());
return handle(Map::cast(context->get(ctor_index)), isolate);
}
}
// This only happens when subclassing TypedArrays. Create a new map with the
// corresponding RAB / GSAB ElementsKind. Note: the map is not cached and
// reused -> every array gets a unique map, making ICs slow.
Handle<Map> rab_gsab_map = Map::Copy(isolate, map, "RAB / GSAB");
rab_gsab_map->set_elements_kind(
GetCorrespondingRabGsabElementsKind(map->elements_kind()));
return rab_gsab_map;
}
MaybeHandle<Map> JSFunction::GetDerivedRabGsabDataViewMap(
Isolate* isolate, Handle<JSReceiver> new_target) {
Handle<Context> context =
handle(isolate->context()->native_context(), isolate);
Handle<JSFunction> constructor = handle(context->data_view_fun(), isolate);
MaybeHandle<Map> maybe_map = GetDerivedMap(isolate, constructor, new_target);
Handle<Map> map;
if (!maybe_map.ToHandle(&map)) {
return MaybeHandle<Map>();
}
if (*map == constructor->initial_map()) {
return handle(Map::cast(context->js_rab_gsab_data_view_map()), isolate);
}
// This only happens when subclassing DataViews. Create a new map with the
// JS_RAB_GSAB_DATA_VIEW instance type. Note: the map is not cached and
// reused -> every data view gets a unique map, making ICs slow.
Handle<Map> rab_gsab_map = Map::Copy(isolate, map, "RAB / GSAB");
rab_gsab_map->set_instance_type(JS_RAB_GSAB_DATA_VIEW_TYPE);
return rab_gsab_map;
}
int JSFunction::ComputeInstanceSizeWithMinSlack(Isolate* isolate) {
CHECK(has_initial_map());
if (initial_map()->IsInobjectSlackTrackingInProgress()) {
int slack = initial_map()->ComputeMinObjectSlack(isolate);
return initial_map()->InstanceSizeFromSlack(slack);
}
return initial_map()->instance_size();
}
std::unique_ptr<char[]> JSFunction::DebugNameCStr() {
return shared()->DebugNameCStr();
}
void JSFunction::PrintName(FILE* out) {
PrintF(out, "%s", DebugNameCStr().get());
}
namespace {
bool UseFastFunctionNameLookup(Isolate* isolate, Tagged<Map> map) {
DCHECK(IsJSFunctionMap(map));
if (map->NumberOfOwnDescriptors() <
JSFunction::kMinDescriptorsForFastBindAndWrap) {
return false;
}
DCHECK(!map->is_dictionary_map());
Tagged<HeapObject> value;
ReadOnlyRoots roots(isolate);
auto descriptors = map->instance_descriptors(isolate);
InternalIndex kNameIndex{JSFunction::kNameDescriptorIndex};
if (descriptors->GetKey(kNameIndex) != roots.name_string() ||
!descriptors->GetValue(kNameIndex)
.GetHeapObjectIfStrong(isolate, &value)) {
return false;
}
return IsAccessorInfo(value);
}
} // namespace
Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
// Below we use the same fast-path that we already established for
// Function.prototype.bind(), where we avoid a slow "name" property
// lookup if the DescriptorArray for the |function| still has the
// "name" property at the original spot and that property is still
// implemented via an AccessorInfo (which effectively means that
// it must be the FunctionNameGetter).
Isolate* isolate = function->GetIsolate();
if (!UseFastFunctionNameLookup(isolate, function->map())) {
// Normally there should be an else case for the fast-path check
// above, which should invoke JSFunction::GetName(), since that's
// what the FunctionNameGetter does, however GetDataProperty() has
// never invoked accessors and thus always returned undefined for
// JSFunction where the "name" property is untouched, so we retain
// that exact behavior and go with SharedFunctionInfo::DebugName()
// in case of the fast-path.
Handle<Object> name =
GetDataProperty(isolate, function, isolate->factory()->name_string());
if (IsString(*name)) return Handle<String>::cast(name);
}
return SharedFunctionInfo::DebugName(isolate,
handle(function->shared(), isolate));
}
bool JSFunction::SetName(Handle<JSFunction> function, Handle<Name> name,
Handle<String> prefix) {
Isolate* isolate = function->GetIsolate();
Handle<String> function_name;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, function_name,
Name::ToFunctionName(isolate, name), false);
if (prefix->length() > 0) {
IncrementalStringBuilder builder(isolate);
builder.AppendString(prefix);
builder.AppendCharacter(' ');
builder.AppendString(function_name);
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, function_name,
indirect_handle(builder.Finish(), isolate),
false);
}
RETURN_ON_EXCEPTION_VALUE(
isolate,
JSObject::DefinePropertyOrElementIgnoreAttributes(
function, isolate->factory()->name_string(), function_name,
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY)),
false);
return true;
}
namespace {
Handle<String> NativeCodeFunctionSourceString(
Isolate* isolate, Handle<SharedFunctionInfo> shared_info) {
IncrementalStringBuilder builder(isolate);
builder.AppendCStringLiteral("function ");
builder.AppendString(handle(shared_info->Name(), isolate));
builder.AppendCStringLiteral("() { [native code] }");
return indirect_handle(builder.Finish().ToHandleChecked(), isolate);
}
} // namespace
// static
Handle<String> JSFunction::ToString(Handle<JSFunction> function) {
Isolate* const isolate = function->GetIsolate();
Handle<SharedFunctionInfo> shared_info(function->shared(), isolate);
// Check if {function} should hide its source code.
if (!shared_info->IsUserJavaScript()) {
return NativeCodeFunctionSourceString(isolate, shared_info);
}
if (IsClassConstructor(shared_info->kind())) {
// Check if we should print {function} as a class.
Handle<Object> maybe_class_positions = JSReceiver::GetDataProperty(
isolate, function, isolate->factory()->class_positions_symbol());
if (IsClassPositions(*maybe_class_positions)) {
Tagged<ClassPositions> class_positions =
ClassPositions::cast(*maybe_class_positions);
int start_position = class_positions->start();
int end_position = class_positions->end();
Handle<String> script_source(
String::cast(Script::cast(shared_info->script())->source()), isolate);
return isolate->factory()->NewSubString(script_source, start_position,
end_position);
}
}
// Check if we have source code for the {function}.
if (!shared_info->HasSourceCode()) {
return NativeCodeFunctionSourceString(isolate, shared_info);
}
// If this function was compiled from asm.js, use the recorded offset
// information.
#if V8_ENABLE_WEBASSEMBLY
if (shared_info->HasWasmExportedFunctionData()) {
Handle<WasmExportedFunctionData> function_data(
shared_info->wasm_exported_function_data(), isolate);
const wasm::WasmModule* module = function_data->instance()->module();
if (is_asmjs_module(module)) {
std::pair<int, int> offsets =
module->asm_js_offset_information->GetFunctionOffsets(
declared_function_index(module, function_data->function_index()));
Handle<String> source(
String::cast(Script::cast(shared_info->script())->source()), isolate);
return isolate->factory()->NewSubString(source, offsets.first,
offsets.second);
}
}
#endif // V8_ENABLE_WEBASSEMBLY
if (shared_info->function_token_position() == kNoSourcePosition) {
// If the function token position isn't valid, return [native code] to
// ensure calling eval on the returned source code throws rather than
// giving inconsistent call behaviour.
isolate->CountUsage(
v8::Isolate::UseCounterFeature::kFunctionTokenOffsetTooLongForToString);
return NativeCodeFunctionSourceString(isolate, shared_info);
}
return Handle<String>::cast(
SharedFunctionInfo::GetSourceCodeHarmony(isolate, shared_info));
}
// static
int JSFunction::CalculateExpectedNofProperties(Isolate* isolate,
Handle<JSFunction> function) {
int expected_nof_properties = 0;
for (PrototypeIterator iter(isolate, function, kStartAtReceiver);
!iter.IsAtEnd(); iter.Advance()) {
Handle<JSReceiver> current =
PrototypeIterator::GetCurrent<JSReceiver>(iter);
if (!IsJSFunction(*current)) break;
Handle<JSFunction> func = Handle<JSFunction>::cast(current);
// The super constructor should be compiled for the number of expected
// properties to be available.
Handle<SharedFunctionInfo> shared(func->shared(), isolate);
IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate));
if (is_compiled_scope.is_compiled() ||
Compiler::Compile(isolate, func, Compiler::CLEAR_EXCEPTION,
&is_compiled_scope)) {
DCHECK(shared->is_compiled());
int count = shared->expected_nof_properties();
// Check that the estimate is sensible.
if (expected_nof_properties <= JSObject::kMaxInObjectProperties - count) {
expected_nof_properties += count;
} else {
return JSObject::kMaxInObjectProperties;
}
} else {
// In case there was a compilation error proceed iterating in case there
// will be a builtin function in the prototype chain that requires
// certain number of in-object properties.
continue;
}
}
// Inobject slack tracking will reclaim redundant inobject space
// later, so we can afford to adjust the estimate generously,
// meaning we over-allocate by at least 8 slots in the beginning.
if (expected_nof_properties > 0) {
expected_nof_properties += 8;
if (expected_nof_properties > JSObject::kMaxInObjectProperties) {
expected_nof_properties = JSObject::kMaxInObjectProperties;
}
}
return expected_nof_properties;
}
// static
void JSFunction::CalculateInstanceSizeHelper(InstanceType instance_type,
bool has_prototype_slot,
int requested_embedder_fields,
int requested_in_object_properties,
int* instance_size,
int* in_object_properties) {
DCHECK_LE(static_cast<unsigned>(requested_embedder_fields),
JSObject::kMaxEmbedderFields);
int header_size = JSObject::GetHeaderSize(instance_type, has_prototype_slot);
requested_embedder_fields *= kEmbedderDataSlotSizeInTaggedSlots;
int max_nof_fields =
(JSObject::kMaxInstanceSize - header_size) >> kTaggedSizeLog2;
CHECK_LE(max_nof_fields, JSObject::kMaxInObjectProperties);
CHECK_LE(static_cast<unsigned>(requested_embedder_fields),
static_cast<unsigned>(max_nof_fields));
*in_object_properties = std::min(requested_in_object_properties,
max_nof_fields - requested_embedder_fields);
*instance_size =
header_size +
((requested_embedder_fields + *in_object_properties) << kTaggedSizeLog2);
CHECK_EQ(*in_object_properties,
((*instance_size - header_size) >> kTaggedSizeLog2) -
requested_embedder_fields);
CHECK_LE(static_cast<unsigned>(*instance_size),
static_cast<unsigned>(JSObject::kMaxInstanceSize));
}
void JSFunction::ClearAllTypeFeedbackInfoForTesting() {
Isolate* isolate = GetIsolate();
ResetIfCodeFlushed(isolate);
if (has_feedback_vector()) {
Tagged<FeedbackVector> vector = feedback_vector();
if (vector->ClearAllSlotsForTesting(isolate)) {
IC::OnFeedbackChanged(isolate, vector, FeedbackSlot::Invalid(),
"ClearAllTypeFeedbackInfoForTesting");
}
}
}
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"