Extract JSFunction code into dedicated files

A small step for a JSFunction, one giant leap for V8.

Tbr: clemensb@chromium.org
Bug: v8:8888
Change-Id: I968bb819763994ec611cde7e502adea30339a387
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2315979
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69018}
This commit is contained in:
Jakob Gruber 2020-07-23 12:17:18 +02:00 committed by Commit Bot
parent 1a9c73c3c9
commit 9414d53980
17 changed files with 1441 additions and 1368 deletions

View File

@ -1004,48 +1004,52 @@ action("postmortem-metadata") {
# NOSORT
sources = [
"src/objects/objects.h",
"src/objects/objects-inl.h",
"src/objects/allocation-site-inl.h",
"$target_gen_dir/torque-generated/instance-types-tq.h",
"src/objects/allocation-site.h",
"src/objects/cell-inl.h",
"src/objects/allocation-site-inl.h",
"src/objects/cell.h",
"src/objects/code-inl.h",
"src/objects/cell-inl.h",
"src/objects/code.h",
"src/objects/code-inl.h",
"src/objects/data-handler.h",
"src/objects/data-handler-inl.h",
"src/objects/descriptor-array.h",
"src/objects/descriptor-array-inl.h",
"src/objects/feedback-cell.h",
"src/objects/feedback-cell-inl.h",
"src/objects/fixed-array-inl.h",
"src/objects/fixed-array.h",
"src/objects/heap-number-inl.h",
"src/objects/fixed-array-inl.h",
"src/objects/heap-number.h",
"src/objects/heap-object-inl.h",
"src/objects/heap-number-inl.h",
"src/objects/heap-object.h",
"src/objects/heap-object-inl.h",
"src/objects/instance-type.h",
"src/objects/js-array-inl.h",
"src/objects/js-array.h",
"src/objects/js-array-buffer-inl.h",
"src/objects/js-array-buffer.h",
"src/objects/js-objects-inl.h",
"src/objects/js-array-buffer-inl.h",
"src/objects/js-array.h",
"src/objects/js-array-inl.h",
"src/objects/js-function.cc",
"src/objects/js-function.h",
"src/objects/js-function-inl.h",
"src/objects/js-objects.cc",
"src/objects/js-objects.h",
"src/objects/js-promise-inl.h",
"src/objects/js-objects-inl.h",
"src/objects/js-promise.h",
"src/objects/js-regexp-inl.h",
"src/objects/js-promise-inl.h",
"src/objects/js-regexp.cc",
"src/objects/js-regexp.h",
"src/objects/js-regexp-string-iterator-inl.h",
"src/objects/js-regexp-inl.h",
"src/objects/js-regexp-string-iterator.h",
"src/objects/map.h",
"src/objects/js-regexp-string-iterator-inl.h",
"src/objects/map.cc",
"src/objects/map.h",
"src/objects/map-inl.h",
"src/objects/js-objects.cc",
"src/objects/name.h",
"src/objects/name-inl.h",
"src/objects/oddball-inl.h",
"src/objects/objects.h",
"src/objects/objects-inl.h",
"src/objects/oddball.h",
"src/objects/oddball-inl.h",
"src/objects/primitive-heap-object.h",
"src/objects/primitive-heap-object-inl.h",
"src/objects/scope-info.h",
@ -1055,13 +1059,12 @@ action("postmortem-metadata") {
"src/objects/shared-function-info.h",
"src/objects/shared-function-info-inl.h",
"src/objects/string.cc",
"src/objects/string.h",
"src/objects/string-comparator.cc",
"src/objects/string-comparator.h",
"src/objects/string.h",
"src/objects/string-inl.h",
"src/objects/struct.h",
"src/objects/struct-inl.h",
"$target_gen_dir/torque-generated/instance-types-tq.h",
]
outputs = [ "$target_gen_dir/debug-support.cc" ]
@ -2822,6 +2825,9 @@ v8_source_set("v8_base_without_compiler") {
"src/objects/js-display-names-inl.h",
"src/objects/js-display-names.cc",
"src/objects/js-display-names.h",
"src/objects/js-function-inl.h",
"src/objects/js-function.cc",
"src/objects/js-function.h",
"src/objects/js-generator-inl.h",
"src/objects/js-generator.h",
"src/objects/js-list-format-inl.h",

View File

@ -11,6 +11,7 @@
#include 'src/objects/bigint.h'
#include 'src/objects/elements-kind.h'
#include 'src/objects/free-space.h'
#include 'src/objects/js-function.h'
#include 'src/objects/js-generator.h'
#include 'src/objects/js-promise.h'
#include 'src/objects/js-regexp-string-iterator.h'

View File

@ -15,6 +15,7 @@
#include "src/compiler/code-assembler.h"
#include "src/objects/arguments.h"
#include "src/objects/bigint.h"
#include "src/objects/js-function.h"
#include "src/objects/objects.h"
#include "src/objects/promise.h"
#include "src/objects/shared-function-info.h"

View File

@ -18,6 +18,7 @@
#include "src/execution/frame-constants.h"
#include "src/execution/isolate.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/js-function.h"
#include "src/objects/shared-function-info.h"
#include "src/utils/allocation.h"
#include "src/utils/boxed-float.h"

View File

@ -7,6 +7,7 @@
#include "src/execution/isolate.h"
#include "src/objects/cell-inl.h"
#include "src/objects/js-function-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/oddball.h"
#include "src/objects/property-cell.h"

View File

@ -9,6 +9,7 @@
#include "src/objects/contexts.h"
#include "src/objects/dictionary-inl.h"
#include "src/objects/fixed-array-inl.h"
#include "src/objects/js-function-inl.h"
#include "src/objects/js-objects-inl.h"
#include "src/objects/map-inl.h"
#include "src/objects/objects-inl.h"

View File

@ -5,12 +5,12 @@
#ifndef V8_OBJECTS_FEEDBACK_VECTOR_INL_H_
#define V8_OBJECTS_FEEDBACK_VECTOR_INL_H_
#include "src/objects/feedback-vector.h"
#include "src/common/globals.h"
#include "src/heap/factory-inl.h"
#include "src/heap/heap-write-barrier-inl.h"
#include "src/objects/code-inl.h"
#include "src/objects/feedback-cell-inl.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/maybe-object-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/smi.h"

View File

@ -0,0 +1,289 @@
// 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/diagnostics/code-tracer.h"
#include "src/objects/feedback-cell-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
TQ_OBJECT_CONSTRUCTORS_IMPL(JSFunctionOrBoundFunction)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSBoundFunction)
OBJECT_CONSTRUCTORS_IMPL(JSFunction, JSFunctionOrBoundFunction)
CAST_ACCESSOR(JSFunction)
ACCESSORS(JSFunction, raw_feedback_cell, FeedbackCell, kFeedbackCellOffset)
FeedbackVector JSFunction::feedback_vector() const {
DCHECK(has_feedback_vector());
return FeedbackVector::cast(raw_feedback_cell().value());
}
ClosureFeedbackCellArray JSFunction::closure_feedback_cell_array() const {
DCHECK(has_closure_feedback_cell_array());
return ClosureFeedbackCellArray::cast(raw_feedback_cell().value());
}
// Code objects that are marked for deoptimization are not considered to be
// optimized. This is because the JSFunction might have been already
// deoptimized but its code() still needs to be unlinked, which will happen on
// its next activation.
// TODO(jupvfranco): rename this function. Maybe RunOptimizedCode,
// or IsValidOptimizedCode.
bool JSFunction::IsOptimized() {
return is_compiled() && code().kind() == Code::OPTIMIZED_FUNCTION &&
!code().marked_for_deoptimization();
}
bool JSFunction::HasOptimizedCode() {
return IsOptimized() ||
(has_feedback_vector() && feedback_vector().has_optimized_code() &&
!feedback_vector().optimized_code().marked_for_deoptimization());
}
bool JSFunction::HasOptimizationMarker() {
return has_feedback_vector() && feedback_vector().has_optimization_marker();
}
void JSFunction::ClearOptimizationMarker() {
DCHECK(has_feedback_vector());
feedback_vector().ClearOptimizationMarker();
}
// Optimized code marked for deoptimization will tier back down to running
// interpreted on its next activation, and already doesn't count as IsOptimized.
bool JSFunction::IsInterpreted() {
return is_compiled() && (code().is_interpreter_trampoline_builtin() ||
(code().kind() == Code::OPTIMIZED_FUNCTION &&
code().marked_for_deoptimization()));
}
bool JSFunction::ChecksOptimizationMarker() {
return code().checks_optimization_marker();
}
bool JSFunction::IsMarkedForOptimization() {
return has_feedback_vector() && feedback_vector().optimization_marker() ==
OptimizationMarker::kCompileOptimized;
}
bool JSFunction::IsMarkedForConcurrentOptimization() {
return has_feedback_vector() &&
feedback_vector().optimization_marker() ==
OptimizationMarker::kCompileOptimizedConcurrent;
}
bool JSFunction::IsInOptimizationQueue() {
return has_feedback_vector() && feedback_vector().optimization_marker() ==
OptimizationMarker::kInOptimizationQueue;
}
void JSFunction::CompleteInobjectSlackTrackingIfActive() {
if (!has_prototype_slot()) return;
if (has_initial_map() && initial_map().IsInobjectSlackTrackingInProgress()) {
initial_map().CompleteInobjectSlackTracking(GetIsolate());
}
}
AbstractCode JSFunction::abstract_code() {
if (IsInterpreted()) {
return AbstractCode::cast(shared().GetBytecodeArray());
} else {
return AbstractCode::cast(code());
}
}
int JSFunction::length() { return shared().length(); }
Code JSFunction::code() const {
return Code::cast(RELAXED_READ_FIELD(*this, kCodeOffset));
}
void JSFunction::set_code(Code value) {
DCHECK(!ObjectInYoungGeneration(value));
RELAXED_WRITE_FIELD(*this, kCodeOffset, value);
#ifndef V8_DISABLE_WRITE_BARRIERS
WriteBarrier::Marking(*this, RawField(kCodeOffset), value);
#endif
}
void JSFunction::set_code_no_write_barrier(Code value) {
DCHECK(!ObjectInYoungGeneration(value));
RELAXED_WRITE_FIELD(*this, kCodeOffset, value);
}
// TODO(ishell): Why relaxed read but release store?
DEF_GETTER(JSFunction, shared, SharedFunctionInfo) {
return SharedFunctionInfo::cast(
RELAXED_READ_FIELD(*this, kSharedFunctionInfoOffset));
}
void JSFunction::set_shared(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);
}
void JSFunction::ClearOptimizedCodeSlot(const char* reason) {
if (has_feedback_vector() && feedback_vector().has_optimized_code()) {
if (FLAG_trace_opt) {
CodeTracer::Scope scope(GetIsolate()->GetCodeTracer());
PrintF(scope.file(),
"[evicting entry from optimizing code feedback slot (%s) for ",
reason);
ShortPrint(scope.file());
PrintF(scope.file(), "]\n");
}
feedback_vector().ClearOptimizedCode();
}
}
void JSFunction::SetOptimizationMarker(OptimizationMarker marker) {
DCHECK(has_feedback_vector());
DCHECK(ChecksOptimizationMarker());
DCHECK(!HasOptimizedCode());
feedback_vector().SetOptimizationMarker(marker);
}
bool JSFunction::has_feedback_vector() const {
return shared().is_compiled() &&
raw_feedback_cell().value().IsFeedbackVector();
}
bool JSFunction::has_closure_feedback_cell_array() const {
return shared().is_compiled() &&
raw_feedback_cell().value().IsClosureFeedbackCellArray();
}
Context JSFunction::context() {
return TaggedField<Context, kContextOffset>::load(*this);
}
bool JSFunction::has_context() const {
return TaggedField<HeapObject, kContextOffset>::load(*this).IsContext();
}
JSGlobalProxy JSFunction::global_proxy() { return context().global_proxy(); }
NativeContext JSFunction::native_context() {
return context().native_context();
}
void JSFunction::set_context(HeapObject value) {
DCHECK(value.IsUndefined() || value.IsContext());
WRITE_FIELD(*this, kContextOffset, value);
WRITE_BARRIER(*this, kContextOffset, value);
}
ACCESSORS_CHECKED(JSFunction, prototype_or_initial_map, HeapObject,
kPrototypeOrInitialMapOffset, map().has_prototype_slot())
DEF_GETTER(JSFunction, has_prototype_slot, bool) {
return map(isolate).has_prototype_slot();
}
DEF_GETTER(JSFunction, initial_map, Map) {
return Map::cast(prototype_or_initial_map(isolate));
}
DEF_GETTER(JSFunction, has_initial_map, bool) {
DCHECK(has_prototype_slot(isolate));
return prototype_or_initial_map(isolate).IsMap(isolate);
}
DEF_GETTER(JSFunction, has_instance_prototype, bool) {
DCHECK(has_prototype_slot(isolate));
// Can't use ReadOnlyRoots(isolate) as this isolate could be produced by
// i::GetIsolateForPtrCompr(HeapObject).
return has_initial_map(isolate) ||
!prototype_or_initial_map(isolate).IsTheHole(
GetReadOnlyRoots(isolate));
}
DEF_GETTER(JSFunction, has_prototype, bool) {
DCHECK(has_prototype_slot(isolate));
return map(isolate).has_non_instance_prototype() ||
has_instance_prototype(isolate);
}
DEF_GETTER(JSFunction, has_prototype_property, bool) {
return (has_prototype_slot(isolate) && IsConstructor(isolate)) ||
IsGeneratorFunction(shared(isolate).kind());
}
DEF_GETTER(JSFunction, PrototypeRequiresRuntimeLookup, bool) {
return !has_prototype_property(isolate) ||
map(isolate).has_non_instance_prototype();
}
DEF_GETTER(JSFunction, instance_prototype, HeapObject) {
DCHECK(has_instance_prototype(isolate));
if (has_initial_map(isolate)) return initial_map(isolate).prototype(isolate);
// When there is no initial map and the prototype is a JSReceiver, the
// initial map field is used for the prototype field.
return HeapObject::cast(prototype_or_initial_map(isolate));
}
DEF_GETTER(JSFunction, prototype, Object) {
DCHECK(has_prototype(isolate));
// 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.
if (map(isolate).has_non_instance_prototype()) {
Object prototype = map(isolate).GetConstructor(isolate);
// The map must have a prototype in that field, not a back pointer.
DCHECK(!prototype.IsMap(isolate));
DCHECK(!prototype.IsFunctionTemplateInfo(isolate));
return prototype;
}
return instance_prototype(isolate);
}
bool JSFunction::is_compiled() const {
return code().builtin_index() != Builtins::kCompileLazy &&
shared().is_compiled();
}
bool JSFunction::NeedsResetDueToFlushedBytecode() {
// Do a raw read for shared and code fields here since this function may be
// called on a concurrent thread and the JSFunction might not be fully
// initialized yet.
Object maybe_shared = ACQUIRE_READ_FIELD(*this, kSharedFunctionInfoOffset);
Object maybe_code = RELAXED_READ_FIELD(*this, kCodeOffset);
if (!maybe_shared.IsSharedFunctionInfo() || !maybe_code.IsCode()) {
return false;
}
SharedFunctionInfo shared = SharedFunctionInfo::cast(maybe_shared);
Code code = Code::cast(maybe_code);
return !shared.is_compiled() &&
code.builtin_index() != Builtins::kCompileLazy;
}
void JSFunction::ResetIfBytecodeFlushed(
base::Optional<std::function<void(HeapObject object, ObjectSlot slot,
HeapObject target)>>
gc_notify_updated_slot) {
if (FLAG_flush_bytecode && NeedsResetDueToFlushedBytecode()) {
// Bytecode was flushed and function is now uncompiled, reset JSFunction
// by setting code to CompileLazy and clearing the feedback vector.
set_code(GetIsolate()->builtins()->builtin(i::Builtins::kCompileLazy));
raw_feedback_cell().reset_feedback_vector(gc_notify_updated_slot);
}
}
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_FUNCTION_INL_H_

822
src/objects/js-function.cc Normal file
View File

@ -0,0 +1,822 @@
// 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/codegen/compiler.h"
#include "src/heap/heap-inl.h"
#include "src/ic/ic.h"
#include "src/init/bootstrapper.h"
#include "src/objects/js-function-inl.h"
#include "src/strings/string-builder-inl.h"
namespace v8 {
namespace internal {
// static
MaybeHandle<NativeContext> JSBoundFunction::GetFunctionRealm(
Handle<JSBoundFunction> function) {
DCHECK(function->map().is_constructor());
return JSReceiver::GetFunctionRealm(
handle(function->bound_target_function(), function->GetIsolate()));
}
// 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 (function->bound_target_function().IsJSBoundFunction()) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, target_name,
factory->NewConsString(prefix, target_name),
String);
function = handle(JSBoundFunction::cast(function->bound_target_function()),
isolate);
}
if (function->bound_target_function().IsJSFunction()) {
Handle<JSFunction> target(
JSFunction::cast(function->bound_target_function()), isolate);
Handle<Object> name = JSFunction::GetName(isolate, target);
if (!name->IsString()) return target_name;
return factory->NewConsString(target_name, Handle<String>::cast(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 (function->bound_target_function().IsJSBoundFunction()) {
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;
}
}
// 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 = 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
Handle<Object> 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
Handle<NativeContext> JSFunction::GetFunctionRealm(
Handle<JSFunction> function) {
DCHECK(function->map().is_constructor());
return handle(function->context().native_context(), function->GetIsolate());
}
void JSFunction::MarkForOptimization(ConcurrencyMode mode) {
Isolate* isolate = GetIsolate();
if (!isolate->concurrent_recompilation_enabled() ||
isolate->bootstrapper()->IsActive()) {
mode = ConcurrencyMode::kNotConcurrent;
}
DCHECK(!is_compiled() || IsInterpreted());
DCHECK(shared().IsInterpreted());
DCHECK(!IsOptimized());
DCHECK(!HasOptimizedCode());
DCHECK(shared().allows_lazy_compilation() ||
!shared().optimization_disabled());
if (mode == ConcurrencyMode::kConcurrent) {
if (IsInOptimizationQueue()) {
if (FLAG_trace_concurrent_recompilation) {
PrintF(" ** Not marking ");
ShortPrint();
PrintF(" -- already in optimization queue.\n");
}
return;
}
if (FLAG_trace_concurrent_recompilation) {
PrintF(" ** Marking ");
ShortPrint();
PrintF(" for concurrent recompilation.\n");
}
}
SetOptimizationMarker(mode == ConcurrencyMode::kConcurrent
? OptimizationMarker::kCompileOptimizedConcurrent
: OptimizationMarker::kCompileOptimized);
}
// static
void JSFunction::EnsureClosureFeedbackCellArray(Handle<JSFunction> function) {
Isolate* const isolate = function->GetIsolate();
DCHECK(function->shared().is_compiled());
DCHECK(function->shared().HasFeedbackMetadata());
if (function->has_closure_feedback_cell_array() ||
function->has_feedback_vector()) {
return;
}
if (function->shared().HasAsmWasmData()) return;
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
DCHECK(function->shared().HasBytecodeArray());
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 funciton. 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);
} else {
function->raw_feedback_cell().set_value(*feedback_cell_array);
}
}
// static
void JSFunction::EnsureFeedbackVector(Handle<JSFunction> function,
IsCompiledScope* is_compiled_scope) {
Isolate* const isolate = function->GetIsolate();
DCHECK(is_compiled_scope->is_compiled());
DCHECK(function->shared().HasFeedbackMetadata());
if (function->has_feedback_vector()) return;
if (function->shared().HasAsmWasmData()) return;
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
DCHECK(function->shared().HasBytecodeArray());
EnsureClosureFeedbackCellArray(function);
Handle<ClosureFeedbackCellArray> closure_feedback_cell_array =
handle(function->closure_feedback_cell_array(), isolate);
Handle<HeapObject> feedback_vector = FeedbackVector::New(
isolate, shared, closure_feedback_cell_array, is_compiled_scope);
// 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());
function->raw_feedback_cell().set_value(*feedback_vector);
function->raw_feedback_cell().SetInterruptBudget();
}
// static
void JSFunction::InitializeFeedbackCell(Handle<JSFunction> function,
IsCompiledScope* is_compiled_scope) {
Isolate* const isolate = function->GetIsolate();
if (function->has_feedback_vector()) {
CHECK_EQ(function->feedback_vector().length(),
function->feedback_vector().metadata().slot_count());
return;
}
const bool needs_feedback_vector =
!FLAG_lazy_feedback_allocation || FLAG_always_opt ||
function->shared().may_have_cached_code() ||
// We also need a feedback vector for certain log events, collecting type
// profile and more precise code coverage.
FLAG_log_function_events || !isolate->is_best_effort_code_coverage() ||
isolate->is_collecting_type_profile();
if (needs_feedback_vector) {
EnsureFeedbackVector(function, is_compiled_scope);
} else {
EnsureClosureFeedbackCellArray(function);
}
}
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);
} else {
Handle<Map> new_map =
Map::Copy(isolate, initial_map, "SetInstancePrototype");
JSFunction::SetInitialMap(function, new_map, value);
// If the function is used as the global Array function, cache the
// updated initial maps (and transitioned versions) in the native context.
Handle<Context> native_context(function->context().native_context(),
isolate);
Handle<Object> array_function(
native_context->get(Context::ARRAY_FUNCTION_INDEX), isolate);
if (array_function->IsJSFunction() &&
*function == JSFunction::cast(*array_function)) {
CacheInitialJSArrayMaps(isolate, native_context, new_map);
}
}
// Deoptimize all code that embeds the previous initial map.
initial_map->dependent_code().DeoptimizeDependentCodeGroup(
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);
if (value->IsJSObject()) {
// 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(function->IsConstructor() ||
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 (!value->IsJSReceiver()) {
// 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");
JSObject::MigrateToMap(isolate, function, new_map);
new_map->SetConstructor(*value);
new_map->set_has_non_instance_prototype(true);
FunctionKind kind = function->shared().kind();
Handle<Context> native_context(function->context().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(Handle<JSFunction> function, Handle<Map> map,
Handle<HeapObject> prototype) {
if (map->prototype() != *prototype)
Map::SetPrototype(function->GetIsolate(), map, prototype);
function->set_prototype_or_initial_map(*map);
map->SetConstructor(*function);
if (FLAG_trace_maps) {
LOG(function->GetIsolate(), MapEvent("InitialMap", Handle<Map>(), map, "",
handle(function->shared().DebugName(),
function->GetIsolate())));
}
}
void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
DCHECK(function->has_prototype_slot());
DCHECK(function->IsConstructor() ||
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<Map> map = isolate->factory()->NewMap(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);
} else {
prototype = isolate->factory()->NewFunctionPrototype(function);
}
DCHECK(map->has_fast_object_elements());
// Finally link initial map and constructor function.
DCHECK(prototype->IsJSReceiver());
JSFunction::SetInitialMap(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_TYPE:
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_DATA_VIEW_TYPE:
case JS_DATE_TYPE:
case JS_FUNCTION_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_COLLATOR_TYPE:
case JS_DATE_TIME_FORMAT_TYPE:
case JS_DISPLAY_NAMES_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_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_SPECIAL_API_OBJECT_TYPE:
case JS_TYPED_ARRAY_TYPE:
case JS_PRIMITIVE_WRAPPER_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_WEAK_REF_TYPE:
case JS_WEAK_SET_TYPE:
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:
return true;
case BIGINT_TYPE:
case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
case BYTECODE_ARRAY_TYPE:
case BYTE_ARRAY_TYPE:
case CELL_TYPE:
case CODE_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 STRING_TABLE_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 MAP_TYPE:
case ODDBALL_TYPE:
case PROPERTY_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();
// Fall through.
default:
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(new_target->instance_prototype().IsJSReceiver());
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 =
Max(static_cast<int>(constructor->shared().expected_nof_properties()),
JSFunction::CalculateExpectedNofProperties(isolate, new_target));
JSFunction::CalculateInstanceSizeHelper(
instance_type, true, 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(new_target, map, prototype);
DCHECK(new_target->instance_prototype().IsJSReceiver());
map->SetConstructor(*constructor);
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.
if (new_target->IsJSFunction()) {
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 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 (new_target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
if (function->has_prototype_slot()) {
// Make sure the new.target.prototype is cached.
EnsureHasInitialMap(function);
prototype = handle(function->prototype(), isolate);
} else {
// No prototype property, use the intrinsict default proto further down.
prototype = isolate->factory()->undefined_value();
}
} else {
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 (!prototype->IsJSReceiver()) {
Handle<Context> context;
ASSIGN_RETURN_ON_EXCEPTION(isolate, context,
JSReceiver::GetFunctionRealm(new_target), Map);
DCHECK(context->IsNativeContext());
Handle<Object> maybe_index = JSReceiver::GetDataProperty(
constructor, isolate->factory()->native_context_index_symbol());
int index = maybe_index->IsSmi() ? Smi::ToInt(*maybe_index)
: Context::OBJECT_FUNCTION_INDEX;
Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index)),
isolate);
prototype = handle(realm_constructor->prototype(), isolate);
}
Handle<Map> map = Map::CopyInitialMap(isolate, constructor_initial_map);
map->set_new_target_is_base(false);
CHECK(prototype->IsJSReceiver());
if (map->prototype() != *prototype)
Map::SetPrototype(isolate, map, Handle<HeapObject>::cast(prototype));
map->SetConstructor(*constructor);
return 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();
}
void JSFunction::PrintName(FILE* out) {
std::unique_ptr<char[]> name = shared().DebugName().ToCString();
PrintF(out, "%s", name.get());
}
Handle<String> JSFunction::GetName(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Object> name =
JSReceiver::GetDataProperty(function, isolate->factory()->name_string());
if (name->IsString()) return Handle<String>::cast(name);
return handle(function->shared().DebugName(), isolate);
}
Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Object> name = JSReceiver::GetDataProperty(
function, isolate->factory()->display_name_string());
if (name->IsString()) return Handle<String>::cast(name);
return JSFunction::GetName(function);
}
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, builder.Finish(),
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(
Handle<SharedFunctionInfo> shared_info) {
Isolate* const isolate = shared_info->GetIsolate();
IncrementalStringBuilder builder(isolate);
builder.AppendCString("function ");
builder.AppendString(handle(shared_info->Name(), isolate));
builder.AppendCString("() { [native code] }");
return builder.Finish().ToHandleChecked();
}
} // 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(shared_info);
}
// Check if we should print {function} as a class.
Handle<Object> maybe_class_positions = JSReceiver::GetDataProperty(
function, isolate->factory()->class_positions_symbol());
if (maybe_class_positions->IsClassPositions()) {
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(shared_info);
}
// If this function was compiled from asm.js, use the recorded offset
// information.
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);
}
}
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(shared_info);
}
return Handle<String>::cast(
SharedFunctionInfo::GetSourceCodeHarmony(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 (!current->IsJSFunction()) 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(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);
if (requested_embedder_fields) {
// If there are embedder fields, then the embedder fields start offset must
// be properly aligned (embedder fields are located between object header
// and inobject fields).
header_size = RoundUp<kSystemPointerSize>(header_size);
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 = 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::ClearTypeFeedbackInfo() {
ResetIfBytecodeFlushed();
if (has_feedback_vector()) {
FeedbackVector vector = feedback_vector();
Isolate* isolate = GetIsolate();
if (vector.ClearSlots(isolate)) {
IC::OnFeedbackChanged(isolate, vector, FeedbackSlot::Invalid(),
"ClearTypeFeedbackInfo");
}
}
}
} // namespace internal
} // namespace v8

292
src/objects/js-function.h Normal file
View File

@ -0,0 +1,292 @@
// 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_H_
#define V8_OBJECTS_JS_FUNCTION_H_
#include "src/objects/js-objects.h"
#include "torque-generated/class-definitions-tq.h"
#include "torque-generated/field-offsets-tq.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
// An abstract superclass for classes representing JavaScript function values.
// It doesn't carry any functionality but allows function classes to be
// identified in the type system.
class JSFunctionOrBoundFunction
: public TorqueGeneratedJSFunctionOrBoundFunction<JSFunctionOrBoundFunction,
JSObject> {
public:
STATIC_ASSERT(kHeaderSize == JSObject::kHeaderSize);
TQ_OBJECT_CONSTRUCTORS(JSFunctionOrBoundFunction)
};
// JSBoundFunction describes a bound function exotic object.
class JSBoundFunction
: public TorqueGeneratedJSBoundFunction<JSBoundFunction,
JSFunctionOrBoundFunction> {
public:
static MaybeHandle<String> GetName(Isolate* isolate,
Handle<JSBoundFunction> function);
static Maybe<int> GetLength(Isolate* isolate,
Handle<JSBoundFunction> function);
static MaybeHandle<NativeContext> GetFunctionRealm(
Handle<JSBoundFunction> function);
// Dispatched behavior.
DECL_PRINTER(JSBoundFunction)
DECL_VERIFIER(JSBoundFunction)
// The bound function's string representation implemented according
// to ES6 section 19.2.3.5 Function.prototype.toString ( ).
static Handle<String> ToString(Handle<JSBoundFunction> function);
TQ_OBJECT_CONSTRUCTORS(JSBoundFunction)
};
// JSFunction describes JavaScript functions.
class JSFunction : public JSFunctionOrBoundFunction {
public:
// [prototype_or_initial_map]:
DECL_ACCESSORS(prototype_or_initial_map, HeapObject)
// [shared]: The information about the function that
// can be shared by instances.
DECL_ACCESSORS(shared, SharedFunctionInfo)
static const int kLengthDescriptorIndex = 0;
static const int kNameDescriptorIndex = 1;
// Home object descriptor index when function has a [[HomeObject]] slot.
static const int kMaybeHomeObjectDescriptorIndex = 2;
// Fast binding requires length and name accessors.
static const int kMinDescriptorsForFastBind = 2;
// [context]: The context for this function.
inline Context context();
inline bool has_context() const;
inline void set_context(HeapObject context);
inline JSGlobalProxy global_proxy();
inline NativeContext native_context();
inline int length();
static Handle<Object> GetName(Isolate* isolate, Handle<JSFunction> function);
static Handle<NativeContext> GetFunctionRealm(Handle<JSFunction> function);
// [code]: The generated code object for this function. Executed
// when the function is invoked, e.g. foo() or new foo(). See
// [[Call]] and [[Construct]] description in ECMA-262, section
// 8.6.2, page 27.
inline Code code() const;
inline void set_code(Code code);
inline void set_code_no_write_barrier(Code code);
// Get the abstract code associated with the function, which will either be
// a Code object or a BytecodeArray.
inline AbstractCode abstract_code();
// Tells whether or not this function is interpreted.
//
// Note: function->IsInterpreted() does not necessarily return the same value
// as function->shared()->IsInterpreted() because the closure might have been
// optimized.
inline bool IsInterpreted();
// Tells whether or not this function checks its optimization marker in its
// feedback vector.
inline bool ChecksOptimizationMarker();
// Tells whether or not this function holds optimized code.
//
// Note: Returning false does not necessarily mean that this function hasn't
// been optimized, as it may have optimized code on its feedback vector.
inline bool IsOptimized();
// Tells whether or not this function has optimized code available to it,
// either because it is optimized or because it has optimized code in its
// feedback vector.
inline bool HasOptimizedCode();
// Tells whether or not this function has a (non-zero) optimization marker.
inline bool HasOptimizationMarker();
// Mark this function for lazy recompilation. The function will be recompiled
// the next time it is executed.
void MarkForOptimization(ConcurrencyMode mode);
// Tells whether or not the function is already marked for lazy recompilation.
inline bool IsMarkedForOptimization();
inline bool IsMarkedForConcurrentOptimization();
// Tells whether or not the function is on the concurrent recompilation queue.
inline bool IsInOptimizationQueue();
// Clears the optimized code slot in the function's feedback vector.
inline void ClearOptimizedCodeSlot(const char* reason);
// Sets the optimization marker in the function's feedback vector.
inline void SetOptimizationMarker(OptimizationMarker marker);
// Clears the optimization marker in the function's feedback vector.
inline void ClearOptimizationMarker();
// If slack tracking is active, it computes instance size of the initial map
// with minimum permissible object slack. If it is not active, it simply
// returns the initial map's instance size.
int ComputeInstanceSizeWithMinSlack(Isolate* isolate);
// Completes inobject slack tracking on initial map if it is active.
inline void CompleteInobjectSlackTrackingIfActive();
// [raw_feedback_cell]: Gives raw access to the FeedbackCell used to hold the
/// FeedbackVector eventually. Generally this shouldn't be used to get the
// feedback_vector, instead use feedback_vector() which correctly deals with
// the JSFunction's bytecode being flushed.
DECL_ACCESSORS(raw_feedback_cell, FeedbackCell)
// Functions related to feedback vector. feedback_vector() can be used once
// the function has feedback vectors allocated. feedback vectors may not be
// available after compile when lazily allocating feedback vectors.
inline FeedbackVector feedback_vector() const;
inline bool has_feedback_vector() const;
V8_EXPORT_PRIVATE static void EnsureFeedbackVector(
Handle<JSFunction> function, IsCompiledScope* compiled_scope);
// Functions related to clousre feedback cell array that holds feedback cells
// used to create closures from this function. We allocate closure feedback
// cell arrays after compile, when we want to allocate feedback vectors
// lazily.
inline bool has_closure_feedback_cell_array() const;
inline ClosureFeedbackCellArray closure_feedback_cell_array() const;
static void EnsureClosureFeedbackCellArray(Handle<JSFunction> function);
// Initializes the feedback cell of |function|. In lite mode, this would be
// initialized to the closure feedback cell array that holds the feedback
// cells for create closure calls from this function. In the regular mode,
// this allocates feedback vector.
static void InitializeFeedbackCell(Handle<JSFunction> function,
IsCompiledScope* compiled_scope);
// Unconditionally clear the type feedback vector.
void ClearTypeFeedbackInfo();
// Resets function to clear compiled data after bytecode has been flushed.
inline bool NeedsResetDueToFlushedBytecode();
inline void ResetIfBytecodeFlushed(
base::Optional<std::function<void(HeapObject object, ObjectSlot slot,
HeapObject target)>>
gc_notify_updated_slot = base::nullopt);
DECL_GETTER(has_prototype_slot, bool)
// The initial map for an object created by this constructor.
DECL_GETTER(initial_map, Map)
static void SetInitialMap(Handle<JSFunction> function, Handle<Map> map,
Handle<HeapObject> prototype);
DECL_GETTER(has_initial_map, bool)
V8_EXPORT_PRIVATE static void EnsureHasInitialMap(
Handle<JSFunction> function);
// Creates a map that matches the constructor's initial map, but with
// [[prototype]] being new.target.prototype. Because new.target can be a
// JSProxy, this can call back into JavaScript.
static V8_WARN_UNUSED_RESULT MaybeHandle<Map> GetDerivedMap(
Isolate* isolate, Handle<JSFunction> constructor,
Handle<JSReceiver> new_target);
// Get and set the prototype property on a JSFunction. If the
// function has an initial map the prototype is set on the initial
// map. Otherwise, the prototype is put in the initial map field
// until an initial map is needed.
DECL_GETTER(has_prototype, bool)
DECL_GETTER(has_instance_prototype, bool)
DECL_GETTER(prototype, Object)
DECL_GETTER(instance_prototype, HeapObject)
DECL_GETTER(has_prototype_property, bool)
DECL_GETTER(PrototypeRequiresRuntimeLookup, bool)
static void SetPrototype(Handle<JSFunction> function, Handle<Object> value);
// Returns if this function has been compiled to native code yet.
inline bool is_compiled() const;
static int GetHeaderSize(bool function_has_prototype_slot) {
return function_has_prototype_slot ? JSFunction::kSizeWithPrototype
: JSFunction::kSizeWithoutPrototype;
}
// Prints the name of the function using PrintF.
void PrintName(FILE* out = stdout);
DECL_CAST(JSFunction)
// Calculate the instance size and in-object properties count.
// {CalculateExpectedNofProperties} can trigger compilation.
static V8_WARN_UNUSED_RESULT int CalculateExpectedNofProperties(
Isolate* isolate, Handle<JSFunction> function);
static void CalculateInstanceSizeHelper(InstanceType instance_type,
bool has_prototype_slot,
int requested_embedder_fields,
int requested_in_object_properties,
int* instance_size,
int* in_object_properties);
// Dispatched behavior.
DECL_PRINTER(JSFunction)
DECL_VERIFIER(JSFunction)
// The function's name if it is configured, otherwise shared function info
// debug name.
static Handle<String> GetName(Handle<JSFunction> function);
// ES6 section 9.2.11 SetFunctionName
// Because of the way this abstract operation is used in the spec,
// it should never fail, but in practice it will fail if the generated
// function name's length exceeds String::kMaxLength.
static V8_WARN_UNUSED_RESULT bool SetName(Handle<JSFunction> function,
Handle<Name> name,
Handle<String> prefix);
// The function's displayName if it is set, otherwise name if it is
// configured, otherwise shared function info
// debug name.
static Handle<String> GetDebugName(Handle<JSFunction> function);
// The function's string representation implemented according to
// ES6 section 19.2.3.5 Function.prototype.toString ( ).
static Handle<String> ToString(Handle<JSFunction> function);
struct FieldOffsets {
DEFINE_FIELD_OFFSET_CONSTANTS(JSFunctionOrBoundFunction::kHeaderSize,
TORQUE_GENERATED_JS_FUNCTION_FIELDS)
};
static constexpr int kSharedFunctionInfoOffset =
FieldOffsets::kSharedFunctionInfoOffset;
static constexpr int kContextOffset = FieldOffsets::kContextOffset;
static constexpr int kFeedbackCellOffset = FieldOffsets::kFeedbackCellOffset;
static constexpr int kCodeOffset = FieldOffsets::kCodeOffset;
static constexpr int kPrototypeOrInitialMapOffset =
FieldOffsets::kPrototypeOrInitialMapOffset;
private:
// JSFunction doesn't have a fixed header size:
// Hide JSFunctionOrBoundFunction::kHeaderSize to avoid confusion.
static const int kHeaderSize;
public:
static constexpr int kSizeWithoutPrototype = kPrototypeOrInitialMapOffset;
static constexpr int kSizeWithPrototype = FieldOffsets::kHeaderSize;
OBJECT_CONSTRUCTORS(JSFunction, JSFunctionOrBoundFunction);
};
} // namespace internal
} // namespace v8
#include "src/objects/object-macros-undef.h"
#endif // V8_OBJECTS_JS_FUNCTION_H_

