/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/objects/js-function-inl.h
497 строк
19 KB
Michaël Zasso
deps: update V8 to 14.6.202.33
24 апр 2026, 19:01
Не верифицирован
24 апр 2026, 19:01
f1e0b83
Код
Авторство
О чём код?
// 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. #ifndef V8_OBJECTS_JS_FUNCTION_INL_H_ #define V8_OBJECTS_JS_FUNCTION_INL_H_ #include "src/objects/js-function.h" // Include the non-inl header before the rest of the headers. #include <optional> // Include other inline headers *after* including js-function.h, such that e.g. // the definition of JSFunction is available (and this comment prevents // clang-format from merging that include into the following ones). #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" // Has to be the last include (doesn't have include guards): #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()) { // We can only set context-specialized code for single-closure cells. if (raw_feedback_cell()->map() != ReadOnlyRoots(isolate).one_closure_cell_map()) { return; } } // Required for being able to deoptimize this code. 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(isolate, value, mode); } else { UpdateDispatchEntry(isolate, value, mode); } if (V8_UNLIKELY(v8_flags.log_function_events)) { isolate->js_dispatch_table().SetTieringRequest( dispatch_handle(), TieringBuiltin::kFunctionLogNextExecution, isolate); } } void JSFunction::UpdateCode(Isolate* isolate, Tagged<Code> code, WriteBarrierMode mode) { // Optimized code must go through UpdateOptimized code, which sets a // back-reference in the code object to the dispatch handle for // deoptimization. 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 Isolate::Current()->js_dispatch_table().GetCode(dispatch_handle()); } Tagged<Code> JSFunction::code(IsolateForSandbox isolate, AcquireLoadTag tag) const { return Isolate::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 Isolate::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 Isolate::Current()->js_dispatch_table().GetCode(handle); } // static 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(Isolate* isolate, Tagged<Code> new_code, WriteBarrierMode mode) { JSDispatchHandle handle = dispatch_handle(); isolate->js_dispatch_table().SetCodeNoWriteBarrier(handle, new_code); CONDITIONAL_JS_DISPATCH_HANDLE_WRITE_BARRIER(*this, handle, mode); } void JSFunction::UpdateDispatchEntryKeepTieringRequest(Isolate* isolate, Tagged<Code> new_code, WriteBarrierMode mode) { JSDispatchHandle handle = dispatch_handle(); isolate->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(); } // TODO(ishell): Why relaxed read but release store? 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 semantics to support acquire read in NeedsResetDueToFlushedBytecode 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(Isolate* isolate) const { if (!has_feedback_vector()) return false; return tiering_in_progress() || isolate->js_dispatch_table().IsTieringRequested(dispatch_handle()); } bool JSFunction::IsLoggingRequested(Isolate* isolate) const { return isolate->js_dispatch_table().IsTieringRequested( dispatch_handle(), TieringBuiltin::kFunctionLogNextExecution, isolate); } bool JSFunction::IsMaglevRequested(Isolate* isolate) const { JSDispatchTable& jdt = isolate->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 = isolate->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 = isolate->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(Isolate* isolate) { isolate->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); } // When there is no initial map and the prototype is a JSReceiver, the // initial map field is used for the prototype field. return Cast<JSPrototype>(prototype_or_initial_map(cage_base, kAcquireLoad)); } DEF_GETTER(JSFunction, prototype, Tagged<Object>) { DCHECK(has_prototype(cage_base)); // If the function's prototype property has been set to a non-JSReceiver // value, that value is stored in the constructor field of the map. 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) { // The function is only used sequentially. Concurrent cases need to take care // of loading the fields themselves. 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 || // With leaptiering we can have CompileLazy as the code object but // still an optimization trampoline installed. 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)) { // Bytecode was flushed and function is now uncompiled, reset JSFunction // by setting code to CompileLazy and clearing the feedback vector. ResetTieringRequests(isolate); UpdateCode(isolate, *BUILTIN_CODE(isolate, CompileLazy), SKIP_WRITE_BARRIER); raw_feedback_cell()->reset_feedback_vector(gc_notify_updated_slot); return; } DCHECK_IMPLIES(NeedsResetDueToFlushedBaselineCode(isolate), kBaselineCodeCanFlush); if (kBaselineCodeCanFlush && NeedsResetDueToFlushedBaselineCode(isolate)) { // Flush baseline code from the closure if required ResetTieringRequests(isolate); UpdateCode(isolate, *BUILTIN_CODE(isolate, InterpreterEntryTrampoline), SKIP_WRITE_BARRIER); } } } // namespace v8::internal #include "src/objects/object-macros-undef.h" #endif // V8_OBJECTS_JS_FUNCTION_INL_H_