blob: 5b5d79174bd9ff455726acbda631868de655e6fe [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/codegen.h"
#include "src/code-stubs.h"
#include "src/cpu-profiler.h"
#include "src/debug.h"
#include "src/deoptimizer.h"
#include "src/date.h"
#include "src/elements.h"
#include "src/execution.h"
#include "src/field-index.h"
#include "src/field-index-inl.h"
#include "src/full-codegen.h"
#include "src/hydrogen.h"
#include "src/isolate-inl.h"
#include "src/log.h"
#include "src/lookup.h"
#include "src/objects-inl.h"
#include "src/objects-visiting-inl.h"
#include "src/macro-assembler.h"
#include "src/mark-compact.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() {
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);
}
}
ASSERT(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->GetJSProxy(), it->GetReceiver(), it->name());
case LookupIterator::INTERCEPTOR: {
MaybeHandle<Object> maybe_result = JSObject::GetPropertyWithInterceptor(
it->GetHolder(), 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(), 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);
ASSERT(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: {
ASSERT(iterator.Complete());
current = *CheckedCast<char*>(current);
return handle(*CheckedCast<Object*>(current), isolate);
}
case kDescriptorPointerDereference:
ASSERT(!iterator.Complete());
current = *reinterpret_cast<char**>(current);
break;
case kDescriptorPointerShift:
ASSERT(!iterator.Complete());
current += data->pointer_shift_descriptor.byte_offset;
break;
case kDescriptorObjectDereference: {
ASSERT(!iterator.Complete());
Object* object = CheckedCast<Object>(current);
int field = data->object_dereference_descriptor.internal_field;
Object* smi = JSObject::cast(object)->GetInternalField(field);
ASSERT(smi->IsSmi());
current = reinterpret_cast<char*>(smi);
break;
}
case kDescriptorBitmaskCompare:
ASSERT(iterator.Complete());
return PerformCompare(data->bitmask_compare_descriptor,
current,
isolate);
case kDescriptorPointerCompare:
ASSERT(iterator.Complete());
return PerformCompare(data->pointer_compare_descriptor,
current,
isolate);
case kDescriptorPrimitiveValue:
ASSERT(iterator.Complete());
return GetPrimitiveValue(data->primitive_value_descriptor,
current,
isolate);
}
}
UNREACHABLE();
return isolate->factory()->undefined_value();
}
Handle<FixedArray> JSObject::EnsureWritableFastElements(
Handle<JSObject> object) {
ASSERT(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();
ASSERT(!structure->IsForeign());
// api style callbacks.
if (structure->IsAccessorInfo()) {
Handle<AccessorInfo> accessor_info = Handle<AccessorInfo>::cast(structure);
if (!accessor_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();
}
MaybeHandle<Object> Object::SetPropertyWithCallback(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.
ASSERT(!value->IsTheHole());
ASSERT(!structure->IsForeign());
if (structure->IsExecutableAccessorInfo()) {
// api style callbacks
ExecutableAccessorInfo* data = ExecutableAccessorInfo::cast(*structure);
if (!data->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 = data->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, data->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),
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;
} else if (accessors->IsAccessorPair()) {
if (AccessorPair::cast(*accessors)->all_can_read()) return true;
}
}
}
return false;
}
MaybeHandle<Object> JSObject::GetPropertyWithFailedAccessCheck(
LookupIterator* it) {
Handle<JSObject> checked = Handle<JSObject>::cast(it->GetHolder());
if (FindAllCanReadHolder(it)) {
return GetPropertyWithAccessor(
it->GetReceiver(), it->name(), it->GetHolder(), it->GetAccessors());
}
it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_GET);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(it->isolate(), Object);
return it->factory()->undefined_value();
}
PropertyAttributes JSObject::GetPropertyAttributesWithFailedAccessCheck(
LookupIterator* it) {
Handle<JSObject> checked = Handle<JSObject>::cast(it->GetHolder());
if (FindAllCanReadHolder(it)) return it->property_details().attributes();
it->isolate()->ReportFailedAccessCheck(checked, v8::ACCESS_HAS);
// TODO(yangguo): Issue 3269, check for scheduled exception missing?
return ABSENT;
}
static bool FindAllCanWriteHolder(LookupResult* result,
Handle<Name> name,
bool check_prototype) {
if (result->IsInterceptor()) {
result->holder()->LookupOwnRealNamedProperty(name, result);
}
while (result->IsProperty()) {
if (result->type() == CALLBACKS) {
Object* callback_obj = result->GetCallbackObject();
if (callback_obj->IsAccessorInfo()) {
if (AccessorInfo::cast(callback_obj)->all_can_write()) return true;
} else if (callback_obj->IsAccessorPair()) {
if (AccessorPair::cast(callback_obj)->all_can_write()) return true;
}
}
if (!check_prototype) break;
result->holder()->LookupRealNamedPropertyInPrototypes(name, result);
}
return false;
}
MaybeHandle<Object> JSObject::SetPropertyWithFailedAccessCheck(
Handle<JSObject> object,
LookupResult* result,
Handle<Name> name,
Handle<Object> value,
bool check_prototype,
StrictMode strict_mode) {
if (check_prototype && !result->IsProperty()) {
object->LookupRealNamedPropertyInPrototypes(name, result);
}
if (FindAllCanWriteHolder(result, name, check_prototype)) {
Handle<JSObject> holder(result->holder());
Handle<Object> callbacks(result->GetCallbackObject(), result->isolate());
return SetPropertyWithCallback(
object, name, value, holder, callbacks, strict_mode);
}
Isolate* isolate = object->GetIsolate();
isolate->ReportFailedAccessCheck(object, v8::ACCESS_SET);
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object);
return value;
}
Object* JSObject::GetNormalizedProperty(const LookupResult* result) {
ASSERT(!HasFastProperties());
Object* value = property_dictionary()->ValueAt(result->GetDictionaryEntry());
if (IsGlobalObject()) {
value = PropertyCell::cast(value)->value();
}
ASSERT(!value->IsPropertyCell() && !value->IsCell());
return value;
}
Handle<Object> JSObject::GetNormalizedProperty(Handle<JSObject> object,
const LookupResult* result) {
ASSERT(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
Handle<Object> value(object->property_dictionary()->ValueAt(
result->GetDictionaryEntry()), isolate);
if (object->IsGlobalObject()) {
value = Handle<Object>(Handle<PropertyCell>::cast(value)->value(), isolate);
}
ASSERT(!value->IsPropertyCell() && !value->IsCell());
return value;
}
void JSObject::SetNormalizedProperty(Handle<JSObject> object,
const LookupResult* result,
Handle<Object> value) {
ASSERT(!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) {
ASSERT(!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();
ASSERT(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) {
ASSERT(!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()));
ASSERT(new_map->is_dictionary_map());
object->set_map(*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) {
Handle<Object> holder;
// Iterate up the prototype chain until an element is found or the null
// prototype is encountered.
for (holder = object;
!holder->IsNull();
holder = Handle<Object>(holder->GetPrototype(isolate), isolate)) {
if (!holder->IsJSObject()) {
Context* native_context = isolate->context()->native_context();
if (holder->IsNumber()) {
holder = Handle<Object>(
native_context->number_function()->instance_prototype(), isolate);
} else if (holder->IsString()) {
holder = Handle<Object>(
native_context->string_function()->instance_prototype(), isolate);
} else if (holder->IsSymbol()) {
holder = Handle<Object>(
native_context->symbol_function()->instance_prototype(), isolate);
} else if (holder->IsBoolean()) {
holder = Handle<Object>(
native_context->boolean_function()->instance_prototype(), isolate);
} else if (holder->IsJSProxy()) {
return JSProxy::GetElementWithHandler(
Handle<JSProxy>::cast(holder), receiver, index);
} else {
// Undefined and null have no indexed properties.
ASSERT(holder->IsUndefined() || holder->IsNull());
return isolate->factory()->undefined_value();
}
}
// 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(holder);
// 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();
}
Object* Object::GetPrototype(Isolate* isolate) {
DisallowHeapAllocation no_alloc;
if (IsSmi()) {
Context* context = isolate->context()->native_context();
return context->number_function()->instance_prototype();
}
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()->prototype();
}
Context* context = isolate->context()->native_context();
if (heap_object->IsHeapNumber()) {
return context->number_function()->instance_prototype();
}
if (heap_object->IsString()) {
return context->string_function()->instance_prototype();
}
if (heap_object->IsSymbol()) {
return context->symbol_function()->instance_prototype();
}
if (heap_object->IsBoolean()) {
return context->boolean_function()->instance_prototype();
} else {
return isolate->heap()->null_value();
}
}
Handle<Object> Object::GetPrototype(Isolate* isolate,
Handle<Object> object) {
return handle(object->GetPrototype(isolate), isolate);
}
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);
}
ASSERT(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);
ASSERT(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) {
HeapStringAllocator allocator;
StringStream accumulator(&allocator);
ShortPrint(&accumulator);
accumulator.OutputToFile(out);
}
void Object::ShortPrint(StringStream* accumulator) {
if (IsSmi()) {
Smi::cast(this)->SmiPrint(accumulator);
} else {
HeapObject::cast(this)->HeapObjectShortPrint(accumulator);
}
}
void Smi::SmiPrint(FILE* out) {
PrintF(out, "%d", value());
}
void Smi::SmiPrint(StringStream* accumulator) {
accumulator->Add("%d", 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) {
ASSERT(AllowHeapAllocation::IsAllowed());
ASSERT(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());
ASSERT(result->IsFlat());
return result;
}
bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
// Externalizing twice leaks the external resource, so it's
// prohibited by the API.
ASSERT(!this->IsExternalString());
#ifdef ENABLE_SLOW_ASSERTS
if (FLAG_enable_slow_asserts) {
// Assert that the resource and the string are equivalent.
ASSERT(static_cast<size_t>(this->length()) == resource->length());
ScopedVector<uc16> smart_chars(this->length());
String::WriteToFlat(this, smart_chars.start(), 0, this->length());
ASSERT(memcmp(smart_chars.start(),
resource->data(),
resource->length() * sizeof(smart_chars[0])) == 0);
}
#endif // DEBUG
Heap* heap = GetHeap();
int size = this->Size(); // Byte size of the original string.
if (size < ExternalString::kShortSize) {
return false;
}
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) {
#ifdef ENABLE_SLOW_ASSERTS
if (FLAG_enable_slow_asserts) {
// Assert that the resource and the string are equivalent.
ASSERT(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());
ASSERT(String::IsOneByte(smart_chars.start(), this->length()));
}
ScopedVector<char> smart_chars(this->length());
String::WriteToFlat(this, smart_chars.start(), 0, this->length());
ASSERT(memcmp(smart_chars.start(),
resource->data(),
resource->length() * sizeof(smart_chars[0])) == 0);
}
#endif // DEBUG
Heap* heap = GetHeap();
int size = this->Size(); // Byte size of the original string.
if (size < ExternalString::kShortSize) {
return false;
}
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 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) {
PrintF(file, "elements transition [");
PrintElementsKind(file, from_kind);
PrintF(file, " -> ");
PrintElementsKind(file, to_kind);
PrintF(file, "] 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) {
PrintF(file, "[generalizing ");
constructor_name()->PrintOn(file);
PrintF(file, "] ");
Name* name = instance_descriptors()->GetKey(modify_index);
if (name->IsString()) {
String::cast(name)->PrintOn(file);
} else {
PrintF(file, "{symbol %p}", static_cast<void*>(name));
}
PrintF(file, ":");
if (constant_to_field) {
PrintF(file, "c");
} else {
PrintF(file, "%s", old_representation.Mnemonic());
PrintF(file, "{");
old_field_type->TypePrint(file, HeapType::SEMANTIC_DIM);
PrintF(file, "}");
}
PrintF(file, "->%s", new_representation.Mnemonic());
PrintF(file, "{");
new_field_type->TypePrint(file, HeapType::SEMANTIC_DIM);
PrintF(file, "}");
PrintF(file, " (");
if (strlen(reason) > 0) {
PrintF(file, "%s", reason);
} else {
PrintF(file, "+%i maps", descriptors - split);
}
PrintF(file, ") [");
JavaScriptFrame::PrintTop(GetIsolate(), file, false, true);
PrintF(file, "]\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(StringStream* accumulator) {
Heap* heap = GetHeap();
if (!heap->Contains(this)) {
accumulator->Add("!!!INVALID POINTER!!!");
return;
}
if (!heap->Contains(map())) {
accumulator->Add("!!!INVALID MAP!!!");
return;
}
accumulator->Add("%p ", this);
if (IsString()) {
String::cast(this)->StringShortPrint(accumulator);
return;
}
if (IsJSObject()) {
JSObject::cast(this)->JSObjectShortPrint(accumulator);
return;
}
switch (map()->instance_type()) {
case MAP_TYPE:
accumulator->Add("<Map(elements=%u)>", Map::cast(this)->elements_kind());
break;
case FIXED_ARRAY_TYPE:
accumulator->Add("<FixedArray[%u]>", FixedArray::cast(this)->length());
break;
case FIXED_DOUBLE_ARRAY_TYPE:
accumulator->Add("<FixedDoubleArray[%u]>",
FixedDoubleArray::cast(this)->length());
break;
case BYTE_ARRAY_TYPE:
accumulator->Add("<ByteArray[%u]>", ByteArray::cast(this)->length());
break;
case FREE_SPACE_TYPE:
accumulator->Add("<FreeSpace[%u]>", FreeSpace::cast(this)->Size());
break;
#define TYPED_ARRAY_SHORT_PRINT(Type, type, TYPE, ctype, size) \
case EXTERNAL_##TYPE##_ARRAY_TYPE: \
accumulator->Add("<External" #Type "Array[%u]>", \
External##Type##Array::cast(this)->length()); \
break; \
case FIXED_##TYPE##_ARRAY_TYPE: \
accumulator->Add("<Fixed" #Type "Array[%u]>", \
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) {
accumulator->Add("<SharedFunctionInfo %s>", debug_name.get());
} else {
accumulator->Add("<SharedFunctionInfo>");
}
break;
}
case JS_MESSAGE_OBJECT_TYPE:
accumulator->Add("<JSMessageObject>");
break;
#define MAKE_STRUCT_CASE(NAME, Name, name) \
case NAME##_TYPE: \
accumulator->Put('<'); \
accumulator->Add(#Name); \
accumulator->Put('>'); \
break;
STRUCT_LIST(MAKE_STRUCT_CASE)
#undef MAKE_STRUCT_CASE
case CODE_TYPE:
accumulator->Add("<Code>");
break;
case ODDBALL_TYPE: {
if (IsUndefined())
accumulator->Add("<undefined>");
else if (IsTheHole())
accumulator->Add("<the hole>");
else if (IsNull())
accumulator->Add("<null>");
else if (IsTrue())
accumulator->Add("<true>");
else if (IsFalse())
accumulator->Add("<false>");
else
accumulator->Add("<Odd Oddball>");
break;
}
case SYMBOL_TYPE: {
Symbol* symbol = Symbol::cast(this);
accumulator->Add("<Symbol: %d", symbol->Hash());
if (!symbol->name()->IsUndefined()) {
accumulator->Add(" ");
String::cast(symbol->name())->StringShortPrint(accumulator);
}
accumulator->Add(">");
break;
}
case HEAP_NUMBER_TYPE:
accumulator->Add("<Number: ");
HeapNumber::cast(this)->HeapNumberPrint(accumulator);
accumulator->Put('>');
break;
case JS_PROXY_TYPE:
accumulator->Add("<JSProxy>");
break;
case JS_FUNCTION_PROXY_TYPE:
accumulator->Add("<JSFunctionProxy>");
break;
case FOREIGN_TYPE:
accumulator->Add("<Foreign>");
break;
case CELL_TYPE:
accumulator->Add("Cell for ");
Cell::cast(this)->value()->ShortPrint(accumulator);
break;
case PROPERTY_CELL_TYPE:
accumulator->Add("PropertyCell for ");
PropertyCell::cast(this)->value()->ShortPrint(accumulator);
break;
default:
accumulator->Add("<Other heap object (%d)>", 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 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() {
// NaN, +0, and -0 should return the false object
#if __BYTE_ORDER == __LITTLE_ENDIAN
union IeeeDoubleLittleEndianArchType u;
#elif __BYTE_ORDER == __BIG_ENDIAN
union IeeeDoubleBigEndianArchType u;
#endif
u.d = value();
if (u.bits.exp == 2047) {
// Detect NaN for IEEE double precision floating point.
if ((u.bits.man_low | u.bits.man_high) != 0) return false;
}
if (u.bits.exp == 0) {
// Detect +0, and -0 for IEEE double precision floating point.
if ((u.bits.man_low | u.bits.man_high) == 0) return false;
}
return true;
}
void HeapNumber::HeapNumberPrint(FILE* out) {
PrintF(out, "%.16g", Number());
}
void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
// The Windows version of vsnprintf can allocate when printing a %g string
// into a buffer that may not be big enough. We don't want random memory
// allocation when producing post-crash stack traces, so we print into a
// buffer that is plenty big enough for any floating point number, then
// print that using vsnprintf (which may truncate but never allocate if
// there is no more space in the buffer).
EmbeddedVector<char, 100> buffer;
SNPrintF(buffer, "%.16g", Number());
accumulator->Add("%s", buffer.start());
}
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) {
ASSERT(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,
ValueType value_type,
TransitionFlag flag) {
ASSERT(!object->IsJSGlobalProxy());
MaybeHandle<Map> maybe_map;
if (value->IsJSFunction()) {
maybe_map = Map::CopyWithConstant(
handle(object->map()), name, value, attributes, flag);
} else if (!object->TooManyFastProperties(store_mode)) {
Isolate* isolate = object->GetIsolate();
Representation representation = value->OptimalRepresentation(value_type);
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) {
ASSERT(!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::AddProperty(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
PropertyAttributes attributes,
StrictMode strict_mode,
JSReceiver::StoreFromKeyed store_mode,
ExtensibilityCheck extensibility_check,
ValueType value_type,
StoreMode mode,
TransitionFlag transition_flag) {
ASSERT(!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()) {
if (strict_mode == SLOPPY) {
return value;
} else {
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,
value_type, 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) {
ASSERT(!object->IsJSGlobalProxy());
ASSERT(!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();
}
MaybeHandle<Object> JSObject::SetPropertyPostInterceptor(
Handle<JSObject> object,
Handle<Name> name,
Handle<Object> value,
PropertyAttributes attributes,
StrictMode strict_mode) {
// Check own property, ignore interceptor.
Isolate* isolate = object->GetIsolate();
LookupResult result(isolate);
object->LookupOwnRealNamedProperty(name, &result);
if (!result.IsFound()) {
object->map()->LookupTransition(*object, *name, &result);
}
return SetPropertyForResult(object, &result, name, value, attributes,
strict_mode, MAY_BE_STORE_FROM_KEYED);
}
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;
}
}
static void ZapEndOfFixedArray(Address new_end, int to_trim) {
// If we are doing a big trim in old space then we zap the space.
Object** zap = reinterpret_cast<Object**>(new_end);
zap++; // Header of filler must be at least one word so skip that.
for (int i = 1; i < to_trim; i++) {
*zap++ = Smi::FromInt(0);
}
}
template<Heap::InvocationMode mode>
static void RightTrimFixedArray(Heap* heap, FixedArray* elms, int to_trim) {
ASSERT(elms->map() != heap->fixed_cow_array_map());
// For now this trick is only applied to fixed arrays in new and paged space.
ASSERT(!heap->lo_space()->Contains(elms));
const int len = elms->length();
ASSERT(to_trim < len);
Address new_end = elms->address() + FixedArray::SizeFor(len - to_trim);
if (mode != Heap::FROM_GC || Heap::ShouldZapGarbage()) {
ZapEndOfFixedArray(new_end, to_trim);
}
int size_delta = to_trim * kPointerSize;
// Technically in new space this write might be omitted (except for
// debug mode which iterates through the heap), but to play safer
// we still do it.
heap->CreateFillerObjectAt(new_end, size_delta);
// We are storing the new length using release store after creating a filler
// for the left-over space to avoid races with the sweeper thread.
elms->synchronized_set_length(len - to_trim);
heap->AdjustLiveBytes(elms->address(), -size_delta, mode);
// The array may not be moved during GC,
// and size has to be adjusted nevertheless.
HeapProfiler* profiler = heap->isolate()->heap_profiler();
if (profiler->is_tracking_allocations()) {
profiler->UpdateObjectSizeEvent(elms->address(), elms->Size());
}
}
bool Map::InstancesNeedRewriting(Map* target,
int target_number_of_fields,
int target_inobject,
int target_unused) {
// If fields were added (or removed), rewrite the instance.
int number_of_fields = NumberOfFields();
ASSERT(target_number_of_fields >= number_of_fields);
if (target_number_of_fields != 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.
ASSERT(target_inobject < inobject_properties());
if (target_number_of_fields <= target_inobject) {
ASSERT(target_number_of_fields + target_unused == target_inobject);
return false;
}
// Otherwise, properties will need to be moved to the backing store.
return true;
}
Handle<TransitionArray> Map::SetElementsTransitionMap(
Handle<Map> map, Handle<Map> transitioned_map) {
Handle<TransitionArray> transitions = TransitionArray::CopyInsert(
map,
map->GetIsolate()->factory()->elements_transition_symbol(),
transitioned_map,
FULL_TRANSITION);
map->set_transitions(*transitions);
return transitions;
}
// To migrate an instance to a 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::MigrateToMap(Handle<JSObject> object, Handle<Map> new_map) {
Isolate* isolate = object->GetIsolate();
Handle<Map> old_map(object->map());
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)) {
// Writing the new map here does not require synchronization since it does
// not change the actual object size.
object->synchronized_set_map(*new_map);
return;
}
int total_size = number_of_fields + unused;
int external = total_size - inobject;
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.
ASSERT(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) {
ASSERT(details.representation().IsTagged());
continue;
}
ASSERT(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());
}
ASSERT(!(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);
} 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));
}
// 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;
ASSERT(instance_size_delta >= 0);
Address address = object->address() + new_instance_size;
// The trimming is performed on a newly allocated object, which is on a
// fresly allocated page or on an already swept page. Hence, the sweeper
// thread can not get confused with the filler creation. No synchronization
// needed.
isolate->heap()->CreateFillerObjectAt(address, instance_size_delta);
// 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) {
RightTrimFixedArray<Heap::FROM_MUTATOR>(isolate->heap(), *array, inobject);
object->set_properties(*array);
}
// The trimming is performed on a newly allocated object, which is on a
// fresly allocated page or on an already swept page. Hence, the sweeper
// thread can not get confused with the filler creation. No synchronization
// needed.
object->set_map(*new_map);
}
void JSObject::GeneralizeFieldRepresentation(Handle<JSObject> object,
int modify_index,
Representation new_representation,
Handle<HeapType> new_field_type,
StoreMode store_mode) {
Handle<Map> new_map = Map::GeneralizeRepresentation(
handle(object->map()), modify_index, new_representation,
new_field_type, store_mode);
if (object->map() == *new_map) return;
return 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) {
FieldDescriptor d(handle(descriptors->GetKey(modify_index), isolate),
new_map->NumberOfFields(),
attributes,
Representation::Tagged());
descriptors->Replace(modify_index, &d);
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);
}
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.
ASSERT(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;
ASSERT_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::UpdateDescriptor(int descriptor_number, Descriptor* desc) {
DisallowHeapAllocation no_allocation;
if (HasTransitionArray()) {
TransitionArray* transitions = this->transitions();
for (int i = 0; i < transitions->number_of_transitions(); ++i) {
transitions->GetTarget(i)->UpdateDescriptor(descriptor_number, desc);
}
}
instance_descriptors()->Replace(descriptor_number, desc);;
}
// 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()) {