blob: a994718bd079007a3af15c4b7fa7577547672f2a [file] [log] [blame]
// Copyright 2013 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/v8.h"
#include "src/accessors.h"
#include "src/allocation-site-scopes.h"
#include "src/api.h"
#include "src/arguments.h"
#include "src/bootstrapper.h"
#include "src/code-stubs.h"
#include "src/codegen.h"
#include "src/cpu-profiler.h"
#include "src/date.h"
#include "src/debug.h"
#include "src/deoptimizer.h"
#include "src/elements.h"
#include "src/execution.h"
#include "src/field-index-inl.h"
#include "src/field-index.h"
#include "src/full-codegen.h"
#include "src/heap/mark-compact.h"
#include "src/heap/objects-visiting-inl.h"
#include "src/hydrogen.h"
#include "src/isolate-inl.h"
#include "src/log.h"
#include "src/lookup.h"
#include "src/macro-assembler.h"
#include "src/objects-inl.h"
#include "src/prototype.h"
#include "src/safepoint-table.h"
#include "src/string-search.h"
#include "src/string-stream.h"
#include "src/utils.h"
#ifdef ENABLE_DISASSEMBLER
#include "src/disasm.h"
#include "src/disassembler.h"
#endif
namespace v8 {
namespace internal {
Handle<HeapType> Object::OptimalType(Isolate* isolate,
Representation representation) {
if (representation.IsNone()) return HeapType::None(isolate);
if (FLAG_track_field_types) {
if (representation.IsHeapObject() && IsHeapObject()) {
// We can track only JavaScript objects with stable maps.
Handle<Map> map(HeapObject::cast(this)->map(), isolate);
if (map->is_stable() &&
map->instance_type() >= FIRST_NONCALLABLE_SPEC_OBJECT_TYPE &&
map->instance_type() <= LAST_NONCALLABLE_SPEC_OBJECT_TYPE) {
return HeapType::Class(map, isolate);
}
}
}
return HeapType::Any(isolate);
}
MaybeHandle<JSReceiver> Object::ToObject(Isolate* isolate,
Handle<Object> object,
Handle<Context> native_context) {
if (object->IsJSReceiver()) return Handle<JSReceiver>::cast(object);
Handle<JSFunction> constructor;
if (object->IsNumber()) {
constructor = handle(native_context->number_function(), isolate);
} else if (object->IsBoolean()) {
constructor = handle(native_context->boolean_function(), isolate);
} else if (object->IsString()) {
constructor = handle(native_context->string_function(), isolate);
} else if (object->IsSymbol()) {
constructor = handle(native_context->symbol_function(), isolate);
} else {
return MaybeHandle<JSReceiver>();
}
Handle<JSObject> result = isolate->factory()->NewJSObject(constructor);
Handle<JSValue>::cast(result)->set_value(*object);
return result;
}
bool Object::BooleanValue() {
if (IsBoolean()) return IsTrue();
if (IsSmi()) return Smi::cast(this)->value() != 0;
if (IsUndefined() || IsNull()) return false;
if (IsUndetectableObject()) return false; // Undetectable object is false.
if (IsString()) return String::cast(this)->length() != 0;
if (IsHeapNumber()) return HeapNumber::cast(this)->HeapNumberBooleanValue();
return true;
}
bool Object::IsCallable() const {
const Object* fun = this;
while (fun->IsJSFunctionProxy()) {
fun = JSFunctionProxy::cast(fun)->call_trap();
}
return fun->IsJSFunction() ||
(fun->IsHeapObject() &&
HeapObject::cast(fun)->map()->has_instance_call_handler());
}
void Object::Lookup(Handle<Name> name, LookupResult* result) {
DisallowHeapAllocation no_gc;
Object* holder = NULL;
if (IsJSReceiver()) {
holder = this;
} else {
Context* native_context = result->isolate()->context()->native_context();
if (IsNumber()) {
holder = native_context->number_function()->instance_prototype();
} else if (IsString()) {
holder = native_context->string_function()->instance_prototype();
} else if (IsSymbol()) {
holder = native_context->symbol_function()->instance_prototype();
} else if (IsBoolean()) {
holder = native_context->boolean_function()->instance_prototype();
} else {
result->isolate()->PushStackTraceAndDie(
0xDEAD0000, this, JSReceiver::cast(this)->map(), 0xDEAD0001);
}
}
DCHECK(holder != NULL); // Cannot handle null or undefined.
JSReceiver::cast(holder)->Lookup(name, result);
}
MaybeHandle<Object> Object::GetProperty(LookupIterator* it) {
for (; it->IsFound(); it->Next()) {
switch (it->state()) {
case LookupIterator::NOT_FOUND:
UNREACHABLE();
case LookupIterator::JSPROXY:
return JSProxy::GetPropertyWithHandler(it->GetHolder<JSProxy>(),
it->GetReceiver(), it->name());
case LookupIterator::INTERCEPTOR: {
MaybeHandle<Object> maybe_result = JSObject::GetPropertyWithInterceptor(
it->GetHolder<JSObject>(), it->GetReceiver(), it->name());
if (!maybe_result.is_null()) return maybe_result;
if (it->isolate()->has_pending_exception()) return maybe_result;
break;
}
case LookupIterator::ACCESS_CHECK:
if (it->HasAccess(v8::ACCESS_GET)) break;
return JSObject::GetPropertyWithFailedAccessCheck(it);
case LookupIterator::PROPERTY:
if (it->HasProperty()) {
switch (it->property_kind()) {
case LookupIterator::ACCESSOR:
return GetPropertyWithAccessor(it->GetReceiver(), it->name(),
it->GetHolder<JSObject>(),
it->GetAccessors());
case LookupIterator::DATA:
return it->GetDataValue();
}
}
break;
}
}
return it->factory()->undefined_value();
}
bool Object::ToInt32(int32_t* value) {
if (IsSmi()) {
*value = Smi::cast(this)->value();
return true;
}
if (IsHeapNumber()) {
double num = HeapNumber::cast(this)->value();
if (FastI2D(FastD2I(num)) == num) {
*value = FastD2I(num);
return true;
}
}
return false;
}
bool Object::ToUint32(uint32_t* value) {
if (IsSmi()) {
int num = Smi::cast(this)->value();
if (num >= 0) {
*value = static_cast<uint32_t>(num);
return true;
}
}
if (IsHeapNumber()) {
double num = HeapNumber::cast(this)->value();
if (num >= 0 && FastUI2D(FastD2UI(num)) == num) {
*value = FastD2UI(num);
return true;
}
}
return false;
}
bool FunctionTemplateInfo::IsTemplateFor(Object* object) {
if (!object->IsHeapObject()) return false;
return IsTemplateFor(HeapObject::cast(object)->map());
}
bool FunctionTemplateInfo::IsTemplateFor(Map* map) {
// There is a constraint on the object; check.
if (!map->IsJSObjectMap()) return false;
// Fetch the constructor function of the object.
Object* cons_obj = map->constructor();
if (!cons_obj->IsJSFunction()) return false;
JSFunction* fun = JSFunction::cast(cons_obj);
// Iterate through the chain of inheriting function templates to
// see if the required one occurs.
for (Object* type = fun->shared()->function_data();
type->IsFunctionTemplateInfo();
type = FunctionTemplateInfo::cast(type)->parent_template()) {
if (type == this) return true;
}
// Didn't find the required type in the inheritance chain.
return false;
}
template<typename To>
static inline To* CheckedCast(void *from) {
uintptr_t temp = reinterpret_cast<uintptr_t>(from);
DCHECK(temp % sizeof(To) == 0);
return reinterpret_cast<To*>(temp);
}
static Handle<Object> PerformCompare(const BitmaskCompareDescriptor& descriptor,
char* ptr,
Isolate* isolate) {
uint32_t bitmask = descriptor.bitmask;
uint32_t compare_value = descriptor.compare_value;
uint32_t value;
switch (descriptor.size) {
case 1:
value = static_cast<uint32_t>(*CheckedCast<uint8_t>(ptr));
compare_value &= 0xff;
bitmask &= 0xff;
break;
case 2:
value = static_cast<uint32_t>(*CheckedCast<uint16_t>(ptr));
compare_value &= 0xffff;
bitmask &= 0xffff;
break;
case 4:
value = *CheckedCast<uint32_t>(ptr);
break;
default:
UNREACHABLE();
return isolate->factory()->undefined_value();
}
return isolate->factory()->ToBoolean(
(bitmask & value) == (bitmask & compare_value));
}
static Handle<Object> PerformCompare(const PointerCompareDescriptor& descriptor,
char* ptr,
Isolate* isolate) {
uintptr_t compare_value =
reinterpret_cast<uintptr_t>(descriptor.compare_value);
uintptr_t value = *CheckedCast<uintptr_t>(ptr);
return isolate->factory()->ToBoolean(compare_value == value);
}
static Handle<Object> GetPrimitiveValue(
const PrimitiveValueDescriptor& descriptor,
char* ptr,
Isolate* isolate) {
int32_t int32_value = 0;
switch (descriptor.data_type) {
case kDescriptorInt8Type:
int32_value = *CheckedCast<int8_t>(ptr);
break;
case kDescriptorUint8Type:
int32_value = *CheckedCast<uint8_t>(ptr);
break;
case kDescriptorInt16Type:
int32_value = *CheckedCast<int16_t>(ptr);
break;
case kDescriptorUint16Type:
int32_value = *CheckedCast<uint16_t>(ptr);
break;
case kDescriptorInt32Type:
int32_value = *CheckedCast<int32_t>(ptr);
break;
case kDescriptorUint32Type: {
uint32_t value = *CheckedCast<uint32_t>(ptr);
AllowHeapAllocation allow_gc;
return isolate->factory()->NewNumberFromUint(value);
}
case kDescriptorBoolType: {
uint8_t byte = *CheckedCast<uint8_t>(ptr);
return isolate->factory()->ToBoolean(
byte & (0x1 << descriptor.bool_offset));
}
case kDescriptorFloatType: {
float value = *CheckedCast<float>(ptr);
AllowHeapAllocation allow_gc;
return isolate->factory()->NewNumber(value);
}
case kDescriptorDoubleType: {
double value = *CheckedCast<double>(ptr);
AllowHeapAllocation allow_gc;
return isolate->factory()->NewNumber(value);
}
}
AllowHeapAllocation allow_gc;
return isolate->factory()->NewNumberFromInt(int32_value);
}
static Handle<Object> GetDeclaredAccessorProperty(
Handle<Object> receiver,
Handle<DeclaredAccessorInfo> info,
Isolate* isolate) {
DisallowHeapAllocation no_gc;
char* current = reinterpret_cast<char*>(*receiver);
DeclaredAccessorDescriptorIterator iterator(info->descriptor());
while (true) {
const DeclaredAccessorDescriptorData* data = iterator.Next();
switch (data->type) {
case kDescriptorReturnObject: {
DCHECK(iterator.Complete());
current = *CheckedCast<char*>(current);
return handle(*CheckedCast<Object*>(current), isolate);
}
case kDescriptorPointerDereference:
DCHECK(!iterator.Complete());
current = *reinterpret_cast<char**>(current);
break;
case kDescriptorPointerShift:
DCHECK(!iterator.Complete());
current += data->pointer_shift_descriptor.byte_offset;
break;
case kDescriptorObjectDereference: {
DCHECK(!iterator.Complete());
Object* object = CheckedCast<Object>(current);
int field = data->object_dereference_descriptor.internal_field;
Object* smi = JSObject::cast(object)->GetInternalField(field);
DCHECK(smi->IsSmi());
current = reinterpret_cast<char*>(smi);
break;
}
case kDescriptorBitmaskCompare:
DCHECK(iterator.Complete());
return PerformCompare(data->bitmask_compare_descriptor,
current,
isolate);
case kDescriptorPointerCompare:
DCHECK(iterator.Complete());
return PerformCompare(data->pointer_compare_descriptor,
current,
isolate);
case kDescriptorPrimitiveValue:
DCHECK(iterator.Complete());
return GetPrimitiveValue(data->primitive_value_descriptor,
current,
isolate);
}
}
UNREACHABLE();
return isolate->factory()->undefined_value();
}
Handle<FixedArray> JSObject::EnsureWritableFastElements(
Handle<JSObject> object) {
DCHECK(object->HasFastSmiOrObjectElements());
Isolate* isolate = object->GetIsolate();
Handle<FixedArray> elems(FixedArray::cast(object->elements()), isolate);
if (elems->map() != isolate->heap()->fixed_cow_array_map()) return elems;
Handle<FixedArray> writable_elems = isolate->factory()->CopyFixedArrayWithMap(
elems, isolate->factory()->fixed_array_map());
object->set_elements(*writable_elems);
isolate->counters()->cow_arrays_converted()->Increment();
return writable_elems;
}
MaybeHandle<Object> JSProxy::GetPropertyWithHandler(Handle<JSProxy> proxy,
Handle<Object> receiver,
Handle<Name> name) {
Isolate* isolate = proxy->GetIsolate();
// TODO(rossberg): adjust once there is a story for symbols vs proxies.
if (name->IsSymbol()) return isolate->factory()->undefined_value();
Handle<Object> args[] = { receiver, name };
return CallTrap(
proxy, "get", isolate->derived_get_trap(), ARRAY_SIZE(args), args);
}
MaybeHandle<Object> Object::GetPropertyWithAccessor(Handle<Object> receiver,
Handle<Name> name,
Handle<JSObject> holder,
Handle<Object> structure) {
Isolate* isolate = name->GetIsolate();
DCHECK(!structure->IsForeign());
// api style callbacks.
if (structure->IsAccessorInfo()) {
Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(structure);
if (!info->IsCompatibleReceiver(*receiver)) {
Handle<Object> args[2] = { name, receiver };
Handle<Object> error =
isolate->factory()->NewTypeError("incompatible_method_receiver",
HandleVector(args,
ARRAY_SIZE(args)));
return isolate->Throw<Object>(error);
}
// TODO(rossberg): Handling symbols in the API requires changing the API,
// so we do not support it for now.
if (name->IsSymbol()) return isolate->factory()->undefined_value();
if (structure->IsDeclaredAccessorInfo()) {
return GetDeclaredAccessorProperty(
receiver,
Handle<DeclaredAccessorInfo>::cast(structure),
isolate);
}
Handle<ExecutableAccessorInfo> data =
Handle<ExecutableAccessorInfo>::cast(structure);
v8::AccessorGetterCallback call_fun =
v8::ToCData<v8::AccessorGetterCallback>(data->getter());
if (call_fun == NULL) return isolate->factory()->undefined_value();
Handle<String> key = Handle<String>::cast(name);
LOG(isolate, ApiNamedPropertyAccess("load", *holder, *name));
PropertyCallbackArguments args(isolate, data->data(), *receiver, *holder);
v8::Handle<v8::Value> result =
args.Call(call_fun, v8::Utils::ToLocal(key));
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
if (result.IsEmpty()) {
return isolate->factory()->undefined_value();
}
Handle<Object> return_value = v8::Utils::OpenHandle(*result);
return_value->VerifyApiCallResultType();
// Rebox handle before return.
return handle(*return_value, isolate);
}
// __defineGetter__ callback
Handle<Object> getter(Handle<AccessorPair>::cast(structure)->getter(),
isolate);
if (getter->IsSpecFunction()) {
// TODO(rossberg): nicer would be to cast to some JSCallable here...
return Object::GetPropertyWithDefinedGetter(
receiver, Handle<JSReceiver>::cast(getter));
}
// Getter is not a function.
return isolate->factory()->undefined_value();
}
bool AccessorInfo::IsCompatibleReceiverType(Isolate* isolate,
Handle<AccessorInfo> info,
Handle<HeapType> type) {
if (!info->HasExpectedReceiverType()) return true;
Handle<Map> map = IC::TypeToMap(*type, isolate);
if (!map->IsJSObjectMap()) return false;
return FunctionTemplateInfo::cast(info->expected_receiver_type())
->IsTemplateFor(*map);
}
MaybeHandle<Object> Object::SetPropertyWithAccessor(
Handle<Object> receiver, Handle<Name> name, Handle<Object> value,
Handle<JSObject> holder, Handle<Object> structure, StrictMode strict_mode) {
Isolate* isolate = name->GetIsolate();
// We should never get here to initialize a const with the hole
// value since a const declaration would conflict with the setter.
DCHECK(!structure->IsForeign());
if (structure->IsExecutableAccessorInfo()) {
// Don't call executable accessor setters with non-JSObject receivers.
if (!receiver->IsJSObject()) return value;
// api style callbacks
ExecutableAccessorInfo* info = ExecutableAccessorInfo::cast(*structure);
if (!info->IsCompatibleReceiver(*receiver)) {
Handle<Object> args[2] = { name, receiver };
Handle<Object> error =
isolate->factory()->NewTypeError("incompatible_method_receiver",
HandleVector(args,
ARRAY_SIZE(args)));
return isolate->Throw<Object>(error);
}
// TODO(rossberg): Support symbols in the API.
if (name->IsSymbol()) return value;
Object* call_obj = info->setter();
v8::AccessorSetterCallback call_fun =
v8::ToCData<v8::AccessorSetterCallback>(call_obj);
if (call_fun == NULL) return value;
Handle<String> key = Handle<String>::cast(name);
LOG(isolate, ApiNamedPropertyAccess("store", *holder, *name));
PropertyCallbackArguments args(isolate, info->data(), *receiver, *holder);
args.Call(call_fun,
v8::Utils::ToLocal(key),
v8::Utils::ToLocal(value));
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
return value;
}
if (structure->IsAccessorPair()) {
Handle<Object> setter(AccessorPair::cast(*structure)->setter(), isolate);
if (setter->IsSpecFunction()) {
// TODO(rossberg): nicer would be to cast to some JSCallable here...
return SetPropertyWithDefinedSetter(
receiver, Handle<JSReceiver>::cast(setter), value);
} else {
if (strict_mode == SLOPPY) return value;
Handle<Object> args[2] = { name, holder };
Handle<Object> error =
isolate->factory()->NewTypeError("no_setter_in_callback",
HandleVector(args, 2));
return isolate->Throw<Object>(error);
}
}
// TODO(dcarney): Handle correctly.
if (structure->IsDeclaredAccessorInfo()) {
return value;
}
UNREACHABLE();
return MaybeHandle<Object>();
}
MaybeHandle<Object> Object::GetPropertyWithDefinedGetter(
Handle<Object> receiver,
Handle<JSReceiver> getter) {
Isolate* isolate = getter->GetIsolate();
Debug* debug = isolate->debug();
// Handle stepping into a getter if step into is active.
// TODO(rossberg): should this apply to getters that are function proxies?
if (debug->StepInActive() && getter->IsJSFunction()) {
debug->HandleStepIn(
Handle<JSFunction>::cast(getter), Handle<Object>::null(), 0, false);
}
return Execution::Call(isolate, getter, receiver, 0, NULL, true);
}
MaybeHandle<Object> Object::SetPropertyWithDefinedSetter(
Handle<Object> receiver,
Handle<JSReceiver> setter,
Handle<Object> value) {
Isolate* isolate = setter->GetIsolate();
Debug* debug = isolate->debug();
// Handle stepping into a setter if step into is active.
// TODO(rossberg): should this apply to getters that are function proxies?
if (debug->StepInActive() && setter->IsJSFunction()) {
debug->HandleStepIn(
Handle<JSFunction>::cast(setter), Handle<Object>::null(), 0, false);
}
Handle<Object> argv[] = { value };
RETURN_ON_EXCEPTION(isolate, Execution::Call(isolate, setter, receiver,
ARRAY_SIZE(argv), argv, true),
Object);
return value;
}
static bool FindAllCanReadHolder(LookupIterator* it) {
it->skip_interceptor();
it->skip_access_check();
for (; it->IsFound(); it->Next()) {
if (it->state() == LookupIterator::PROPERTY &&
it->HasProperty() &&
it->property_kind() == LookupIterator::ACCESSOR) {
Handle<Object> accessors = it->GetAccessors();
if (accessors->IsAccessorInfo()) {
if (AccessorInfo::cast(*accessors)->all_can_read()) return true;
}
}
}
return false;
}
MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck(
LookupIterator* it) {
Handle<JSObject> checked = it->GetHolder<JSObject>();
if (FindAllCanReadHolder(it)) {
return GetPropertyWithAccessor(it->GetReceiver(), it->name(),
it->GetHolder<JSObject>(),
it->GetAccessors());
}
it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_GET);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
return it->factory()->undefined_value();
}
Maybe<PropertyAttributes> JSObject::GetPropertyAttributesWithFailedAccessCheck(
LookupIterator* it) {
Handle<JSObject> checked = it->GetHolder<JSObject>();
if (FindAllCanReadHolder(it))
return maybe(it->property_details().attributes());
it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_HAS);
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(it->isolate(),
Maybe<PropertyAttributes>());
return maybe(ABSENT);
}
static bool FindAllCanWriteHolder(LookupIterator* it) {
it->skip_interceptor();
it->skip_access_check();
for (; it->IsFound(); it->Next()) {
if (it->state() == LookupIterator::PROPERTY && it->HasProperty() &&
it->property_kind() == LookupIterator::ACCESSOR) {
Handle<Object> accessors = it->GetAccessors();
if (accessors->IsAccessorInfo()) {
if (AccessorInfo::cast(*accessors)->all_can_write()) return true;
}
}
}
return false;
}
MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck(
LookupIterator* it, Handle<Object> value, StrictMode strict_mode) {
Handle<JSObject> checked = it->GetHolder<JSObject>();
if (FindAllCanWriteHolder(it)) {
return SetPropertyWithAccessor(it->GetReceiver(), it->name(), value,
it->GetHolder<JSObject>(),
it->GetAccessors(), strict_mode);
}
it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_SET);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
return value;
}
Object* JSObject::GetNormalizedProperty(const LookupResult* result) {
DCHECK(!HasFastProperties());
Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
if (IsGlobalObject()) {
value = PropertyCell::cast(value)->value();
}
DCHECK(!value->IsPropertyCell() && !value->IsCell());
return value;
}
Handle<Object> JSObject::GetNormalizedProperty(Handle<JSObject> object,
const LookupResult* result) {
DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
Handle<Object> value(object->property_dictionary()->ValueAt(
result->GetDictionaryEntry()), isolate);
if (object->IsGlobalObject()) {
value = handle(Handle<PropertyCell>::cast(value)->value(), isolate);
DCHECK(!value->IsTheHole());
}
DCHECK(!value->IsPropertyCell() && !value->IsCell());
return value;
}
void JSObject::SetNormalizedProperty(Handle<JSObject> object,
const LookupResult* result,
Handle<Object> value) {
DCHECK(!object->HasFastProperties());
NameDictionary* property_dictionary = object->property_dictionary();
if (object->IsGlobalObject()) {
Handle<PropertyCell> cell(PropertyCell::cast(
property_dictionary->ValueAt(result->GetDictionaryEntry())));
PropertyCell::SetValueInferType(cell, value);
} else {
property_dictionary->ValueAtPut(result->GetDictionaryEntry(), *value);
}
}
void JSObject::SetNormalizedProperty(Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
PropertyDetails details) {
CHECK(!object->HasFastProperties());
Handle<NameDictionary> property_dictionary(object->property_dictionary());
if (!name->IsUniqueName()) {
name = object->GetIsolate()->factory()->InternalizeString(
Handle<String>::cast(name));
}
int entry = property_dictionary->FindEntry(name);
if (entry == NameDictionary::kNotFound) {
Handle<Object> store_value = value;
if (object->IsGlobalObject()) {
store_value = object->GetIsolate()->factory()->NewPropertyCell(value);
}
property_dictionary = NameDictionary::Add(
property_dictionary, name, store_value, details);
object->set_properties(*property_dictionary);
return;
}
PropertyDetails original_details = property_dictionary->DetailsAt(entry);
int enumeration_index;
// Preserve the enumeration index unless the property was deleted.
if (original_details.IsDeleted()) {
enumeration_index = property_dictionary->NextEnumerationIndex();
property_dictionary->SetNextEnumerationIndex(enumeration_index + 1);
} else {
enumeration_index = original_details.dictionary_index();
DCHECK(enumeration_index > 0);
}
details = PropertyDetails(
details.attributes(), details.type(), enumeration_index);
if (object->IsGlobalObject()) {
Handle<PropertyCell> cell(
PropertyCell::cast(property_dictionary->ValueAt(entry)));
PropertyCell::SetValueInferType(cell, value);
// Please note we have to update the property details.
property_dictionary->DetailsAtPut(entry, details);
} else {
property_dictionary->SetEntry(entry, name, value, details);
}
}
Handle<Object> JSObject::DeleteNormalizedProperty(Handle<JSObject> object,
Handle<Name> name,
DeleteMode mode) {
DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
Handle<NameDictionary> dictionary(object->property_dictionary());
int entry = dictionary->FindEntry(name);
if (entry != NameDictionary::kNotFound) {
// If we have a global object set the cell to the hole.
if (object->IsGlobalObject()) {
PropertyDetails details = dictionary->DetailsAt(entry);
if (details.IsDontDelete()) {
if (mode != FORCE_DELETION) return isolate->factory()->false_value();
// When forced to delete global properties, we have to make a
// map change to invalidate any ICs that think they can load
// from the DontDelete cell without checking if it contains
// the hole value.
Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map()));
DCHECK(new_map->is_dictionary_map());
JSObject::MigrateToMap(object, new_map);
}
Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry)));
Handle<Object> value = isolate->factory()->the_hole_value();
PropertyCell::SetValueInferType(cell, value);
dictionary->DetailsAtPut(entry, details.AsDeleted());
} else {
Handle<Object> deleted(
NameDictionary::DeleteProperty(dictionary, entry, mode));
if (*deleted == isolate->heap()->true_value()) {
Handle<NameDictionary> new_properties =
NameDictionary::Shrink(dictionary, name);
object->set_properties(*new_properties);
}
return deleted;
}
}
return isolate->factory()->true_value();
}
bool JSObject::IsDirty() {
Object* cons_obj = map()->constructor();
if (!cons_obj->IsJSFunction())
return true;
JSFunction* fun = JSFunction::cast(cons_obj);
if (!fun->shared()->IsApiFunction())
return true;
// If the object is fully fast case and has the same map it was
// created with then no changes can have been made to it.
return map() != fun->initial_map()
|| !HasFastObjectElements()
|| !HasFastProperties();
}
MaybeHandle<Object> Object::GetElementWithReceiver(Isolate* isolate,
Handle<Object> object,
Handle<Object> receiver,
uint32_t index) {
if (object->IsUndefined()) {
// TODO(verwaest): Why is this check here?
UNREACHABLE();
return isolate->factory()->undefined_value();
}
// Iterate up the prototype chain until an element is found or the null
// prototype is encountered.
for (PrototypeIterator iter(isolate, object,
object->IsJSProxy() || object->IsJSObject()
? PrototypeIterator::START_AT_RECEIVER
: PrototypeIterator::START_AT_PROTOTYPE);
!iter.IsAtEnd(); iter.Advance()) {
if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
return JSProxy::GetElementWithHandler(
Handle<JSProxy>::cast(PrototypeIterator::GetCurrent(iter)), receiver,
index);
}
// Inline the case for JSObjects. Doing so significantly improves the
// performance of fetching elements where checking the prototype chain is
// necessary.
Handle<JSObject> js_object =
Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
// Check access rights if needed.
if (js_object->IsAccessCheckNeeded()) {
if (!isolate->MayIndexedAccess(js_object, index, v8::ACCESS_GET)) {
isolate->ReportFailedAccessCheck(js_object, v8::ACCESS_GET);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
return isolate->factory()->undefined_value();
}
}
if (js_object->HasIndexedInterceptor()) {
return JSObject::GetElementWithInterceptor(js_object, receiver, index);
}
if (js_object->elements() != isolate->heap()->empty_fixed_array()) {
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,
js_object->GetElementsAccessor()->Get(receiver, js_object, index),
Object);
if (!result->IsTheHole()) return result;
}
}
return isolate->factory()->undefined_value();
}
Map* Object::GetRootMap(Isolate* isolate) {
DisallowHeapAllocation no_alloc;
if (IsSmi()) {
Context* context = isolate->context()->native_context();
return context->number_function()->initial_map();
}
HeapObject* heap_object = HeapObject::cast(this);
// The object is either a number, a string, a boolean,
// a real JS object, or a Harmony proxy.
if (heap_object->IsJSReceiver()) {
return heap_object->map();
}
Context* context = isolate->context()->native_context();
if (heap_object->IsHeapNumber()) {
return context->number_function()->initial_map();
}
if (heap_object->IsString()) {
return context->string_function()->initial_map();
}
if (heap_object->IsSymbol()) {
return context->symbol_function()->initial_map();
}
if (heap_object->IsBoolean()) {
return context->boolean_function()->initial_map();
}
return isolate->heap()->null_value()->map();
}
Object* Object::GetHash() {
// The object is either a number, a name, an odd-ball,
// a real JS object, or a Harmony proxy.
if (IsNumber()) {
uint32_t hash = ComputeLongHash(double_to_uint64(Number()));
return Smi::FromInt(hash & Smi::kMaxValue);
}
if (IsName()) {
uint32_t hash = Name::cast(this)->Hash();
return Smi::FromInt(hash);
}
if (IsOddball()) {
uint32_t hash = Oddball::cast(this)->to_string()->Hash();
return Smi::FromInt(hash);
}
DCHECK(IsJSReceiver());
return JSReceiver::cast(this)->GetIdentityHash();
}
Handle<Smi> Object::GetOrCreateHash(Isolate* isolate, Handle<Object> object) {
Handle<Object> hash(object->GetHash(), isolate);
if (hash->IsSmi()) return Handle<Smi>::cast(hash);
DCHECK(object->IsJSReceiver());
return JSReceiver::GetOrCreateIdentityHash(Handle<JSReceiver>::cast(object));
}
bool Object::SameValue(Object* other) {
if (other == this) return true;
// The object is either a number, a name, an odd-ball,
// a real JS object, or a Harmony proxy.
if (IsNumber() && other->IsNumber()) {
double this_value = Number();
double other_value = other->Number();
bool equal = this_value == other_value;
// SameValue(NaN, NaN) is true.
if (!equal) return std::isnan(this_value) && std::isnan(other_value);
// SameValue(0.0, -0.0) is false.
return (this_value != 0) || ((1 / this_value) == (1 / other_value));
}
if (IsString() && other->IsString()) {
return String::cast(this)->Equals(String::cast(other));
}
return false;
}
bool Object::SameValueZero(Object* other) {
if (other == this) return true;
// The object is either a number, a name, an odd-ball,
// a real JS object, or a Harmony proxy.
if (IsNumber() && other->IsNumber()) {
double this_value = Number();
double other_value = other->Number();
// +0 == -0 is true
return this_value == other_value
|| (std::isnan(this_value) && std::isnan(other_value));
}
if (IsString() && other->IsString()) {
return String::cast(this)->Equals(String::cast(other));
}
return false;
}
void Object::ShortPrint(FILE* out) {
OFStream os(out);
os << Brief(this);
}
void Object::ShortPrint(StringStream* accumulator) {
OStringStream os;
os << Brief(this);
accumulator->Add(os.c_str());
}
OStream& operator<<(OStream& os, const Brief& v) {
if (v.value->IsSmi()) {
Smi::cast(v.value)->SmiPrint(os);
} else {
// TODO(svenpanne) Const-correct HeapObjectShortPrint!
HeapObject* obj = const_cast<HeapObject*>(HeapObject::cast(v.value));
obj->HeapObjectShortPrint(os);
}
return os;
}
void Smi::SmiPrint(OStream& os) const { // NOLINT
os << value();
}
// Should a word be prefixed by 'a' or 'an' in order to read naturally in
// English? Returns false for non-ASCII or words that don't start with
// a capital letter. The a/an rule follows pronunciation in English.
// We don't use the BBC's overcorrect "an historic occasion" though if
// you speak a dialect you may well say "an 'istoric occasion".
static bool AnWord(String* str) {
if (str->length() == 0) return false; // A nothing.
int c0 = str->Get(0);
int c1 = str->length() > 1 ? str->Get(1) : 0;
if (c0 == 'U') {
if (c1 > 'Z') {
return true; // An Umpire, but a UTF8String, a U.
}
} else if (c0 == 'A' || c0 == 'E' || c0 == 'I' || c0 == 'O') {
return true; // An Ape, an ABCBook.
} else if ((c1 == 0 || (c1 >= 'A' && c1 <= 'Z')) &&
(c0 == 'F' || c0 == 'H' || c0 == 'M' || c0 == 'N' || c0 == 'R' ||
c0 == 'S' || c0 == 'X')) {
return true; // An MP3File, an M.
}
return false;
}
Handle<String> String::SlowFlatten(Handle<ConsString> cons,
PretenureFlag pretenure) {
DCHECK(AllowHeapAllocation::IsAllowed());
DCHECK(cons->second()->length() != 0);
Isolate* isolate = cons->GetIsolate();
int length = cons->length();
PretenureFlag tenure = isolate->heap()->InNewSpace(*cons) ? pretenure
: TENURED;
Handle<SeqString> result;
if (cons->IsOneByteRepresentation()) {
Handle<SeqOneByteString> flat = isolate->factory()->NewRawOneByteString(
length, tenure).ToHandleChecked();
DisallowHeapAllocation no_gc;
WriteToFlat(*cons, flat->GetChars(), 0, length);
result = flat;
} else {
Handle<SeqTwoByteString> flat = isolate->factory()->NewRawTwoByteString(
length, tenure).ToHandleChecked();
DisallowHeapAllocation no_gc;
WriteToFlat(*cons, flat->GetChars(), 0, length);
result = flat;
}
cons->set_first(*result);
cons->set_second(isolate->heap()->empty_string());
DCHECK(result->IsFlat());
return result;
}
bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
// Externalizing twice leaks the external resource, so it's
// prohibited by the API.
DCHECK(!this->IsExternalString());
#ifdef ENABLE_SLOW_DCHECKS
if (FLAG_enable_slow_asserts) {
// Assert that the resource and the string are equivalent.
DCHECK(static_cast<size_t>(this->length()) == resource->length());
ScopedVector<uc16> smart_chars(this->length());
String::WriteToFlat(this, smart_chars.start(), 0, this->length());
DCHECK(memcmp(smart_chars.start(),
resource->data(),
resource->length() * sizeof(smart_chars[0])) == 0);
}
#endif // DEBUG
int size = this->Size(); // Byte size of the original string.
// Abort if size does not allow in-place conversion.
if (size < ExternalString::kShortSize) return false;
Heap* heap = GetHeap();
bool is_ascii = this->IsOneByteRepresentation();
bool is_internalized = this->IsInternalizedString();
// Morph the string to an external string by replacing the map and
// reinitializing the fields. This won't work if
// - the space the existing string occupies is too small for a regular
// external string.
// - the existing string is in old pointer space and the backing store of
// the external string is not aligned. The GC cannot deal with a field
// containing a possibly unaligned address to outside of V8's heap.
// In either case we resort to a short external string instead, omitting
// the field caching the address of the backing store. When we encounter
// short external strings in generated code, we need to bailout to runtime.
Map* new_map;
if (size < ExternalString::kSize ||
heap->old_pointer_space()->Contains(this)) {
new_map = is_internalized
? (is_ascii
? heap->
short_external_internalized_string_with_one_byte_data_map()
: heap->short_external_internalized_string_map())
: (is_ascii
? heap->short_external_string_with_one_byte_data_map()
: heap->short_external_string_map());
} else {
new_map = is_internalized
? (is_ascii
? heap->external_internalized_string_with_one_byte_data_map()
: heap->external_internalized_string_map())
: (is_ascii
? heap->external_string_with_one_byte_data_map()
: heap->external_string_map());
}
// Byte size of the external String object.
int new_size = this->SizeFromMap(new_map);
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
// We are storing the new map using release store after creating a filler for
// the left-over space to avoid races with the sweeper thread.
this->synchronized_set_map(new_map);
ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
self->set_resource(resource);
if (is_internalized) self->Hash(); // Force regeneration of the hash value.
heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR);
return true;
}
bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
// Externalizing twice leaks the external resource, so it's
// prohibited by the API.
DCHECK(!this->IsExternalString());
#ifdef ENABLE_SLOW_DCHECKS
if (FLAG_enable_slow_asserts) {
// Assert that the resource and the string are equivalent.
DCHECK(static_cast<size_t>(this->length()) == resource->length());
if (this->IsTwoByteRepresentation()) {
ScopedVector<uint16_t> smart_chars(this->length());
String::WriteToFlat(this, smart_chars.start(), 0, this->length());
DCHECK(String::IsOneByte(smart_chars.start(), this->length()));
}
ScopedVector<char> smart_chars(this->length());
String::WriteToFlat(this, smart_chars.start(), 0, this->length());
DCHECK(memcmp(smart_chars.start(),
resource->data(),
resource->length() * sizeof(smart_chars[0])) == 0);
}
#endif // DEBUG
int size = this->Size(); // Byte size of the original string.
// Abort if size does not allow in-place conversion.
if (size < ExternalString::kShortSize) return false;
Heap* heap = GetHeap();
bool is_internalized = this->IsInternalizedString();
// Morph the string to an external string by replacing the map and
// reinitializing the fields. This won't work if
// - the space the existing string occupies is too small for a regular
// external string.
// - the existing string is in old pointer space and the backing store of
// the external string is not aligned. The GC cannot deal with a field
// containing a possibly unaligned address to outside of V8's heap.
// In either case we resort to a short external string instead, omitting
// the field caching the address of the backing store. When we encounter
// short external strings in generated code, we need to bailout to runtime.
Map* new_map;
if (size < ExternalString::kSize ||
heap->old_pointer_space()->Contains(this)) {
new_map = is_internalized
? heap->short_external_ascii_internalized_string_map()
: heap->short_external_ascii_string_map();
} else {
new_map = is_internalized
? heap->external_ascii_internalized_string_map()
: heap->external_ascii_string_map();
}
// Byte size of the external String object.
int new_size = this->SizeFromMap(new_map);
heap->CreateFillerObjectAt(this->address() + new_size, size - new_size);
// We are storing the new map using release store after creating a filler for
// the left-over space to avoid races with the sweeper thread.
this->synchronized_set_map(new_map);
ExternalAsciiString* self = ExternalAsciiString::cast(this);
self->set_resource(resource);
if (is_internalized) self->Hash(); // Force regeneration of the hash value.
heap->AdjustLiveBytes(this->address(), new_size - size, Heap::FROM_MUTATOR);
return true;
}
void String::StringShortPrint(StringStream* accumulator) {
int len = length();
if (len > kMaxShortPrintLength) {
accumulator->Add("<Very long string[%u]>", len);
return;
}
if (!LooksValid()) {
accumulator->Add("<Invalid String>");
return;
}
ConsStringIteratorOp op;
StringCharacterStream stream(this, &op);
bool truncated = false;
if (len > kMaxShortPrintLength) {
len = kMaxShortPrintLength;
truncated = true;
}
bool ascii = true;
for (int i = 0; i < len; i++) {
uint16_t c = stream.GetNext();
if (c < 32 || c >= 127) {
ascii = false;
}
}
stream.Reset(this);
if (ascii) {
accumulator->Add("<String[%u]: ", length());
for (int i = 0; i < len; i++) {
accumulator->Put(static_cast<char>(stream.GetNext()));
}
accumulator->Put('>');
} else {
// Backslash indicates that the string contains control
// characters and that backslashes are therefore escaped.
accumulator->Add("<String[%u]\\: ", length());
for (int i = 0; i < len; i++) {
uint16_t c = stream.GetNext();
if (c == '\n') {
accumulator->Add("\\n");
} else if (c == '\r') {
accumulator->Add("\\r");
} else if (c == '\\') {
accumulator->Add("\\\\");
} else if (c < 32 || c > 126) {
accumulator->Add("\\x%02x", c);
} else {
accumulator->Put(static_cast<char>(c));
}
}
if (truncated) {
accumulator->Put('.');
accumulator->Put('.');
accumulator->Put('.');
}
accumulator->Put('>');
}
return;
}
void String::PrintUC16(OStream& os, int start, int end) { // NOLINT
if (end < 0) end = length();
ConsStringIteratorOp op;
StringCharacterStream stream(this, &op, start);
for (int i = start; i < end && stream.HasMore(); i++) {
os << AsUC16(stream.GetNext());
}
}
void JSObject::JSObjectShortPrint(StringStream* accumulator) {
switch (map()->instance_type()) {
case JS_ARRAY_TYPE: {
double length = JSArray::cast(this)->length()->IsUndefined()
? 0
: JSArray::cast(this)->length()->Number();
accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length));
break;
}
case JS_WEAK_MAP_TYPE: {
accumulator->Add("<JS WeakMap>");
break;
}
case JS_WEAK_SET_TYPE: {
accumulator->Add("<JS WeakSet>");
break;
}
case JS_REGEXP_TYPE: {
accumulator->Add("<JS RegExp>");
break;
}
case JS_FUNCTION_TYPE: {
JSFunction* function = JSFunction::cast(this);
Object* fun_name = function->shared()->DebugName();
bool printed = false;
if (fun_name->IsString()) {
String* str = String::cast(fun_name);
if (str->length() > 0) {
accumulator->Add("<JS Function ");
accumulator->Put(str);
printed = true;
}
}
if (!printed) {
accumulator->Add("<JS Function");
}
accumulator->Add(" (SharedFunctionInfo %p)",
reinterpret_cast<void*>(function->shared()));
accumulator->Put('>');
break;
}
case JS_GENERATOR_OBJECT_TYPE: {
accumulator->Add("<JS Generator>");
break;
}
case JS_MODULE_TYPE: {
accumulator->Add("<JS Module>");
break;
}
// All other JSObjects are rather similar to each other (JSObject,
// JSGlobalProxy, JSGlobalObject, JSUndetectableObject, JSValue).
default: {
Map* map_of_this = map();
Heap* heap = GetHeap();
Object* constructor = map_of_this->constructor();
bool printed = false;
if (constructor->IsHeapObject() &&
!heap->Contains(HeapObject::cast(constructor))) {
accumulator->Add("!!!INVALID CONSTRUCTOR!!!");
} else {
bool global_object = IsJSGlobalProxy();
if (constructor->IsJSFunction()) {
if (!heap->Contains(JSFunction::cast(constructor)->shared())) {
accumulator->Add("!!!INVALID SHARED ON CONSTRUCTOR!!!");
} else {
Object* constructor_name =
JSFunction::cast(constructor)->shared()->name();
if (constructor_name->IsString()) {
String* str = String::cast(constructor_name);
if (str->length() > 0) {
bool vowel = AnWord(str);
accumulator->Add("<%sa%s ",
global_object ? "Global Object: " : "",
vowel ? "n" : "");
accumulator->Put(str);
accumulator->Add(" with %smap %p",
map_of_this->is_deprecated() ? "deprecated " : "",
map_of_this);
printed = true;
}
}
}
}
if (!printed) {
accumulator->Add("<JS %sObject", global_object ? "Global " : "");
}
}
if (IsJSValue()) {
accumulator->Add(" value = ");
JSValue::cast(this)->value()->ShortPrint(accumulator);
}
accumulator->Put('>');
break;
}
}
}
void JSObject::PrintElementsTransition(
FILE* file, Handle<JSObject> object,
ElementsKind from_kind, Handle<FixedArrayBase> from_elements,
ElementsKind to_kind, Handle<FixedArrayBase> to_elements) {
if (from_kind != to_kind) {
OFStream os(file);
os << "elements transition [" << ElementsKindToString(from_kind) << " -> "
<< ElementsKindToString(to_kind) << "] in ";
JavaScriptFrame::PrintTop(object->GetIsolate(), file, false, true);
PrintF(file, " for ");
object->ShortPrint(file);
PrintF(file, " from ");
from_elements->ShortPrint(file);
PrintF(file, " to ");
to_elements->ShortPrint(file);
PrintF(file, "\n");
}
}
void Map::PrintGeneralization(FILE* file,
const char* reason,
int modify_index,
int split,
int descriptors,
bool constant_to_field,
Representation old_representation,
Representation new_representation,
HeapType* old_field_type,
HeapType* new_field_type) {
OFStream os(file);
os << "[generalizing ";
constructor_name()->PrintOn(file);
os << "] ";
Name* name = instance_descriptors()->GetKey(modify_index);
if (name->IsString()) {
String::cast(name)->PrintOn(file);
} else {
os << "{symbol " << static_cast<void*>(name) << "}";
}
os << ":";
if (constant_to_field) {
os << "c";
} else {
os << old_representation.Mnemonic() << "{";
old_field_type->PrintTo(os, HeapType::SEMANTIC_DIM);
os << "}";
}
os << "->" << new_representation.Mnemonic() << "{";
new_field_type->PrintTo(os, HeapType::SEMANTIC_DIM);
os << "} (";
if (strlen(reason) > 0) {
os << reason;
} else {
os << "+" << (descriptors - split) << " maps";
}
os << ") [";
JavaScriptFrame::PrintTop(GetIsolate(), file, false, true);
os << "]\n";
}
void JSObject::PrintInstanceMigration(FILE* file,
Map* original_map,
Map* new_map) {
PrintF(file, "[migrating ");
map()->constructor_name()->PrintOn(file);
PrintF(file, "] ");
DescriptorArray* o = original_map->instance_descriptors();
DescriptorArray* n = new_map->instance_descriptors();
for (int i = 0; i < original_map->NumberOfOwnDescriptors(); i++) {
Representation o_r = o->GetDetails(i).representation();
Representation n_r = n->GetDetails(i).representation();
if (!o_r.Equals(n_r)) {
String::cast(o->GetKey(i))->PrintOn(file);
PrintF(file, ":%s->%s ", o_r.Mnemonic(), n_r.Mnemonic());
} else if (o->GetDetails(i).type() == CONSTANT &&
n->GetDetails(i).type() == FIELD) {
Name* name = o->GetKey(i);
if (name->IsString()) {
String::cast(name)->PrintOn(file);
} else {
PrintF(file, "{symbol %p}", static_cast<void*>(name));
}
PrintF(file, " ");
}
}
PrintF(file, "\n");
}
void HeapObject::HeapObjectShortPrint(OStream& os) { // NOLINT
Heap* heap = GetHeap();
if (!heap->Contains(this)) {
os << "!!!INVALID POINTER!!!";
return;
}
if (!heap->Contains(map())) {
os << "!!!INVALID MAP!!!";
return;
}
os << this << " ";
if (IsString()) {
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
String::cast(this)->StringShortPrint(&accumulator);
os << accumulator.ToCString().get();
return;
}
if (IsJSObject()) {
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
JSObject::cast(this)->JSObjectShortPrint(&accumulator);
os << accumulator.ToCString().get();
return;
}
switch (map()->instance_type()) {
case MAP_TYPE:
os << "<Map(elements=" << Map::cast(this)->elements_kind() << ")>";
break;
case FIXED_ARRAY_TYPE:
os << "<FixedArray[" << FixedArray::cast(this)->length() << "]>";
break;
case FIXED_DOUBLE_ARRAY_TYPE:
os << "<FixedDoubleArray[" << FixedDoubleArray::cast(this)->length()
<< "]>";
break;
case BYTE_ARRAY_TYPE:
os << "<ByteArray[" << ByteArray::cast(this)->length() << "]>";
break;
case FREE_SPACE_TYPE:
os << "<FreeSpace[" << FreeSpace::cast(this)->Size() << "]>";
break;
#define TYPED_ARRAY_SHORT_PRINT(Type, type, TYPE, ctype, size) \
case EXTERNAL_##TYPE##_ARRAY_TYPE: \
os << "<External" #Type "Array[" \
<< External##Type##Array::cast(this)->length() << "]>"; \
break; \
case FIXED_##TYPE##_ARRAY_TYPE: \
os << "<Fixed" #Type "Array[" << Fixed##Type##Array::cast(this)->length() \
<< "]>"; \
break;
TYPED_ARRAYS(TYPED_ARRAY_SHORT_PRINT)
#undef TYPED_ARRAY_SHORT_PRINT
case SHARED_FUNCTION_INFO_TYPE: {
SharedFunctionInfo* shared = SharedFunctionInfo::cast(this);
SmartArrayPointer<char> debug_name =
shared->DebugName()->ToCString();
if (debug_name[0] != 0) {
os << "<SharedFunctionInfo " << debug_name.get() << ">";
} else {
os << "<SharedFunctionInfo>";
}
break;
}
case JS_MESSAGE_OBJECT_TYPE:
os << "<JSMessageObject>";
break;
#define MAKE_STRUCT_CASE(NAME, Name, name) \
case NAME##_TYPE: \
os << "<" #Name ">"; \
break;
STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
case CODE_TYPE: {
Code* code = Code::cast(this);
os << "<Code: " << Code::Kind2String(code->kind()) << ">";
break;
}
case ODDBALL_TYPE: {
if (IsUndefined()) {
os << "<undefined>";
} else if (IsTheHole()) {
os << "<the hole>";
} else if (IsNull()) {
os << "<null>";
} else if (IsTrue()) {
os << "<true>";
} else if (IsFalse()) {
os << "<false>";
} else {
os << "<Odd Oddball>";
}
break;
}
case SYMBOL_TYPE: {
Symbol* symbol = Symbol::cast(this);
os << "<Symbol: " << symbol->Hash();
if (!symbol->name()->IsUndefined()) {
os << " ";
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
String::cast(symbol->name())->StringShortPrint(&accumulator);
os << accumulator.ToCString().get();
}
os << ">";
break;
}
case HEAP_NUMBER_TYPE: {
os << "<Number: ";
HeapNumber::cast(this)->HeapNumberPrint(os);
os << ">";
break;
}
case MUTABLE_HEAP_NUMBER_TYPE: {
os << "<MutableNumber: ";
HeapNumber::cast(this)->HeapNumberPrint(os);
os << '>';
break;
}
case JS_PROXY_TYPE:
os << "<JSProxy>";
break;
case JS_FUNCTION_PROXY_TYPE:
os << "<JSFunctionProxy>";
break;
case FOREIGN_TYPE:
os << "<Foreign>";
break;
case CELL_TYPE: {
os << "Cell for ";
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
Cell::cast(this)->value()->ShortPrint(&accumulator);
os << accumulator.ToCString().get();
break;
}
case PROPERTY_CELL_TYPE: {
os << "PropertyCell for ";
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
PropertyCell::cast(this)->value()->ShortPrint(&accumulator);
os << accumulator.ToCString().get();
break;
}
default:
os << "<Other heap object (" << map()->instance_type() << ")>";
break;
}
}
void HeapObject::Iterate(ObjectVisitor* v) {
// Handle header
IteratePointer(v, kMapOffset);
// Handle object body
Map* m = map();
IterateBody(m->instance_type(), SizeFromMap(m), v);
}
void HeapObject::IterateBody(InstanceType type, int object_size,
ObjectVisitor* v) {
// Avoiding <Type>::cast(this) because it accesses the map pointer field.
// During GC, the map pointer field is encoded.
if (type < FIRST_NONSTRING_TYPE) {
switch (type & kStringRepresentationMask) {
case kSeqStringTag:
break;
case kConsStringTag:
ConsString::BodyDescriptor::IterateBody(this, v);
break;
case kSlicedStringTag:
SlicedString::BodyDescriptor::IterateBody(this, v);
break;
case kExternalStringTag:
if ((type & kStringEncodingMask) == kOneByteStringTag) {
reinterpret_cast<ExternalAsciiString*>(this)->
ExternalAsciiStringIterateBody(v);
} else {
reinterpret_cast<ExternalTwoByteString*>(this)->
ExternalTwoByteStringIterateBody(v);
}
break;
}
return;
}
switch (type) {
case FIXED_ARRAY_TYPE:
FixedArray::BodyDescriptor::IterateBody(this, object_size, v);
break;
case CONSTANT_POOL_ARRAY_TYPE:
reinterpret_cast<ConstantPoolArray*>(this)->ConstantPoolIterateBody(v);
break;
case FIXED_DOUBLE_ARRAY_TYPE:
break;
case JS_OBJECT_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
case JS_MODULE_TYPE:
case JS_VALUE_TYPE:
case JS_DATE_TYPE:
case JS_ARRAY_TYPE:
case JS_ARRAY_BUFFER_TYPE:
case JS_TYPED_ARRAY_TYPE:
case JS_DATA_VIEW_TYPE:
case JS_SET_TYPE:
case JS_MAP_TYPE:
case JS_SET_ITERATOR_TYPE:
case JS_MAP_ITERATOR_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_WEAK_SET_TYPE:
case JS_REGEXP_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
case JS_BUILTINS_OBJECT_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
JSObject::BodyDescriptor::IterateBody(this, object_size, v);
break;
case JS_FUNCTION_TYPE:
reinterpret_cast<JSFunction*>(this)
->JSFunctionIterateBody(object_size, v);
break;
case ODDBALL_TYPE:
Oddball::BodyDescriptor::IterateBody(this, v);
break;
case JS_PROXY_TYPE:
JSProxy::BodyDescriptor::IterateBody(this, v);
break;
case JS_FUNCTION_PROXY_TYPE:
JSFunctionProxy::BodyDescriptor::IterateBody(this, v);
break;
case FOREIGN_TYPE:
reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
break;
case MAP_TYPE:
Map::BodyDescriptor::IterateBody(this, v);
break;
case CODE_TYPE:
reinterpret_cast<Code*>(this)->CodeIterateBody(v);
break;
case CELL_TYPE:
Cell::BodyDescriptor::IterateBody(this, v);
break;
case PROPERTY_CELL_TYPE:
PropertyCell::BodyDescriptor::IterateBody(this, v);
break;
case SYMBOL_TYPE:
Symbol::BodyDescriptor::IterateBody(this, v);
break;
case HEAP_NUMBER_TYPE:
case MUTABLE_HEAP_NUMBER_TYPE:
case FILLER_TYPE:
case BYTE_ARRAY_TYPE:
case FREE_SPACE_TYPE:
break;
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case EXTERNAL_##TYPE##_ARRAY_TYPE: \
case FIXED_##TYPE##_ARRAY_TYPE: \
break;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
case SHARED_FUNCTION_INFO_TYPE: {
SharedFunctionInfo::BodyDescriptor::IterateBody(this, v);
break;
}
#define MAKE_STRUCT_CASE(NAME, Name, name) \
case NAME##_TYPE:
STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
if (type == ALLOCATION_SITE_TYPE) {
AllocationSite::BodyDescriptor::IterateBody(this, v);
} else {
StructBodyDescriptor::IterateBody(this, object_size, v);
}
break;
default:
PrintF("Unknown type: %d\n", type);
UNREACHABLE();
}
}
bool HeapNumber::HeapNumberBooleanValue() {
return DoubleToBoolean(value());
}
void HeapNumber::HeapNumberPrint(OStream& os) { // NOLINT
os << value();
}
String* JSReceiver::class_name() {
if (IsJSFunction() || IsJSFunctionProxy()) {
return GetHeap()->function_class_string();
}
if (map()->constructor()->IsJSFunction()) {
JSFunction* constructor = JSFunction::cast(map()->constructor());
return String::cast(constructor->shared()->instance_class_name());
}
// If the constructor is not present, return "Object".
return GetHeap()->Object_string();
}
String* Map::constructor_name() {
if (constructor()->IsJSFunction()) {
JSFunction* constructor = JSFunction::cast(this->constructor());
String* name = String::cast(constructor->shared()->name());
if (name->length() > 0) return name;
String* inferred_name = constructor->shared()->inferred_name();
if (inferred_name->length() > 0) return inferred_name;
Object* proto = prototype();
if (proto->IsJSObject()) return JSObject::cast(proto)->constructor_name();
}
// TODO(rossberg): what about proxies?
// If the constructor is not present, return "Object".
return GetHeap()->Object_string();
}
String* JSReceiver::constructor_name() {
return map()->constructor_name();
}
MaybeHandle<Map> Map::CopyWithField(Handle<Map> map,
Handle<Name> name,
Handle<HeapType> type,
PropertyAttributes attributes,
Representation representation,
TransitionFlag flag) {
DCHECK(DescriptorArray::kNotFound ==
map->instance_descriptors()->Search(
*name, map->NumberOfOwnDescriptors()));
// Ensure the descriptor array does not get too big.
if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors) {
return MaybeHandle<Map>();
}
Isolate* isolate = map->GetIsolate();
// Compute the new index for new field.
int index = map->NextFreePropertyIndex();
if (map->instance_type() == JS_CONTEXT_EXTENSION_OBJECT_TYPE) {
representation = Representation::Tagged();
type = HeapType::Any(isolate);
}
FieldDescriptor new_field_desc(name, index, type, attributes, representation);
Handle<Map> new_map = Map::CopyAddDescriptor(map, &new_field_desc, flag);
int unused_property_fields = new_map->unused_property_fields() - 1;
if (unused_property_fields < 0) {
unused_property_fields += JSObject::kFieldsAdded;
}
new_map->set_unused_property_fields(unused_property_fields);
return new_map;
}
MaybeHandle<Map> Map::CopyWithConstant(Handle<Map> map,
Handle<Name> name,
Handle<Object> constant,
PropertyAttributes attributes,
TransitionFlag flag) {
// Ensure the descriptor array does not get too big.
if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors) {
return MaybeHandle<Map>();
}
// Allocate new instance descriptors with (name, constant) added.
ConstantDescriptor new_constant_desc(name, constant, attributes);
return Map::CopyAddDescriptor(map, &new_constant_desc, flag);
}
void JSObject::AddFastProperty(Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
PropertyAttributes attributes,
StoreFromKeyed store_mode,
TransitionFlag flag) {
DCHECK(!object->IsJSGlobalProxy());
MaybeHandle<Map> maybe_map;
if (value->IsJSFunction()) {
maybe_map = Map::CopyWithConstant(
handle(object->map()), name, value, attributes, flag);
} else if (!object->map()->TooManyFastProperties(store_mode)) {
Isolate* isolate = object->GetIsolate();
Representation representation = value->OptimalRepresentation();
maybe_map = Map::CopyWithField(
handle(object->map(), isolate), name,
value->OptimalType(isolate, representation),
attributes, representation, flag);
}
Handle<Map> new_map;
if (!maybe_map.ToHandle(&new_map)) {
NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0);
return;
}
JSObject::MigrateToNewProperty(object, new_map, value);
}
void JSObject::AddSlowProperty(Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
PropertyAttributes attributes) {
DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
Handle<NameDictionary> dict(object->property_dictionary());
if (object->IsGlobalObject()) {
// In case name is an orphaned property reuse the cell.
int entry = dict->FindEntry(name);
if (entry != NameDictionary::kNotFound) {
Handle<PropertyCell> cell(PropertyCell::cast(dict->ValueAt(entry)));
PropertyCell::SetValueInferType(cell, value);
// Assign an enumeration index to the property and update
// SetNextEnumerationIndex.
int index = dict->NextEnumerationIndex();
PropertyDetails details = PropertyDetails(attributes, NORMAL, index);
dict->SetNextEnumerationIndex(index + 1);
dict->SetEntry(entry, name, cell, details);
return;
}
Handle<PropertyCell> cell = isolate->factory()->NewPropertyCell(value);
PropertyCell::SetValueInferType(cell, value);
value = cell;
}
PropertyDetails details = PropertyDetails(attributes, NORMAL, 0);
Handle<NameDictionary> result =
NameDictionary::Add(dict, name, value, details);
if (*dict != *result) object->set_properties(*result);
}
MaybeHandle<Object> JSObject::AddPropertyInternal(
Handle<JSObject> object, Handle<Name> name, Handle<Object> value,
PropertyAttributes attributes, JSReceiver::StoreFromKeyed store_mode,
ExtensibilityCheck extensibility_check, TransitionFlag transition_flag) {
DCHECK(!object->IsJSGlobalProxy());
Isolate* isolate = object->GetIsolate();
if (!name->IsUniqueName()) {
name = isolate->factory()->InternalizeString(
Handle<String>::cast(name));
}
if (extensibility_check == PERFORM_EXTENSIBILITY_CHECK &&
!object->map()->is_extensible()) {
Handle<Object> args[1] = {name};
Handle<Object> error = isolate->factory()->NewTypeError(
"object_not_extensible", HandleVector(args, ARRAY_SIZE(args)));
return isolate->Throw<Object>(error);
}
if (object->HasFastProperties()) {
AddFastProperty(object, name, value, attributes, store_mode,
transition_flag);
}
if (!object->HasFastProperties()) {
AddSlowProperty(object, name, value, attributes);
}
if (object->map()->is_observed() &&
*name != isolate->heap()->hidden_string()) {
Handle<Object> old_value = isolate->factory()->the_hole_value();
EnqueueChangeRecord(object, "add", name, old_value);
}
return value;
}
Context* JSObject::GetCreationContext() {
Object* constructor = this->map()->constructor();
JSFunction* function;
if (!constructor->IsJSFunction()) {
// Functions have null as a constructor,
// but any JSFunction knows its context immediately.
function = JSFunction::cast(this);
} else {
function = JSFunction::cast(constructor);
}
return function->context()->native_context();
}
void JSObject::EnqueueChangeRecord(Handle<JSObject> object,
const char* type_str,
Handle<Name> name,
Handle<Object> old_value) {
DCHECK(!object->IsJSGlobalProxy());
DCHECK(!object->IsJSGlobalObject());
Isolate* isolate = object->GetIsolate();
HandleScope scope(isolate);
Handle<String> type = isolate->factory()->InternalizeUtf8String(type_str);
Handle<Object> args[] = { type, object, name, old_value };
int argc = name.is_null() ? 2 : old_value->IsTheHole() ? 3 : 4;
Execution::Call(isolate,
Handle<JSFunction>(isolate->observers_notify_change()),
isolate->factory()->undefined_value(),
argc, args).Assert();
}
static void ReplaceSlowProperty(Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
PropertyAttributes attributes) {
NameDictionary* dictionary = object->property_dictionary();
int old_index = dictionary->FindEntry(name);
int new_enumeration_index = 0; // 0 means "Use the next available index."
if (old_index != -1) {
// All calls to ReplaceSlowProperty have had all transitions removed.
new_enumeration_index = dictionary->DetailsAt(old_index).dictionary_index();
}
PropertyDetails new_details(attributes, NORMAL, new_enumeration_index);
JSObject::SetNormalizedProperty(object, name, value, new_details);
}
const char* Representation::Mnemonic() const {
switch (kind_) {
case kNone: return "v";
case kTagged: return "t";
case kSmi: return "s";
case kDouble: return "d";
case kInteger32: return "i";
case kHeapObject: return "h";
case kExternal: return "x";
default:
UNREACHABLE();
return NULL;
}
}
bool Map::InstancesNeedRewriting(Map* target, int target_number_of_fields,
int target_inobject, int target_unused,
int* old_number_of_fields) {
// If fields were added (or removed), rewrite the instance.
*old_number_of_fields = NumberOfFields();
DCHECK(target_number_of_fields >= *old_number_of_fields);
if (target_number_of_fields != *old_number_of_fields) return true;
// If smi descriptors were replaced by double descriptors, rewrite.
DescriptorArray* old_desc = instance_descriptors();
DescriptorArray* new_desc = target->instance_descriptors();
int limit = NumberOfOwnDescriptors();
for (int i = 0; i < limit; i++) {
if (new_desc->GetDetails(i).representation().IsDouble() !=
old_desc->GetDetails(i).representation().IsDouble()) {
return true;
}
}
// If no fields were added, and no inobject properties were removed, setting
// the map is sufficient.
if (target_inobject == inobject_properties()) return false;
// In-object slack tracking may have reduced the object size of the new map.
// In that case, succeed if all existing fields were inobject, and they still
// fit within the new inobject size.
DCHECK(target_inobject < inobject_properties());
if (target_number_of_fields <= target_inobject) {
DCHECK(target_number_of_fields + target_unused == target_inobject);
return false;
}
// Otherwise, properties will need to be moved to the backing store.
return true;
}
void Map::ConnectElementsTransition(Handle<Map> parent, Handle<Map> child) {
Isolate* isolate = parent->GetIsolate();
Handle<Name> name = isolate->factory()->elements_transition_symbol();
ConnectTransition(parent, child, name, FULL_TRANSITION);
}
void JSObject::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
if (object->map() == *new_map) return;
if (object->HasFastProperties()) {
if (!new_map->is_dictionary_map()) {
Handle<Map> old_map(object->map());
MigrateFastToFast(object, new_map);
if (old_map->is_prototype_map()) {
// Clear out the old descriptor array to avoid problems to sharing
// the descriptor array without using an explicit.
old_map->InitializeDescriptors(
old_map->GetHeap()->empty_descriptor_array());
// Ensure that no transition was inserted for prototype migrations.
DCHECK(!old_map->HasTransitionArray());
DCHECK(new_map->GetBackPointer()->IsUndefined());
}
} else {
MigrateFastToSlow(object, new_map, 0);
}
} else {
// For slow-to-fast migrations JSObject::TransformToFastProperties()
// must be used instead.
CHECK(new_map->is_dictionary_map());
// Slow-to-slow migration is trivial.
object->set_map(*new_map);
}
}
// To migrate a fast instance to a fast map:
// - First check whether the instance needs to be rewritten. If not, simply
// change the map.
// - Otherwise, allocate a fixed array large enough to hold all fields, in
// addition to unused space.
// - Copy all existing properties in, in the following order: backing store
// properties, unused fields, inobject properties.
// - If all allocation succeeded, commit the state atomically:
// * Copy inobject properties from the backing store back into the object.
// * Trim the difference in instance size of the object. This also cleanly
// frees inobject properties that moved to the backing store.
// * If there are properties left in the backing store, trim of the space used
// to temporarily store the inobject properties.
// * If there are properties left in the backing store, install the backing
// store.
void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
Isolate* isolate = object->GetIsolate();
Handle<Map> old_map(object->map());
int old_number_of_fields;
int number_of_fields = new_map->NumberOfFields();
int inobject = new_map->inobject_properties();
int unused = new_map->unused_property_fields();
// Nothing to do if no functions were converted to fields and no smis were
// converted to doubles.
if (!old_map->InstancesNeedRewriting(*new_map, number_of_fields, inobject,
unused, &old_number_of_fields)) {
object->synchronized_set_map(*new_map);
return;
}
int total_size = number_of_fields + unused;
int external = total_size - inobject;
if ((old_map->unused_property_fields() == 0) &&
(number_of_fields != old_number_of_fields) &&
(new_map->GetBackPointer() == *old_map)) {
DCHECK(number_of_fields == old_number_of_fields + 1);
// This migration is a transition from a map that has run out out property
// space. Therefore it could be done by extending the backing store.
Handle<FixedArray> old_storage = handle(object->properties(), isolate);
Handle<FixedArray> new_storage =
FixedArray::CopySize(old_storage, external);
// Properly initialize newly added property.
PropertyDetails details = new_map->GetLastDescriptorDetails();
Handle<Object> value;
if (details.representation().IsDouble()) {
value = isolate->factory()->NewHeapNumber(0, MUTABLE);
} else {
value = isolate->factory()->uninitialized_value();
}
DCHECK(details.type() == FIELD);
int target_index = details.field_index() - inobject;
DCHECK(target_index >= 0); // Must be a backing store index.
new_storage->set(target_index, *value);
// From here on we cannot fail and we shouldn't GC anymore.
DisallowHeapAllocation no_allocation;
// Set the new property value and do the map transition.
object->set_properties(*new_storage);
object->synchronized_set_map(*new_map);
return;
}
Handle<FixedArray> array = isolate->factory()->NewFixedArray(total_size);
Handle<DescriptorArray> old_descriptors(old_map->instance_descriptors());
Handle<DescriptorArray> new_descriptors(new_map->instance_descriptors());
int old_nof = old_map->NumberOfOwnDescriptors();
int new_nof = new_map->NumberOfOwnDescriptors();
// This method only supports generalizing instances to at least the same
// number of properties.
DCHECK(old_nof <= new_nof);
for (int i = 0; i < old_nof; i++) {
PropertyDetails details = new_descriptors->GetDetails(i);
if (details.type() != FIELD) continue;
PropertyDetails old_details = old_descriptors->GetDetails(i);
if (old_details.type() == CALLBACKS) {
DCHECK(details.representation().IsTagged());
continue;
}
DCHECK(old_details.type() == CONSTANT ||
old_details.type() == FIELD);
Object* raw_value = old_details.type() == CONSTANT
? old_descriptors->GetValue(i)
: object->RawFastPropertyAt(FieldIndex::ForDescriptor(*old_map, i));
Handle<Object> value(raw_value, isolate);
if (!old_details.representation().IsDouble() &&
details.representation().IsDouble()) {
if (old_details.representation().IsNone()) {
value = handle(Smi::FromInt(0), isolate);
}
value = Object::NewStorageFor(isolate, value, details.representation());
} else if (old_details.representation().IsDouble() &&
!details.representation().IsDouble()) {
value = Object::WrapForRead(isolate, value, old_details.representation());
}
DCHECK(!(details.representation().IsDouble() && value->IsSmi()));
int target_index = new_descriptors->GetFieldIndex(i) - inobject;
if (target_index < 0) target_index += total_size;
array->set(target_index, *value);
}
for (int i = old_nof; i < new_nof; i++) {
PropertyDetails details = new_descriptors->GetDetails(i);
if (details.type() != FIELD) continue;
Handle<Object> value;
if (details.representation().IsDouble()) {
value = isolate->factory()->NewHeapNumber(0, MUTABLE);
} else {
value = isolate->factory()->uninitialized_value();
}
int target_index = new_descriptors->GetFieldIndex(i) - inobject;
if (target_index < 0) target_index += total_size;
array->set(target_index, *value);
}
// From here on we cannot fail and we shouldn't GC anymore.
DisallowHeapAllocation no_allocation;
// Copy (real) inobject properties. If necessary, stop at number_of_fields to
// avoid overwriting |one_pointer_filler_map|.
int limit = Min(inobject, number_of_fields);
for (int i = 0; i < limit; i++) {
FieldIndex index = FieldIndex::ForPropertyIndex(*new_map, i);
object->FastPropertyAtPut(index, array->get(external + i));
}
Heap* heap = isolate->heap();
// If there are properties in the new backing store, trim it to the correct
// size and install the backing store into the object.
if (external > 0) {
heap->RightTrimFixedArray<Heap::FROM_MUTATOR>(*array, inobject);
object->set_properties(*array);
}
// Create filler object past the new instance size.
int new_instance_size = new_map->instance_size();
int instance_size_delta = old_map->instance_size() - new_instance_size;
DCHECK(instance_size_delta >= 0);
if (instance_size_delta > 0) {
Address address = object->address();
heap->CreateFillerObjectAt(
address + new_instance_size, instance_size_delta);
heap->AdjustLiveBytes(address, -instance_size_delta, Heap::FROM_MUTATOR);
}
// We are storing the new map using release store after creating a filler for
// the left-over space to avoid races with the sweeper thread.
object->synchronized_set_map(*new_map);
}
void JSObject::GeneralizeFieldRepresentation(Handle<JSObject> object,
int modify_index,
Representation new_representation,
Handle<HeapType> new_field_type) {
Handle<Map> new_map = Map::GeneralizeRepresentation(
handle(object->map()), modify_index, new_representation, new_field_type,
FORCE_FIELD);
MigrateToMap(object, new_map);
}
int Map::NumberOfFields() {
DescriptorArray* descriptors = instance_descriptors();
int result = 0;
for (int i = 0; i < NumberOfOwnDescriptors(); i++) {
if (descriptors->GetDetails(i).type() == FIELD) result++;
}
return result;
}
Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map,
int modify_index,
StoreMode store_mode,
PropertyAttributes attributes,
const char* reason) {
Isolate* isolate = map->GetIsolate();
Handle<Map> new_map = Copy(map);
DescriptorArray* descriptors = new_map->instance_descriptors();
int length = descriptors->number_of_descriptors();
for (int i = 0; i < length; i++) {
descriptors->SetRepresentation(i, Representation::Tagged());
if (descriptors->GetDetails(i).type() == FIELD) {
descriptors->SetValue(i, HeapType::Any());
}
}
// Unless the instance is being migrated, ensure that modify_index is a field.
PropertyDetails details = descriptors->GetDetails(modify_index);
if (store_mode == FORCE_FIELD &&
(details.type() != FIELD || details.attributes() != attributes)) {
int field_index = details.type() == FIELD ? details.field_index()
: new_map->NumberOfFields();
FieldDescriptor d(handle(descriptors->GetKey(modify_index), isolate),
field_index, attributes, Representation::Tagged());
descriptors->Replace(modify_index, &d);
if (details.type() != FIELD) {
int unused_property_fields = new_map->unused_property_fields() - 1;
if (unused_property_fields < 0) {
unused_property_fields += JSObject::kFieldsAdded;
}
new_map->set_unused_property_fields(unused_property_fields);
}
} else {
DCHECK(details.attributes() == attributes);
}
if (FLAG_trace_generalization) {
HeapType* field_type = (details.type() == FIELD)
? map->instance_descriptors()->GetFieldType(modify_index)
: NULL;
map->PrintGeneralization(stdout, reason, modify_index,
new_map->NumberOfOwnDescriptors(),
new_map->NumberOfOwnDescriptors(),
details.type() == CONSTANT && store_mode == FORCE_FIELD,
details.representation(), Representation::Tagged(),
field_type, HeapType::Any());
}
return new_map;
}
// static
Handle<Map> Map::CopyGeneralizeAllRepresentations(Handle<Map> map,
int modify_index,
StoreMode store_mode,
const char* reason) {
PropertyDetails details =
map->instance_descriptors()->GetDetails(modify_index);
return CopyGeneralizeAllRepresentations(map, modify_index, store_mode,
details.attributes(), reason);
}
void Map::DeprecateTransitionTree() {
if (is_deprecated()) return;
if (HasTransitionArray()) {
TransitionArray* transitions = this->transitions();
for (int i = 0; i < transitions->number_of_transitions(); i++) {
transitions->GetTarget(i)->DeprecateTransitionTree();
}
}
deprecate();
dependent_code()->DeoptimizeDependentCodeGroup(
GetIsolate(), DependentCode::kTransitionGroup);
NotifyLeafMapLayoutChange();
}
// Invalidates a transition target at |key|, and installs |new_descriptors| over
// the current instance_descriptors to ensure proper sharing of descriptor
// arrays.
void Map::DeprecateTarget(Name* key, DescriptorArray* new_descriptors) {
if (HasTransitionArray()) {
TransitionArray* transitions = this->transitions();
int transition = transitions->Search(key);
if (transition != TransitionArray::kNotFound) {
transitions->GetTarget(transition)->DeprecateTransitionTree();
}
}
// Don't overwrite the empty descriptor array.
if (NumberOfOwnDescriptors() == 0) return;
DescriptorArray* to_replace = instance_descriptors();
Map* current = this;
GetHeap()->incremental_marking()->RecordWrites(to_replace);
while (current->instance_descriptors() == to_replace) {
current->SetEnumLength(kInvalidEnumCacheSentinel);
current->set_instance_descriptors(new_descriptors);
Object* next = current->GetBackPointer();
if (next->IsUndefined()) break;
current = Map::cast(next);
}
set_owns_descriptors(false);
}
Map* Map::FindRootMap() {
Map* result = this;
while (true) {
Object* back = result->GetBackPointer();
if (back->IsUndefined()) return result;
result = Map::cast(back);
}
}
Map* Map::FindLastMatchMap(int verbatim,
int length,
DescriptorArray* descriptors) {
DisallowHeapAllocation no_allocation;
// This can only be called on roots of transition trees.
DCHECK(GetBackPointer()->IsUndefined());
Map* current = this;
for (int i = verbatim; i < length; i++) {
if (!current->HasTransitionArray()) break;
Name* name = descriptors->GetKey(i);
TransitionArray* transitions = current->transitions();
int transition = transitions->Search(name);
if (transition == TransitionArray::kNotFound) break;
Map* next = transitions->GetTarget(transition);
DescriptorArray* next_descriptors = next->instance_descriptors();
PropertyDetails details = descriptors->GetDetails(i);
PropertyDetails next_details = next_descriptors->GetDetails(i);
if (details.type() != next_details.type()) break;
if (details.attributes() != next_details.attributes()) break;
if (!details.representation().Equals(next_details.representation())) break;
if (next_details.type() == FIELD) {
if (!descriptors->GetFieldType(i)->NowIs(
next_descriptors->GetFieldType(i))) break;
} else {
if (descriptors->GetValue(i) != next_descriptors->GetValue(i)) break;
}
current = next;
}
return current;
}
Map* Map::FindFieldOwner(int descriptor) {
DisallowHeapAllocation no_allocation;
DCHECK_EQ(FIELD, instance_descriptors()->GetDetails(descriptor).type());
Map* result = this;
while (true) {
Object* back = result->GetBackPointer();
if (back->IsUndefined()) break;
Map* parent = Map::cast(back);
if (parent->NumberOfOwnDescriptors() <= descriptor) break;
result = parent;
}
return result;
}
void Map::UpdateFieldType(int descriptor, Handle<Name> name,
Handle<HeapType> new_type) {
DisallowHeapAllocation no_allocation;
PropertyDetails details = instance_descriptors()->GetDetails(descriptor);
if (details.type() != FIELD) return;
if (HasTransitionArray()) {
TransitionArray* transitions = this->transitions();
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
transitions->GetTarget(i)->UpdateFieldType(descriptor, name, new_type);
}
}
// Skip if already updated the shared descriptor.
if (instance_descriptors()->GetFieldType(descriptor) == *new_type) return;
FieldDescriptor d(name, instance_descriptors()->GetFieldIndex(descriptor),
new_type, details.attributes(), details.representation());
instance_descriptors()->Replace(descriptor, &d);
}
// static
Handle<HeapType> Map::GeneralizeFieldType(Handle<HeapType> type1,
Handle<HeapType> type2,
Isolate* isolate) {
static const int kMaxClassesPerFieldType = 5;
if (type1->NowIs(type2)) return type2;
if (type2->NowIs(type1)) return type1;
if (type1->NowStable() && type2->NowStable()) {
Handle<HeapType> type = HeapType::Union(type1, type2, isolate);
if (type->NumClasses() <= kMaxClassesPerFieldType) {
DCHECK(type->NowStable());
DCHECK(type1->NowIs(type));
DCHECK(type2->NowIs(type));
return type;
}
}
return HeapType::Any(isolate);
}
// static