View File

@ -5,11 +5,9 @@
#ifndef V8_OBJECTS_JS_OBJECTS_INL_H_
#define V8_OBJECTS_JS_OBJECTS_INL_H_
#include "src/diagnostics/code-tracer.h"
#include "src/heap/heap-write-barrier.h"
#include "src/objects/elements.h"
#include "src/objects/embedder-data-slot-inl.h"
#include "src/objects/feedback-cell-inl.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/field-index-inl.h"
#include "src/objects/hash-table-inl.h"
@ -34,10 +32,7 @@ TQ_OBJECT_CONSTRUCTORS_IMPL(JSObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSCustomElementsObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSSpecialObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSAsyncFromSyncIterator)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSFunctionOrBoundFunction)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSBoundFunction)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSDate)
OBJECT_CONSTRUCTORS_IMPL(JSFunction, JSFunctionOrBoundFunction)
OBJECT_CONSTRUCTORS_IMPL(JSGlobalObject, JSSpecialObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(JSGlobalProxy)
JSIteratorResult::JSIteratorResult(Address ptr) : JSObject(ptr) {}
@ -47,7 +42,6 @@ TQ_OBJECT_CONSTRUCTORS_IMPL(JSStringIterator)
NEVER_READ_ONLY_SPACE_IMPL(JSReceiver)
CAST_ACCESSOR(JSFunction)
CAST_ACCESSOR(JSGlobalObject)
CAST_ACCESSOR(JSIteratorResult)
CAST_ACCESSOR(JSMessageObject)
@ -451,8 +445,6 @@ void JSObject::InitializeBody(Map map, int start_offset,
}
}
ACCESSORS(JSFunction, raw_feedback_cell, FeedbackCell, kFeedbackCellOffset)
ACCESSORS(JSGlobalObject, native_context, NativeContext, kNativeContextOffset)
ACCESSORS(JSGlobalObject, global_proxy, JSGlobalProxy, kGlobalProxyOffset)
@ -460,265 +452,6 @@ DEF_GETTER(JSGlobalObject, native_context_unchecked, Object) {
return TaggedField<Object, kNativeContextOffset>::load(isolate, *this);
}
FeedbackVector JSFunction::feedback_vector() const {
DCHECK(has_feedback_vector());
return FeedbackVector::cast(raw_feedback_cell().value());
}
ClosureFeedbackCellArray JSFunction::closure_feedback_cell_array() const {
DCHECK(has_closure_feedback_cell_array());
return ClosureFeedbackCellArray::cast(raw_feedback_cell().value());
}
// Code objects that are marked for deoptimization are not considered to be
// optimized. This is because the JSFunction might have been already
// deoptimized but its code() still needs to be unlinked, which will happen on
// its next activation.
// TODO(jupvfranco): rename this function. Maybe RunOptimizedCode,
// or IsValidOptimizedCode.
bool JSFunction::IsOptimized() {
return is_compiled() && code().kind() == Code::OPTIMIZED_FUNCTION &&
!code().marked_for_deoptimization();
}
bool JSFunction::HasOptimizedCode() {
return IsOptimized() ||
(has_feedback_vector() && feedback_vector().has_optimized_code() &&
!feedback_vector().optimized_code().marked_for_deoptimization());
}
bool JSFunction::HasOptimizationMarker() {
return has_feedback_vector() && feedback_vector().has_optimization_marker();
}
void JSFunction::ClearOptimizationMarker() {
DCHECK(has_feedback_vector());
feedback_vector().ClearOptimizationMarker();
}
// Optimized code marked for deoptimization will tier back down to running
// interpreted on its next activation, and already doesn't count as IsOptimized.
bool JSFunction::IsInterpreted() {
return is_compiled() && (code().is_interpreter_trampoline_builtin() ||
(code().kind() == Code::OPTIMIZED_FUNCTION &&
code().marked_for_deoptimization()));
}
bool JSFunction::ChecksOptimizationMarker() {
return code().checks_optimization_marker();
}
bool JSFunction::IsMarkedForOptimization() {
return has_feedback_vector() && feedback_vector().optimization_marker() ==
OptimizationMarker::kCompileOptimized;
}
bool JSFunction::IsMarkedForConcurrentOptimization() {
return has_feedback_vector() &&
feedback_vector().optimization_marker() ==
OptimizationMarker::kCompileOptimizedConcurrent;
}
bool JSFunction::IsInOptimizationQueue() {
return has_feedback_vector() && feedback_vector().optimization_marker() ==
OptimizationMarker::kInOptimizationQueue;
}
void JSFunction::CompleteInobjectSlackTrackingIfActive() {
if (!has_prototype_slot()) return;
if (has_initial_map() && initial_map().IsInobjectSlackTrackingInProgress()) {
initial_map().CompleteInobjectSlackTracking(GetIsolate());
}
}
AbstractCode JSFunction::abstract_code() {
if (IsInterpreted()) {
return AbstractCode::cast(shared().GetBytecodeArray());
} else {
return AbstractCode::cast(code());
}
}
int JSFunction::length() { return shared().length(); }
Code JSFunction::code() const {
return Code::cast(RELAXED_READ_FIELD(*this, kCodeOffset));
}
void JSFunction::set_code(Code value) {
DCHECK(!ObjectInYoungGeneration(value));
RELAXED_WRITE_FIELD(*this, kCodeOffset, value);
#ifndef V8_DISABLE_WRITE_BARRIERS
WriteBarrier::Marking(*this, RawField(kCodeOffset), value);
#endif
}
void JSFunction::set_code_no_write_barrier(Code value) {
DCHECK(!ObjectInYoungGeneration(value));
RELAXED_WRITE_FIELD(*this, kCodeOffset, value);
}
// TODO(ishell): Why relaxed read but release store?
DEF_GETTER(JSFunction, shared, SharedFunctionInfo) {
return SharedFunctionInfo::cast(
RELAXED_READ_FIELD(*this, kSharedFunctionInfoOffset));
}
void JSFunction::set_shared(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);
}
void JSFunction::ClearOptimizedCodeSlot(const char* reason) {
if (has_feedback_vector() && feedback_vector().has_optimized_code()) {
if (FLAG_trace_opt) {
CodeTracer::Scope scope(GetIsolate()->GetCodeTracer());
PrintF(scope.file(),
"[evicting entry from optimizing code feedback slot (%s) for ",
reason);
ShortPrint(scope.file());
PrintF(scope.file(), "]\n");
}
feedback_vector().ClearOptimizedCode();
}
}
void JSFunction::SetOptimizationMarker(OptimizationMarker marker) {
DCHECK(has_feedback_vector());
DCHECK(ChecksOptimizationMarker());
DCHECK(!HasOptimizedCode());
feedback_vector().SetOptimizationMarker(marker);
}
bool JSFunction::has_feedback_vector() const {
return shared().is_compiled() &&
raw_feedback_cell().value().IsFeedbackVector();
}
bool JSFunction::has_closure_feedback_cell_array() const {
return shared().is_compiled() &&
raw_feedback_cell().value().IsClosureFeedbackCellArray();
}
Context JSFunction::context() {
return TaggedField<Context, kContextOffset>::load(*this);
}
bool JSFunction::has_context() const {
return TaggedField<HeapObject, kContextOffset>::load(*this).IsContext();
}
JSGlobalProxy JSFunction::global_proxy() { return context().global_proxy(); }
NativeContext JSFunction::native_context() {
return context().native_context();
}
void JSFunction::set_context(HeapObject value) {
DCHECK(value.IsUndefined() || value.IsContext());
WRITE_FIELD(*this, kContextOffset, value);
WRITE_BARRIER(*this, kContextOffset, value);
}
ACCESSORS_CHECKED(JSFunction, prototype_or_initial_map, HeapObject,
kPrototypeOrInitialMapOffset, map().has_prototype_slot())
DEF_GETTER(JSFunction, has_prototype_slot, bool) {
return map(isolate).has_prototype_slot();
}
DEF_GETTER(JSFunction, initial_map, Map) {
return Map::cast(prototype_or_initial_map(isolate));
}
DEF_GETTER(JSFunction, has_initial_map, bool) {
DCHECK(has_prototype_slot(isolate));
return prototype_or_initial_map(isolate).IsMap(isolate);
}
DEF_GETTER(JSFunction, has_instance_prototype, bool) {
DCHECK(has_prototype_slot(isolate));
// Can't use ReadOnlyRoots(isolate) as this isolate could be produced by
// i::GetIsolateForPtrCompr(HeapObject).
return has_initial_map(isolate) ||
!prototype_or_initial_map(isolate).IsTheHole(
GetReadOnlyRoots(isolate));
}
DEF_GETTER(JSFunction, has_prototype, bool) {
DCHECK(has_prototype_slot(isolate));
return map(isolate).has_non_instance_prototype() ||
has_instance_prototype(isolate);
}
DEF_GETTER(JSFunction, has_prototype_property, bool) {
return (has_prototype_slot(isolate) && IsConstructor(isolate)) ||
IsGeneratorFunction(shared(isolate).kind());
}
DEF_GETTER(JSFunction, PrototypeRequiresRuntimeLookup, bool) {
return !has_prototype_property(isolate) ||
map(isolate).has_non_instance_prototype();
}
DEF_GETTER(JSFunction, instance_prototype, HeapObject) {
DCHECK(has_instance_prototype(isolate));
if (has_initial_map(isolate)) return initial_map(isolate).prototype(isolate);
// When there is no initial map and the prototype is a JSReceiver, the
// initial map field is used for the prototype field.
return HeapObject::cast(prototype_or_initial_map(isolate));
}
DEF_GETTER(JSFunction, prototype, Object) {
DCHECK(has_prototype(isolate));
// 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.
if (map(isolate).has_non_instance_prototype()) {
Object prototype = map(isolate).GetConstructor(isolate);
// The map must have a prototype in that field, not a back pointer.
DCHECK(!prototype.IsMap(isolate));
DCHECK(!prototype.IsFunctionTemplateInfo(isolate));
return prototype;
}
return instance_prototype(isolate);
}
bool JSFunction::is_compiled() const {
return code().builtin_index() != Builtins::kCompileLazy &&
shared().is_compiled();
}
bool JSFunction::NeedsResetDueToFlushedBytecode() {
// Do a raw read for shared and code fields here since this function may be
// called on a concurrent thread and the JSFunction might not be fully
// initialized yet.
Object maybe_shared = ACQUIRE_READ_FIELD(*this, kSharedFunctionInfoOffset);
Object maybe_code = RELAXED_READ_FIELD(*this, kCodeOffset);
if (!maybe_shared.IsSharedFunctionInfo() || !maybe_code.IsCode()) {
return false;
}
SharedFunctionInfo shared = SharedFunctionInfo::cast(maybe_shared);
Code code = Code::cast(maybe_code);
return !shared.is_compiled() &&
code.builtin_index() != Builtins::kCompileLazy;
}
void JSFunction::ResetIfBytecodeFlushed(
base::Optional<std::function<void(HeapObject object, ObjectSlot slot,
HeapObject target)>>
gc_notify_updated_slot) {
if (FLAG_flush_bytecode && NeedsResetDueToFlushedBytecode()) {
// Bytecode was flushed and function is now uncompiled, reset JSFunction
// by setting code to CompileLazy and clearing the feedback vector.
set_code(GetIsolate()->builtins()->builtin(i::Builtins::kCompileLazy));
raw_feedback_cell().reset_feedback_vector(gc_notify_updated_slot);
}
}
bool JSMessageObject::DidEnsureSourcePositionsAvailable() const {
return shared_info().IsUndefined();
}

