#ifndef V8_OBJECTS_JS_FUNCTION_INL_H_
#define V8_OBJECTS_JS_FUNCTION_INL_H_
#include "src/objects/js-function.h"
#include <optional>
#include "src/debug/debug.h"
#include "src/diagnostics/code-tracer.h"
#include "src/ic/ic.h"
#include "src/init/bootstrapper.h"
#include "src/objects/abstract-code-inl.h"
#include "src/objects/feedback-cell-inl.h"
#include "src/objects/feedback-vector-inl.h"
#include "src/objects/instance-type-inl.h"
#include "src/objects/map-updater.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/sandbox/js-dispatch-table-inl.h"
#include "src/snapshot/embedded/embedded-data.h"
#include "src/objects/object-macros.h"
namespace v8::internal {
#include "torque-generated/src/objects/js-function-tq-inl.inc"
TQ_OBJECT_CONSTRUCTORS_IMPL(JSFunctionOrBoundFunctionOrWrappedFunction)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSBoundFunction)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSWrappedFunction)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSFunction)
ACCESSORS(JSFunction, raw_feedback_cell, Tagged<FeedbackCell>,
kFeedbackCellOffset)
RELEASE_ACQUIRE_ACCESSORS(JSFunction, raw_feedback_cell, Tagged<FeedbackCell>,
kFeedbackCellOffset)
DEF_GETTER(JSFunction, feedback_vector, Tagged<FeedbackVector>) {
DCHECK(has_feedback_vector(cage_base));
return Cast<FeedbackVector>(raw_feedback_cell(cage_base)->value(cage_base));
}
Tagged<ClosureFeedbackCellArray> JSFunction::closure_feedback_cell_array()
const {
DCHECK(has_closure_feedback_cell_array());
return Cast<ClosureFeedbackCellArray>(raw_feedback_cell()->value());
}
bool JSFunction::ChecksTieringState(IsolateForSandbox isolate) {
return code(isolate)->checks_tiering_state();
}
void JSFunction::CompleteInobjectSlackTrackingIfActive(Isolate* isolate) {
if (!has_prototype_slot()) return;
if (has_initial_map() && initial_map()->IsInobjectSlackTrackingInProgress()) {
MapUpdater::CompleteInobjectSlackTracking(isolate, initial_map());
}
}
template <typename IsolateT>
Tagged<AbstractCode> JSFunction::abstract_code(IsolateT* isolate) {
if (ActiveTierIsIgnition(isolate)) {
return Cast<AbstractCode>(shared()->GetBytecodeArray(isolate));
} else {
return Cast<AbstractCode>(code(isolate, kAcquireLoad));
}
}
int JSFunction::length() { return shared()->length(); }
void JSFunction::UpdateOptimizedCode(Isolate* isolate, Tagged<Code> code,
WriteBarrierMode mode) {
DisallowGarbageCollection no_gc;
DCHECK(code->is_optimized_code());
if (code->is_context_specialized()) {
if (raw_feedback_cell()->map() !=
ReadOnlyRoots(isolate).one_closure_cell_map()) {
return;
}
}
code->set_js_dispatch_handle(dispatch_handle());
UpdateCodeImpl(isolate, code, mode, false);
}
void JSFunction::UpdateCodeImpl(Isolate* isolate, Tagged<Code> value,
WriteBarrierMode mode,
bool keep_tiering_request) {
DisallowGarbageCollection no_gc;
JSDispatchHandle handle = dispatch_handle();
if (handle == kNullJSDispatchHandle) {
handle = raw_feedback_cell()->dispatch_handle();
DCHECK_NE(handle, kNullJSDispatchHandle);
set_dispatch_handle(handle, mode);
}
if (keep_tiering_request) {
UpdateDispatchEntryKeepTieringRequest(value, mode);
} else {
UpdateDispatchEntry(value, mode);
}
if (V8_UNLIKELY(v8_flags.log_function_events)) {
IsolateGroup::current()->js_dispatch_table()->SetTieringRequest(
dispatch_handle(), TieringBuiltin::kFunctionLogNextExecution, isolate);
}
}
void JSFunction::UpdateCode(Isolate* isolate, Tagged<Code> code,
WriteBarrierMode mode) {
CHECK(!code->is_optimized_code());
UpdateCodeImpl(isolate, code, mode, false);
}
inline void JSFunction::UpdateCodeKeepTieringRequests(Isolate* isolate,
Tagged<Code> code,
WriteBarrierMode mode) {
CHECK(!code->is_optimized_code());
UpdateCodeImpl(isolate, code, mode, true);
}
Tagged<Code> JSFunction::code(IsolateForSandbox isolate) const {
return IsolateGroup::current()->js_dispatch_table()->GetCode(
dispatch_handle());
}
Tagged<Code> JSFunction::code(IsolateForSandbox isolate,
AcquireLoadTag tag) const {
return IsolateGroup::current()->js_dispatch_table()->GetCode(
dispatch_handle(tag));
}
Tagged<Object> JSFunction::raw_code(IsolateForSandbox isolate) const {
JSDispatchHandle handle = dispatch_handle();
if (handle == kNullJSDispatchHandle) return Smi::zero();
return IsolateGroup::current()->js_dispatch_table()->GetCode(handle);
}
Tagged<Object> JSFunction::raw_code(IsolateForSandbox isolate,
AcquireLoadTag tag) const {
JSDispatchHandle handle = dispatch_handle(tag);
if (handle == kNullJSDispatchHandle) return Smi::zero();
return IsolateGroup::current()->js_dispatch_table()->GetCode(handle);
}
JSDispatchHandle JSFunction::AllocateDispatchHandle(Handle<JSFunction> function,
Isolate* isolate,
uint16_t parameter_count,
DirectHandle<Code> code,
WriteBarrierMode mode) {
DCHECK_EQ(function->raw_feedback_cell()->dispatch_handle(),
kNullJSDispatchHandle);
return AllocateAndInstallJSDispatchHandle(
function, kDispatchHandleOffset, isolate, parameter_count, code, mode);
}
void JSFunction::clear_dispatch_handle() {
WriteField<JSDispatchHandle::underlying_type>(kDispatchHandleOffset,
kNullJSDispatchHandle.value());
}
void JSFunction::set_dispatch_handle(JSDispatchHandle handle,
WriteBarrierMode mode) {
Relaxed_WriteField<JSDispatchHandle::underlying_type>(kDispatchHandleOffset,
handle.value());
CONDITIONAL_JS_DISPATCH_HANDLE_WRITE_BARRIER(*this, handle, mode);
}
void JSFunction::UpdateDispatchEntry(Tagged<Code> new_code,
WriteBarrierMode mode) {
JSDispatchHandle handle = dispatch_handle();
IsolateGroup::current()->js_dispatch_table()->SetCodeNoWriteBarrier(handle,
new_code);
CONDITIONAL_JS_DISPATCH_HANDLE_WRITE_BARRIER(*this, handle, mode);
}
void JSFunction::UpdateDispatchEntryKeepTieringRequest(Tagged<Code> new_code,
WriteBarrierMode mode) {
JSDispatchHandle handle = dispatch_handle();
IsolateGroup::current()
->js_dispatch_table()
->SetCodeKeepTieringRequestNoWriteBarrier(handle, new_code);
CONDITIONAL_JS_DISPATCH_HANDLE_WRITE_BARRIER(*this, handle, mode);
}
JSDispatchHandle JSFunction::dispatch_handle() const {
return JSDispatchHandle(Relaxed_ReadField<JSDispatchHandle::underlying_type>(
kDispatchHandleOffset));
}
JSDispatchHandle JSFunction::dispatch_handle(AcquireLoadTag tag) const {
return JSDispatchHandle(Acquire_ReadField<JSDispatchHandle::underlying_type>(
kDispatchHandleOffset));
}
RELEASE_ACQUIRE_ACCESSORS(JSFunction, context, Tagged<Context>, kContextOffset)
Address JSFunction::instruction_start(IsolateForSandbox isolate) const {
return code(isolate)->instruction_start();
}
DEF_GETTER(JSFunction, shared, Tagged<SharedFunctionInfo>) {
return shared(cage_base, kRelaxedLoad);
}
DEF_RELAXED_GETTER(JSFunction, shared, Tagged<SharedFunctionInfo>) {
return TaggedField<SharedFunctionInfo,
kSharedFunctionInfoOffset>::Relaxed_Load(cage_base, *this);
}
void JSFunction::set_shared(Tagged<SharedFunctionInfo> value,
WriteBarrierMode mode) {
RELEASE_WRITE_FIELD(*this, kSharedFunctionInfoOffset, value);
CONDITIONAL_WRITE_BARRIER(*this, kSharedFunctionInfoOffset, value, mode);
}
bool JSFunction::tiering_in_progress() const {
if (!has_feedback_vector()) return false;
return feedback_vector()->tiering_in_progress();
}
bool JSFunction::IsTieringRequestedOrInProgress() const {
if (!has_feedback_vector()) return false;
return tiering_in_progress() ||
IsolateGroup::current()->js_dispatch_table()->IsTieringRequested(
dispatch_handle());
}
bool JSFunction::IsLoggingRequested(Isolate* isolate) const {
return IsolateGroup::current()->js_dispatch_table()->IsTieringRequested(
dispatch_handle(), TieringBuiltin::kFunctionLogNextExecution, isolate);
}
bool JSFunction::IsMaglevRequested(Isolate* isolate) const {
JSDispatchTable* jdt = IsolateGroup::current()->js_dispatch_table();
Address entrypoint = jdt->GetEntrypoint(dispatch_handle());
const EmbeddedData& embedded_data = EmbeddedData::FromBlob(isolate);
#define CASE(name, ...) \
if (entrypoint == embedded_data.InstructionStartOf(Builtin::k##name)) { \
DCHECK(jdt->IsTieringRequested(dispatch_handle(), TieringBuiltin::k##name, \
isolate)); \
return TieringBuiltin::k##name != \
TieringBuiltin::kFunctionLogNextExecution; \
}
BUILTIN_LIST_BASE_TIERING_MAGLEV(CASE)
#undef CASE
return {};
}
bool JSFunction::IsTurbofanRequested(Isolate* isolate) const {
JSDispatchTable* jdt = IsolateGroup::current()->js_dispatch_table();
Address entrypoint = jdt->GetEntrypoint(dispatch_handle());
const EmbeddedData& embedded_data = EmbeddedData::FromBlob(isolate);
#define CASE(name, ...) \
if (entrypoint == embedded_data.InstructionStartOf(Builtin::k##name)) { \
DCHECK(jdt->IsTieringRequested(dispatch_handle(), TieringBuiltin::k##name, \
isolate)); \
return TieringBuiltin::k##name != \
TieringBuiltin::kFunctionLogNextExecution; \
}
BUILTIN_LIST_BASE_TIERING_TURBOFAN(CASE)
#undef CASE
return {};
}
bool JSFunction::IsOptimizationRequested(Isolate* isolate) const {
return IsMaglevRequested(isolate) || IsTurbofanRequested(isolate);
}
std::optional<CodeKind> JSFunction::GetRequestedOptimizationIfAny(
Isolate* isolate, ConcurrencyMode mode) const {
JSDispatchTable* jdt = IsolateGroup::current()->js_dispatch_table();
Address entrypoint = jdt->GetEntrypoint(dispatch_handle());
const EmbeddedData& embedded_data = EmbeddedData::FromBlob(isolate);
auto builtin = ([&]() -> std::optional<TieringBuiltin> {
#define CASE(name, ...) \
if (entrypoint == embedded_data.InstructionStartOf(Builtin::k##name)) { \
DCHECK(jdt->IsTieringRequested(dispatch_handle(), TieringBuiltin::k##name, \
isolate)); \
return TieringBuiltin::k##name; \
}
BUILTIN_LIST_BASE_TIERING(CASE)
#undef CASE
DCHECK(!jdt->IsTieringRequested(dispatch_handle()));
return {};
})();
if (V8_LIKELY(!builtin)) return {};
switch (*builtin) {
case TieringBuiltin::kOptimizeMaglevEager:
if (mode == ConcurrencyMode::kSynchronous) return CodeKind::MAGLEV;
break;
case TieringBuiltin::kStartMaglevOptimizeJob:
if (mode == ConcurrencyMode::kConcurrent) return CodeKind::MAGLEV;
break;
case TieringBuiltin::kOptimizeTurbofanEager:
if (mode == ConcurrencyMode::kSynchronous) return CodeKind::TURBOFAN_JS;
break;
case TieringBuiltin::kStartTurbofanOptimizeJob:
if (mode == ConcurrencyMode::kConcurrent) return CodeKind::TURBOFAN_JS;
break;
case TieringBuiltin::kMarkLazyDeoptimized:
case TieringBuiltin::kMarkReoptimizeLazyDeoptimized:
case TieringBuiltin::kFunctionLogNextExecution:
break;
}
return {};
}
void JSFunction::ResetTieringRequests() {
IsolateGroup::current()->js_dispatch_table()->ResetTieringRequest(
dispatch_handle());
}
void JSFunction::SetTieringInProgress(Isolate* isolate, bool in_progress,
BytecodeOffset osr_offset) {
if (!has_feedback_vector()) return;
if (osr_offset.IsNone()) {
bool was_in_progress = tiering_in_progress();
feedback_vector()->set_tiering_in_progress(in_progress);
if (!in_progress && was_in_progress) {
SetInterruptBudget(isolate, BudgetModification::kReduce);
}
} else {
feedback_vector()->set_osr_tiering_in_progress(in_progress);
}
}
bool JSFunction::osr_tiering_in_progress() {
DCHECK(has_feedback_vector());
return feedback_vector()->osr_tiering_in_progress();
}
DEF_GETTER(JSFunction, has_feedback_vector, bool) {
return shared(cage_base)->is_compiled() &&
IsFeedbackVector(raw_feedback_cell(cage_base)->value(cage_base),
cage_base);
}
bool JSFunction::has_closure_feedback_cell_array() const {
return shared()->is_compiled() &&
IsClosureFeedbackCellArray(raw_feedback_cell()->value());
}
Tagged<Context> JSFunction::context() {
return TaggedField<Context, kContextOffset>::load(*this);
}
DEF_RELAXED_GETTER(JSFunction, context, Tagged<Context>) {
return TaggedField<Context, kContextOffset>::Relaxed_Load(cage_base, *this);
}
bool JSFunction::has_context() const {
return IsContext(TaggedField<HeapObject, kContextOffset>::load(*this));
}
Tagged<JSGlobalProxy> JSFunction::global_proxy() {
return context()->global_proxy();
}
Tagged<NativeContext> JSFunction::native_context() {
return context()->native_context();
}
RELEASE_ACQUIRE_ACCESSORS_CHECKED(JSFunction, prototype_or_initial_map,
(Tagged<UnionOf<JSPrototype, Map, TheHole>>),
kPrototypeOrInitialMapOffset,
map()->has_prototype_slot())
DEF_GETTER(JSFunction, has_prototype_slot, bool) {
return map(cage_base)->has_prototype_slot();
}
DEF_GETTER(JSFunction, initial_map, Tagged<Map>) {
return Cast<Map>(prototype_or_initial_map(cage_base, kAcquireLoad));
}
DEF_GETTER(JSFunction, has_initial_map, bool) {
DCHECK(has_prototype_slot(cage_base));
Tagged<UnionOf<JSPrototype, Map, TheHole>> maybe_map =
prototype_or_initial_map(cage_base, kAcquireLoad);
return IsMap(maybe_map, cage_base);
}
DEF_GETTER(JSFunction, has_instance_prototype, bool) {
DCHECK(has_prototype_slot(cage_base));
return !IsTheHole(prototype_or_initial_map(cage_base, kAcquireLoad));
}
DEF_GETTER(JSFunction, has_prototype, bool) {
DCHECK(has_prototype_slot(cage_base));
return map(cage_base)->has_non_instance_prototype() ||
has_instance_prototype(cage_base);
}
DEF_GETTER(JSFunction, has_prototype_property, bool) {
return (has_prototype_slot(cage_base) && IsConstructor(*this, cage_base)) ||
IsGeneratorFunction(shared(cage_base)->kind());
}
DEF_GETTER(JSFunction, PrototypeRequiresRuntimeLookup, bool) {
return !has_prototype_property(cage_base) ||
map(cage_base)->has_non_instance_prototype();
}
DEF_GETTER(JSFunction, instance_prototype, Tagged<JSPrototype>) {
DCHECK(has_instance_prototype(cage_base));
if (has_initial_map(cage_base)) {
return initial_map(cage_base)->prototype(cage_base);
}
return Cast<JSPrototype>(prototype_or_initial_map(cage_base, kAcquireLoad));
}
DEF_GETTER(JSFunction, prototype, Tagged<Object>) {
DCHECK(has_prototype(cage_base));
Tagged<Map> map = this->map(cage_base);
if (map->has_non_instance_prototype()) {
return map->GetNonInstancePrototype(cage_base);
}
return instance_prototype(cage_base);
}
bool JSFunction::is_compiled(IsolateForSandbox isolate) const {
return code(isolate, kAcquireLoad)->builtin_id() != Builtin::kCompileLazy &&
shared()->is_compiled();
}
bool JSFunction::NeedsResetDueToFlushedBytecode(Isolate* isolate) {
Tagged<SharedFunctionInfo> sfi = TrustedCast<SharedFunctionInfo>(shared());
Tagged<Code> code = TrustedCast<Code>(raw_code(isolate));
return NeedsResetDueToFlushedBytecode(isolate, sfi, code);
}
bool JSFunction::NeedsResetDueToFlushedBytecode(Isolate* isolate,
Tagged<SharedFunctionInfo> sfi,
Tagged<Code> code) {
return !sfi->is_compiled() &&
(code->builtin_id() != Builtin::kCompileLazy ||
IsOptimizationRequested(isolate));
}
bool JSFunction::NeedsResetDueToFlushedBaselineCode(IsolateForSandbox isolate) {
return code(isolate)->kind() == CodeKind::BASELINE &&
!shared()->HasBaselineCode();
}
void JSFunction::ResetIfCodeFlushed(
Isolate* isolate,
std::optional<std::function<void(Tagged<HeapObject> object, ObjectSlot slot,
Tagged<HeapObject> target)>>
gc_notify_updated_slot) {
const bool kBytecodeCanFlush =
v8_flags.flush_bytecode || v8_flags.stress_snapshot;
const bool kBaselineCodeCanFlush =
v8_flags.flush_baseline_code || v8_flags.stress_snapshot;
if (!kBytecodeCanFlush && !kBaselineCodeCanFlush) return;
DCHECK_IMPLIES(NeedsResetDueToFlushedBytecode(isolate), kBytecodeCanFlush);
if (kBytecodeCanFlush && NeedsResetDueToFlushedBytecode(isolate)) {
ResetTieringRequests();
UpdateCode(isolate, *BUILTIN_CODE(isolate, CompileLazy));
raw_feedback_cell()->reset_feedback_vector(gc_notify_updated_slot);
return;
}
DCHECK_IMPLIES(NeedsResetDueToFlushedBaselineCode(isolate),
kBaselineCodeCanFlush);
if (kBaselineCodeCanFlush && NeedsResetDueToFlushedBaselineCode(isolate)) {
ResetTieringRequests();
UpdateCode(isolate, *BUILTIN_CODE(isolate, InterpreterEntryTrampoline));
}
}
}
#include "src/objects/object-macros-undef.h"
#endif