blob: 6bb3665963dfb91f86c4f50a92b2063cca78e615 [file] [log] [blame]
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/objects/js-function.h"
#include "src/codegen/compiler.h"
#include "src/diagnostics/code-tracer.h"
#include "src/heap/heap-inl.h"
#include "src/ic/ic.h"
#include "src/init/bootstrapper.h"
#include "src/objects/feedback-cell-inl.h"
#include "src/strings/string-builder-inl.h"
// Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h"
namespace v8 {
namespace internal {
CodeKinds JSFunction::GetAttachedCodeKinds() const {
CodeKinds result;
// Note: There's a special case when bytecode has been aged away. After
// flushing the bytecode, the JSFunction will still have the interpreter
// entry trampoline attached, but the bytecode is no longer available.
if (code().is_interpreter_trampoline_builtin()) {
result |= CodeKindFlag::INTERPRETED_FUNCTION;
}
const CodeKind kind = code().kind();
if (!CodeKindIsOptimizedJSFunction(kind) ||
code().marked_for_deoptimization()) {
DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
return result;
}
DCHECK(CodeKindIsOptimizedJSFunction(kind));
result |= CodeKindToCodeKindFlag(kind);
DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
return result;
}
CodeKinds JSFunction::GetAvailableCodeKinds() const {
CodeKinds result = GetAttachedCodeKinds();
if ((result & CodeKindFlag::INTERPRETED_FUNCTION) == 0) {
// The SharedFunctionInfo could have attached bytecode.
if (shared().HasBytecodeArray()) {
result |= CodeKindFlag::INTERPRETED_FUNCTION;
}
}
if ((result & kOptimizedJSFunctionCodeKindsMask) == 0) {
// Check the optimized code cache.
if (has_feedback_vector() && feedback_vector().has_optimized_code() &&
!feedback_vector().optimized_code().marked_for_deoptimization()) {
Code code = feedback_vector().optimized_code();
DCHECK(CodeKindIsOptimizedJSFunction(code.kind()));
result |= CodeKindToCodeKindFlag(code.kind());
}
}
DCHECK_EQ((result & ~kJSFunctionCodeKindsMask), 0);
return result;
}
bool JSFunction::HasAttachedOptimizedCode() const {
CodeKinds result = GetAttachedCodeKinds();
return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
}
bool JSFunction::HasAvailableOptimizedCode() const {
CodeKinds result = GetAvailableCodeKinds();
return (result & kOptimizedJSFunctionCodeKindsMask) != 0;
}
bool JSFunction::HasAvailableCodeKind(CodeKind kind) const {
CodeKinds result = GetAvailableCodeKinds();
return (result & CodeKindToCodeKindFlag(kind)) != 0;
}
namespace {
// Returns false if no highest tier exists (i.e. the function is not compiled),
// otherwise returns true and sets highest_tier.
bool HighestTierOf(CodeKinds kinds, CodeKind* highest_tier) {
DCHECK_EQ((kinds & ~kJSFunctionCodeKindsMask), 0);
if ((kinds & CodeKindFlag::TURBOFAN) != 0) {
*highest_tier = CodeKind::TURBOFAN;
return true;
} else if ((kinds & CodeKindFlag::TURBOPROP) != 0) {
*highest_tier = CodeKind::TURBOPROP;
return true;
} else if ((kinds & CodeKindFlag::NATIVE_CONTEXT_INDEPENDENT) != 0) {
*highest_tier = CodeKind::NATIVE_CONTEXT_INDEPENDENT;
return true;
} else if ((kinds & CodeKindFlag::INTERPRETED_FUNCTION) != 0) {
*highest_tier = CodeKind::INTERPRETED_FUNCTION;
return true;
}
DCHECK_EQ(kinds, 0);
return false;
}
} // namespace
bool JSFunction::ActiveTierIsIgnition() const {
CodeKind highest_tier;
if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
bool result = (highest_tier == CodeKind::INTERPRETED_FUNCTION);
DCHECK_IMPLIES(result,
code().is_interpreter_trampoline_builtin() ||
(CodeKindIsOptimizedJSFunction(code().kind()) &&
code().marked_for_deoptimization()) ||
(code().builtin_index() == Builtins::kCompileLazy &&
shared().IsInterpreted()));
return result;
}
bool JSFunction::ActiveTierIsTurbofan() const {
CodeKind highest_tier;
if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
return highest_tier == CodeKind::TURBOFAN;
}
bool JSFunction::ActiveTierIsNCI() const {
CodeKind highest_tier;
if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
return highest_tier == CodeKind::NATIVE_CONTEXT_INDEPENDENT;
}
bool JSFunction::ActiveTierIsToptierTurboprop() const {
CodeKind highest_tier;
if (!FLAG_turboprop) return false;
if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
return highest_tier == CodeKind::TURBOPROP && !FLAG_turboprop_as_midtier;
}
bool JSFunction::ActiveTierIsMidtierTurboprop() const {
CodeKind highest_tier;
if (!FLAG_turboprop_as_midtier) return false;
if (!HighestTierOf(GetAvailableCodeKinds(), &highest_tier)) return false;
return highest_tier == CodeKind::TURBOPROP && FLAG_turboprop_as_midtier;
}
CodeKind JSFunction::NextTier() const {
if (V8_UNLIKELY(FLAG_turbo_nci_as_midtier && ActiveTierIsIgnition())) {
return CodeKind::NATIVE_CONTEXT_INDEPENDENT;
} else if (V8_UNLIKELY(FLAG_turboprop) && ActiveTierIsMidtierTurboprop()) {
return CodeKind::TURBOFAN;
} else if (V8_UNLIKELY(FLAG_turboprop)) {
DCHECK(ActiveTierIsIgnition());
return CodeKind::TURBOPROP;
}
return CodeKind::TURBOFAN;
}
bool JSFunction::CanDiscardCompiled() const {
// Essentially, what we are asking here is, has this function been compiled
// from JS code? We can currently tell only indirectly, by looking at
// available code kinds. If any JS code kind exists, we can discard.
//
// Attached optimized code that is marked for deoptimization will not show up
// in the list of available code kinds, thus we must check for it manually.
//
// Note that when the function has not yet been compiled we also return
// false; that's fine, since nothing must be discarded in that case.
if (CodeKindIsOptimizedJSFunction(code().kind())) return true;
CodeKinds result = GetAvailableCodeKinds();
return (result & kJSFunctionCodeKindsMask) != 0;
}
// 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 = std::max(0, target_length - nof_bound_arguments);
return Just(length);
}
// static
Handle<String> JSBoundFunction::ToString(Handle<JSBoundFunction> function) {
Isolate* const isolate = function->GetIsolate();
return isolate->factory()->function_native_code_string();
}
// static
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());
}
// 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_SEGMENTS_TYPE:
case JS_V8_BREAK_ITERATOR_TYPE:
#endif
case JS_ASYNC_FUNCTION_OBJECT_TYPE:
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
case JS_MAP_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
case JS_OBJECT_TYPE:
case JS_ERROR_TYPE:
case JS_FINALIZATION_REGISTRY_TYPE:
case JS_ARGUMENTS_OBJECT_TYPE:
case JS_PROMISE_TYPE:
case JS_REG_EXP_TYPE:
case JS_SET_TYPE:
case JS_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 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 = std::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 = std::min(requested_in_object_properties,
max_nof_fields - requested_embedder_fields);
*instance_size =
header_size +
((requested_embedder_fields + *in_object_properties) << kTaggedSizeLog2);
CHECK_EQ(*in_object_properties,
((*instance_size - header_size) >> kTaggedSizeLog2) -
requested_embedder_fields);
CHECK_LE(static_cast<unsigned>(*instance_size),
static_cast<unsigned>(JSObject::kMaxInstanceSize));
}
void JSFunction::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
#include "src/objects/object-macros-undef.h"