Reland r22082 "Replace HeapNumber as doublebox with an explicit MutableHeapNumber."
R=verwaest@chromium.org Review URL: https://codereview.chromium.org/334323003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22129 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
dd676cb628
commit
2c94151e6e
@ -5587,7 +5587,7 @@ class Internals {
|
||||
static const int kNullValueRootIndex = 7;
|
||||
static const int kTrueValueRootIndex = 8;
|
||||
static const int kFalseValueRootIndex = 9;
|
||||
static const int kEmptyStringRootIndex = 160;
|
||||
static const int kEmptyStringRootIndex = 161;
|
||||
|
||||
// The external allocation limit should be below 256 MB on all architectures
|
||||
// to avoid that resource-constrained embedders run low on memory.
|
||||
@ -5602,10 +5602,10 @@ class Internals {
|
||||
static const int kNodeIsIndependentShift = 4;
|
||||
static const int kNodeIsPartiallyDependentShift = 5;
|
||||
|
||||
static const int kJSObjectType = 0xbb;
|
||||
static const int kJSObjectType = 0xbc;
|
||||
static const int kFirstNonstringType = 0x80;
|
||||
static const int kOddballType = 0x83;
|
||||
static const int kForeignType = 0x87;
|
||||
static const int kForeignType = 0x88;
|
||||
|
||||
static const int kUndefinedOddballKind = 5;
|
||||
static const int kNullOddballKind = 3;
|
||||
|
@ -3274,14 +3274,19 @@ void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
Register scratch2,
|
||||
Register heap_number_map,
|
||||
Label* gc_required,
|
||||
TaggingMode tagging_mode) {
|
||||
TaggingMode tagging_mode,
|
||||
MutableMode mode) {
|
||||
// Allocate an object in the heap for the heap number and tag it as a heap
|
||||
// object.
|
||||
Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
|
||||
tagging_mode == TAG_RESULT ? TAG_OBJECT : NO_ALLOCATION_FLAGS);
|
||||
|
||||
Heap::RootListIndex map_index = mode == MUTABLE
|
||||
? Heap::kMutableHeapNumberMapRootIndex
|
||||
: Heap::kHeapNumberMapRootIndex;
|
||||
AssertIsRoot(heap_number_map, map_index);
|
||||
|
||||
// Store heap number map in the allocated object.
|
||||
AssertIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
|
||||
if (tagging_mode == TAG_RESULT) {
|
||||
str(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset));
|
||||
} else {
|
||||
|
@ -778,7 +778,8 @@ class MacroAssembler: public Assembler {
|
||||
Register scratch2,
|
||||
Register heap_number_map,
|
||||
Label* gc_required,
|
||||
TaggingMode tagging_mode = TAG_RESULT);
|
||||
TaggingMode tagging_mode = TAG_RESULT,
|
||||
MutableMode mode = IMMUTABLE);
|
||||
void AllocateHeapNumberWithValue(Register result,
|
||||
DwVfpRegister value,
|
||||
Register scratch1,
|
||||
|
@ -426,8 +426,9 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
|
||||
}
|
||||
} else if (representation.IsDouble()) {
|
||||
Label do_store, heap_number;
|
||||
__ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex);
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow);
|
||||
__ LoadRoot(scratch3, Heap::kMutableHeapNumberMapRootIndex);
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow,
|
||||
TAG_RESULT, MUTABLE);
|
||||
|
||||
__ JumpIfNotSmi(value_reg, &heap_number);
|
||||
__ SmiUntag(scratch1, value_reg);
|
||||
|
@ -3570,7 +3570,8 @@ void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
CPURegister value,
|
||||
CPURegister heap_number_map) {
|
||||
CPURegister heap_number_map,
|
||||
MutableMode mode) {
|
||||
ASSERT(!value.IsValid() || value.Is64Bits());
|
||||
UseScratchRegisterScope temps(this);
|
||||
|
||||
@ -3579,6 +3580,10 @@ void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
|
||||
NO_ALLOCATION_FLAGS);
|
||||
|
||||
Heap::RootListIndex map_index = mode == MUTABLE
|
||||
? Heap::kMutableHeapNumberMapRootIndex
|
||||
: Heap::kHeapNumberMapRootIndex;
|
||||
|
||||
// Prepare the heap number map.
|
||||
if (!heap_number_map.IsValid()) {
|
||||
// If we have a valid value register, use the same type of register to store
|
||||
@ -3588,7 +3593,7 @@ void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
} else {
|
||||
heap_number_map = scratch1;
|
||||
}
|
||||
LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
|
||||
LoadRoot(heap_number_map, map_index);
|
||||
}
|
||||
if (emit_debug_code()) {
|
||||
Register map;
|
||||
@ -3598,7 +3603,7 @@ void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
} else {
|
||||
map = Register(heap_number_map);
|
||||
}
|
||||
AssertRegisterIsRoot(map, Heap::kHeapNumberMapRootIndex);
|
||||
AssertRegisterIsRoot(map, map_index);
|
||||
}
|
||||
|
||||
// Store the heap number map and the value in the allocated object.
|
||||
|
@ -1372,7 +1372,8 @@ class MacroAssembler : public Assembler {
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
CPURegister value = NoFPReg,
|
||||
CPURegister heap_number_map = NoReg);
|
||||
CPURegister heap_number_map = NoReg,
|
||||
MutableMode mode = IMMUTABLE);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Support functions.
|
||||
|
@ -399,7 +399,8 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
|
||||
__ Ldr(temp_double, FieldMemOperand(value_reg, HeapNumber::kValueOffset));
|
||||
|
||||
__ Bind(&do_store);
|
||||
__ AllocateHeapNumber(storage_reg, slow, scratch1, scratch2, temp_double);
|
||||
__ AllocateHeapNumber(storage_reg, slow, scratch1, scratch2, temp_double,
|
||||
NoReg, MUTABLE);
|
||||
}
|
||||
|
||||
// Stub never generated for non-global objects that require access checks.
|
||||
|
@ -1813,9 +1813,11 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() {
|
||||
Handle<Map> map = Map::GeneralizeAllFieldRepresentations(
|
||||
Handle<Map>::cast(MaterializeNextValue()));
|
||||
switch (map->instance_type()) {
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
case HEAP_NUMBER_TYPE: {
|
||||
// Reuse the HeapNumber value directly as it is already properly
|
||||
// tagged and skip materializing the HeapNumber explicitly.
|
||||
// tagged and skip materializing the HeapNumber explicitly. Turn mutable
|
||||
// heap numbers immutable.
|
||||
Handle<Object> object = MaterializeNextValue();
|
||||
if (object_index < prev_materialized_count_) {
|
||||
materialized_objects_->Add(Handle<Object>(
|
||||
@ -1877,6 +1879,9 @@ Handle<Object> Deoptimizer::MaterializeNextHeapObject() {
|
||||
Handle<Object> Deoptimizer::MaterializeNextValue() {
|
||||
int value_index = materialization_value_index_++;
|
||||
Handle<Object> value = materialized_values_->at(value_index);
|
||||
if (value->IsMutableHeapNumber()) {
|
||||
HeapNumber::cast(*value)->set_map(isolate_->heap()->heap_number_map());
|
||||
}
|
||||
if (*value == isolate_->heap()->arguments_marker()) {
|
||||
value = MaterializeNextHeapObject();
|
||||
}
|
||||
@ -3383,6 +3388,7 @@ Handle<Object> SlotRefValueBuilder::GetNext(Isolate* isolate, int lvl) {
|
||||
// TODO(jarin) this should be unified with the code in
|
||||
// Deoptimizer::MaterializeNextHeapObject()
|
||||
switch (map->instance_type()) {
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
case HEAP_NUMBER_TYPE: {
|
||||
// Reuse the HeapNumber value directly as it is already properly
|
||||
// tagged and skip materializing the HeapNumber explicitly.
|
||||
|
@ -1008,7 +1008,7 @@ Handle<Object> Factory::NewNumber(double value,
|
||||
// We need to distinguish the minus zero value and this cannot be
|
||||
// done after conversion to int. Doing this by comparing bit
|
||||
// patterns is faster than using fpclassify() et al.
|
||||
if (IsMinusZero(value)) return NewHeapNumber(-0.0, pretenure);
|
||||
if (IsMinusZero(value)) return NewHeapNumber(-0.0, IMMUTABLE, pretenure);
|
||||
|
||||
int int_value = FastD2I(value);
|
||||
if (value == int_value && Smi::IsValid(int_value)) {
|
||||
@ -1016,15 +1016,15 @@ Handle<Object> Factory::NewNumber(double value,
|
||||
}
|
||||
|
||||
// Materialize the value in the heap.
|
||||
return NewHeapNumber(value, pretenure);
|
||||
return NewHeapNumber(value, IMMUTABLE, pretenure);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Factory::NewNumberFromInt(int32_t value,
|
||||
PretenureFlag pretenure) {
|
||||
if (Smi::IsValid(value)) return handle(Smi::FromInt(value), isolate());
|
||||
// Bypass NumberFromDouble to avoid various redundant checks.
|
||||
return NewHeapNumber(FastI2D(value), pretenure);
|
||||
// Bypass NewNumber to avoid various redundant checks.
|
||||
return NewHeapNumber(FastI2D(value), IMMUTABLE, pretenure);
|
||||
}
|
||||
|
||||
|
||||
@ -1034,15 +1034,17 @@ Handle<Object> Factory::NewNumberFromUint(uint32_t value,
|
||||
if (int32v >= 0 && Smi::IsValid(int32v)) {
|
||||
return handle(Smi::FromInt(int32v), isolate());
|
||||
}
|
||||
return NewHeapNumber(FastUI2D(value), pretenure);
|
||||
return NewHeapNumber(FastUI2D(value), IMMUTABLE, pretenure);
|
||||
}
|
||||
|
||||
|
||||
Handle<HeapNumber> Factory::NewHeapNumber(double value,
|
||||
MutableMode mode,
|
||||
PretenureFlag pretenure) {
|
||||
CALL_HEAP_FUNCTION(
|
||||
isolate(),
|
||||
isolate()->heap()->AllocateHeapNumber(value, pretenure), HeapNumber);
|
||||
isolate()->heap()->AllocateHeapNumber(value, mode, pretenure),
|
||||
HeapNumber);
|
||||
}
|
||||
|
||||
|
||||
|
@ -352,9 +352,9 @@ class Factory V8_FINAL {
|
||||
return NewNumber(static_cast<double>(value), pretenure);
|
||||
}
|
||||
Handle<HeapNumber> NewHeapNumber(double value,
|
||||
MutableMode mode = IMMUTABLE,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
|
||||
// These objects are used by the api to create env-independent data
|
||||
// structures in the heap.
|
||||
inline Handle<JSObject> NewNeanderObject() {
|
||||
|
@ -1641,6 +1641,8 @@ void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
|
||||
for (int i = 0; i < real_size; i++) {
|
||||
switch (descs->GetType(i)) {
|
||||
case FIELD: {
|
||||
Representation r = descs->GetDetails(i).representation();
|
||||
if (r.IsSmi() || r.IsDouble()) break;
|
||||
int index = descs->GetFieldIndex(i);
|
||||
|
||||
Name* k = descs->GetKey(i);
|
||||
|
15
src/heap.cc
15
src/heap.cc
@ -2528,6 +2528,8 @@ bool Heap::CreateInitialMaps() {
|
||||
|
||||
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, scope_info)
|
||||
ALLOCATE_MAP(HEAP_NUMBER_TYPE, HeapNumber::kSize, heap_number)
|
||||
ALLOCATE_MAP(
|
||||
MUTABLE_HEAP_NUMBER_TYPE, HeapNumber::kSize, mutable_heap_number)
|
||||
ALLOCATE_MAP(SYMBOL_TYPE, Symbol::kSize, symbol)
|
||||
ALLOCATE_MAP(FOREIGN_TYPE, Foreign::kSize, foreign)
|
||||
|
||||
@ -2652,6 +2654,7 @@ bool Heap::CreateInitialMaps() {
|
||||
|
||||
|
||||
AllocationResult Heap::AllocateHeapNumber(double value,
|
||||
MutableMode mode,
|
||||
PretenureFlag pretenure) {
|
||||
// Statically ensure that it is safe to allocate heap numbers in paged
|
||||
// spaces.
|
||||
@ -2665,7 +2668,8 @@ AllocationResult Heap::AllocateHeapNumber(double value,
|
||||
if (!allocation.To(&result)) return allocation;
|
||||
}
|
||||
|
||||
result->set_map_no_write_barrier(heap_number_map());
|
||||
Map* map = mode == MUTABLE ? mutable_heap_number_map() : heap_number_map();
|
||||
HeapObject::cast(result)->set_map_no_write_barrier(map);
|
||||
HeapNumber::cast(result)->set_value(value);
|
||||
return result;
|
||||
}
|
||||
@ -2771,12 +2775,13 @@ void Heap::CreateInitialObjects() {
|
||||
HandleScope scope(isolate());
|
||||
Factory* factory = isolate()->factory();
|
||||
|
||||
// The -0 value must be set before NumberFromDouble works.
|
||||
set_minus_zero_value(*factory->NewHeapNumber(-0.0, TENURED));
|
||||
// The -0 value must be set before NewNumber works.
|
||||
set_minus_zero_value(*factory->NewHeapNumber(-0.0, IMMUTABLE, TENURED));
|
||||
ASSERT(std::signbit(minus_zero_value()->Number()) != 0);
|
||||
|
||||
set_nan_value(*factory->NewHeapNumber(base::OS::nan_value(), TENURED));
|
||||
set_infinity_value(*factory->NewHeapNumber(V8_INFINITY, TENURED));
|
||||
set_nan_value(
|
||||
*factory->NewHeapNumber(base::OS::nan_value(), IMMUTABLE, TENURED));
|
||||
set_infinity_value(*factory->NewHeapNumber(V8_INFINITY, IMMUTABLE, TENURED));
|
||||
|
||||
// The hole has not been created yet, but we want to put something
|
||||
// predictable in the gaps in the string table, so lets make that Smi zero.
|
||||
|
@ -42,6 +42,7 @@ namespace internal {
|
||||
V(Map, shared_function_info_map, SharedFunctionInfoMap) \
|
||||
V(Map, meta_map, MetaMap) \
|
||||
V(Map, heap_number_map, HeapNumberMap) \
|
||||
V(Map, mutable_heap_number_map, MutableHeapNumberMap) \
|
||||
V(Map, native_context_map, NativeContextMap) \
|
||||
V(Map, fixed_array_map, FixedArrayMap) \
|
||||
V(Map, code_map, CodeMap) \
|
||||
@ -230,6 +231,7 @@ namespace internal {
|
||||
V(shared_function_info_map) \
|
||||
V(meta_map) \
|
||||
V(heap_number_map) \
|
||||
V(mutable_heap_number_map) \
|
||||
V(native_context_map) \
|
||||
V(fixed_array_map) \
|
||||
V(code_map) \
|
||||
@ -1460,7 +1462,9 @@ class Heap {
|
||||
|
||||
// Allocated a HeapNumber from value.
|
||||
MUST_USE_RESULT AllocationResult AllocateHeapNumber(
|
||||
double value, PretenureFlag pretenure = NOT_TENURED);
|
||||
double value,
|
||||
MutableMode mode = IMMUTABLE,
|
||||
PretenureFlag pretenure = NOT_TENURED);
|
||||
|
||||
// Allocate a byte array of the specified length
|
||||
MUST_USE_RESULT AllocationResult AllocateByteArray(
|
||||
|
@ -5781,8 +5781,9 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
|
||||
HInstruction* heap_number = Add<HAllocate>(heap_number_size,
|
||||
HType::HeapObject(),
|
||||
NOT_TENURED,
|
||||
HEAP_NUMBER_TYPE);
|
||||
AddStoreMapConstant(heap_number, isolate()->factory()->heap_number_map());
|
||||
MUTABLE_HEAP_NUMBER_TYPE);
|
||||
AddStoreMapConstant(
|
||||
heap_number, isolate()->factory()->mutable_heap_number_map());
|
||||
Add<HStoreNamedField>(heap_number, HObjectAccess::ForHeapNumberValue(),
|
||||
value);
|
||||
instr = New<HStoreNamedField>(checked_object->ActualValue(),
|
||||
@ -10950,11 +10951,14 @@ void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
|
||||
// 2) we can just use the mode of the parent object for pretenuring
|
||||
HInstruction* double_box =
|
||||
Add<HAllocate>(heap_number_constant, HType::HeapObject(),
|
||||
pretenure_flag, HEAP_NUMBER_TYPE);
|
||||
pretenure_flag, MUTABLE_HEAP_NUMBER_TYPE);
|
||||
AddStoreMapConstant(double_box,
|
||||
isolate()->factory()->heap_number_map());
|
||||
Add<HStoreNamedField>(double_box, HObjectAccess::ForHeapNumberValue(),
|
||||
Add<HConstant>(value));
|
||||
isolate()->factory()->mutable_heap_number_map());
|
||||
// Unwrap the mutable heap number from the boilerplate.
|
||||
HValue* double_value =
|
||||
Add<HConstant>(Handle<HeapNumber>::cast(value)->value());
|
||||
Add<HStoreNamedField>(
|
||||
double_box, HObjectAccess::ForHeapNumberValue(), double_value);
|
||||
value_instruction = double_box;
|
||||
} else if (representation.IsSmi()) {
|
||||
value_instruction = value->IsUninitialized()
|
||||
|
@ -1683,14 +1683,18 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) {
|
||||
void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* gc_required) {
|
||||
Label* gc_required,
|
||||
MutableMode mode) {
|
||||
// Allocate heap number in new space.
|
||||
Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
|
||||
TAG_OBJECT);
|
||||
|
||||
Handle<Map> map = mode == MUTABLE
|
||||
? isolate()->factory()->mutable_heap_number_map()
|
||||
: isolate()->factory()->heap_number_map();
|
||||
|
||||
// Set the map.
|
||||
mov(FieldOperand(result, HeapObject::kMapOffset),
|
||||
Immediate(isolate()->factory()->heap_number_map()));
|
||||
mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map));
|
||||
}
|
||||
|
||||
|
||||
|
@ -638,7 +638,8 @@ class MacroAssembler: public Assembler {
|
||||
void AllocateHeapNumber(Register result,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* gc_required);
|
||||
Label* gc_required,
|
||||
MutableMode mode = IMMUTABLE);
|
||||
|
||||
// Allocate a sequential string. All the header fields of the string object
|
||||
// are initialized.
|
||||
|
@ -523,7 +523,7 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
|
||||
}
|
||||
} else if (representation.IsDouble()) {
|
||||
Label do_store, heap_number;
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow);
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow, MUTABLE);
|
||||
|
||||
__ JumpIfNotSmi(value_reg, &heap_number);
|
||||
__ SmiUntag(value_reg);
|
||||
|
@ -387,11 +387,9 @@ Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
|
||||
Representation expected_representation = details.representation();
|
||||
|
||||
if (value->FitsRepresentation(expected_representation)) {
|
||||
// If the target representation is double and the value is already
|
||||
// double, use the existing box.
|
||||
if (value->IsSmi() && expected_representation.IsDouble()) {
|
||||
value = factory()->NewHeapNumber(
|
||||
Handle<Smi>::cast(value)->value());
|
||||
if (expected_representation.IsDouble()) {
|
||||
value = Object::NewStorageFor(isolate(), value,
|
||||
expected_representation);
|
||||
} else if (expected_representation.IsHeapObject() &&
|
||||
!target->instance_descriptors()->GetFieldType(
|
||||
descriptor)->NowContains(value)) {
|
||||
|
@ -407,6 +407,7 @@ BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
|
||||
|
||||
switch (HeapObject::cast(*object)->map()->instance_type()) {
|
||||
case HEAP_NUMBER_TYPE:
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
if (deferred_string_key) SerializeDeferredKey(comma, key);
|
||||
return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
|
||||
case ODDBALL_TYPE:
|
||||
|
@ -292,6 +292,7 @@ class VerifyNativeContextSeparationVisitor: public ObjectVisitor {
|
||||
case CODE_TYPE:
|
||||
case FIXED_DOUBLE_ARRAY_TYPE:
|
||||
case HEAP_NUMBER_TYPE:
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
case INTERCEPTOR_INFO_TYPE:
|
||||
case ODDBALL_TYPE:
|
||||
case SCRIPT_TYPE:
|
||||
|
@ -3274,14 +3274,19 @@ void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
Register scratch2,
|
||||
Register heap_number_map,
|
||||
Label* need_gc,
|
||||
TaggingMode tagging_mode) {
|
||||
TaggingMode tagging_mode,
|
||||
MutableMode mode) {
|
||||
// Allocate an object in the heap for the heap number and tag it as a heap
|
||||
// object.
|
||||
Allocate(HeapNumber::kSize, result, scratch1, scratch2, need_gc,
|
||||
tagging_mode == TAG_RESULT ? TAG_OBJECT : NO_ALLOCATION_FLAGS);
|
||||
|
||||
Heap::RootListIndex map_index = mode == MUTABLE
|
||||
? Heap::kMutableHeapNumberMapRootIndex
|
||||
: Heap::kHeapNumberMapRootIndex;
|
||||
AssertIsRoot(heap_number_map, map_index);
|
||||
|
||||
// Store heap number map in the allocated object.
|
||||
AssertIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
|
||||
if (tagging_mode == TAG_RESULT) {
|
||||
sw(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset));
|
||||
} else {
|
||||
|
@ -546,7 +546,8 @@ class MacroAssembler: public Assembler {
|
||||
Register scratch2,
|
||||
Register heap_number_map,
|
||||
Label* gc_required,
|
||||
TaggingMode tagging_mode = TAG_RESULT);
|
||||
TaggingMode tagging_mode = TAG_RESULT,
|
||||
MutableMode mode = IMMUTABLE);
|
||||
void AllocateHeapNumberWithValue(Register result,
|
||||
FPURegister value,
|
||||
Register scratch1,
|
||||
|
@ -413,8 +413,9 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
|
||||
}
|
||||
} else if (representation.IsDouble()) {
|
||||
Label do_store, heap_number;
|
||||
__ LoadRoot(scratch3, Heap::kHeapNumberMapRootIndex);
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow);
|
||||
__ LoadRoot(scratch3, Heap::kMutableHeapNumberMapRootIndex);
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, scratch3, slow,
|
||||
TAG_RESULT, MUTABLE);
|
||||
|
||||
__ JumpIfNotSmi(value_reg, &heap_number);
|
||||
__ SmiUntag(scratch1, value_reg);
|
||||
|
@ -54,6 +54,7 @@ void HeapObject::HeapObjectVerify() {
|
||||
Map::cast(this)->MapVerify();
|
||||
break;
|
||||
case HEAP_NUMBER_TYPE:
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
HeapNumber::cast(this)->HeapNumberVerify();
|
||||
break;
|
||||
case FIXED_ARRAY_TYPE:
|
||||
@ -205,7 +206,7 @@ void Symbol::SymbolVerify() {
|
||||
|
||||
|
||||
void HeapNumber::HeapNumberVerify() {
|
||||
CHECK(IsHeapNumber());
|
||||
CHECK(IsHeapNumber() || IsMutableHeapNumber());
|
||||
}
|
||||
|
||||
|
||||
@ -263,7 +264,7 @@ void JSObject::JSObjectVerify() {
|
||||
Representation r = descriptors->GetDetails(i).representation();
|
||||
FieldIndex index = FieldIndex::ForDescriptor(map(), i);
|
||||
Object* value = RawFastPropertyAt(index);
|
||||
if (r.IsDouble()) ASSERT(value->IsHeapNumber());
|
||||
if (r.IsDouble()) ASSERT(value->IsMutableHeapNumber());
|
||||
if (value->IsUninitialized()) continue;
|
||||
if (r.IsSmi()) ASSERT(value->IsSmi());
|
||||
if (r.IsHeapObject()) ASSERT(value->IsHeapObject());
|
||||
|
@ -165,6 +165,7 @@ bool Object::IsHeapObject() const {
|
||||
|
||||
|
||||
TYPE_CHECKER(HeapNumber, HEAP_NUMBER_TYPE)
|
||||
TYPE_CHECKER(MutableHeapNumber, MUTABLE_HEAP_NUMBER_TYPE)
|
||||
TYPE_CHECKER(Symbol, SYMBOL_TYPE)
|
||||
|
||||
|
||||
@ -277,10 +278,27 @@ Handle<Object> Object::NewStorageFor(Isolate* isolate,
|
||||
return handle(Smi::FromInt(0), isolate);
|
||||
}
|
||||
if (!representation.IsDouble()) return object;
|
||||
double value;
|
||||
if (object->IsUninitialized()) {
|
||||
return isolate->factory()->NewHeapNumber(0);
|
||||
value = 0;
|
||||
} else if (object->IsMutableHeapNumber()) {
|
||||
value = HeapNumber::cast(*object)->value();
|
||||
} else {
|
||||
value = object->Number();
|
||||
}
|
||||
return isolate->factory()->NewHeapNumber(object->Number());
|
||||
return isolate->factory()->NewHeapNumber(value, MUTABLE);
|
||||
}
|
||||
|
||||
|
||||
Handle<Object> Object::WrapForRead(Isolate* isolate,
|
||||
Handle<Object> object,
|
||||
Representation representation) {
|
||||
ASSERT(!object->IsUninitialized());
|
||||
if (!representation.IsDouble()) {
|
||||
ASSERT(object->FitsRepresentation(representation));
|
||||
return object;
|
||||
}
|
||||
return isolate->factory()->NewHeapNumber(HeapNumber::cast(*object)->value());
|
||||
}
|
||||
|
||||
|
||||
@ -3079,7 +3097,6 @@ CAST_ACCESSOR(FixedTypedArrayBase)
|
||||
CAST_ACCESSOR(Foreign)
|
||||
CAST_ACCESSOR(FreeSpace)
|
||||
CAST_ACCESSOR(GlobalObject)
|
||||
CAST_ACCESSOR(HeapNumber)
|
||||
CAST_ACCESSOR(HeapObject)
|
||||
CAST_ACCESSOR(JSArray)
|
||||
CAST_ACCESSOR(JSArrayBuffer)
|
||||
@ -5949,6 +5966,18 @@ ACCESSORS(JSModule, scope_info, ScopeInfo, kScopeInfoOffset)
|
||||
ACCESSORS(JSValue, value, Object, kValueOffset)
|
||||
|
||||
|
||||
HeapNumber* HeapNumber::cast(Object* object) {
|
||||
SLOW_ASSERT(object->IsHeapNumber() || object->IsMutableHeapNumber());
|
||||
return reinterpret_cast<HeapNumber*>(object);
|
||||
}
|
||||
|
||||
|
||||
const HeapNumber* HeapNumber::cast(const Object* object) {
|
||||
SLOW_ASSERT(object->IsHeapNumber() || object->IsMutableHeapNumber());
|
||||
return reinterpret_cast<const HeapNumber*>(object);
|
||||
}
|
||||
|
||||
|
||||
ACCESSORS(JSDate, value, Object, kValueOffset)
|
||||
ACCESSORS(JSDate, cache_stamp, Object, kCacheStampOffset)
|
||||
ACCESSORS(JSDate, year, Object, kYearOffset)
|
||||
|
@ -64,6 +64,11 @@ void HeapObject::HeapObjectPrint(FILE* out) {
|
||||
case HEAP_NUMBER_TYPE:
|
||||
HeapNumber::cast(this)->HeapNumberPrint(out);
|
||||
break;
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
PrintF(out, "<mutable ");
|
||||
HeapNumber::cast(this)->HeapNumberPrint(out);
|
||||
PrintF(out, ">");
|
||||
break;
|
||||
case FIXED_DOUBLE_ARRAY_TYPE:
|
||||
FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(out);
|
||||
break;
|
||||
|
@ -148,6 +148,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
|
||||
return kVisitJSFunction;
|
||||
|
||||
case HEAP_NUMBER_TYPE:
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
#define EXTERNAL_ARRAY_CASE(Type, type, TYPE, ctype, size) \
|
||||
case EXTERNAL_##TYPE##_ARRAY_TYPE:
|
||||
|
||||
|
@ -1540,6 +1540,11 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) {
|
||||
HeapNumber::cast(this)->HeapNumberPrint(accumulator);
|
||||
accumulator->Put('>');
|
||||
break;
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
accumulator->Add("<MutableNumber: ");
|
||||
HeapNumber::cast(this)->HeapNumberPrint(accumulator);
|
||||
accumulator->Put('>');
|
||||
break;
|
||||
case JS_PROXY_TYPE:
|
||||
accumulator->Add("<JSProxy>");
|
||||
break;
|
||||
@ -1665,6 +1670,7 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
|
||||
break;
|
||||
|
||||
case HEAP_NUMBER_TYPE:
|
||||
case MUTABLE_HEAP_NUMBER_TYPE:
|
||||
case FILLER_TYPE:
|
||||
case BYTE_ARRAY_TYPE:
|
||||
case FREE_SPACE_TYPE:
|
||||
@ -1706,7 +1712,7 @@ bool HeapNumber::HeapNumberBooleanValue() {
|
||||
|
||||
|
||||
void HeapNumber::HeapNumberPrint(FILE* out) {
|
||||
PrintF(out, "%.16g", Number());
|
||||
PrintF(out, "%.16g", value());
|
||||
}
|
||||
|
||||
|
||||
@ -1718,7 +1724,7 @@ void HeapNumber::HeapNumberPrint(StringStream* accumulator) {
|
||||
// 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());
|
||||
SNPrintF(buffer, "%.16g", value());
|
||||
accumulator->Add("%s", buffer.start());
|
||||
}
|
||||
|
||||
@ -2070,8 +2076,8 @@ bool Map::InstancesNeedRewriting(Map* target,
|
||||
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()) {
|
||||
if (new_desc->GetDetails(i).representation().IsDouble() !=
|
||||
old_desc->GetDetails(i).representation().IsDouble()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2168,7 +2174,7 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
|
||||
PropertyDetails details = new_map->GetLastDescriptorDetails();
|
||||
Handle<Object> value;
|
||||
if (details.representation().IsDouble()) {
|
||||
value = isolate->factory()->NewHeapNumber(0);
|
||||
value = isolate->factory()->NewHeapNumber(0, MUTABLE);
|
||||
} else {
|
||||
value = isolate->factory()->uninitialized_value();
|
||||
}
|
||||
@ -2216,6 +2222,9 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
|
||||
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());
|
||||
}
|
||||
ASSERT(!(details.representation().IsDouble() && value->IsSmi()));
|
||||
int target_index = new_descriptors->GetFieldIndex(i) - inobject;
|
||||
@ -2228,7 +2237,7 @@ void JSObject::MigrateFastToFast(Handle<JSObject> object, Handle<Map> new_map) {
|
||||
if (details.type() != FIELD) continue;
|
||||
Handle<Object> value;
|
||||
if (details.representation().IsDouble()) {
|
||||
value = isolate->factory()->NewHeapNumber(0);
|
||||
value = isolate->factory()->NewHeapNumber(0, MUTABLE);
|
||||
} else {
|
||||
value = isolate->factory()->uninitialized_value();
|
||||
}
|
||||
@ -3975,6 +3984,7 @@ void JSObject::WriteToField(int descriptor, Object* value) {
|
||||
// Nothing more to be done.
|
||||
if (value->IsUninitialized()) return;
|
||||
HeapNumber* box = HeapNumber::cast(RawFastPropertyAt(index));
|
||||
ASSERT(box->IsMutableHeapNumber());
|
||||
box->set_value(value->Number());
|
||||
} else {
|
||||
FastPropertyAtPut(index, value);
|
||||
@ -4644,6 +4654,11 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object,
|
||||
FieldIndex index = FieldIndex::ForDescriptor(*map, i);
|
||||
Handle<Object> value(
|
||||
object->RawFastPropertyAt(index), isolate);
|
||||
if (details.representation().IsDouble()) {
|
||||
ASSERT(value->IsMutableHeapNumber());
|
||||
Handle<HeapNumber> old = Handle<HeapNumber>::cast(value);
|
||||
value = isolate->factory()->NewHeapNumber(old->value());
|
||||
}
|
||||
PropertyDetails d =
|
||||
PropertyDetails(details.attributes(), NORMAL, i + 1);
|
||||
dictionary = NameDictionary::Add(dictionary, key, value, d);
|
||||
@ -5811,7 +5826,7 @@ Handle<Object> JSObject::FastPropertyAt(Handle<JSObject> object,
|
||||
FieldIndex index) {
|
||||
Isolate* isolate = object->GetIsolate();
|
||||
Handle<Object> raw_value(object->RawFastPropertyAt(index), isolate);
|
||||
return Object::NewStorageFor(isolate, raw_value, representation);
|
||||
return Object::WrapForRead(isolate, raw_value, representation);
|
||||
}
|
||||
|
||||
|
||||
@ -7002,7 +7017,7 @@ Object* JSObject::SlowReverseLookup(Object* value) {
|
||||
Object* property =
|
||||
RawFastPropertyAt(FieldIndex::ForDescriptor(map(), i));
|
||||
if (descs->GetDetails(i).representation().IsDouble()) {
|
||||
ASSERT(property->IsHeapNumber());
|
||||
ASSERT(property->IsMutableHeapNumber());
|
||||
if (value->IsNumber() && property->Number() == value->Number()) {
|
||||
return descs->GetKey(i);
|
||||
}
|
||||
|
@ -168,6 +168,12 @@ enum ContextualMode {
|
||||
};
|
||||
|
||||
|
||||
enum MutableMode {
|
||||
MUTABLE,
|
||||
IMMUTABLE
|
||||
};
|
||||
|
||||
|
||||
static const int kGrowICDelta = STORE_AND_GROW_NO_TRANSITION -
|
||||
STANDARD_STORE;
|
||||
STATIC_ASSERT(STANDARD_STORE == 0);
|
||||
@ -352,6 +358,7 @@ const int kStubMinorKeyBits = kBitsPerInt - kSmiTagSize - kStubMajorKeyBits;
|
||||
V(PROPERTY_CELL_TYPE) \
|
||||
\
|
||||
V(HEAP_NUMBER_TYPE) \
|
||||
V(MUTABLE_HEAP_NUMBER_TYPE) \
|
||||
V(FOREIGN_TYPE) \
|
||||
V(BYTE_ARRAY_TYPE) \
|
||||
V(FREE_SPACE_TYPE) \
|
||||
@ -680,6 +687,7 @@ enum InstanceType {
|
||||
// "Data", objects that cannot contain non-map-word pointers to heap
|
||||
// objects.
|
||||
HEAP_NUMBER_TYPE,
|
||||
MUTABLE_HEAP_NUMBER_TYPE,
|
||||
FOREIGN_TYPE,
|
||||
BYTE_ARRAY_TYPE,
|
||||
FREE_SPACE_TYPE,
|
||||
@ -900,6 +908,7 @@ template <class C> inline bool Is(Object* obj);
|
||||
|
||||
#define HEAP_OBJECT_TYPE_LIST(V) \
|
||||
V(HeapNumber) \
|
||||
V(MutableHeapNumber) \
|
||||
V(Name) \
|
||||
V(UniqueName) \
|
||||
V(String) \
|
||||
@ -1427,7 +1436,7 @@ class Object {
|
||||
} else if (FLAG_track_fields && representation.IsSmi()) {
|
||||
return IsSmi();
|
||||
} else if (FLAG_track_double_fields && representation.IsDouble()) {
|
||||
return IsNumber();
|
||||
return IsMutableHeapNumber() || IsNumber();
|
||||
} else if (FLAG_track_heap_object_fields && representation.IsHeapObject()) {
|
||||
return IsHeapObject();
|
||||
}
|
||||
@ -1440,6 +1449,10 @@ class Object {
|
||||
Handle<Object> object,
|
||||
Representation representation);
|
||||
|
||||
inline static Handle<Object> WrapForRead(Isolate* isolate,
|
||||
Handle<Object> object,
|
||||
Representation representation);
|
||||
|
||||
// Returns true if the object is of the correct type to be used as a
|
||||
// implementation of a JSObject's elements.
|
||||
inline bool HasValidElements();
|
||||
|
@ -14542,8 +14542,8 @@ RUNTIME_FUNCTION(Runtime_LoadMutableDouble) {
|
||||
object->properties()->length());
|
||||
}
|
||||
Handle<Object> raw_value(object->RawFastPropertyAt(field_index), isolate);
|
||||
RUNTIME_ASSERT(raw_value->IsNumber() || raw_value->IsUninitialized());
|
||||
return *Object::NewStorageFor(isolate, raw_value, Representation::Double());
|
||||
RUNTIME_ASSERT(raw_value->IsMutableHeapNumber());
|
||||
return *Object::WrapForRead(isolate, raw_value, Representation::Double());
|
||||
}
|
||||
|
||||
|
||||
|
@ -4593,12 +4593,17 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) {
|
||||
|
||||
void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
Register scratch,
|
||||
Label* gc_required) {
|
||||
Label* gc_required,
|
||||
MutableMode mode) {
|
||||
// Allocate heap number in new space.
|
||||
Allocate(HeapNumber::kSize, result, scratch, no_reg, gc_required, TAG_OBJECT);
|
||||
|
||||
Heap::RootListIndex map_index = mode == MUTABLE
|
||||
? Heap::kMutableHeapNumberMapRootIndex
|
||||
: Heap::kHeapNumberMapRootIndex;
|
||||
|
||||
// Set the map.
|
||||
LoadRoot(kScratchRegister, Heap::kHeapNumberMapRootIndex);
|
||||
LoadRoot(kScratchRegister, map_index);
|
||||
movp(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister);
|
||||
}
|
||||
|
||||
|
@ -1186,7 +1186,8 @@ class MacroAssembler: public Assembler {
|
||||
// space is full.
|
||||
void AllocateHeapNumber(Register result,
|
||||
Register scratch,
|
||||
Label* gc_required);
|
||||
Label* gc_required,
|
||||
MutableMode mode = IMMUTABLE);
|
||||
|
||||
// Allocate a sequential string. All the header fields of the string object
|
||||
// are initialized.
|
||||
|
@ -489,7 +489,7 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
|
||||
}
|
||||
} else if (representation.IsDouble()) {
|
||||
Label do_store, heap_number;
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, slow);
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, slow, MUTABLE);
|
||||
|
||||
__ JumpIfNotSmi(value_reg, &heap_number);
|
||||
__ SmiToInteger32(scratch1, value_reg);
|
||||
|
@ -1576,14 +1576,18 @@ void MacroAssembler::UndoAllocationInNewSpace(Register object) {
|
||||
void MacroAssembler::AllocateHeapNumber(Register result,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* gc_required) {
|
||||
Label* gc_required,
|
||||
MutableMode mode) {
|
||||
// Allocate heap number in new space.
|
||||
Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required,
|
||||
TAG_OBJECT);
|
||||
|
||||
Handle<Map> map = mode == MUTABLE
|
||||
? isolate()->factory()->mutable_heap_number_map()
|
||||
: isolate()->factory()->heap_number_map();
|
||||
|
||||
// Set the map.
|
||||
mov(FieldOperand(result, HeapObject::kMapOffset),
|
||||
Immediate(isolate()->factory()->heap_number_map()));
|
||||
mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(map));
|
||||
}
|
||||
|
||||
|
||||
|
@ -617,7 +617,8 @@ class MacroAssembler: public Assembler {
|
||||
void AllocateHeapNumber(Register result,
|
||||
Register scratch1,
|
||||
Register scratch2,
|
||||
Label* gc_required);
|
||||
Label* gc_required,
|
||||
MutableMode mode = IMMUTABLE);
|
||||
|
||||
// Allocate a sequential string. All the header fields of the string object
|
||||
// are initialized.
|
||||
|
@ -523,7 +523,7 @@ void StoreStubCompiler::GenerateStoreTransition(MacroAssembler* masm,
|
||||
}
|
||||
} else if (representation.IsDouble()) {
|
||||
Label do_store, heap_number;
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow);
|
||||
__ AllocateHeapNumber(storage_reg, scratch1, scratch2, slow, MUTABLE);
|
||||
|
||||
__ JumpIfNotSmi(value_reg, &heap_number);
|
||||
__ SmiUntag(value_reg);
|
||||
|
@ -1688,7 +1688,7 @@ TEST(GetHeapValueForNode) {
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
|
||||
|
||||
CompileRun("a = { s_prop: \'value\', n_prop: 0.1 };");
|
||||
CompileRun("a = { s_prop: \'value\', n_prop: \'value2\' };");
|
||||
const v8::HeapSnapshot* snapshot =
|
||||
heap_profiler->TakeHeapSnapshot(v8_str("value"));
|
||||
CHECK(ValidateSnapshot(snapshot));
|
||||
@ -1709,10 +1709,9 @@ TEST(GetHeapValueForNode) {
|
||||
CHECK(js_s_prop == heap_profiler->FindObjectById(s_prop->GetId()));
|
||||
const v8::HeapGraphNode* n_prop =
|
||||
GetProperty(obj, v8::HeapGraphEdge::kProperty, "n_prop");
|
||||
v8::Local<v8::Number> js_n_prop =
|
||||
js_obj->Get(v8_str("n_prop")).As<v8::Number>();
|
||||
CHECK(js_n_prop->NumberValue() ==
|
||||
heap_profiler->FindObjectById(n_prop->GetId())->NumberValue());
|
||||
v8::Local<v8::String> js_n_prop =
|
||||
js_obj->Get(v8_str("n_prop")).As<v8::String>();
|
||||
CHECK(js_n_prop == heap_profiler->FindObjectById(n_prop->GetId()));
|
||||
}
|
||||
|
||||
|
||||
|
311
test/mjsunit/migrations.js
Normal file
311
test/mjsunit/migrations.js
Normal file
@ -0,0 +1,311 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-ayle license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --track-fields --expose-gc
|
||||
|
||||
var global = Function('return this')();
|
||||
var verbose = 0;
|
||||
|
||||
function test(ctor_desc, use_desc, migr_desc) {
|
||||
var n = 5;
|
||||
var objects = [];
|
||||
var results = [];
|
||||
|
||||
if (verbose) {
|
||||
print();
|
||||
print("===========================================================");
|
||||
print("=== " + ctor_desc.name +
|
||||
" | " + use_desc.name + " |--> " + migr_desc.name);
|
||||
print("===========================================================");
|
||||
}
|
||||
|
||||
// Clean ICs and transitions.
|
||||
%NotifyContextDisposed();
|
||||
gc(); gc(); gc();
|
||||
|
||||
|
||||
// create objects
|
||||
if (verbose) {
|
||||
print("-----------------------------");
|
||||
print("--- construct");
|
||||
print();
|
||||
}
|
||||
for (var i = 0; i < n; i++) {
|
||||
objects[i] = ctor_desc.ctor.apply(ctor_desc, ctor_desc.args(i));
|
||||
}
|
||||
|
||||
try {
|
||||
// use them
|
||||
if (verbose) {
|
||||
print("-----------------------------");
|
||||
print("--- use 1");
|
||||
print();
|
||||
}
|
||||
var use = use_desc.use1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
if (i == 3) %OptimizeFunctionOnNextCall(use);
|
||||
results[i] = use(objects[i], i);
|
||||
}
|
||||
|
||||
// trigger migrations
|
||||
if (verbose) {
|
||||
print("-----------------------------");
|
||||
print("--- trigger migration");
|
||||
print();
|
||||
}
|
||||
var migr = migr_desc.migr;
|
||||
for (var i = 0; i < n; i++) {
|
||||
if (i == 3) %OptimizeFunctionOnNextCall(migr);
|
||||
migr(objects[i], i);
|
||||
}
|
||||
|
||||
// use again
|
||||
if (verbose) {
|
||||
print("-----------------------------");
|
||||
print("--- use 2");
|
||||
print();
|
||||
}
|
||||
var use = use_desc.use2 !== undefined ? use_desc.use2 : use_desc.use1;
|
||||
for (var i = 0; i < n; i++) {
|
||||
if (i == 3) %OptimizeFunctionOnNextCall(use);
|
||||
results[i] = use(objects[i], i);
|
||||
if (verbose >= 2) print(results[i]);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (verbose) print("--- incompatible use: " + e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
var ctors = [
|
||||
{
|
||||
name: "none-to-double",
|
||||
ctor: function(v) { return {a: v}; },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "double",
|
||||
ctor: function(v) { var o = {}; o.a = v; return o; },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "none-to-smi",
|
||||
ctor: function(v) { return {a: v}; },
|
||||
args: function(i) { return [i]; },
|
||||
},
|
||||
{
|
||||
name: "smi",
|
||||
ctor: function(v) { var o = {}; o.a = v; return o; },
|
||||
args: function(i) { return [i]; },
|
||||
},
|
||||
{
|
||||
name: "none-to-object",
|
||||
ctor: function(v) { return {a: v}; },
|
||||
args: function(i) { return ["s"]; },
|
||||
},
|
||||
{
|
||||
name: "object",
|
||||
ctor: function(v) { var o = {}; o.a = v; return o; },
|
||||
args: function(i) { return ["s"]; },
|
||||
},
|
||||
{
|
||||
name: "{a:, b:, c:}",
|
||||
ctor: function(v1, v2, v3) { return {a: v1, b: v2, c: v3}; },
|
||||
args: function(i) { return [1.5 + i, 1.6, 1.7]; },
|
||||
},
|
||||
{
|
||||
name: "{a..h:}",
|
||||
ctor: function(v) { var o = {}; o.h=o.g=o.f=o.e=o.d=o.c=o.b=o.a=v; return o; },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "1",
|
||||
ctor: function(v) { var o = 1; o.a = v; return o; },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "f()",
|
||||
ctor: function(v) { var o = function() { return v;}; o.a = v; return o; },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "f().bind",
|
||||
ctor: function(v) { var o = function(a,b,c) { return a+b+c; }; o = o.bind(o, v, v+1, v+2.2); return o; },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "dictionary elements",
|
||||
ctor: function(v) { var o = []; o[1] = v; o[200000] = v; return o; },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "json",
|
||||
ctor: function(v) { var json = '{"a":' + v + ',"b":' + v + '}'; return JSON.parse(json); },
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "fast accessors",
|
||||
accessor: {
|
||||
get: function() { return this.a_; },
|
||||
set: function(value) {this.a_ = value; },
|
||||
configurable: true,
|
||||
},
|
||||
ctor: function(v) {
|
||||
var o = {a_:v};
|
||||
Object.defineProperty(o, "a", this.accessor);
|
||||
return o;
|
||||
},
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "slow accessor",
|
||||
accessor1: { value: this.a_, configurable: true },
|
||||
accessor2: {
|
||||
get: function() { return this.a_; },
|
||||
set: function(value) {this.a_ = value; },
|
||||
configurable: true,
|
||||
},
|
||||
ctor: function(v) {
|
||||
var o = {a_:v};
|
||||
Object.defineProperty(o, "a", this.accessor1);
|
||||
Object.defineProperty(o, "a", this.accessor2);
|
||||
return o;
|
||||
},
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "slow",
|
||||
proto: {},
|
||||
ctor: function(v) {
|
||||
var o = {__proto__: this.proto};
|
||||
o.a = v;
|
||||
for (var i = 0; %HasFastProperties(o); i++) o["f"+i] = v;
|
||||
return o;
|
||||
},
|
||||
args: function(i) { return [1.5 + i]; },
|
||||
},
|
||||
{
|
||||
name: "global",
|
||||
ctor: function(v) { return global; },
|
||||
args: function(i) { return [i]; },
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
var uses = [
|
||||
{
|
||||
name: "o.a+1.0",
|
||||
use1: function(o, i) { return o.a + 1.0; },
|
||||
use2: function(o, i) { return o.a + 1.1; },
|
||||
},
|
||||
{
|
||||
name: "o.b+1.0",
|
||||
use1: function(o, i) { return o.b + 1.0; },
|
||||
use2: function(o, i) { return o.b + 1.1; },
|
||||
},
|
||||
{
|
||||
name: "o[1]+1.0",
|
||||
use1: function(o, i) { return o[1] + 1.0; },
|
||||
use2: function(o, i) { return o[1] + 1.1; },
|
||||
},
|
||||
{
|
||||
name: "o[-1]+1.0",
|
||||
use1: function(o, i) { return o[-1] + 1.0; },
|
||||
use2: function(o, i) { return o[-1] + 1.1; },
|
||||
},
|
||||
{
|
||||
name: "()",
|
||||
use1: function(o, i) { return o() + 1.0; },
|
||||
use2: function(o, i) { return o() + 1.1; },
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
var migrations = [
|
||||
{
|
||||
name: "to smi",
|
||||
migr: function(o, i) { if (i == 0) o.a = 1; },
|
||||
},
|
||||
{
|
||||
name: "to double",
|
||||
migr: function(o, i) { if (i == 0) o.a = 1.1; },
|
||||
},
|
||||
{
|
||||
name: "to object",
|
||||
migr: function(o, i) { if (i == 0) o.a = {}; },
|
||||
},
|
||||
{
|
||||
name: "set prototype {}",
|
||||
migr: function(o, i) { o.__proto__ = {}; },
|
||||
},
|
||||
{
|
||||
name: "%FunctionSetPrototype",
|
||||
migr: function(o, i) { %FunctionSetPrototype(o, null); },
|
||||
},
|
||||
{
|
||||
name: "modify prototype",
|
||||
migr: function(o, i) { if (i == 0) o.__proto__.__proto1__ = [,,,5,,,]; },
|
||||
},
|
||||
{
|
||||
name: "freeze prototype",
|
||||
migr: function(o, i) { if (i == 0) Object.freeze(o.__proto__); },
|
||||
},
|
||||
{
|
||||
name: "delete and re-add property",
|
||||
migr: function(o, i) { var v = o.a; delete o.a; o.a = v; },
|
||||
},
|
||||
{
|
||||
name: "modify prototype",
|
||||
migr: function(o, i) { if (i >= 0) o.__proto__ = {}; },
|
||||
},
|
||||
{
|
||||
name: "set property callback",
|
||||
migr: function(o, i) {
|
||||
Object.defineProperty(o, "a", {
|
||||
get: function() { return 1.5 + i; },
|
||||
set: function(value) {},
|
||||
configurable: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "observe",
|
||||
migr: function(o, i) { Object.observe(o, function(){}); },
|
||||
},
|
||||
{
|
||||
name: "%EnableAccessChecks",
|
||||
migr: function(o, i) {
|
||||
if (typeof (o) !== 'function') %EnableAccessChecks(o);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "%DisableAccessChecks",
|
||||
migr: function(o, i) {
|
||||
if ((typeof (o) !== 'function') && (o !== global)) %DisableAccessChecks(o);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "seal",
|
||||
migr: function(o, i) { Object.seal(o); },
|
||||
},
|
||||
{ // Must be the last in the sequence, because after the global object freeze
|
||||
// the other modifications does not make sence.
|
||||
name: "freeze",
|
||||
migr: function(o, i) { Object.freeze(o); },
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
migrations.forEach(function(migr) {
|
||||
uses.forEach(function(use) {
|
||||
ctors.forEach(function(ctor) {
|
||||
test(ctor, use, migr);
|
||||
});
|
||||
});
|
||||
});
|
@ -87,6 +87,7 @@
|
||||
##############################################################################
|
||||
# Skip long running tests that time out in debug mode.
|
||||
'generated-transition-stub': [PASS, ['mode == debug', SKIP]],
|
||||
'migrations': [PASS, ['mode == debug', SLOW]],
|
||||
|
||||
##############################################################################
|
||||
# This test sets the umask on a per-process basis and hence cannot be
|
||||
|
Loading…
Reference in New Issue
Block a user