View File

@ -5,7 +5,6 @@
#include "src/objects/js-objects.h"
#include "src/api/api-arguments-inl.h"
#include "src/codegen/compiler.h"
#include "src/date/date.h"
#include "src/execution/arguments.h"
#include "src/execution/frames.h"
@ -15,7 +14,6 @@
#include "src/heap/factory-inl.h"
#include "src/heap/heap-inl.h"
#include "src/heap/memory-chunk.h"
#include "src/ic/ic.h"
#include "src/init/bootstrapper.h"
#include "src/logging/counters.h"
#include "src/logging/log.h"
@ -4863,814 +4861,6 @@ bool JSObject::IsDroppableApiWrapper() {
instance_type == JS_SPECIAL_API_OBJECT_TYPE;
}
// static
MaybeHandle<NativeContext> JSBoundFunction::GetFunctionRealm(
Handle<JSBoundFunction> function) {
DCHECK(function->map().is_constructor());
return JSReceiver::GetFunctionRealm(
handle(function->bound_target_function(), function->GetIsolate()));
}
// 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 (function->bound_target_function().IsJSBoundFunction()) {
ASSIGN_RETURN_ON_EXCEPTION(isolate, target_name,
factory->NewConsString(prefix, target_name),
String);
function = handle(JSBoundFunction::cast(function->bound_target_function()),
isolate);
}
if (function->bound_target_function().IsJSFunction()) {
Handle<JSFunction> target(
JSFunction::cast(function->bound_target_function()), isolate);
Handle<Object> name = JSFunction::GetName(isolate, target);
if (!name->IsString()) return target_name;
return factory->NewConsString(target_name, Handle<String>::cast(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 (function->bound_target_function().IsJSBoundFunction()) {
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;
}
}
// 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 = 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
Handle<Object> 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
Handle<NativeContext> JSFunction::GetFunctionRealm(
Handle<JSFunction> function) {
DCHECK(function->map().is_constructor());
return handle(function->context().native_context(), function->GetIsolate());
}
void JSFunction::MarkForOptimization(ConcurrencyMode mode) {
Isolate* isolate = GetIsolate();
if (!isolate->concurrent_recompilation_enabled() ||
isolate->bootstrapper()->IsActive()) {
mode = ConcurrencyMode::kNotConcurrent;
}
DCHECK(!is_compiled() || IsInterpreted());
DCHECK(shared().IsInterpreted());
DCHECK(!IsOptimized());
DCHECK(!HasOptimizedCode());
DCHECK(shared().allows_lazy_compilation() ||
!shared().optimization_disabled());
if (mode == ConcurrencyMode::kConcurrent) {
if (IsInOptimizationQueue()) {
if (FLAG_trace_concurrent_recompilation) {
PrintF(" ** Not marking ");
ShortPrint();
PrintF(" -- already in optimization queue.\n");
}
return;
}
if (FLAG_trace_concurrent_recompilation) {
PrintF(" ** Marking ");
ShortPrint();
PrintF(" for concurrent recompilation.\n");
}
}
SetOptimizationMarker(mode == ConcurrencyMode::kConcurrent
? OptimizationMarker::kCompileOptimizedConcurrent
: OptimizationMarker::kCompileOptimized);
}
// static
void JSFunction::EnsureClosureFeedbackCellArray(Handle<JSFunction> function) {
Isolate* const isolate = function->GetIsolate();
DCHECK(function->shared().is_compiled());
DCHECK(function->shared().HasFeedbackMetadata());
if (function->has_closure_feedback_cell_array() ||
function->has_feedback_vector()) {
return;
}
if (function->shared().HasAsmWasmData()) return;
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
DCHECK(function->shared().HasBytecodeArray());
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 funciton. 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);
} else {
function->raw_feedback_cell().set_value(*feedback_cell_array);
}
}
// static
void JSFunction::EnsureFeedbackVector(Handle<JSFunction> function,
IsCompiledScope* is_compiled_scope) {
Isolate* const isolate = function->GetIsolate();
DCHECK(is_compiled_scope->is_compiled());
DCHECK(function->shared().HasFeedbackMetadata());
if (function->has_feedback_vector()) return;
if (function->shared().HasAsmWasmData()) return;
Handle<SharedFunctionInfo> shared(function->shared(), isolate);
DCHECK(function->shared().HasBytecodeArray());
EnsureClosureFeedbackCellArray(function);
Handle<ClosureFeedbackCellArray> closure_feedback_cell_array =
handle(function->closure_feedback_cell_array(), isolate);
Handle<HeapObject> feedback_vector = FeedbackVector::New(
isolate, shared, closure_feedback_cell_array, is_compiled_scope);
// 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());
function->raw_feedback_cell().set_value(*feedback_vector);
function->raw_feedback_cell().SetInterruptBudget();
}
// static
void JSFunction::InitializeFeedbackCell(Handle<JSFunction> function,
IsCompiledScope* is_compiled_scope) {
Isolate* const isolate = function->GetIsolate();
if (function->has_feedback_vector()) {
CHECK_EQ(function->feedback_vector().length(),
function->feedback_vector().metadata().slot_count());
return;
}
const bool needs_feedback_vector =
!FLAG_lazy_feedback_allocation || FLAG_always_opt ||
function->shared().may_have_cached_code() ||
// We also need a feedback vector for certain log events, collecting type
// profile and more precise code coverage.
FLAG_log_function_events || !isolate->is_best_effort_code_coverage() ||
isolate->is_collecting_type_profile();
if (needs_feedback_vector) {
EnsureFeedbackVector(function, is_compiled_scope);
} else {
EnsureClosureFeedbackCellArray(function);
}
}
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);
} else {
Handle<Map> new_map =
Map::Copy(isolate, initial_map, "SetInstancePrototype");
JSFunction::SetInitialMap(function, new_map, value);
// If the function is used as the global Array function, cache the
// updated initial maps (and transitioned versions) in the native context.
Handle<Context> native_context(function->context().native_context(),
isolate);
Handle<Object> array_function(
native_context->get(Context::ARRAY_FUNCTION_INDEX), isolate);
if (array_function->IsJSFunction() &&
*function == JSFunction::cast(*array_function)) {
CacheInitialJSArrayMaps(isolate, native_context, new_map);
}
}
// Deoptimize all code that embeds the previous initial map.
initial_map->dependent_code().DeoptimizeDependentCodeGroup(
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);
if (value->IsJSObject()) {
// 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(function->IsConstructor() ||
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 (!value->IsJSReceiver()) {
// 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");
JSObject::MigrateToMap(isolate, function, new_map);
new_map->SetConstructor(*value);
new_map->set_has_non_instance_prototype(true);
FunctionKind kind = function->shared().kind();
Handle<Context> native_context(function->context().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(Handle<JSFunction> function, Handle<Map> map,
Handle<HeapObject> prototype) {
if (map->prototype() != *prototype)
Map::SetPrototype(function->GetIsolate(), map, prototype);
function->set_prototype_or_initial_map(*map);
map->SetConstructor(*function);
if (FLAG_trace_maps) {
LOG(function->GetIsolate(), MapEvent("InitialMap", Handle<Map>(), map, "",
handle(function->shared().DebugName(),
function->GetIsolate())));
}
}
void JSFunction::EnsureHasInitialMap(Handle<JSFunction> function) {
DCHECK(function->has_prototype_slot());
DCHECK(function->IsConstructor() ||
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<Map> map = isolate->factory()->NewMap(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);
} else {
prototype = isolate->factory()->NewFunctionPrototype(function);
}
DCHECK(map->has_fast_object_elements());
// Finally link initial map and constructor function.
DCHECK(prototype->IsJSReceiver());
JSFunction::SetInitialMap(function, map, prototype);
map->StartInobjectSlackTracking();
}
#ifdef DEBUG
namespace {
bool CanSubclassHaveInobjectProperties(InstanceType instance_type) {
switch (instance_type) {
case JS_API_OBJECT_TYPE:
case JS_ARRAY_BUFFER_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_DATE_TYPE:
case JS_FUNCTION_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
#ifdef V8_INTL_SUPPORT
case JS_COLLATOR_TYPE:
case JS_DATE_TIME_FORMAT_TYPE:
case JS_DISPLAY_NAMES_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_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_SPECIAL_API_OBJECT_TYPE:
case JS_TYPED_ARRAY_TYPE:
case JS_PRIMITIVE_WRAPPER_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_WEAK_REF_TYPE:
case JS_WEAK_SET_TYPE:
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:
return true;
case BIGINT_TYPE:
case OBJECT_BOILERPLATE_DESCRIPTION_TYPE:
case BYTECODE_ARRAY_TYPE:
case BYTE_ARRAY_TYPE:
case CELL_TYPE:
case CODE_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 STRING_TABLE_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 MAP_TYPE:
case ODDBALL_TYPE:
case PROPERTY_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();
// Fall through.
default:
return false;
}
}
} // namespace
#endif
namespace {
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(new_target->instance_prototype().IsJSReceiver());
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 =
Max(static_cast<int>(constructor->shared().expected_nof_properties()),
JSFunction::CalculateExpectedNofProperties(isolate, new_target));
JSFunction::CalculateInstanceSizeHelper(
instance_type, true, 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(new_target, map, prototype);
DCHECK(new_target->instance_prototype().IsJSReceiver());
map->SetConstructor(*constructor);
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.
if (new_target->IsJSFunction()) {
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 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 (new_target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(new_target);
if (function->has_prototype_slot()) {
// Make sure the new.target.prototype is cached.
EnsureHasInitialMap(function);
prototype = handle(function->prototype(), isolate);
} else {
// No prototype property, use the intrinsict default proto further down.
prototype = isolate->factory()->undefined_value();
}
} else {
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 (!prototype->IsJSReceiver()) {
Handle<Context> context;
ASSIGN_RETURN_ON_EXCEPTION(isolate, context,
JSReceiver::GetFunctionRealm(new_target), Map);
DCHECK(context->IsNativeContext());
Handle<Object> maybe_index = JSReceiver::GetDataProperty(
constructor, isolate->factory()->native_context_index_symbol());
int index = maybe_index->IsSmi() ? Smi::ToInt(*maybe_index)
: Context::OBJECT_FUNCTION_INDEX;
Handle<JSFunction> realm_constructor(JSFunction::cast(context->get(index)),
isolate);
prototype = handle(realm_constructor->prototype(), isolate);
}
Handle<Map> map = Map::CopyInitialMap(isolate, constructor_initial_map);
map->set_new_target_is_base(false);
CHECK(prototype->IsJSReceiver());
if (map->prototype() != *prototype)
Map::SetPrototype(isolate, map, Handle<HeapObject>::cast(prototype));
map->SetConstructor(*constructor);
return 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();
}
void JSFunction::PrintName(FILE* out) {
std::unique_ptr<char[]> name = shared().DebugName().ToCString();
PrintF(out, "%s", name.get());
}
Handle<String> JSFunction::GetName(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Object> name =
JSReceiver::GetDataProperty(function, isolate->factory()->name_string());
if (name->IsString()) return Handle<String>::cast(name);
return handle(function->shared().DebugName(), isolate);
}
Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Object> name = JSReceiver::GetDataProperty(
function, isolate->factory()->display_name_string());
if (name->IsString()) return Handle<String>::cast(name);
return JSFunction::GetName(function);
}
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, builder.Finish(),
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(
Handle<SharedFunctionInfo> shared_info) {
Isolate* const isolate = shared_info->GetIsolate();
IncrementalStringBuilder builder(isolate);
builder.AppendCString("function ");
builder.AppendString(handle(shared_info->Name(), isolate));
builder.AppendCString("() { [native code] }");
return builder.Finish().ToHandleChecked();
}
} // 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(shared_info);
}
// Check if we should print {function} as a class.
Handle<Object> maybe_class_positions = JSReceiver::GetDataProperty(
function, isolate->factory()->class_positions_symbol());
if (maybe_class_positions->IsClassPositions()) {
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(shared_info);
}
// If this function was compiled from asm.js, use the recorded offset
// information.
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);
}
}
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(shared_info);
}
return Handle<String>::cast(
SharedFunctionInfo::GetSourceCodeHarmony(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 (!current->IsJSFunction()) 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(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);
if (requested_embedder_fields) {
// If there are embedder fields, then the embedder fields start offset must
// be properly aligned (embedder fields are located between object header
// and inobject fields).
header_size = RoundUp<kSystemPointerSize>(header_size);
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 = 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::ClearTypeFeedbackInfo() {
ResetIfBytecodeFlushed();
if (has_feedback_vector()) {
FeedbackVector vector = feedback_vector();
Isolate* isolate = GetIsolate();
if (vector.ClearSlots(isolate)) {
IC::OnFeedbackChanged(isolate, vector, FeedbackSlot::Invalid(),
"ClearTypeFeedbackInfo");
}
}
}
void JSGlobalObject::InvalidatePropertyCell(Handle<JSGlobalObject> global,
Handle<Name> name) {
// Regardless of whether the property is there or not invalidate

View File

@ -926,275 +926,6 @@ class JSIteratorResult : public JSObject {
OBJECT_CONSTRUCTORS(JSIteratorResult, JSObject);
};
// An abstract superclass for classes representing JavaScript function values.
// It doesn't carry any functionality but allows function classes to be
// identified in the type system.
class JSFunctionOrBoundFunction
: public TorqueGeneratedJSFunctionOrBoundFunction<JSFunctionOrBoundFunction,
JSObject> {
public:
STATIC_ASSERT(kHeaderSize == JSObject::kHeaderSize);
TQ_OBJECT_CONSTRUCTORS(JSFunctionOrBoundFunction)
};
// JSBoundFunction describes a bound function exotic object.
class JSBoundFunction
: public TorqueGeneratedJSBoundFunction<JSBoundFunction,
JSFunctionOrBoundFunction> {
public:
static MaybeHandle<String> GetName(Isolate* isolate,
Handle<JSBoundFunction> function);
static Maybe<int> GetLength(Isolate* isolate,
Handle<JSBoundFunction> function);
static MaybeHandle<NativeContext> GetFunctionRealm(
Handle<JSBoundFunction> function);
// Dispatched behavior.
DECL_PRINTER(JSBoundFunction)
DECL_VERIFIER(JSBoundFunction)
// The bound function's string representation implemented according
// to ES6 section 19.2.3.5 Function.prototype.toString ( ).
static Handle<String> ToString(Handle<JSBoundFunction> function);
TQ_OBJECT_CONSTRUCTORS(JSBoundFunction)
};
// JSFunction describes JavaScript functions.
class JSFunction : public JSFunctionOrBoundFunction {
public:
// [prototype_or_initial_map]:
DECL_ACCESSORS(prototype_or_initial_map, HeapObject)
// [shared]: The information about the function that
// can be shared by instances.
DECL_ACCESSORS(shared, SharedFunctionInfo)
static const int kLengthDescriptorIndex = 0;
static const int kNameDescriptorIndex = 1;
// Home object descriptor index when function has a [[HomeObject]] slot.
static const int kMaybeHomeObjectDescriptorIndex = 2;
// Fast binding requires length and name accessors.
static const int kMinDescriptorsForFastBind = 2;
// [context]: The context for this function.
inline Context context();
inline bool has_context() const;
inline void set_context(HeapObject context);
inline JSGlobalProxy global_proxy();
inline NativeContext native_context();
inline int length();
static Handle<Object> GetName(Isolate* isolate, Handle<JSFunction> function);
static Handle<NativeContext> GetFunctionRealm(Handle<JSFunction> function);
// [code]: The generated code object for this function. Executed
// when the function is invoked, e.g. foo() or new foo(). See
// [[Call]] and [[Construct]] description in ECMA-262, section
// 8.6.2, page 27.
inline Code code() const;
inline void set_code(Code code);
inline void set_code_no_write_barrier(Code code);
// Get the abstract code associated with the function, which will either be
// a Code object or a BytecodeArray.
inline AbstractCode abstract_code();
// Tells whether or not this function is interpreted.
//
// Note: function->IsInterpreted() does not necessarily return the same value
// as function->shared()->IsInterpreted() because the closure might have been
// optimized.
inline bool IsInterpreted();
// Tells whether or not this function checks its optimization marker in its
// feedback vector.
inline bool ChecksOptimizationMarker();
// Tells whether or not this function holds optimized code.
//
// Note: Returning false does not necessarily mean that this function hasn't
// been optimized, as it may have optimized code on its feedback vector.
inline bool IsOptimized();
// Tells whether or not this function has optimized code available to it,
// either because it is optimized or because it has optimized code in its
// feedback vector.
inline bool HasOptimizedCode();
// Tells whether or not this function has a (non-zero) optimization marker.
inline bool HasOptimizationMarker();
// Mark this function for lazy recompilation. The function will be recompiled
// the next time it is executed.
void MarkForOptimization(ConcurrencyMode mode);
// Tells whether or not the function is already marked for lazy recompilation.
inline bool IsMarkedForOptimization();
inline bool IsMarkedForConcurrentOptimization();
// Tells whether or not the function is on the concurrent recompilation queue.
inline bool IsInOptimizationQueue();
// Clears the optimized code slot in the function's feedback vector.
inline void ClearOptimizedCodeSlot(const char* reason);
// Sets the optimization marker in the function's feedback vector.
inline void SetOptimizationMarker(OptimizationMarker marker);
// Clears the optimization marker in the function's feedback vector.
inline void ClearOptimizationMarker();
// If slack tracking is active, it computes instance size of the initial map
// with minimum permissible object slack. If it is not active, it simply
// returns the initial map's instance size.
int ComputeInstanceSizeWithMinSlack(Isolate* isolate);
// Completes inobject slack tracking on initial map if it is active.
inline void CompleteInobjectSlackTrackingIfActive();
// [raw_feedback_cell]: Gives raw access to the FeedbackCell used to hold the
/// FeedbackVector eventually. Generally this shouldn't be used to get the
// feedback_vector, instead use feedback_vector() which correctly deals with
// the JSFunction's bytecode being flushed.
DECL_ACCESSORS(raw_feedback_cell, FeedbackCell)
// Functions related to feedback vector. feedback_vector() can be used once
// the function has feedback vectors allocated. feedback vectors may not be
// available after compile when lazily allocating feedback vectors.
inline FeedbackVector feedback_vector() const;
inline bool has_feedback_vector() const;
V8_EXPORT_PRIVATE static void EnsureFeedbackVector(
Handle<JSFunction> function, IsCompiledScope* compiled_scope);
// Functions related to clousre feedback cell array that holds feedback cells
// used to create closures from this function. We allocate closure feedback
// cell arrays after compile, when we want to allocate feedback vectors
// lazily.
inline bool has_closure_feedback_cell_array() const;
inline ClosureFeedbackCellArray closure_feedback_cell_array() const;
static void EnsureClosureFeedbackCellArray(Handle<JSFunction> function);
// Initializes the feedback cell of |function|. In lite mode, this would be
// initialized to the closure feedback cell array that holds the feedback
// cells for create closure calls from this function. In the regular mode,
// this allocates feedback vector.
static void InitializeFeedbackCell(Handle<JSFunction> function,
IsCompiledScope* compiled_scope);
// Unconditionally clear the type feedback vector.
void ClearTypeFeedbackInfo();
// Resets function to clear compiled data after bytecode has been flushed.
inline bool NeedsResetDueToFlushedBytecode();
inline void ResetIfBytecodeFlushed(
base::Optional<std::function<void(HeapObject object, ObjectSlot slot,
HeapObject target)>>
gc_notify_updated_slot = base::nullopt);
DECL_GETTER(has_prototype_slot, bool)
// The initial map for an object created by this constructor.
DECL_GETTER(initial_map, Map)
static void SetInitialMap(Handle<JSFunction> function, Handle<Map> map,
Handle<HeapObject> prototype);
DECL_GETTER(has_initial_map, bool)
V8_EXPORT_PRIVATE static void EnsureHasInitialMap(
Handle<JSFunction> function);
// Creates a map that matches the constructor's initial map, but with
// [[prototype]] being new.target.prototype. Because new.target can be a
// JSProxy, this can call back into JavaScript.
static V8_WARN_UNUSED_RESULT MaybeHandle<Map> GetDerivedMap(
Isolate* isolate, Handle<JSFunction> constructor,
Handle<JSReceiver> new_target);
// Get and set the prototype property on a JSFunction. If the
// function has an initial map the prototype is set on the initial
// map. Otherwise, the prototype is put in the initial map field
// until an initial map is needed.
DECL_GETTER(has_prototype, bool)
DECL_GETTER(has_instance_prototype, bool)
DECL_GETTER(prototype, Object)
DECL_GETTER(instance_prototype, HeapObject)
DECL_GETTER(has_prototype_property, bool)
DECL_GETTER(PrototypeRequiresRuntimeLookup, bool)
static void SetPrototype(Handle<JSFunction> function, Handle<Object> value);
// Returns if this function has been compiled to native code yet.
inline bool is_compiled() const;
static int GetHeaderSize(bool function_has_prototype_slot) {
return function_has_prototype_slot ? JSFunction::kSizeWithPrototype
: JSFunction::kSizeWithoutPrototype;
}
// Prints the name of the function using PrintF.
void PrintName(FILE* out = stdout);
DECL_CAST(JSFunction)
// Calculate the instance size and in-object properties count.
// {CalculateExpectedNofProperties} can trigger compilation.
static V8_WARN_UNUSED_RESULT int CalculateExpectedNofProperties(
Isolate* isolate, Handle<JSFunction> function);
static void CalculateInstanceSizeHelper(InstanceType instance_type,
bool has_prototype_slot,
int requested_embedder_fields,
int requested_in_object_properties,
int* instance_size,
int* in_object_properties);
// Dispatched behavior.
DECL_PRINTER(JSFunction)
DECL_VERIFIER(JSFunction)
// The function's name if it is configured, otherwise shared function info
// debug name.
static Handle<String> GetName(Handle<JSFunction> function);
// ES6 section 9.2.11 SetFunctionName
// Because of the way this abstract operation is used in the spec,
// it should never fail, but in practice it will fail if the generated
// function name's length exceeds String::kMaxLength.
static V8_WARN_UNUSED_RESULT bool SetName(Handle<JSFunction> function,
Handle<Name> name,
Handle<String> prefix);
// The function's displayName if it is set, otherwise name if it is
// configured, otherwise shared function info
// debug name.
static Handle<String> GetDebugName(Handle<JSFunction> function);
// The function's string representation implemented according to
// ES6 section 19.2.3.5 Function.prototype.toString ( ).
static Handle<String> ToString(Handle<JSFunction> function);
struct FieldOffsets {
DEFINE_FIELD_OFFSET_CONSTANTS(JSFunctionOrBoundFunction::kHeaderSize,
TORQUE_GENERATED_JS_FUNCTION_FIELDS)
};
static constexpr int kSharedFunctionInfoOffset =
FieldOffsets::kSharedFunctionInfoOffset;
static constexpr int kContextOffset = FieldOffsets::kContextOffset;
static constexpr int kFeedbackCellOffset = FieldOffsets::kFeedbackCellOffset;
static constexpr int kCodeOffset = FieldOffsets::kCodeOffset;
static constexpr int kPrototypeOrInitialMapOffset =
FieldOffsets::kPrototypeOrInitialMapOffset;
private:
// JSFunction doesn't have a fixed header size:
// Hide JSFunctionOrBoundFunction::kHeaderSize to avoid confusion.
static const int kHeaderSize;
public:
static constexpr int kSizeWithoutPrototype = kPrototypeOrInitialMapOffset;
static constexpr int kSizeWithPrototype = FieldOffsets::kHeaderSize;
OBJECT_CONSTRUCTORS(JSFunction, JSFunctionOrBoundFunction);
};
// JSGlobalProxy's prototype must be a JSGlobalObject or null,
// and the prototype is hidden. JSGlobalProxy always delegates
// property accesses to its prototype if the prototype is not null.

View File

@ -4006,6 +4006,8 @@ void EmitClassDefinitionHeadersIncludes(const std::string& basename,
header << "#include <type_traits>\n\n";
inline_header << "#include \"torque-generated/class-definitions-tq.h\"\n";
inline_header << "#include \"src/objects/js-function.h\"\n";
inline_header << "#include \"src/objects/js-objects.h\"\n";
inline_header << "#include \"src/objects/js-promise.h\"\n";
inline_header << "#include \"src/objects/js-weak-refs.h\"\n";
inline_header << "#include \"src/objects/module.h\"\n";

View File

@ -7,6 +7,7 @@
#include "src/common/globals.h"
#include "src/objects/fixed-array.h"
#include "src/objects/js-function.h"
#include "src/objects/js-objects.h"
#include "src/objects/shared-function-info.h"

View File

@ -12,6 +12,7 @@
#include "src/codegen/signature.h"
#include "src/debug/debug.h"
#include "src/heap/heap.h"
#include "src/objects/js-function.h"
#include "src/objects/objects.h"
#include "src/wasm/struct-types.h"
#include "src/wasm/value-type.h"

View File

@ -40,6 +40,7 @@
#include "src/flags/flags.h"
#include "src/heap/factory.h"
#include "src/init/v8.h"
#include "src/objects/js-function.h"
#include "src/objects/objects.h"
#include "src/zone/accounting-allocator.h"