Version 3.16.6
Made the Isolate parameter mandatory in Locker and Unlocker classes. (issue 2487)
Avoid pointer underflow in CopyCharsUnsigned. (issue 2493)
Generate shim headers when using system v8. (Chromium issue 165264)
Fixed arguments materialization for inlined apply(). (issue 2489)
Sync'ed laziness between BuildFunctionInfo and MakeFunctionInfo. (Chromium issue 147497)
Added sanity check to CodeFlusher::AddCandidate. (Chromium issue 169209)
Performance and stability improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@13439 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/SConscript b/src/SConscript
index 16bfb55..2fa8874 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -89,7 +89,6 @@
hydrogen.cc
ic.cc
incremental-marking.cc
- inspector.cc
interface.cc
interpreter-irregexp.cc
isolate.cc
@@ -97,7 +96,6 @@
lithium-allocator.cc
lithium.cc
liveedit.cc
- liveobjectlist.cc
log-utils.cc
log.cc
mark-compact.cc
diff --git a/src/api.cc b/src/api.cc
index 0563443..54b233c 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -668,13 +668,6 @@
}
-void V8::MarkIndependent(i::Isolate* isolate, i::Object** object) {
- ASSERT(isolate == i::Isolate::Current());
- LOG_API(isolate, "MarkIndependent");
- isolate->global_handles()->MarkIndependent(object);
-}
-
-
void V8::MarkPartiallyDependent(i::Object** object) {
i::Isolate* isolate = i::Isolate::Current();
LOG_API(isolate, "MarkPartiallyDependent");
@@ -682,13 +675,6 @@
}
-void V8::MarkPartiallyDependent(i::Isolate* isolate, i::Object** object) {
- ASSERT(isolate == i::Isolate::Current());
- LOG_API(isolate, "MarkPartiallyDependent");
- isolate->global_handles()->MarkPartiallyDependent(object);
-}
-
-
bool V8::IsGlobalIndependent(i::Object** obj) {
i::Isolate* isolate = i::Isolate::Current();
LOG_API(isolate, "IsGlobalIndependent");
@@ -697,14 +683,6 @@
}
-bool V8::IsGlobalIndependent(i::Isolate* isolate, i::Object** obj) {
- ASSERT(isolate == i::Isolate::Current());
- LOG_API(isolate, "IsGlobalIndependent");
- if (!isolate->IsInitialized()) return false;
- return i::GlobalHandles::IsIndependent(obj);
-}
-
-
bool V8::IsGlobalNearDeath(i::Object** obj) {
i::Isolate* isolate = i::Isolate::Current();
LOG_API(isolate, "IsGlobalNearDeath");
@@ -1862,8 +1840,7 @@
if (!raw_obj->IsJSObject()) return v8::Local<Value>();
i::HandleScope scope(isolate_);
i::Handle<i::JSObject> obj(i::JSObject::cast(raw_obj), isolate_);
- i::Handle<i::String> name =
- isolate_->factory()->LookupOneByteSymbol(STATIC_ASCII_VECTOR("stack"));
+ i::Handle<i::String> name = isolate_->factory()->stack_symbol();
if (!obj->HasProperty(*name)) return v8::Local<Value>();
i::Handle<i::Object> value = i::GetProperty(obj, name);
if (value.is_null()) return v8::Local<Value>();
@@ -3893,6 +3870,15 @@
}
+bool String::IsOneByte() const {
+ i::Handle<i::String> str = Utils::OpenHandle(this);
+ if (IsDeadCheck(str->GetIsolate(), "v8::String::IsOneByte()")) {
+ return false;
+ }
+ return str->IsOneByteConvertible();
+}
+
+
class Utf8LengthVisitor {
public:
explicit Utf8LengthVisitor()
@@ -4194,18 +4180,20 @@
}
-int String::Write(uint16_t* buffer,
- int start,
- int length,
- int options) const {
- i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
+template<typename CharType>
+static inline int WriteHelper(const String* string,
+ CharType* buffer,
+ int start,
+ int length,
+ int options) {
+ i::Isolate* isolate = Utils::OpenHandle(string)->GetIsolate();
if (IsDeadCheck(isolate, "v8::String::Write()")) return 0;
LOG_API(isolate, "String::Write");
ENTER_V8(isolate);
ASSERT(start >= 0 && length >= -1);
- i::Handle<i::String> str = Utils::OpenHandle(this);
+ i::Handle<i::String> str = Utils::OpenHandle(string);
isolate->string_tracker()->RecordWrite(str);
- if (options & HINT_MANY_WRITES_EXPECTED) {
+ if (options & String::HINT_MANY_WRITES_EXPECTED) {
// Flatten the string for efficiency. This applies whether we are
// using StringCharacterStream or Get(i) to access the characters.
FlattenString(str);
@@ -4215,7 +4203,7 @@
end = str->length();
if (end < 0) return 0;
i::String::WriteToFlat(*str, buffer, start, end);
- if (!(options & NO_NULL_TERMINATION) &&
+ if (!(options & String::NO_NULL_TERMINATION) &&
(length == -1 || end - start < length)) {
buffer[end - start] = '\0';
}
@@ -4223,6 +4211,22 @@
}
+int String::WriteOneByte(uint8_t* buffer,
+ int start,
+ int length,
+ int options) const {
+ return WriteHelper(this, buffer, start, length, options);
+}
+
+
+int String::Write(uint16_t* buffer,
+ int start,
+ int length,
+ int options) const {
+ return WriteHelper(this, buffer, start, length, options);
+}
+
+
bool v8::String::IsExternal() const {
i::Handle<i::String> str = Utils::OpenHandle(this);
if (IsDeadCheck(str->GetIsolate(), "v8::String::IsExternal()")) {
@@ -4407,14 +4411,6 @@
}
-void* Object::GetPointerFromInternalField(int index) {
- i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
- const char* location = "v8::Object::GetPointerFromInternalField()";
- if (!InternalFieldOK(obj, index, location)) return NULL;
- return ExternalValue(obj->GetInternalField(index));
-}
-
-
// --- E n v i r o n m e n t ---
@@ -4684,6 +4680,12 @@
}
+v8::Isolate* Context::GetIsolate() {
+ i::Handle<i::Context> env = Utils::OpenHandle(this);
+ return reinterpret_cast<Isolate*>(env->GetIsolate());
+}
+
+
v8::Local<v8::Context> Context::GetEntered() {
i::Isolate* isolate = i::Isolate::Current();
if (!EnsureInitializedForIsolate(isolate, "v8::Context::GetEntered()")) {
@@ -4803,16 +4805,6 @@
}
-void V8::SetWrapperClassId(i::Object** global_handle, uint16_t class_id) {
- i::GlobalHandles::SetWrapperClassId(global_handle, class_id);
-}
-
-
-uint16_t V8::GetWrapperClassId(internal::Object** global_handle) {
- return i::GlobalHandles::GetWrapperClassId(global_handle);
-}
-
-
Local<v8::Object> ObjectTemplate::NewInstance() {
i::Isolate* isolate = i::Isolate::Current();
ON_BAILOUT(isolate, "v8::ObjectTemplate::NewInstance()",
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index e5c1c3e..521231c 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -344,7 +344,7 @@
MacroAssembler* masm,
int length,
FastCloneShallowArrayStub::Mode mode,
- AllocationSiteInfoMode allocation_site_info_mode,
+ AllocationSiteMode allocation_site_mode,
Label* fail) {
// Registers on entry:
//
@@ -358,9 +358,10 @@
? FixedDoubleArray::SizeFor(length)
: FixedArray::SizeFor(length);
}
+
int size = JSArray::kSize;
int allocation_info_start = size;
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
size += AllocationSiteInfo::kSize;
}
size += elements_size;
@@ -373,7 +374,7 @@
}
__ AllocateInNewSpace(size, r0, r1, r2, fail, flags);
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
__ mov(r2, Operand(Handle<Map>(masm->isolate()->heap()->
allocation_site_info_map())));
__ str(r2, FieldMemOperand(r0, allocation_info_start));
@@ -392,7 +393,7 @@
// Get hold of the elements array of the boilerplate and setup the
// elements pointer in the resulting object.
__ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
__ add(r2, r0, Operand(JSArray::kSize + AllocationSiteInfo::kSize));
} else {
__ add(r2, r0, Operand(JSArray::kSize));
@@ -423,22 +424,14 @@
__ b(eq, &slow_case);
FastCloneShallowArrayStub::Mode mode = mode_;
- AllocationSiteInfoMode allocation_site_info_mode =
- DONT_TRACK_ALLOCATION_SITE_INFO;
- if (mode == CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO) {
- mode = CLONE_ANY_ELEMENTS;
- allocation_site_info_mode = TRACK_ALLOCATION_SITE_INFO;
- }
-
if (mode == CLONE_ANY_ELEMENTS) {
Label double_elements, check_fast_elements;
__ ldr(r0, FieldMemOperand(r3, JSArray::kElementsOffset));
__ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
__ CompareRoot(r0, Heap::kFixedCOWArrayMapRootIndex);
__ b(ne, &check_fast_elements);
- GenerateFastCloneShallowArrayCommon(masm, 0,
- COPY_ON_WRITE_ELEMENTS,
- allocation_site_info_mode,
+ GenerateFastCloneShallowArrayCommon(masm, 0, COPY_ON_WRITE_ELEMENTS,
+ allocation_site_mode_,
&slow_case);
// Return and remove the on-stack parameters.
__ add(sp, sp, Operand(3 * kPointerSize));
@@ -447,9 +440,8 @@
__ bind(&check_fast_elements);
__ CompareRoot(r0, Heap::kFixedArrayMapRootIndex);
__ b(ne, &double_elements);
- GenerateFastCloneShallowArrayCommon(masm, length_,
- CLONE_ELEMENTS,
- allocation_site_info_mode,
+ GenerateFastCloneShallowArrayCommon(masm, length_, CLONE_ELEMENTS,
+ allocation_site_mode_,
&slow_case);
// Return and remove the on-stack parameters.
__ add(sp, sp, Operand(3 * kPointerSize));
@@ -483,7 +475,8 @@
}
GenerateFastCloneShallowArrayCommon(masm, length_, mode,
- allocation_site_info_mode, &slow_case);
+ allocation_site_mode_,
+ &slow_case);
// Return and remove the on-stack parameters.
__ add(sp, sp, Operand(3 * kPointerSize));
@@ -6607,6 +6600,11 @@
__ tst(r4, Operand(kAsciiDataHintMask));
__ tst(r5, Operand(kAsciiDataHintMask), ne);
__ b(ne, &ascii_data);
+ __ eor(r4, r4, Operand(r5));
+ STATIC_ASSERT(kOneByteStringTag != 0 && kAsciiDataHintTag != 0);
+ __ and_(r4, r4, Operand(kOneByteStringTag | kAsciiDataHintTag));
+ __ cmp(r4, Operand(kOneByteStringTag | kAsciiDataHintTag));
+ __ b(eq, &ascii_data);
// Allocate a two byte cons string.
__ AllocateTwoByteConsString(r7, r6, r4, r5, &call_runtime);
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index 9d00823..2146a0e 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -144,7 +144,8 @@
#define __ ACCESS_MASM(masm)
void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
- MacroAssembler* masm) {
+ MacroAssembler* masm, AllocationSiteMode mode,
+ Label* allocation_site_info_found) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
@@ -153,6 +154,12 @@
// -- r3 : target map, scratch for subsequent call
// -- r4 : scratch (elements)
// -----------------------------------
+ if (mode == TRACK_ALLOCATION_SITE) {
+ ASSERT(allocation_site_info_found != NULL);
+ masm->TestJSArrayForAllocationSiteInfo(r2, r4,
+ allocation_site_info_found);
+ }
+
// Set transitioned map.
__ str(r3, FieldMemOperand(r2, HeapObject::kMapOffset));
__ RecordWriteField(r2,
@@ -167,7 +174,7 @@
void ElementsTransitionGenerator::GenerateSmiToDouble(
- MacroAssembler* masm, Label* fail) {
+ MacroAssembler* masm, AllocationSiteMode mode, Label* fail) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
@@ -179,7 +186,7 @@
Label loop, entry, convert_hole, gc_required, only_change_map, done;
bool vfp2_supported = CpuFeatures::IsSupported(VFP2);
- if (FLAG_track_allocation_sites) {
+ if (mode == TRACK_ALLOCATION_SITE) {
masm->TestJSArrayForAllocationSiteInfo(r2, r4, fail);
}
@@ -308,7 +315,7 @@
void ElementsTransitionGenerator::GenerateDoubleToObject(
- MacroAssembler* masm, Label* fail) {
+ MacroAssembler* masm, AllocationSiteMode mode, Label* fail) {
// ----------- S t a t e -------------
// -- r0 : value
// -- r1 : key
@@ -319,6 +326,10 @@
// -----------------------------------
Label entry, loop, convert_hole, gc_required, only_change_map;
+ if (mode == TRACK_ALLOCATION_SITE) {
+ masm->TestJSArrayForAllocationSiteInfo(r2, r4, fail);
+ }
+
// Check for empty arrays, which only require a map transition and no changes
// to the backing store.
__ ldr(r4, FieldMemOperand(r2, JSObject::kElementsOffset));
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 6a97201..cb83860 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -1719,7 +1719,9 @@
if (has_fast_elements && constant_elements_values->map() ==
isolate()->heap()->fixed_cow_array_map()) {
FastCloneShallowArrayStub stub(
- FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
+ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE,
+ length);
__ CallStub(&stub);
__ IncrementCounter(
isolate()->counters()->cow_arrays_created_stub(), 1, r1, r2);
@@ -1730,19 +1732,17 @@
} else {
ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
FLAG_smi_only_arrays);
- FastCloneShallowArrayStub::Mode mode = has_fast_elements
- ? FastCloneShallowArrayStub::CLONE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ AllocationSiteMode allocation_site_mode = FLAG_track_allocation_sites
+ ? TRACK_ALLOCATION_SITE : DONT_TRACK_ALLOCATION_SITE;
- // Tracking allocation info allows us to pre-transition later if it makes
- // sense.
- if (mode == FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS &&
- FLAG_track_allocation_sites) {
- mode = FastCloneShallowArrayStub::
- CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO;
+ if (has_fast_elements) {
+ mode = FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
}
- FastCloneShallowArrayStub stub(mode, length);
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
__ CallStub(&stub);
}
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 5e17694..0db63e7 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -1249,7 +1249,9 @@
// Must return the modified receiver in r0.
if (!FLAG_trace_elements_transitions) {
Label fail;
- ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, &fail);
__ mov(r0, r2);
__ Ret();
__ bind(&fail);
@@ -1270,7 +1272,9 @@
// Must return the modified receiver in r0.
if (!FLAG_trace_elements_transitions) {
Label fail;
- ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_DOUBLE_ELEMENTS,
+ FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, &fail);
__ mov(r0, r2);
__ Ret();
__ bind(&fail);
@@ -1406,7 +1410,9 @@
r4,
slow);
ASSERT(receiver_map.is(r3)); // Transition code expects map in r3
- ElementsTransitionGenerator::GenerateSmiToDouble(masm, slow);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow);
__ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
__ jmp(&fast_double_without_map_check);
@@ -1418,7 +1424,9 @@
r4,
slow);
ASSERT(receiver_map.is(r3)); // Transition code expects map in r3
- ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm);
+ mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode,
+ slow);
__ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
__ jmp(&finish_object_store);
@@ -1432,7 +1440,8 @@
r4,
slow);
ASSERT(receiver_map.is(r3)); // Transition code expects map in r3
- ElementsTransitionGenerator::GenerateDoubleToObject(masm, slow);
+ mode = AllocationSiteInfo::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow);
__ ldr(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
__ jmp(&finish_object_store);
}
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index f194720..2400bf0 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -682,6 +682,11 @@
}
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index 1b589ce..41639e7 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -93,6 +93,7 @@
V(Deoptimize) \
V(DivI) \
V(DoubleToI) \
+ V(DummyUse) \
V(ElementsKind) \
V(FastLiteral) \
V(FixedArrayBaseLength) \
@@ -403,6 +404,15 @@
};
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index c7fe06c..3c8e568 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -5431,6 +5431,8 @@
Handle<FixedArray> literals(instr->environment()->closure()->literals());
ElementsKind boilerplate_elements_kind =
instr->hydrogen()->boilerplate_elements_kind();
+ AllocationSiteMode allocation_site_mode =
+ instr->hydrogen()->allocation_site_mode();
// Deopt if the array literal boilerplate ElementsKind is of a type different
// than the expected one. The check isn't necessary if the boilerplate has
@@ -5462,7 +5464,7 @@
ASSERT(instr->hydrogen()->depth() == 1);
FastCloneShallowArrayStub::Mode mode =
FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS;
- FastCloneShallowArrayStub stub(mode, length);
+ FastCloneShallowArrayStub stub(mode, DONT_TRACK_ALLOCATION_SITE, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
} else if (instr->hydrogen()->depth() > 1) {
CallRuntime(Runtime::kCreateArrayLiteral, 3, instr);
@@ -5471,9 +5473,9 @@
} else {
FastCloneShallowArrayStub::Mode mode =
boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
- ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ELEMENTS;
- FastCloneShallowArrayStub stub(mode, length);
+ ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+ : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
}
@@ -5482,10 +5484,14 @@
void LCodeGen::EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
- int* offset) {
+ int* offset,
+ AllocationSiteMode mode) {
ASSERT(!source.is(r2));
ASSERT(!result.is(r2));
+ bool create_allocation_site_info = mode == TRACK_ALLOCATION_SITE &&
+ object->map()->CanTrackAllocationSite();
+
// Only elements backing stores for non-COW arrays need to be copied.
Handle<FixedArrayBase> elements(object->elements());
bool has_elements = elements->length() > 0 &&
@@ -5495,8 +5501,13 @@
// this object and its backing store.
int object_offset = *offset;
int object_size = object->map()->instance_size();
- int elements_offset = *offset + object_size;
int elements_size = has_elements ? elements->Size() : 0;
+ int elements_offset = *offset + object_size;
+ if (create_allocation_site_info) {
+ elements_offset += AllocationSiteInfo::kSize;
+ *offset += AllocationSiteInfo::kSize;
+ }
+
*offset += object_size + elements_size;
// Copy object header.
@@ -5521,7 +5532,8 @@
__ add(r2, result, Operand(*offset));
__ str(r2, FieldMemOperand(result, total_offset));
__ LoadHeapObject(source, value_object);
- EmitDeepCopy(value_object, result, source, offset);
+ EmitDeepCopy(value_object, result, source, offset,
+ DONT_TRACK_ALLOCATION_SITE);
} else if (value->IsHeapObject()) {
__ LoadHeapObject(r2, Handle<HeapObject>::cast(value));
__ str(r2, FieldMemOperand(result, total_offset));
@@ -5531,6 +5543,14 @@
}
}
+ // Build Allocation Site Info if desired
+ if (create_allocation_site_info) {
+ __ mov(r2, Operand(Handle<Map>(isolate()->heap()->
+ allocation_site_info_map())));
+ __ str(r2, FieldMemOperand(result, object_size));
+ __ str(source, FieldMemOperand(result, object_size + kPointerSize));
+ }
+
if (has_elements) {
// Copy elements backing store header.
__ LoadHeapObject(source, elements);
@@ -5566,7 +5586,8 @@
__ add(r2, result, Operand(*offset));
__ str(r2, FieldMemOperand(result, total_offset));
__ LoadHeapObject(source, value_object);
- EmitDeepCopy(value_object, result, source, offset);
+ EmitDeepCopy(value_object, result, source, offset,
+ DONT_TRACK_ALLOCATION_SITE);
} else if (value->IsHeapObject()) {
__ LoadHeapObject(r2, Handle<HeapObject>::cast(value));
__ str(r2, FieldMemOperand(result, total_offset));
@@ -5617,7 +5638,8 @@
__ bind(&allocated);
int offset = 0;
__ LoadHeapObject(r1, instr->hydrogen()->boilerplate());
- EmitDeepCopy(instr->hydrogen()->boilerplate(), r0, r1, &offset);
+ EmitDeepCopy(instr->hydrogen()->boilerplate(), r0, r1, &offset,
+ instr->hydrogen()->allocation_site_mode());
ASSERT_EQ(size, offset);
}
@@ -5892,6 +5914,11 @@
}
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
Register object = ToRegister(instr->object());
Register key = ToRegister(instr->key());
diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h
index f4d8269..8e83547 100644
--- a/src/arm/lithium-codegen-arm.h
+++ b/src/arm/lithium-codegen-arm.h
@@ -365,7 +365,8 @@
void EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
- int* offset);
+ int* offset,
+ AllocationSiteMode mode);
// Emit optimized code for integer division.
// Inputs are signed.
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index d7ea107..ffa25eb 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -3399,10 +3399,8 @@
Register scratch2,
Label* failure) {
int kFlatAsciiStringMask =
- kIsNotStringMask | kStringEncodingMask | kAsciiDataHintMask |
- kStringRepresentationMask;
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
int kFlatAsciiStringTag = ASCII_STRING_TYPE;
- ASSERT_EQ(ASCII_STRING_TYPE, ASCII_STRING_TYPE & kFlatAsciiStringMask);
and_(scratch1, first, Operand(kFlatAsciiStringMask));
and_(scratch2, second, Operand(kFlatAsciiStringMask));
cmp(scratch1, Operand(kFlatAsciiStringTag));
@@ -3416,10 +3414,8 @@
Register scratch,
Label* failure) {
int kFlatAsciiStringMask =
- kIsNotStringMask | kStringEncodingMask | kAsciiDataHintMask |
- kStringRepresentationMask;
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
int kFlatAsciiStringTag = ASCII_STRING_TYPE;
- ASSERT_EQ(ASCII_STRING_TYPE, ASCII_STRING_TYPE & kFlatAsciiStringMask);
and_(scratch, type, Operand(kFlatAsciiStringMask));
cmp(scratch, Operand(kFlatAsciiStringTag));
b(ne, failure);
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index dcc88f7..d1b51e4 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -1694,7 +1694,9 @@
&try_holey_map);
__ mov(r2, receiver);
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm());
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
__ jmp(&fast_object);
__ bind(&try_holey_map);
@@ -1705,7 +1707,9 @@
&call_builtin);
__ mov(r2, receiver);
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm());
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
__ bind(&fast_object);
} else {
__ CheckFastObjectElements(r3, r3, &call_builtin);
diff --git a/src/builtins.h b/src/builtins.h
index 3ca397e..9a02ad0 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -151,13 +151,13 @@
Code::kNoExtraICState) \
V(KeyedLoadIC_PreMonomorphic, KEYED_LOAD_IC, PREMONOMORPHIC, \
Code::kNoExtraICState) \
- V(KeyedLoadIC_Generic, KEYED_LOAD_IC, MEGAMORPHIC, \
+ V(KeyedLoadIC_Generic, KEYED_LOAD_IC, GENERIC, \
Code::kNoExtraICState) \
- V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, \
+ V(KeyedLoadIC_String, KEYED_LOAD_IC, GENERIC, \
Code::kNoExtraICState) \
- V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC, \
+ V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, GENERIC, \
Code::kNoExtraICState) \
- V(KeyedLoadIC_NonStrictArguments, KEYED_LOAD_IC, MEGAMORPHIC, \
+ V(KeyedLoadIC_NonStrictArguments, KEYED_LOAD_IC, GENERIC, \
Code::kNoExtraICState) \
\
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED, \
@@ -168,7 +168,7 @@
Code::kNoExtraICState) \
V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC, \
Code::kNoExtraICState) \
- V(StoreIC_GlobalProxy, STORE_IC, MEGAMORPHIC, \
+ V(StoreIC_GlobalProxy, STORE_IC, GENERIC, \
Code::kNoExtraICState) \
V(StoreIC_Initialize_Strict, STORE_IC, UNINITIALIZED, \
kStrictMode) \
@@ -178,21 +178,21 @@
kStrictMode) \
V(StoreIC_Megamorphic_Strict, STORE_IC, MEGAMORPHIC, \
kStrictMode) \
- V(StoreIC_GlobalProxy_Strict, STORE_IC, MEGAMORPHIC, \
+ V(StoreIC_GlobalProxy_Strict, STORE_IC, GENERIC, \
kStrictMode) \
V(StoreIC_Setter_ForDeopt, STORE_IC, MONOMORPHIC, \
kStrictMode) \
\
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, \
Code::kNoExtraICState) \
- V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC, \
+ V(KeyedStoreIC_Generic, KEYED_STORE_IC, GENERIC, \
Code::kNoExtraICState) \
\
V(KeyedStoreIC_Initialize_Strict, KEYED_STORE_IC, UNINITIALIZED, \
kStrictMode) \
- V(KeyedStoreIC_Generic_Strict, KEYED_STORE_IC, MEGAMORPHIC, \
+ V(KeyedStoreIC_Generic_Strict, KEYED_STORE_IC, GENERIC, \
kStrictMode) \
- V(KeyedStoreIC_NonStrictArguments, KEYED_STORE_IC, MEGAMORPHIC, \
+ V(KeyedStoreIC_NonStrictArguments, KEYED_STORE_IC, GENERIC, \
Code::kNoExtraICState) \
V(TransitionElementsSmiToDouble, BUILTIN, UNINITIALIZED, \
Code::kNoExtraICState) \
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index c7d4c80..7b1d1b4 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -570,15 +570,16 @@
void ElementsTransitionAndStoreStub::Generate(MacroAssembler* masm) {
Label fail;
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(from_, to_);
ASSERT(!IsFastHoleyElementsKind(from_) || IsFastHoleyElementsKind(to_));
if (!FLAG_trace_elements_transitions) {
if (IsFastSmiOrObjectElementsKind(to_)) {
if (IsFastSmiOrObjectElementsKind(from_)) {
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm);
+ GenerateMapChangeElementsTransition(masm, mode, &fail);
} else if (IsFastDoubleElementsKind(from_)) {
ASSERT(!IsFastSmiElementsKind(to_));
- ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, &fail);
} else {
UNREACHABLE();
}
@@ -588,14 +589,14 @@
grow_mode_);
} else if (IsFastSmiElementsKind(from_) &&
IsFastDoubleElementsKind(to_)) {
- ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, &fail);
KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
is_jsarray_,
grow_mode_);
} else if (IsFastDoubleElementsKind(from_)) {
ASSERT(to_ == FAST_HOLEY_DOUBLE_ELEMENTS);
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm);
+ GenerateMapChangeElementsTransition(masm, mode, &fail);
} else {
UNREACHABLE();
}
diff --git a/src/code-stubs.h b/src/code-stubs.h
index a02a959..8cba966 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -427,14 +427,16 @@
CLONE_DOUBLE_ELEMENTS,
COPY_ON_WRITE_ELEMENTS,
CLONE_ANY_ELEMENTS,
- CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO,
- LAST_CLONE_MODE = CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO
+ LAST_CLONE_MODE = CLONE_ANY_ELEMENTS
};
static const int kFastCloneModeCount = LAST_CLONE_MODE + 1;
- FastCloneShallowArrayStub(Mode mode, int length)
+ FastCloneShallowArrayStub(Mode mode,
+ AllocationSiteMode allocation_site_mode,
+ int length)
: mode_(mode),
+ allocation_site_mode_(allocation_site_mode),
length_((mode == COPY_ON_WRITE_ELEMENTS) ? 0 : length) {
ASSERT_GE(length_, 0);
ASSERT_LE(length_, kMaximumClonedLength);
@@ -444,12 +446,21 @@
private:
Mode mode_;
+ AllocationSiteMode allocation_site_mode_;
int length_;
+ class AllocationSiteModeBits: public BitField<AllocationSiteMode, 0, 1> {};
+ class ModeBits: public BitField<Mode, 1, 4> {};
+ class LengthBits: public BitField<int, 5, 4> {};
+ // Ensure data fits within available bits.
+ STATIC_ASSERT(LAST_ALLOCATION_SITE_MODE == 1);
+ STATIC_ASSERT(kFastCloneModeCount < 16);
+ STATIC_ASSERT(kMaximumClonedLength < 16);
Major MajorKey() { return FastCloneShallowArray; }
int MinorKey() {
- ASSERT(mode_ >= 0 && mode_ <= LAST_CLONE_MODE);
- return length_ * kFastCloneModeCount + mode_;
+ return AllocationSiteModeBits::encode(allocation_site_mode_)
+ | ModeBits::encode(mode_)
+ | LengthBits::encode(length_);
}
};
diff --git a/src/codegen.cc b/src/codegen.cc
index 2eebef6..b42645c 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -86,12 +86,12 @@
PrintF(" ***\n");
}
- if (print_source) {
+ if (!info->IsStub() && print_source) {
PrintF("--- Source from AST ---\n%s\n",
PrettyPrinter().PrintProgram(info->function()));
}
- if (print_ast) {
+ if (!info->IsStub() && print_ast) {
PrintF("--- AST ---\n%s\n",
AstPrinter().PrintProgram(info->function()));
}
diff --git a/src/codegen.h b/src/codegen.h
index 0ac68c2..09907c4 100644
--- a/src/codegen.h
+++ b/src/codegen.h
@@ -96,9 +96,17 @@
class ElementsTransitionGenerator : public AllStatic {
public:
- static void GenerateMapChangeElementsTransition(MacroAssembler* masm);
- static void GenerateSmiToDouble(MacroAssembler* masm, Label* fail);
- static void GenerateDoubleToObject(MacroAssembler* masm, Label* fail);
+ // If |mode| is set to DONT_TRACK_ALLOCATION_SITE,
+ // |allocation_site_info_found| may be NULL.
+ static void GenerateMapChangeElementsTransition(MacroAssembler* masm,
+ AllocationSiteMode mode,
+ Label* allocation_site_info_found);
+ static void GenerateSmiToDouble(MacroAssembler* masm,
+ AllocationSiteMode mode,
+ Label* fail);
+ static void GenerateDoubleToObject(MacroAssembler* masm,
+ AllocationSiteMode mode,
+ Label* fail);
private:
DISALLOW_COPY_AND_ASSIGN(ElementsTransitionGenerator);
diff --git a/src/compiler.cc b/src/compiler.cc
index daeca1e..84ea9c6 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -474,6 +474,13 @@
#endif
+static bool DebuggerWantsEagerCompilation(CompilationInfo* info,
+ bool allow_lazy_without_ctx = false) {
+ return LiveEditFunctionTracker::IsActive(info->isolate()) ||
+ (info->isolate()->DebuggerHasBreakPoints() && !allow_lazy_without_ctx);
+}
+
+
static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
Isolate* isolate = info->isolate();
ZoneScope zone_scope(info->zone(), DELETE_ON_EXIT);
@@ -511,8 +518,9 @@
// Only allow non-global compiles for eval.
ASSERT(info->is_eval() || info->is_global());
ParsingFlags flags = kNoParsingFlags;
- if (info->pre_parse_data() != NULL ||
- String::cast(script->source())->length() > FLAG_min_preparse_length) {
+ if ((info->pre_parse_data() != NULL ||
+ String::cast(script->source())->length() > FLAG_min_preparse_length) &&
+ !DebuggerWantsEagerCompilation(info)) {
flags = kAllowLazy;
}
if (!ParserApi::Parse(info, flags)) {
@@ -1006,8 +1014,7 @@
// Debug::FindSharedFunctionInfoInScript.
bool allow_lazy_without_ctx = literal->AllowsLazyCompilationWithoutContext();
bool allow_lazy = literal->AllowsLazyCompilation() &&
- !LiveEditFunctionTracker::IsActive(info.isolate()) &&
- (!info.isolate()->DebuggerHasBreakPoints() || allow_lazy_without_ctx);
+ !DebuggerWantsEagerCompilation(&info, allow_lazy_without_ctx);
Handle<ScopeInfo> scope_info(ScopeInfo::Empty());
diff --git a/src/d8-debug.cc b/src/d8-debug.cc
index de0faa8..f044328 100644
--- a/src/d8-debug.cc
+++ b/src/d8-debug.cc
@@ -273,7 +273,7 @@
void RemoteDebugger::HandleMessageReceived(char* message) {
- Locker lock;
+ Locker lock(v8::Isolate::GetCurrent());
HandleScope scope;
// Print the event details.
@@ -302,7 +302,7 @@
void RemoteDebugger::HandleKeyboardCommand(char* command) {
- Locker lock;
+ Locker lock(v8::Isolate::GetCurrent());
HandleScope scope;
// Convert the debugger command to a JSON debugger request.
diff --git a/src/d8.cc b/src/d8.cc
index b098628..407488f 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -886,7 +886,7 @@
Handle<Value> Shell::Yield(const Arguments& args) {
- v8::Unlocker unlocker;
+ v8::Unlocker unlocker(args.GetIsolate());
return Undefined();
}
@@ -1093,8 +1093,8 @@
}
-void Shell::InstallUtilityScript() {
- Locker lock;
+void Shell::InstallUtilityScript(Isolate* isolate) {
+ Locker lock(isolate);
HandleScope scope;
// If we use the utility context, we have to set the security tokens so that
// utility, evaluation and debug context can all access each other.
@@ -1235,12 +1235,6 @@
global_template->Set(String::New("Uint8ClampedArray"),
CreateArrayTemplate(Uint8ClampedArray), attr);
-#ifdef LIVE_OBJECT_LIST
- global_template->Set(String::New("lol_is_enabled"), True());
-#else
- global_template->Set(String::New("lol_is_enabled"), False());
-#endif
-
#if !defined(V8_SHARED) && !defined(_WIN32) && !defined(_WIN64)
Handle<ObjectTemplate> os_templ = ObjectTemplate::New();
AddOSMethods(os_templ);
@@ -1278,7 +1272,7 @@
void Shell::InitializeDebugger(Isolate* isolate) {
if (options.test_shell) return;
#ifndef V8_SHARED
- Locker lock;
+ Locker lock(isolate);
HandleScope scope;
Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
utility_context_ = Context::New(NULL, global_template);
@@ -1496,7 +1490,7 @@
void Shell::RunShell(Isolate* isolate) {
- Locker locker;
+ Locker locker(isolate);
Context::Scope context_scope(evaluation_context_);
HandleScope outer_scope;
Handle<String> name = String::New("(d8)");
@@ -1546,7 +1540,7 @@
}
// Prepare the context for this thread.
- Locker locker;
+ Locker locker(isolate_);
HandleScope outer_scope;
Persistent<Context> thread_context =
Shell::CreateEvaluationContext(isolate_);
@@ -1845,7 +1839,7 @@
}
#endif // V8_SHARED
{ // NOLINT
- Locker lock;
+ Locker lock(isolate);
HandleScope scope;
Persistent<Context> context = CreateEvaluationContext(isolate);
if (options.last_run) {
@@ -1855,7 +1849,7 @@
// If the interactive debugger is enabled make sure to activate
// it before running the files passed on the command line.
if (i::FLAG_debugger) {
- InstallUtilityScript();
+ InstallUtilityScript(isolate);
}
#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
}
@@ -1893,7 +1887,7 @@
}
if (threads.length() > 0 && options.use_preemption) {
- Locker lock;
+ Locker lock(isolate);
Locker::StopPreemption();
}
#endif // V8_SHARED
@@ -1940,7 +1934,7 @@
#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
// Run remote debugger if requested, but never on --test
if (i::FLAG_remote_debugger && !options.test_shell) {
- InstallUtilityScript();
+ InstallUtilityScript(isolate);
RunRemoteDebugger(i::FLAG_debugger_port);
return 0;
}
@@ -1953,7 +1947,7 @@
&& !options.test_shell ) {
#if !defined(V8_SHARED) && defined(ENABLE_DEBUGGER_SUPPORT)
if (!i::FLAG_debugger) {
- InstallUtilityScript();
+ InstallUtilityScript(isolate);
}
#endif // !V8_SHARED && ENABLE_DEBUGGER_SUPPORT
RunShell(isolate);
diff --git a/src/d8.h b/src/d8.h
index 07699e8..63efd66 100644
--- a/src/d8.h
+++ b/src/d8.h
@@ -381,7 +381,7 @@
static i::Mutex* context_mutex_;
static Counter* GetCounter(const char* name, bool is_histogram);
- static void InstallUtilityScript();
+ static void InstallUtilityScript(Isolate* isolate);
#endif // V8_SHARED
static void Initialize(Isolate* isolate);
static void InitializeDebugger(Isolate* isolate);
diff --git a/src/d8.js b/src/d8.js
index 819135a..3cb1819 100644
--- a/src/d8.js
+++ b/src/d8.js
@@ -123,10 +123,6 @@
var trace_compile = false; // Tracing all compile events?
var trace_debug_json = false; // Tracing all debug json packets?
var last_cmd = '';
-//var lol_is_enabled; // Set to true in d8.cc if LIVE_OBJECT_LIST is defined.
-var lol_next_dump_index = 0;
-var kDefaultLolLinesToPrintAtATime = 10;
-var kMaxLolLinesToPrintAtATime = 1000;
var repeat_cmd_line = '';
var is_running = true;
// Global variable used to store whether a handle was requested.
@@ -507,13 +503,6 @@
this.request_ = void 0;
break;
- case 'liveobjectlist':
- case 'lol':
- if (lol_is_enabled) {
- this.request_ = this.lolToJSONRequest_(args, is_repeating);
- break;
- }
-
default:
throw new Error('Unknown command "' + cmd + '"');
}
@@ -558,53 +547,10 @@
};
-// Note: we use detected command repetition as a signal for continuation here.
-DebugRequest.prototype.createLOLRequest = function(command,
- start_index,
- lines_to_dump,
- is_continuation) {
- if (is_continuation) {
- start_index = lol_next_dump_index;
- }
-
- if (lines_to_dump) {
- lines_to_dump = parseInt(lines_to_dump);
- } else {
- lines_to_dump = kDefaultLolLinesToPrintAtATime;
- }
- if (lines_to_dump > kMaxLolLinesToPrintAtATime) {
- lines_to_dump = kMaxLolLinesToPrintAtATime;
- }
-
- // Save the next start_index to dump from:
- lol_next_dump_index = start_index + lines_to_dump;
-
- var request = this.createRequest(command);
- request.arguments = {};
- request.arguments.start = start_index;
- request.arguments.count = lines_to_dump;
-
- return request;
-};
-
-
// Create a JSON request for the evaluation command.
DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
lookup_handle = null;
- if (lol_is_enabled) {
- // Check if the expression is a obj id in the form @<obj id>.
- var obj_id_match = expression.match(/^@([0-9]+)$/);
- if (obj_id_match) {
- var obj_id = parseInt(obj_id_match[1]);
- // Build a dump request.
- var request = this.createRequest('getobj');
- request.arguments = {};
- request.arguments.obj_id = obj_id;
- return request.toJSONProtocol();
- }
- }
-
// Check if the expression is a handle id in the form #<handle>#.
var handle_match = expression.match(/^#([0-9]*)#$/);
if (handle_match) {
@@ -1170,10 +1116,6 @@
// Build a evaluate request from the text command.
request = this.createRequest('frame');
last_cmd = 'info args';
- } else if (lol_is_enabled &&
- args && (args == 'liveobjectlist' || args == 'lol')) {
- // Build a evaluate request from the text command.
- return this.liveObjectListToJSONRequest_(null);
} else {
throw new Error('Invalid info arguments.');
}
@@ -1224,262 +1166,6 @@
};
-// Args: [v[erbose]] [<N>] [i[ndex] <i>] [t[ype] <type>] [sp[ace] <space>]
-DebugRequest.prototype.lolMakeListRequest =
- function(cmd, args, first_arg_index, is_repeating) {
-
- var request;
- var start_index = 0;
- var dump_limit = void 0;
- var type_filter = void 0;
- var space_filter = void 0;
- var prop_filter = void 0;
- var is_verbose = false;
- var i;
-
- for (i = first_arg_index; i < args.length; i++) {
- var arg = args[i];
- // Check for [v[erbose]]:
- if (arg === 'verbose' || arg === 'v') {
- // Nothing to do. This is already implied by args.length > 3.
- is_verbose = true;
-
- // Check for [<N>]:
- } else if (arg.match(/^[0-9]+$/)) {
- dump_limit = arg;
- is_verbose = true;
-
- // Check for i[ndex] <i>:
- } else if (arg === 'index' || arg === 'i') {
- i++;
- if (args.length < i) {
- throw new Error('Missing index after ' + arg + '.');
- }
- start_index = parseInt(args[i]);
- // The user input start index starts at 1:
- if (start_index <= 0) {
- throw new Error('Invalid index ' + args[i] + '.');
- }
- start_index -= 1;
- is_verbose = true;
-
- // Check for t[ype] <type>:
- } else if (arg === 'type' || arg === 't') {
- i++;
- if (args.length < i) {
- throw new Error('Missing type after ' + arg + '.');
- }
- type_filter = args[i];
-
- // Check for space <heap space name>:
- } else if (arg === 'space' || arg === 'sp') {
- i++;
- if (args.length < i) {
- throw new Error('Missing space name after ' + arg + '.');
- }
- space_filter = args[i];
-
- // Check for property <prop name>:
- } else if (arg === 'property' || arg === 'prop') {
- i++;
- if (args.length < i) {
- throw new Error('Missing property name after ' + arg + '.');
- }
- prop_filter = args[i];
-
- } else {
- throw new Error('Unknown args at ' + arg + '.');
- }
- }
-
- // Build the verbose request:
- if (is_verbose) {
- request = this.createLOLRequest('lol-'+cmd,
- start_index,
- dump_limit,
- is_repeating);
- request.arguments.verbose = true;
- } else {
- request = this.createRequest('lol-'+cmd);
- request.arguments = {};
- }
-
- request.arguments.filter = {};
- if (type_filter) {
- request.arguments.filter.type = type_filter;
- }
- if (space_filter) {
- request.arguments.filter.space = space_filter;
- }
- if (prop_filter) {
- request.arguments.filter.prop = prop_filter;
- }
-
- return request;
-};
-
-
-function extractObjId(args) {
- var id = args;
- id = id.match(/^@([0-9]+)$/);
- if (id) {
- id = id[1];
- } else {
- throw new Error('Invalid obj id ' + args + '.');
- }
- return parseInt(id);
-}
-
-
-DebugRequest.prototype.lolToJSONRequest_ = function(args, is_repeating) {
- var request;
- // Use default command if one is not specified:
- if (!args) {
- args = 'info';
- }
-
- var orig_args = args;
- var first_arg_index;
-
- var arg, i;
- var args = args.split(/\s+/g);
- var cmd = args[0];
- var id;
-
- // Command: <id> [v[erbose]] ...
- if (cmd.match(/^[0-9]+$/)) {
- // Convert to the padded list command:
- // Command: l[ist] <dummy> <id> [v[erbose]] ...
-
- // Insert the implicit 'list' in front and process as normal:
- cmd = 'list';
- args.unshift(cmd);
- }
-
- switch(cmd) {
- // Command: c[apture]
- case 'capture':
- case 'c':
- request = this.createRequest('lol-capture');
- break;
-
- // Command: clear|d[elete] <id>|all
- case 'clear':
- case 'delete':
- case 'del': {
- if (args.length < 2) {
- throw new Error('Missing argument after ' + cmd + '.');
- } else if (args.length > 2) {
- throw new Error('Too many arguments after ' + cmd + '.');
- }
- id = args[1];
- if (id.match(/^[0-9]+$/)) {
- // Delete a specific lol record:
- request = this.createRequest('lol-delete');
- request.arguments = {};
- request.arguments.id = parseInt(id);
- } else if (id === 'all') {
- // Delete all:
- request = this.createRequest('lol-reset');
- } else {
- throw new Error('Invalid argument after ' + cmd + '.');
- }
- break;
- }
-
- // Command: diff <id1> <id2> [<dump options>]
- case 'diff':
- first_arg_index = 3;
-
- // Command: list <dummy> <id> [<dump options>]
- case 'list':
-
- // Command: ret[ainers] <obj id> [<dump options>]
- case 'retainers':
- case 'ret':
- case 'retaining-paths':
- case 'rp': {
- if (cmd === 'ret') cmd = 'retainers';
- else if (cmd === 'rp') cmd = 'retaining-paths';
-
- if (!first_arg_index) first_arg_index = 2;
-
- if (args.length < first_arg_index) {
- throw new Error('Too few arguments after ' + cmd + '.');
- }
-
- var request_cmd = (cmd === 'list') ? 'diff':cmd;
- request = this.lolMakeListRequest(request_cmd,
- args,
- first_arg_index,
- is_repeating);
-
- if (cmd === 'diff') {
- request.arguments.id1 = parseInt(args[1]);
- request.arguments.id2 = parseInt(args[2]);
- } else if (cmd == 'list') {
- request.arguments.id1 = 0;
- request.arguments.id2 = parseInt(args[1]);
- } else {
- request.arguments.id = extractObjId(args[1]);
- }
- break;
- }
-
- // Command: getid
- case 'getid': {
- request = this.createRequest('lol-getid');
- request.arguments = {};
- request.arguments.address = args[1];
- break;
- }
-
- // Command: inf[o] [<N>]
- case 'info':
- case 'inf': {
- if (args.length > 2) {
- throw new Error('Too many arguments after ' + cmd + '.');
- }
- // Built the info request:
- request = this.createLOLRequest('lol-info', 0, args[1], is_repeating);
- break;
- }
-
- // Command: path <obj id 1> <obj id 2>
- case 'path': {
- request = this.createRequest('lol-path');
- request.arguments = {};
- if (args.length > 2) {
- request.arguments.id1 = extractObjId(args[1]);
- request.arguments.id2 = extractObjId(args[2]);
- } else {
- request.arguments.id1 = 0;
- request.arguments.id2 = extractObjId(args[1]);
- }
- break;
- }
-
- // Command: print
- case 'print': {
- request = this.createRequest('lol-print');
- request.arguments = {};
- request.arguments.id = extractObjId(args[1]);
- break;
- }
-
- // Command: reset
- case 'reset': {
- request = this.createRequest('lol-reset');
- break;
- }
-
- default:
- throw new Error('Invalid arguments.');
- }
- return request.toJSONProtocol();
-};
-
-
// Create a JSON request for the threads command.
DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) {
// Build a threads request from the text command.
@@ -1545,7 +1231,6 @@
print('inf[o] br[eak] - prints info about breakpoints in use');
print('inf[o] ar[gs] - prints info about arguments of the current function');
print('inf[o] lo[cals] - prints info about locals in the current function');
- print('inf[o] liveobjectlist|lol - same as \'lol info\'');
print('');
print('step [in | next | out| min [step count]]');
print('c[ontinue] - continue executing after a breakpoint');
@@ -1566,49 +1251,6 @@
print('');
print('gc - runs the garbage collector');
print('');
-
- if (lol_is_enabled) {
- print('liveobjectlist|lol <command> - live object list tracking.');
- print(' where <command> can be:');
- print(' c[apture] - captures a LOL list.');
- print(' clear|del[ete] <id>|all - clears LOL of id <id>.');
- print(' If \'all\' is unspecified instead, will clear all.');
- print(' diff <id1> <id2> [<dump options>]');
- print(' - prints the diff between LOLs id1 and id2.');
- print(' - also see <dump options> below.');
- print(' getid <address> - gets the obj id for the specified address if available.');
- print(' The address must be in hex form prefixed with 0x.');
- print(' inf[o] [<N>] - lists summary info of all LOL lists.');
- print(' If N is specified, will print N items at a time.');
- print(' [l[ist]] <id> [<dump options>]');
- print(' - prints the listing of objects in LOL id.');
- print(' - also see <dump options> below.');
- print(' reset - clears all LOL lists.');
- print(' ret[ainers] <id> [<dump options>]');
- print(' - prints the list of retainers of obj id.');
- print(' - also see <dump options> below.');
- print(' path <id1> <id2> - prints the retaining path from obj id1 to id2.');
- print(' If only one id is specified, will print the path from');
- print(' roots to the specified object if available.');
- print(' print <id> - prints the obj for the specified obj id if available.');
- print('');
- print(' <dump options> includes:');
- print(' [v[erbose]] - do verbose dump.');
- print(' [<N>] - dump N items at a time. Implies verbose dump.');
- print(' If unspecified, N will default to '+
- kDefaultLolLinesToPrintAtATime+'. Max N is '+
- kMaxLolLinesToPrintAtATime+'.');
- print(' [i[ndex] <i>] - start dump from index i. Implies verbose dump.');
- print(' [t[ype] <type>] - filter by type.');
- print(' [sp[ace] <space name>] - filter by heap space where <space name> is one of');
- print(' { cell, code, lo, map, new, old-data, old-pointer }.');
- print('');
- print(' If the verbose option, or an option that implies a verbose dump');
- print(' is specified, then a verbose dump will requested. Else, a summary dump');
- print(' will be requested.');
- print('');
- }
-
print('trace compile');
// hidden command: trace debug json - toggles tracing of debug json packets
print('');
@@ -1709,237 +1351,6 @@
}
-function decodeLolCaptureResponse(body) {
- var result;
- result = 'Captured live object list '+ body.id +
- ': count '+ body.count + ' size ' + body.size;
- return result;
-}
-
-
-function decodeLolDeleteResponse(body) {
- var result;
- result = 'Deleted live object list '+ body.id;
- return result;
-}
-
-
-function digitsIn(value) {
- var digits = 0;
- if (value === 0) value = 1;
- while (value >= 1) {
- digits++;
- value /= 10;
- }
- return digits;
-}
-
-
-function padding(value, max_digits) {
- var padding_digits = max_digits - digitsIn(value);
- var padding = '';
- while (padding_digits > 0) {
- padding += ' ';
- padding_digits--;
- }
- return padding;
-}
-
-
-function decodeLolInfoResponse(body) {
- var result;
- var lists = body.lists;
- var length = lists.length;
- var first_index = body.first_index + 1;
- var has_more = ((first_index + length) <= body.count);
- result = 'captured live object lists';
- if (has_more || (first_index != 1)) {
- result += ' ['+ length +' of '+ body.count +
- ': starting from '+ first_index +']';
- }
- result += ':\n';
- var max_digits = digitsIn(body.count);
- var last_count = 0;
- var last_size = 0;
- for (var i = 0; i < length; i++) {
- var entry = lists[i];
- var count = entry.count;
- var size = entry.size;
- var index = first_index + i;
- result += ' [' + padding(index, max_digits) + index + '] id '+ entry.id +
- ': count '+ count;
- if (last_count > 0) {
- result += '(+' + (count - last_count) + ')';
- }
- result += ' size '+ size;
- if (last_size > 0) {
- result += '(+' + (size - last_size) + ')';
- }
- result += '\n';
- last_count = count;
- last_size = size;
- }
- result += ' total: '+length+' lists\n';
- if (has_more) {
- result += ' -- press <enter> for more --\n';
- } else {
- repeat_cmd_line = '';
- }
- if (length === 0) result += ' none\n';
-
- return result;
-}
-
-
-function decodeLolListResponse(body, title) {
-
- var result;
- var total_count = body.count;
- var total_size = body.size;
- var length;
- var max_digits;
- var i;
- var entry;
- var index;
-
- var max_count_digits = digitsIn(total_count);
- var max_size_digits;
-
- var summary = body.summary;
- if (summary) {
-
- var roots_count = 0;
- var found_root = body.found_root || 0;
- var found_weak_root = body.found_weak_root || 0;
-
- // Print the summary result:
- result = 'summary of objects:\n';
- length = summary.length;
- if (found_root !== 0) {
- roots_count++;
- }
- if (found_weak_root !== 0) {
- roots_count++;
- }
- max_digits = digitsIn(length + roots_count);
- max_size_digits = digitsIn(total_size);
-
- index = 1;
- if (found_root !== 0) {
- result += ' [' + padding(index, max_digits) + index + '] ' +
- ' count '+ 1 + padding(0, max_count_digits) +
- ' '+ padding(0, max_size_digits+1) +
- ' : <root>\n';
- index++;
- }
- if (found_weak_root !== 0) {
- result += ' [' + padding(index, max_digits) + index + '] ' +
- ' count '+ 1 + padding(0, max_count_digits) +
- ' '+ padding(0, max_size_digits+1) +
- ' : <weak root>\n';
- index++;
- }
-
- for (i = 0; i < length; i++) {
- entry = summary[i];
- var count = entry.count;
- var size = entry.size;
- result += ' [' + padding(index, max_digits) + index + '] ' +
- ' count '+ count + padding(count, max_count_digits) +
- ' size '+ size + padding(size, max_size_digits) +
- ' : <' + entry.desc + '>\n';
- index++;
- }
- result += '\n total count: '+(total_count+roots_count)+'\n';
- if (body.size) {
- result += ' total size: '+body.size+'\n';
- }
-
- } else {
- // Print the full dump result:
- var first_index = body.first_index + 1;
- var elements = body.elements;
- length = elements.length;
- var has_more = ((first_index + length) <= total_count);
- result = title;
- if (has_more || (first_index != 1)) {
- result += ' ['+ length +' of '+ total_count +
- ': starting from '+ first_index +']';
- }
- result += ':\n';
- if (length === 0) result += ' none\n';
- max_digits = digitsIn(length);
-
- var max_id = 0;
- var max_size = 0;
- for (i = 0; i < length; i++) {
- entry = elements[i];
- if (entry.id > max_id) max_id = entry.id;
- if (entry.size > max_size) max_size = entry.size;
- }
- var max_id_digits = digitsIn(max_id);
- max_size_digits = digitsIn(max_size);
-
- for (i = 0; i < length; i++) {
- entry = elements[i];
- index = first_index + i;
- result += ' ['+ padding(index, max_digits) + index +']';
- if (entry.id !== 0) {
- result += ' @' + entry.id + padding(entry.id, max_id_digits) +
- ': size ' + entry.size + ', ' +
- padding(entry.size, max_size_digits) + entry.desc + '\n';
- } else {
- // Must be a root or weak root:
- result += ' ' + entry.desc + '\n';
- }
- }
- if (has_more) {
- result += ' -- press <enter> for more --\n';
- } else {
- repeat_cmd_line = '';
- }
- if (length === 0) result += ' none\n';
- }
-
- return result;
-}
-
-
-function decodeLolDiffResponse(body) {
- var title = 'objects';
- return decodeLolListResponse(body, title);
-}
-
-
-function decodeLolRetainersResponse(body) {
- var title = 'retainers for @' + body.id;
- return decodeLolListResponse(body, title);
-}
-
-
-function decodeLolPathResponse(body) {
- return body.path;
-}
-
-
-function decodeLolResetResponse(body) {
- return 'Reset all live object lists.';
-}
-
-
-function decodeLolGetIdResponse(body) {
- if (body.id == 0) {
- return 'Address is invalid, or object has been moved or collected';
- }
- return 'obj id is @' + body.id;
-}
-
-
-function decodeLolPrintResponse(body) {
- return body.dump;
-}
-
-
// Rounds number 'num' to 'length' decimal places.
function roundNumber(num, length) {
var factor = Math.pow(10, length);
@@ -2276,34 +1687,6 @@
}
break;
- case 'lol-capture':
- details.text = decodeLolCaptureResponse(body);
- break;
- case 'lol-delete':
- details.text = decodeLolDeleteResponse(body);
- break;
- case 'lol-diff':
- details.text = decodeLolDiffResponse(body);
- break;
- case 'lol-getid':
- details.text = decodeLolGetIdResponse(body);
- break;
- case 'lol-info':
- details.text = decodeLolInfoResponse(body);
- break;
- case 'lol-print':
- details.text = decodeLolPrintResponse(body);
- break;
- case 'lol-reset':
- details.text = decodeLolResetResponse(body);
- break;
- case 'lol-retainers':
- details.text = decodeLolRetainersResponse(body);
- break;
- case 'lol-path':
- details.text = decodeLolPathResponse(body);
- break;
-
default:
details.text =
'Response for unknown command \'' + response.command() + '\'' +
diff --git a/src/debug-debugger.js b/src/debug-debugger.js
index eef12a9..7787312 100644
--- a/src/debug-debugger.js
+++ b/src/debug-debugger.js
@@ -110,7 +110,6 @@
}
},
};
-var lol_is_enabled = %HasLOLEnabled();
// Create a new break point object and add it to the list of break points.
@@ -1437,8 +1436,6 @@
this.setVariableValueRequest_(request, response);
} else if (request.command == 'evaluate') {
this.evaluateRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'getobj') {
- this.getobjRequest_(request, response);
} else if (request.command == 'lookup') {
this.lookupRequest_(request, response);
} else if (request.command == 'references') {
@@ -1468,28 +1465,6 @@
} else if (request.command == 'gc') {
this.gcRequest_(request, response);
- // LiveObjectList tools:
- } else if (lol_is_enabled && request.command == 'lol-capture') {
- this.lolCaptureRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-delete') {
- this.lolDeleteRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-diff') {
- this.lolDiffRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-getid') {
- this.lolGetIdRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-info') {
- this.lolInfoRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-reset') {
- this.lolResetRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-retainers') {
- this.lolRetainersRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-path') {
- this.lolPathRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-print') {
- this.lolPrintRequest_(request, response);
- } else if (lol_is_enabled && request.command == 'lol-stats') {
- this.lolStatsRequest_(request, response);
-
} else {
throw new Error('Unknown command "' + request.command + '" in request');
}
@@ -2191,24 +2166,6 @@
};
-DebugCommandProcessor.prototype.getobjRequest_ = function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
-
- // Pull out arguments.
- var obj_id = request.arguments.obj_id;
-
- // Check for legal arguments.
- if (IS_UNDEFINED(obj_id)) {
- return response.failed('Argument "obj_id" missing');
- }
-
- // Dump the object.
- response.body = MakeMirror(%GetLOLObj(obj_id));
-};
-
-
DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
if (!request.arguments) {
return response.failed('Missing arguments');
@@ -2567,86 +2524,6 @@
};
-DebugCommandProcessor.prototype.lolCaptureRequest_ =
- function(request, response) {
- response.body = %CaptureLOL();
-};
-
-
-DebugCommandProcessor.prototype.lolDeleteRequest_ =
- function(request, response) {
- var id = request.arguments.id;
- var result = %DeleteLOL(id);
- if (result) {
- response.body = { id: id };
- } else {
- response.failed('Failed to delete: live object list ' + id + ' not found.');
- }
-};
-
-
-DebugCommandProcessor.prototype.lolDiffRequest_ = function(request, response) {
- var id1 = request.arguments.id1;
- var id2 = request.arguments.id2;
- var verbose = request.arguments.verbose;
- var filter = request.arguments.filter;
- if (verbose === true) {
- var start = request.arguments.start;
- var count = request.arguments.count;
- response.body = %DumpLOL(id1, id2, start, count, filter);
- } else {
- response.body = %SummarizeLOL(id1, id2, filter);
- }
-};
-
-
-DebugCommandProcessor.prototype.lolGetIdRequest_ = function(request, response) {
- var address = request.arguments.address;
- response.body = {};
- response.body.id = %GetLOLObjId(address);
-};
-
-
-DebugCommandProcessor.prototype.lolInfoRequest_ = function(request, response) {
- var start = request.arguments.start;
- var count = request.arguments.count;
- response.body = %InfoLOL(start, count);
-};
-
-
-DebugCommandProcessor.prototype.lolResetRequest_ = function(request, response) {
- %ResetLOL();
-};
-
-
-DebugCommandProcessor.prototype.lolRetainersRequest_ =
- function(request, response) {
- var id = request.arguments.id;
- var verbose = request.arguments.verbose;
- var start = request.arguments.start;
- var count = request.arguments.count;
- var filter = request.arguments.filter;
-
- response.body = %GetLOLObjRetainers(id, Mirror.prototype, verbose,
- start, count, filter);
-};
-
-
-DebugCommandProcessor.prototype.lolPathRequest_ = function(request, response) {
- var id1 = request.arguments.id1;
- var id2 = request.arguments.id2;
- response.body = {};
- response.body.path = %GetLOLPath(id1, id2, Mirror.prototype);
-};
-
-
-DebugCommandProcessor.prototype.lolPrintRequest_ = function(request, response) {
- var id = request.arguments.id;
- response.body = {};
- response.body.dump = %PrintLOLObj(id);
-};
-
-
// Check whether the previously processed command caused the VM to become
// running.
DebugCommandProcessor.prototype.isRunning = function() {
diff --git a/src/debug.cc b/src/debug.cc
index 1524349..7baed88 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -3767,6 +3767,7 @@
void MessageDispatchHelperThread::Run() {
+ Isolate* isolate = Isolate::Current();
while (true) {
sem_->Wait();
{
@@ -3774,8 +3775,8 @@
already_signalled_ = false;
}
{
- Locker locker;
- Isolate::Current()->debugger()->CallMessageDispatchHandler();
+ Locker locker(reinterpret_cast<v8::Isolate*>(isolate));
+ isolate->debugger()->CallMessageDispatchHandler();
}
}
}
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index a7a2573..5bf7df3 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -194,6 +194,8 @@
DEFINE_bool(trace_range, false, "trace range analysis")
DEFINE_bool(trace_gvn, false, "trace global value numbering")
DEFINE_bool(trace_representation, false, "trace representation types")
+DEFINE_bool(trace_track_allocation_sites, false,
+ "trace the tracking of allocation sites")
DEFINE_bool(stress_pointer_maps, false, "pointer map for every instruction")
DEFINE_bool(stress_environments, false, "environment for every instruction")
DEFINE_int(deopt_every_n_times,
@@ -419,12 +421,6 @@
// ic.cc
DEFINE_bool(use_ic, true, "use inline caching")
-#ifdef LIVE_OBJECT_LIST
-// liveobjectlist.cc
-DEFINE_string(lol_workdir, NULL, "path for lol temp files")
-DEFINE_bool(verify_lol, false, "perform debugging verification for lol")
-#endif
-
// macro-assembler-ia32.cc
DEFINE_bool(native_code_counters, false,
"generate extra code for manipulating stats counters")
diff --git a/src/global-handles.cc b/src/global-handles.cc
index 392a181..94ccc28 100644
--- a/src/global-handles.cc
+++ b/src/global-handles.cc
@@ -46,7 +46,7 @@
// State transition diagram:
// FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE }
enum State {
- FREE,
+ FREE = 0,
NORMAL, // Normal global handle.
WEAK, // Flagged as weak but not yet finalized.
PENDING, // Has been recognized as only reachable by weak handles.
@@ -59,7 +59,14 @@
return reinterpret_cast<Node*>(location);
}
- Node() {}
+ Node() {
+ ASSERT(OFFSET_OF(Node, flags_) == Internals::kNodeFlagsOffset);
+ ASSERT(OFFSET_OF(Node, class_id_) == Internals::kNodeClassIdOffset);
+ ASSERT(static_cast<int>(IsIndependent::kShift) ==
+ Internals::kNodeIsIndependentShift);
+ ASSERT(static_cast<int>(IsPartiallyDependent::kShift) ==
+ Internals::kNodeIsPartiallyDependentShift);
+ }
#ifdef DEBUG
~Node() {
@@ -68,9 +75,9 @@
object_ = NULL;
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
index_ = 0;
- independent_ = false;
- partially_dependent_ = false;
- in_new_space_list_ = false;
+ set_independent(false);
+ set_partially_dependent(false);
+ set_in_new_space_list(false);
parameter_or_next_free_.next_free = NULL;
callback_ = NULL;
}
@@ -79,33 +86,33 @@
void Initialize(int index, Node** first_free) {
index_ = static_cast<uint8_t>(index);
ASSERT(static_cast<int>(index_) == index);
- state_ = FREE;
- in_new_space_list_ = false;
+ set_state(FREE);
+ set_in_new_space_list(false);
parameter_or_next_free_.next_free = *first_free;
*first_free = this;
}
void Acquire(Object* object, GlobalHandles* global_handles) {
- ASSERT(state_ == FREE);
+ ASSERT(state() == FREE);
object_ = object;
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
- independent_ = false;
- partially_dependent_ = false;
- state_ = NORMAL;
+ set_independent(false);
+ set_partially_dependent(false);
+ set_state(NORMAL);
parameter_or_next_free_.parameter = NULL;
callback_ = NULL;
IncreaseBlockUses(global_handles);
}
void Release(GlobalHandles* global_handles) {
- ASSERT(state_ != FREE);
+ ASSERT(state() != FREE);
if (IsWeakRetainer()) {
global_handles->number_of_weak_handles_--;
if (object_->IsJSGlobalObject()) {
global_handles->number_of_global_object_weak_handles_--;
}
}
- state_ = FREE;
+ set_state(FREE);
parameter_or_next_free_.next_free = global_handles->first_free_;
global_handles->first_free_ = this;
DecreaseBlockUses(global_handles);
@@ -120,115 +127,133 @@
bool has_wrapper_class_id() const {
return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId;
}
+
uint16_t wrapper_class_id() const { return class_id_; }
- void set_wrapper_class_id(uint16_t class_id) {
- class_id_ = class_id;
+
+ // State and flag accessors.
+
+ State state() const {
+ return NodeState::decode(flags_);
+ }
+ void set_state(State state) {
+ flags_ = NodeState::update(flags_, state);
}
- // State accessors.
+ bool is_independent() {
+ return IsIndependent::decode(flags_);
+ }
+ void set_independent(bool v) {
+ flags_ = IsIndependent::update(flags_, v);
+ }
- State state() const { return state_; }
+ bool is_partially_dependent() {
+ return IsPartiallyDependent::decode(flags_);
+ }
+ void set_partially_dependent(bool v) {
+ flags_ = IsPartiallyDependent::update(flags_, v);
+ }
+
+ bool is_in_new_space_list() {
+ return IsInNewSpaceList::decode(flags_);
+ }
+ void set_in_new_space_list(bool v) {
+ flags_ = IsInNewSpaceList::update(flags_, v);
+ }
bool IsNearDeath() const {
// Check for PENDING to ensure correct answer when processing callbacks.
- return state_ == PENDING || state_ == NEAR_DEATH;
+ return state() == PENDING || state() == NEAR_DEATH;
}
- bool IsWeak() const { return state_ == WEAK; }
+ bool IsWeak() const { return state() == WEAK; }
- bool IsRetainer() const { return state_ != FREE; }
+ bool IsRetainer() const { return state() != FREE; }
- bool IsStrongRetainer() const { return state_ == NORMAL; }
+ bool IsStrongRetainer() const { return state() == NORMAL; }
bool IsWeakRetainer() const {
- return state_ == WEAK || state_ == PENDING || state_ == NEAR_DEATH;
+ return state() == WEAK || state() == PENDING || state() == NEAR_DEATH;
}
void MarkPending() {
- ASSERT(state_ == WEAK);
- state_ = PENDING;
+ ASSERT(state() == WEAK);
+ set_state(PENDING);
}
// Independent flag accessors.
void MarkIndependent() {
- ASSERT(state_ != FREE);
- independent_ = true;
+ ASSERT(state() != FREE);
+ set_independent(true);
}
- bool is_independent() const { return independent_; }
void MarkPartiallyDependent(GlobalHandles* global_handles) {
- ASSERT(state_ != FREE);
+ ASSERT(state() != FREE);
if (global_handles->isolate()->heap()->InNewSpace(object_)) {
- partially_dependent_ = true;
+ set_partially_dependent(true);
}
}
- bool is_partially_dependent() const { return partially_dependent_; }
- void clear_partially_dependent() { partially_dependent_ = false; }
-
- // In-new-space-list flag accessors.
- void set_in_new_space_list(bool v) { in_new_space_list_ = v; }
- bool is_in_new_space_list() const { return in_new_space_list_; }
+ void clear_partially_dependent() { set_partially_dependent(false); }
// Callback accessor.
WeakReferenceCallback callback() { return callback_; }
// Callback parameter accessors.
void set_parameter(void* parameter) {
- ASSERT(state_ != FREE);
+ ASSERT(state() != FREE);
parameter_or_next_free_.parameter = parameter;
}
void* parameter() const {
- ASSERT(state_ != FREE);
+ ASSERT(state() != FREE);
return parameter_or_next_free_.parameter;
}
// Accessors for next free node in the free list.
Node* next_free() {
- ASSERT(state_ == FREE);
+ ASSERT(state() == FREE);
return parameter_or_next_free_.next_free;
}
void set_next_free(Node* value) {
- ASSERT(state_ == FREE);
+ ASSERT(state() == FREE);
parameter_or_next_free_.next_free = value;
}
void MakeWeak(GlobalHandles* global_handles,
void* parameter,
WeakReferenceCallback callback) {
- ASSERT(state_ != FREE);
+ ASSERT(state() != FREE);
if (!IsWeakRetainer()) {
global_handles->number_of_weak_handles_++;
if (object_->IsJSGlobalObject()) {
global_handles->number_of_global_object_weak_handles_++;
}
}
- state_ = WEAK;
+ set_state(WEAK);
set_parameter(parameter);
callback_ = callback;
}
void ClearWeakness(GlobalHandles* global_handles) {
- ASSERT(state_ != FREE);
+ ASSERT(state() != FREE);
if (IsWeakRetainer()) {
global_handles->number_of_weak_handles_--;
if (object_->IsJSGlobalObject()) {
global_handles->number_of_global_object_weak_handles_--;
}
}
- state_ = NORMAL;
+ set_state(NORMAL);
set_parameter(NULL);
}
bool PostGarbageCollectionProcessing(Isolate* isolate,
GlobalHandles* global_handles) {
- if (state_ != Node::PENDING) return false;
+ if (state() != Node::PENDING) return false;
WeakReferenceCallback func = callback();
if (func == NULL) {
Release(global_handles);
return false;
}
void* par = parameter();
- state_ = NEAR_DEATH;
+ set_state(NEAR_DEATH);
set_parameter(NULL);
v8::Persistent<v8::Object> object = ToApi<v8::Object>(handle());
@@ -245,7 +270,7 @@
}
// Absence of explicit cleanup or revival of weak handle
// in most of the cases would lead to memory leak.
- ASSERT(state_ != NEAR_DEATH);
+ ASSERT(state() != NEAR_DEATH);
return true;
}
@@ -267,12 +292,14 @@
// Index in the containing handle block.
uint8_t index_;
- // Need one more bit for MSVC as it treats enums as signed.
- State state_ : 4;
+ // This stores three flags (independent, partially_dependent and
+ // in_new_space_list) and a State.
+ class NodeState: public BitField<State, 0, 4> {};
+ class IsIndependent: public BitField<bool, 4, 1> {};
+ class IsPartiallyDependent: public BitField<bool, 5, 1> {};
+ class IsInNewSpaceList: public BitField<bool, 6, 1> {};
- bool independent_ : 1;
- bool partially_dependent_ : 1;
- bool in_new_space_list_ : 1;
+ uint8_t flags_;
// Handle specific callback.
WeakReferenceCallback callback_;
@@ -480,14 +507,6 @@
}
-void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
- Node::FromLocation(location)->set_wrapper_class_id(class_id);
-}
-
-uint16_t GlobalHandles::GetWrapperClassId(Object** location) {
- return Node::FromLocation(location)->wrapper_class_id();
-}
-
void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
for (NodeIterator it(this); !it.done(); it.Advance()) {
if (it.node()->IsWeakRetainer()) v->VisitPointer(it.node()->location());
diff --git a/src/global-handles.h b/src/global-handles.h
index 7808d16..a28a96f 100644
--- a/src/global-handles.h
+++ b/src/global-handles.h
@@ -130,9 +130,6 @@
void* parameter,
WeakReferenceCallback callback);
- static void SetWrapperClassId(Object** location, uint16_t class_id);
- static uint16_t GetWrapperClassId(Object** location);
-
// Returns the current number of weak handles.
int NumberOfWeakHandles() { return number_of_weak_handles_; }
diff --git a/src/handles.cc b/src/handles.cc
index ed92c41..c0dea20 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -375,6 +375,15 @@
Handle<JSFunction> constructor = isolate->script_function();
Handle<JSValue> result =
Handle<JSValue>::cast(isolate->factory()->NewJSObject(constructor));
+
+ // The allocation might have triggered a GC, which could have called this
+ // function recursively, and a wrapper has already been created and cached.
+ // In that case, simply return the cached wrapper.
+ if (script->wrapper()->foreign_address() != NULL) {
+ return Handle<JSValue>(
+ reinterpret_cast<JSValue**>(script->wrapper()->foreign_address()));
+ }
+
result->set_value(*script);
// Create a new weak global handle and use it to cache the wrapper
diff --git a/src/heap-inl.h b/src/heap-inl.h
index 6f4b69c..b8fb1e8 100644
--- a/src/heap-inl.h
+++ b/src/heap-inl.h
@@ -633,20 +633,24 @@
// TODO(yangguo): check that the object is indeed an external string.
ASSERT(heap_->InNewSpace(obj));
ASSERT(obj != HEAP->the_hole_value());
+#ifndef ENABLE_LATIN_1
if (obj->IsExternalAsciiString()) {
ExternalAsciiString* string = ExternalAsciiString::cast(obj);
ASSERT(String::IsAscii(string->GetChars(), string->length()));
}
+#endif
}
for (int i = 0; i < old_space_strings_.length(); ++i) {
Object* obj = Object::cast(old_space_strings_[i]);
// TODO(yangguo): check that the object is indeed an external string.
ASSERT(!heap_->InNewSpace(obj));
ASSERT(obj != HEAP->the_hole_value());
+#ifndef ENABLE_LATIN_1
if (obj->IsExternalAsciiString()) {
ExternalAsciiString* string = ExternalAsciiString::cast(obj);
ASSERT(String::IsAscii(string->GetChars(), string->length()));
}
+#endif
}
#endif
}
@@ -669,6 +673,19 @@
}
+void ErrorObjectList::Add(JSObject* object) {
+ list_.Add(object);
+}
+
+
+void ErrorObjectList::Iterate(ObjectVisitor* v) {
+ if (!list_.is_empty()) {
+ Object** start = &list_[0];
+ v->VisitPointers(start, start + list_.length());
+ }
+}
+
+
void Heap::ClearInstanceofCache() {
set_instanceof_cache_function(the_hole_value());
}
diff --git a/src/heap.cc b/src/heap.cc
index 013a18c..5edaa35 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -37,7 +37,6 @@
#include "global-handles.h"
#include "heap-profiler.h"
#include "incremental-marking.h"
-#include "liveobjectlist-inl.h"
#include "mark-compact.h"
#include "natives.h"
#include "objects-visiting.h"
@@ -439,7 +438,6 @@
ReportStatisticsBeforeGC();
#endif // DEBUG
- LiveObjectList::GCPrologue();
store_buffer()->GCPrologue();
}
@@ -466,7 +464,6 @@
void Heap::GarbageCollectionEpilogue() {
store_buffer()->GCEpilogue();
- LiveObjectList::GCEpilogue();
// In release mode, we only zap the from space under heap verification.
if (Heap::ShouldZapGarbage()) {
@@ -550,6 +547,8 @@
#ifdef ENABLE_DEBUGGER_SUPPORT
isolate_->debug()->AfterGarbageCollection();
#endif // ENABLE_DEBUGGER_SUPPORT
+
+ error_object_list_.DeferredFormatStackTrace(isolate());
}
@@ -886,15 +885,20 @@
if (collector == MARK_COMPACTOR && global_gc_prologue_callback_) {
ASSERT(!allocation_allowed_);
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+ VMState state(isolate_, EXTERNAL);
global_gc_prologue_callback_();
}
GCType gc_type =
collector == MARK_COMPACTOR ? kGCTypeMarkSweepCompact : kGCTypeScavenge;
- for (int i = 0; i < gc_prologue_callbacks_.length(); ++i) {
- if (gc_type & gc_prologue_callbacks_[i].gc_type) {
- gc_prologue_callbacks_[i].callback(gc_type, kNoGCCallbackFlags);
+ {
+ GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+ VMState state(isolate_, EXTERNAL);
+ for (int i = 0; i < gc_prologue_callbacks_.length(); ++i) {
+ if (gc_type & gc_prologue_callbacks_[i].gc_type) {
+ gc_prologue_callbacks_[i].callback(gc_type, kNoGCCallbackFlags);
+ }
}
}
@@ -1002,16 +1006,21 @@
amount_of_external_allocated_memory_;
}
- GCCallbackFlags callback_flags = kNoGCCallbackFlags;
- for (int i = 0; i < gc_epilogue_callbacks_.length(); ++i) {
- if (gc_type & gc_epilogue_callbacks_[i].gc_type) {
- gc_epilogue_callbacks_[i].callback(gc_type, callback_flags);
+ {
+ GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+ VMState state(isolate_, EXTERNAL);
+ GCCallbackFlags callback_flags = kNoGCCallbackFlags;
+ for (int i = 0; i < gc_epilogue_callbacks_.length(); ++i) {
+ if (gc_type & gc_epilogue_callbacks_[i].gc_type) {
+ gc_epilogue_callbacks_[i].callback(gc_type, callback_flags);
+ }
}
}
if (collector == MARK_COMPACTOR && global_gc_epilogue_callback_) {
ASSERT(!allocation_allowed_);
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
+ VMState state(isolate_, EXTERNAL);
global_gc_epilogue_callback_();
}
@@ -1377,9 +1386,10 @@
UpdateNewSpaceReferencesInExternalStringTable(
&UpdateNewSpaceReferenceInExternalStringTableEntry);
+ error_object_list_.UpdateReferencesInNewSpace(this);
+
promotion_queue_.Destroy();
- LiveObjectList::UpdateReferencesForScavengeGC();
if (!FLAG_watch_ic_patching) {
isolate()->runtime_profiler()->UpdateSamplesAfterScavenge();
}
@@ -4384,7 +4394,8 @@
}
-MaybeObject* Heap::CopyJSObject(JSObject* source) {
+MaybeObject* Heap::CopyJSObject(JSObject* source,
+ AllocationSiteMode mode) {
// Never used to copy functions. If functions need to be copied we
// have to be careful to clear the literals array.
SLOW_ASSERT(!source->IsJSFunction());
@@ -4394,13 +4405,25 @@
int object_size = map->instance_size();
Object* clone;
+ bool track_origin = mode == TRACK_ALLOCATION_SITE &&
+ map->CanTrackAllocationSite();
+
WriteBarrierMode wb_mode = UPDATE_WRITE_BARRIER;
// If we're forced to always allocate, we use the general allocation
// functions which may leave us with an object in old space.
+ int adjusted_object_size = object_size;
if (always_allocate()) {
+ // We'll only track origin if we are certain to allocate in new space
+ if (track_origin) {
+ const int kMinFreeNewSpaceAfterGC = InitialSemiSpaceSize() * 3/4;
+ if ((object_size + AllocationSiteInfo::kSize) < kMinFreeNewSpaceAfterGC) {
+ adjusted_object_size += AllocationSiteInfo::kSize;
+ }
+ }
+
{ MaybeObject* maybe_clone =
- AllocateRaw(object_size, NEW_SPACE, OLD_POINTER_SPACE);
+ AllocateRaw(adjusted_object_size, NEW_SPACE, OLD_POINTER_SPACE);
if (!maybe_clone->ToObject(&clone)) return maybe_clone;
}
Address clone_address = HeapObject::cast(clone)->address();
@@ -4413,7 +4436,11 @@
(object_size - JSObject::kHeaderSize) / kPointerSize);
} else {
wb_mode = SKIP_WRITE_BARRIER;
- { MaybeObject* maybe_clone = new_space_.AllocateRaw(object_size);
+ if (track_origin) {
+ adjusted_object_size += AllocationSiteInfo::kSize;
+ }
+
+ { MaybeObject* maybe_clone = new_space_.AllocateRaw(adjusted_object_size);
if (!maybe_clone->ToObject(&clone)) return maybe_clone;
}
SLOW_ASSERT(InNewSpace(clone));
@@ -4424,6 +4451,13 @@
object_size);
}
+ if (adjusted_object_size > object_size) {
+ AllocationSiteInfo* alloc_info = reinterpret_cast<AllocationSiteInfo*>(
+ reinterpret_cast<Address>(clone) + object_size);
+ alloc_info->set_map(allocation_site_info_map());
+ alloc_info->set_payload(source);
+ }
+
SLOW_ASSERT(
JSObject::cast(clone)->GetElementsKind() == source->GetElementsKind());
FixedArrayBase* elements = FixedArrayBase::cast(source->elements());
@@ -4640,82 +4674,57 @@
}
-template<typename T>
-class AllocateInternalSymbolHelper {
- public:
- static void WriteOneByteData(T t, char* chars, int len);
- static void WriteTwoByteData(T t, uint16_t* chars, int len);
- private:
- DISALLOW_COPY_AND_ASSIGN(AllocateInternalSymbolHelper);
-};
+static inline void WriteOneByteData(Vector<const char> vector,
+ uint8_t* chars,
+ int len) {
+ // Only works for ascii.
+ ASSERT(vector.length() == len);
+ memcpy(chars, vector.start(), len);
+}
-
-template<>
-class AllocateInternalSymbolHelper< Vector<const char> > {
- public:
- static inline void WriteOneByteData(Vector<const char> vector,
- uint8_t* chars,
- int len) {
- // Only works for ascii.
- ASSERT(vector.length() == len);
- memcpy(chars, vector.start(), len);
- }
-
- static inline void WriteTwoByteData(Vector<const char> vector,
- uint16_t* chars,
- int len) {
- const uint8_t* stream = reinterpret_cast<const uint8_t*>(vector.start());
- unsigned stream_length = vector.length();
- while (stream_length != 0) {
- unsigned consumed = 0;
- uint32_t c = unibrow::Utf8::ValueOf(stream, stream_length, &consumed);
- ASSERT(c != unibrow::Utf8::kBadChar);
- ASSERT(consumed <= stream_length);
- stream_length -= consumed;
- stream += consumed;
- if (c > unibrow::Utf16::kMaxNonSurrogateCharCode) {
- len -= 2;
- if (len < 0) break;
- *chars++ = unibrow::Utf16::LeadSurrogate(c);
- *chars++ = unibrow::Utf16::TrailSurrogate(c);
- } else {
- len -= 1;
- if (len < 0) break;
- *chars++ = c;
- }
+static inline void WriteTwoByteData(Vector<const char> vector,
+ uint16_t* chars,
+ int len) {
+ const uint8_t* stream = reinterpret_cast<const uint8_t*>(vector.start());
+ unsigned stream_length = vector.length();
+ while (stream_length != 0) {
+ unsigned consumed = 0;
+ uint32_t c = unibrow::Utf8::ValueOf(stream, stream_length, &consumed);
+ ASSERT(c != unibrow::Utf8::kBadChar);
+ ASSERT(consumed <= stream_length);
+ stream_length -= consumed;
+ stream += consumed;
+ if (c > unibrow::Utf16::kMaxNonSurrogateCharCode) {
+ len -= 2;
+ if (len < 0) break;
+ *chars++ = unibrow::Utf16::LeadSurrogate(c);
+ *chars++ = unibrow::Utf16::TrailSurrogate(c);
+ } else {
+ len -= 1;
+ if (len < 0) break;
+ *chars++ = c;
}
- ASSERT(stream_length == 0);
- ASSERT(len == 0);
}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AllocateInternalSymbolHelper);
-};
+ ASSERT(stream_length == 0);
+ ASSERT(len == 0);
+}
-template<>
-class AllocateInternalSymbolHelper<String*> {
- public:
- static inline void WriteOneByteData(String* s, uint8_t* chars, int len) {
- ASSERT(s->length() == len);
- String::WriteToFlat(s, chars, 0, len);
- }
+static inline void WriteOneByteData(String* s, uint8_t* chars, int len) {
+ ASSERT(s->length() == len);
+ String::WriteToFlat(s, chars, 0, len);
+}
- static inline void WriteTwoByteData(String* s, uint16_t* chars, int len) {
- ASSERT(s->length() == len);
- String::WriteToFlat(s, chars, 0, len);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AllocateInternalSymbolHelper<String*>);
-};
+static inline void WriteTwoByteData(String* s, uint16_t* chars, int len) {
+ ASSERT(s->length() == len);
+ String::WriteToFlat(s, chars, 0, len);
+}
template<bool is_one_byte, typename T>
MaybeObject* Heap::AllocateInternalSymbol(T t,
int chars,
uint32_t hash_field) {
- typedef AllocateInternalSymbolHelper<T> H;
ASSERT(chars >= 0);
// Compute map and object size.
int size;
@@ -4752,9 +4761,9 @@
ASSERT_EQ(size, answer->Size());
if (is_one_byte) {
- H::WriteOneByteData(t, SeqOneByteString::cast(answer)->GetChars(), chars);
+ WriteOneByteData(t, SeqOneByteString::cast(answer)->GetChars(), chars);
} else {
- H::WriteTwoByteData(t, SeqTwoByteString::cast(answer)->GetChars(), chars);
+ WriteTwoByteData(t, SeqTwoByteString::cast(answer)->GetChars(), chars);
}
return answer;
}
@@ -5959,6 +5968,7 @@
mode != VISIT_ALL_IN_SWEEP_NEWSPACE) {
// Scavenge collections have special processing for this.
external_string_table_.Iterate(v);
+ error_object_list_.Iterate(v);
}
v->Synchronize(VisitorSynchronization::kExternalStringsTable);
}
@@ -6332,6 +6342,8 @@
external_string_table_.TearDown();
+ error_object_list_.TearDown();
+
new_space_.TearDown();
if (old_pointer_space_ != NULL) {
@@ -6731,7 +6743,7 @@
}
-#if defined(DEBUG) || defined(LIVE_OBJECT_LIST)
+#ifdef DEBUG
Object* const PathTracer::kAnyGlobalObject = reinterpret_cast<Object*>(NULL);
@@ -6898,10 +6910,8 @@
PrintF("=====================================\n");
}
}
-#endif // DEBUG || LIVE_OBJECT_LIST
-#ifdef DEBUG
// Triggers a depth-first traversal of reachable objects from one
// given root object and finds a path to a specific heap object and
// prints it.
@@ -7238,6 +7248,8 @@
}
}
new_space_strings_.Rewind(last);
+ new_space_strings_.Trim();
+
last = 0;
for (int i = 0; i < old_space_strings_.length(); ++i) {
if (old_space_strings_[i] == heap_->the_hole_value()) {
@@ -7247,6 +7259,7 @@
old_space_strings_[last++] = old_space_strings_[i];
}
old_space_strings_.Rewind(last);
+ old_space_strings_.Trim();
#ifdef VERIFY_HEAP
if (FLAG_verify_heap) {
Verify();
@@ -7261,6 +7274,119 @@
}
+// Update all references.
+void ErrorObjectList::UpdateReferences() {
+ for (int i = 0; i < list_.length(); i++) {
+ HeapObject* object = HeapObject::cast(list_[i]);
+ MapWord first_word = object->map_word();
+ if (first_word.IsForwardingAddress()) {
+ list_[i] = first_word.ToForwardingAddress();
+ }
+ }
+}
+
+
+// Unforwarded objects in new space are dead and removed from the list.
+void ErrorObjectList::UpdateReferencesInNewSpace(Heap* heap) {
+ if (list_.is_empty()) return;
+ if (!nested_) {
+ int write_index = 0;
+ for (int i = 0; i < list_.length(); i++) {
+ MapWord first_word = HeapObject::cast(list_[i])->map_word();
+ if (first_word.IsForwardingAddress()) {
+ list_[write_index++] = first_word.ToForwardingAddress();
+ }
+ }
+ list_.Rewind(write_index);
+ } else {
+ // If a GC is triggered during DeferredFormatStackTrace, we do not move
+ // objects in the list, just remove dead ones, as to not confuse the
+ // loop in DeferredFormatStackTrace.
+ for (int i = 0; i < list_.length(); i++) {
+ MapWord first_word = HeapObject::cast(list_[i])->map_word();
+ list_[i] = first_word.IsForwardingAddress()
+ ? first_word.ToForwardingAddress()
+ : heap->the_hole_value();
+ }
+ }
+}
+
+
+void ErrorObjectList::DeferredFormatStackTrace(Isolate* isolate) {
+ // If formatting the stack trace causes a GC, this method will be
+ // recursively called. In that case, skip the recursive call, since
+ // the loop modifies the list while iterating over it.
+ if (nested_ || list_.is_empty() || isolate->has_pending_exception()) return;
+ nested_ = true;
+ HandleScope scope(isolate);
+ Handle<String> stack_key = isolate->factory()->stack_symbol();
+ int write_index = 0;
+ int budget = kBudgetPerGC;
+ for (int i = 0; i < list_.length(); i++) {
+ Object* object = list_[i];
+ JSFunction* getter_fun;
+
+ { AssertNoAllocation assert;
+ // Skip possible holes in the list.
+ if (object->IsTheHole()) continue;
+ if (isolate->heap()->InNewSpace(object) || budget == 0) {
+ list_[write_index++] = object;
+ continue;
+ }
+
+ // Check whether the stack property is backed by the original getter.
+ LookupResult lookup(isolate);
+ JSObject::cast(object)->LocalLookupRealNamedProperty(*stack_key, &lookup);
+ if (!lookup.IsFound() || lookup.type() != CALLBACKS) continue;
+ Object* callback = lookup.GetCallbackObject();
+ if (!callback->IsAccessorPair()) continue;
+ Object* getter_obj = AccessorPair::cast(callback)->getter();
+ if (!getter_obj->IsJSFunction()) continue;
+ getter_fun = JSFunction::cast(getter_obj);
+ String* key = isolate->heap()->hidden_stack_trace_symbol();
+ if (key != getter_fun->GetHiddenProperty(key)) continue;
+ }
+
+ budget--;
+ HandleScope scope(isolate);
+ bool has_exception = false;
+#ifdef DEBUG
+ Handle<Map> map(HeapObject::cast(object)->map(), isolate);
+#endif
+ Handle<Object> object_handle(object, isolate);
+ Handle<Object> getter_handle(getter_fun, isolate);
+ Execution::Call(getter_handle, object_handle, 0, NULL, &has_exception);
+ ASSERT(*map == HeapObject::cast(*object_handle)->map());
+ if (has_exception) {
+ // Hit an exception (most likely a stack overflow).
+ // Wrap up this pass and retry after another GC.
+ isolate->clear_pending_exception();
+ // We use the handle since calling the getter might have caused a GC.
+ list_[write_index++] = *object_handle;
+ budget = 0;
+ }
+ }
+ list_.Rewind(write_index);
+ list_.Trim();
+ nested_ = false;
+}
+
+
+void ErrorObjectList::RemoveUnmarked(Heap* heap) {
+ for (int i = 0; i < list_.length(); i++) {
+ HeapObject* object = HeapObject::cast(list_[i]);
+ if (!Marking::MarkBitFrom(object).Get()) {
+ list_[i] = heap->the_hole_value();
+ }
+ }
+}
+
+
+void ErrorObjectList::TearDown() {
+ list_.Free();
+}
+
+
void Heap::QueueMemoryChunkForFree(MemoryChunk* chunk) {
chunk->set_next_chunk(chunks_queued_for_free_);
chunks_queued_for_free_ = chunk;
diff --git a/src/heap.h b/src/heap.h
index a2564a5..b7ef1f7 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -209,6 +209,7 @@
V(char_at_symbol, "CharAt") \
V(undefined_symbol, "undefined") \
V(value_of_symbol, "valueOf") \
+ V(stack_symbol, "stack") \
V(InitializeVarGlobal_symbol, "InitializeVarGlobal") \
V(InitializeConstGlobal_symbol, "InitializeConstGlobal") \
V(KeyedLoadElementMonomorphic_symbol, \
@@ -427,6 +428,41 @@
};
+// The stack property of an error object is implemented as a getter that
+// formats the attached raw stack trace into a string. This raw stack trace
+// keeps code and function objects alive until the getter is called the first
+// time. To release those objects, we call the getter after each GC for
+// newly tenured error objects that are kept in a list.
+class ErrorObjectList {
+ public:
+ inline void Add(JSObject* object);
+
+ inline void Iterate(ObjectVisitor* v);
+
+ void TearDown();
+
+ void RemoveUnmarked(Heap* heap);
+
+ void DeferredFormatStackTrace(Isolate* isolate);
+
+ void UpdateReferences();
+
+ void UpdateReferencesInNewSpace(Heap* heap);
+
+ private:
+ static const int kBudgetPerGC = 16;
+
+ ErrorObjectList() : nested_(false) { }
+
+ friend class Heap;
+
+ List<Object*> list_;
+ bool nested_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrorObjectList);
+};
+
+
enum ArrayStorageAllocationMode {
DONT_INITIALIZE_ARRAY_ELEMENTS,
INITIALIZE_ARRAY_ELEMENTS_WITH_HOLE
@@ -588,7 +624,9 @@
// Returns a deep copy of the JavaScript object.
// Properties and elements are copied too.
// Returns failure if allocation failed.
- MUST_USE_RESULT MaybeObject* CopyJSObject(JSObject* source);
+ MUST_USE_RESULT MaybeObject* CopyJSObject(
+ JSObject* source,
+ AllocationSiteMode mode = DONT_TRACK_ALLOCATION_SITE);
// Allocates the function prototype.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
@@ -1589,6 +1627,10 @@
return &external_string_table_;
}
+ ErrorObjectList* error_object_list() {
+ return &error_object_list_;
+ }
+
// Returns the current sweep generation.
int sweep_generation() {
return sweep_generation_;
@@ -2165,6 +2207,8 @@
ExternalStringTable external_string_table_;
+ ErrorObjectList error_object_list_;
+
VisitorDispatchTable<ScavengingCallback> scavenging_visitors_table_;
MemoryChunk* chunks_queued_for_free_;
@@ -2798,7 +2842,7 @@
};
-#if defined(DEBUG) || defined(LIVE_OBJECT_LIST)
+#ifdef DEBUG
// Helper class for tracing paths to a search target Object from all roots.
// The TracePathFrom() method can be used to trace paths from a specific
// object to the search target object.
@@ -2855,7 +2899,7 @@
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(PathTracer);
};
-#endif // DEBUG || LIVE_OBJECT_LIST
+#endif // DEBUG
} } // namespace v8::internal
diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc
index 67a679f..4bcb498 100644
--- a/src/hydrogen-instructions.cc
+++ b/src/hydrogen-instructions.cc
@@ -737,6 +737,11 @@
#endif
+void HDummyUse::PrintDataTo(StringStream* stream) {
+ value()->PrintNameTo(stream);
+}
+
+
void HUnaryCall::PrintDataTo(StringStream* stream) {
value()->PrintNameTo(stream);
stream->Add(" ");
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index 30919f5..88d217a 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -104,6 +104,7 @@
V(DeleteProperty) \
V(Deoptimize) \
V(Div) \
+ V(DummyUse) \
V(ElementsKind) \
V(EnterInlined) \
V(FastLiteral) \
@@ -1024,6 +1025,28 @@
};
+class HDummyUse: public HTemplateInstruction<1> {
+ public:
+ explicit HDummyUse(HValue* value) {
+ SetOperandAt(0, value);
+ // Pretend to be a Smi so that the HChange instructions inserted
+ // before any use generate as little code as possible.
+ set_representation(Representation::Tagged());
+ set_type(HType::Smi());
+ }
+
+ HValue* value() { return OperandAt(0); }
+
+ virtual Representation RequiredInputRepresentation(int index) {
+ return Representation::None();
+ }
+
+ virtual void PrintDataTo(StringStream* stream);
+
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse);
+};
+
+
// We insert soft-deoptimize when we hit code with unknown typefeedback,
// so that we get a chance of re-optimizing with useful typefeedback.
// HSoftDeoptimize does not end a basic block as opposed to HDeoptimize.
@@ -4968,19 +4991,29 @@
template <int V>
class HMaterializedLiteral: public HTemplateInstruction<V> {
public:
+ HMaterializedLiteral<V>(int index, int depth, AllocationSiteMode mode)
+ : literal_index_(index), depth_(depth), allocation_site_mode_(mode) {
+ this->set_representation(Representation::Tagged());
+ }
+
HMaterializedLiteral<V>(int index, int depth)
- : literal_index_(index), depth_(depth) {
+ : literal_index_(index), depth_(depth),
+ allocation_site_mode_(DONT_TRACK_ALLOCATION_SITE) {
this->set_representation(Representation::Tagged());
}
int literal_index() const { return literal_index_; }
int depth() const { return depth_; }
+ AllocationSiteMode allocation_site_mode() const {
+ return allocation_site_mode_;
+ }
private:
virtual bool IsDeletable() const { return true; }
int literal_index_;
int depth_;
+ AllocationSiteMode allocation_site_mode_;
};
@@ -4990,8 +5023,9 @@
Handle<JSObject> boilerplate,
int total_size,
int literal_index,
- int depth)
- : HMaterializedLiteral<1>(literal_index, depth),
+ int depth,
+ AllocationSiteMode mode)
+ : HMaterializedLiteral<1>(literal_index, depth, mode),
boilerplate_(boilerplate),
total_size_(total_size) {
SetOperandAt(0, context);
@@ -5006,7 +5040,6 @@
HValue* context() { return OperandAt(0); }
Handle<JSObject> boilerplate() const { return boilerplate_; }
int total_size() const { return total_size_; }
-
virtual Representation RequiredInputRepresentation(int index) {
return Representation::Tagged();
}
@@ -5026,8 +5059,9 @@
Handle<HeapObject> boilerplate_object,
int length,
int literal_index,
- int depth)
- : HMaterializedLiteral<1>(literal_index, depth),
+ int depth,
+ AllocationSiteMode mode)
+ : HMaterializedLiteral<1>(literal_index, depth, mode),
length_(length),
boilerplate_object_(boilerplate_object) {
SetOperandAt(0, context);
@@ -5043,7 +5077,6 @@
}
Handle<HeapObject> boilerplate_object() const { return boilerplate_object_; }
int length() const { return length_; }
-
bool IsCopyOnWrite() const;
virtual Representation RequiredInputRepresentation(int index) {
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index e5b1da8..a4578ee 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -877,6 +877,7 @@
zone_(info->zone()),
is_recursive_(false),
use_optimistic_licm_(false),
+ has_soft_deoptimize_(false),
type_change_checksum_(0) {
if (info->IsStub()) {
start_environment_ =
@@ -1244,13 +1245,18 @@
}
}
+
// Mark all blocks that are dominated by an unconditional soft deoptimize to
// prevent code motion across those blocks.
void HGraph::PropagateDeoptimizingMark() {
HPhase phase("H_Propagate deoptimizing mark", this);
+ // Skip this phase if there is nothing to be done anyway.
+ if (!has_soft_deoptimize()) return;
MarkAsDeoptimizingRecursively(entry_block());
+ NullifyUnreachableInstructions();
}
+
void HGraph::MarkAsDeoptimizingRecursively(HBasicBlock* block) {
for (int i = 0; i < block->dominated_blocks()->length(); ++i) {
HBasicBlock* dominated = block->dominated_blocks()->at(i);
@@ -1259,6 +1265,67 @@
}
}
+
+void HGraph::NullifyUnreachableInstructions() {
+ int block_count = blocks_.length();
+ for (int i = 0; i < block_count; ++i) {
+ HBasicBlock* block = blocks_.at(i);
+ bool nullify = false;
+ const ZoneList<HBasicBlock*>* predecessors = block->predecessors();
+ int predecessors_length = predecessors->length();
+ bool all_predecessors_deoptimizing = (predecessors_length > 0);
+ for (int j = 0; j < predecessors_length; ++j) {
+ if (!predecessors->at(j)->IsDeoptimizing()) {
+ all_predecessors_deoptimizing = false;
+ break;
+ }
+ }
+ if (all_predecessors_deoptimizing) nullify = true;
+ for (HInstruction* instr = block->first(); instr != NULL;
+ instr = instr->next()) {
+ // Leave the basic structure of the graph intact.
+ if (instr->IsBlockEntry()) continue;
+ if (instr->IsControlInstruction()) continue;
+ if (instr->IsSimulate()) continue;
+ if (instr->IsEnterInlined()) continue;
+ if (instr->IsLeaveInlined()) continue;
+ if (nullify) {
+ HInstruction* last_dummy = NULL;
+ for (int j = 0; j < instr->OperandCount(); ++j) {
+ HValue* operand = instr->OperandAt(j);
+ // Insert an HDummyUse for each operand, unless the operand
+ // is an HDummyUse itself. If it's even from the same block,
+ // remember it as a potential replacement for the instruction.
+ if (operand->IsDummyUse()) {
+ if (operand->block() == instr->block() &&
+ last_dummy == NULL) {
+ last_dummy = HInstruction::cast(operand);
+ }
+ continue;
+ }
+ if (operand->IsControlInstruction()) {
+ // Inserting a dummy use for a value that's not defined anywhere
+ // will fail. Some instructions define fake inputs on such
+ // values as control flow dependencies.
+ continue;
+ }
+ HDummyUse* dummy = new(zone()) HDummyUse(operand);
+ dummy->InsertBefore(instr);
+ last_dummy = dummy;
+ }
+ if (last_dummy == NULL) last_dummy = GetConstant1();
+ instr->DeleteAndReplaceWith(last_dummy);
+ continue;
+ }
+ if (instr->IsSoftDeoptimize()) {
+ ASSERT(block->IsDeoptimizing());
+ nullify = true;
+ }
+ }
+ }
+}
+
+
void HGraph::EliminateRedundantPhis() {
HPhase phase("H_Redundant phi elimination", this);
@@ -3985,6 +4052,15 @@
}
+void HOptimizedGraphBuilder::AddSoftDeoptimize() {
+ if (FLAG_always_opt) return;
+ if (current_block()->IsDeoptimizing()) return;
+ AddInstruction(new(zone()) HSoftDeoptimize());
+ current_block()->MarkAsDeoptimizing();
+ graph()->set_has_soft_deoptimize(true);
+}
+
+
template <class Instruction>
HInstruction* HOptimizedGraphBuilder::PreProcessCall(Instruction* call) {
int count = call->argument_count();
@@ -5213,7 +5289,8 @@
boilerplate_object,
total_size,
expr->literal_index(),
- expr->depth());
+ expr->depth(),
+ DONT_TRACK_ALLOCATION_SITE);
} else {
literal = new(zone()) HObjectLiteral(context,
expr->constant_properties(),
@@ -5323,7 +5400,13 @@
Handle<JSObject> boilerplate = Handle<JSObject>::cast(raw_boilerplate);
ElementsKind boilerplate_elements_kind =
- Handle<JSObject>::cast(boilerplate)->GetElementsKind();
+ Handle<JSObject>::cast(boilerplate)->GetElementsKind();
+
+ // TODO(mvstanton): This heuristic is only a temporary solution. In the
+ // end, we want to quit creating allocation site info after a certain number
+ // of GCs for a call site.
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(
+ boilerplate_elements_kind);
// Check whether to use fast or slow deep-copying for boilerplate.
int total_size = 0;
@@ -5332,17 +5415,22 @@
HFastLiteral::kMaxLiteralDepth,
&max_properties,
&total_size)) {
+ if (mode == TRACK_ALLOCATION_SITE) {
+ total_size += AllocationSiteInfo::kSize;
+ }
literal = new(zone()) HFastLiteral(context,
boilerplate,
total_size,
expr->literal_index(),
- expr->depth());
+ expr->depth(),
+ mode);
} else {
literal = new(zone()) HArrayLiteral(context,
boilerplate,
length,
expr->literal_index(),
- expr->depth());
+ expr->depth(),
+ mode);
}
// The array is expected in the bailout environment during computation
@@ -6160,9 +6248,8 @@
HValue* object,
Handle<String> name,
Property* expr) {
- if (expr->IsUninitialized() && !FLAG_always_opt) {
- AddInstruction(new(zone()) HSoftDeoptimize);
- current_block()->MarkAsDeoptimizing();
+ if (expr->IsUninitialized()) {
+ AddSoftDeoptimize();
}
HValue* context = environment()->LookupContext();
return new(zone()) HLoadNamedGeneric(context, object, name);
@@ -8057,8 +8144,7 @@
TypeInfo info = oracle()->UnaryType(expr);
Representation rep = ToRepresentation(info);
if (info.IsUninitialized()) {
- AddInstruction(new(zone()) HSoftDeoptimize);
- current_block()->MarkAsDeoptimizing();
+ AddSoftDeoptimize();
info = TypeInfo::Unknown();
}
HBinaryOperation::cast(instr)->set_observed_input_representation(rep, rep);
@@ -8071,8 +8157,7 @@
HValue* value = Pop();
TypeInfo info = oracle()->UnaryType(expr);
if (info.IsUninitialized()) {
- AddInstruction(new(zone()) HSoftDeoptimize);
- current_block()->MarkAsDeoptimizing();
+ AddSoftDeoptimize();
}
HInstruction* instr = new(zone()) HBitNot(value);
return ast_context()->ReturnInstruction(instr, expr->id());
@@ -8428,8 +8513,7 @@
if (left_info.IsUninitialized()) {
// Can't have initialized one but not the other.
ASSERT(right_info.IsUninitialized());
- AddInstruction(new(zone()) HSoftDeoptimize);
- current_block()->MarkAsDeoptimizing();
+ AddSoftDeoptimize();
left_info = right_info = TypeInfo::Unknown();
}
HInstruction* instr = NULL;
@@ -8745,8 +8829,7 @@
// Check if this expression was ever executed according to type feedback.
// Note that for the special typeof/null/undefined cases we get unknown here.
if (overall_type_info.IsUninitialized()) {
- AddInstruction(new(zone()) HSoftDeoptimize);
- current_block()->MarkAsDeoptimizing();
+ AddSoftDeoptimize();
overall_type_info = left_type = right_type = TypeInfo::Unknown();
}
diff --git a/src/hydrogen.h b/src/hydrogen.h
index eb5ec52..faaffc3 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -355,6 +355,14 @@
use_optimistic_licm_ = value;
}
+ bool has_soft_deoptimize() {
+ return has_soft_deoptimize_;
+ }
+
+ void set_has_soft_deoptimize(bool value) {
+ has_soft_deoptimize_ = value;
+ }
+
void MarkRecursive() {
is_recursive_ = true;
}
@@ -377,6 +385,7 @@
int32_t integer_value);
void MarkAsDeoptimizingRecursively(HBasicBlock* block);
+ void NullifyUnreachableInstructions();
void InsertTypeConversions(HInstruction* instr);
void PropagateMinusZeroChecks(HValue* value, BitVector* visited);
void RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi);
@@ -414,6 +423,7 @@
bool is_recursive_;
bool use_optimistic_licm_;
+ bool has_soft_deoptimize_;
int type_change_checksum_;
DISALLOW_COPY_AND_ASSIGN(HGraph);
@@ -956,6 +966,8 @@
bool inline_bailout() { return inline_bailout_; }
+ void AddSoftDeoptimize();
+
// Bailout environment manipulation.
void Push(HValue* value) { environment()->Push(value); }
HValue* Pop() { return environment()->Pop(); }
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 28663ab..4b71af6 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -323,7 +323,7 @@
MacroAssembler* masm,
int length,
FastCloneShallowArrayStub::Mode mode,
- AllocationSiteInfoMode allocation_site_info_mode,
+ AllocationSiteMode allocation_site_mode,
Label* fail) {
// Registers on entry:
//
@@ -339,7 +339,7 @@
}
int size = JSArray::kSize;
int allocation_info_start = size;
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
size += AllocationSiteInfo::kSize;
}
size += elements_size;
@@ -352,7 +352,7 @@
}
__ AllocateInNewSpace(size, eax, ebx, edx, fail, flags);
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
__ mov(FieldOperand(eax, allocation_info_start),
Immediate(Handle<Map>(masm->isolate()->heap()->
allocation_site_info_map())));
@@ -371,7 +371,7 @@
// Get hold of the elements array of the boilerplate and setup the
// elements pointer in the resulting object.
__ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset));
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
__ lea(edx, Operand(eax, JSArray::kSize + AllocationSiteInfo::kSize));
} else {
__ lea(edx, Operand(eax, JSArray::kSize));
@@ -425,30 +425,21 @@
FastCloneShallowArrayStub::Mode mode = mode_;
// ecx is boilerplate object.
- AllocationSiteInfoMode allocation_site_info_mode =
- DONT_TRACK_ALLOCATION_SITE_INFO;
- if (mode == CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO) {
- mode = CLONE_ANY_ELEMENTS;
- allocation_site_info_mode = TRACK_ALLOCATION_SITE_INFO;
- }
-
if (mode == CLONE_ANY_ELEMENTS) {
Label double_elements, check_fast_elements;
__ mov(ebx, FieldOperand(ecx, JSArray::kElementsOffset));
__ CheckMap(ebx, factory->fixed_cow_array_map(),
&check_fast_elements, DONT_DO_SMI_CHECK);
- GenerateFastCloneShallowArrayCommon(masm, 0,
- COPY_ON_WRITE_ELEMENTS,
- allocation_site_info_mode,
+ GenerateFastCloneShallowArrayCommon(masm, 0, COPY_ON_WRITE_ELEMENTS,
+ allocation_site_mode_,
&slow_case);
__ ret(3 * kPointerSize);
__ bind(&check_fast_elements);
__ CheckMap(ebx, factory->fixed_array_map(),
&double_elements, DONT_DO_SMI_CHECK);
- GenerateFastCloneShallowArrayCommon(masm, length_,
- CLONE_ELEMENTS,
- allocation_site_info_mode,
+ GenerateFastCloneShallowArrayCommon(masm, length_, CLONE_ELEMENTS,
+ allocation_site_mode_,
&slow_case);
__ ret(3 * kPointerSize);
@@ -479,7 +470,9 @@
}
GenerateFastCloneShallowArrayCommon(masm, length_, mode,
- allocation_site_info_mode, &slow_case);
+ allocation_site_mode_,
+ &slow_case);
+
// Return and remove the on-stack parameters.
__ ret(3 * kPointerSize);
@@ -5720,6 +5713,13 @@
// edi: second instance type.
__ test(ecx, Immediate(kAsciiDataHintMask));
__ j(not_zero, &ascii_data);
+ __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ __ xor_(edi, ecx);
+ STATIC_ASSERT(kOneByteStringTag != 0 && kAsciiDataHintTag != 0);
+ __ and_(edi, kOneByteStringTag | kAsciiDataHintTag);
+ __ cmp(edi, kOneByteStringTag | kAsciiDataHintTag);
+ __ j(equal, &ascii_data);
// Allocate a two byte cons string.
__ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
__ jmp(&allocated);
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index cb2fa26..7dc3768 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -390,7 +390,8 @@
void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
- MacroAssembler* masm) {
+ MacroAssembler* masm, AllocationSiteMode mode,
+ Label* allocation_site_info_found) {
// ----------- S t a t e -------------
// -- eax : value
// -- ebx : target map
@@ -398,6 +399,12 @@
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
+ if (mode == TRACK_ALLOCATION_SITE) {
+ ASSERT(allocation_site_info_found != NULL);
+ masm->TestJSArrayForAllocationSiteInfo(edx, edi,
+ allocation_site_info_found);
+ }
+
// Set transitioned map.
__ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
__ RecordWriteField(edx,
@@ -411,7 +418,7 @@
void ElementsTransitionGenerator::GenerateSmiToDouble(
- MacroAssembler* masm, Label* fail) {
+ MacroAssembler* masm, AllocationSiteMode mode, Label* fail) {
// ----------- S t a t e -------------
// -- eax : value
// -- ebx : target map
@@ -421,7 +428,7 @@
// -----------------------------------
Label loop, entry, convert_hole, gc_required, only_change_map;
- if (FLAG_track_allocation_sites) {
+ if (mode == TRACK_ALLOCATION_SITE) {
masm->TestJSArrayForAllocationSiteInfo(edx, edi, fail);
}
@@ -550,7 +557,7 @@
void ElementsTransitionGenerator::GenerateDoubleToObject(
- MacroAssembler* masm, Label* fail) {
+ MacroAssembler* masm, AllocationSiteMode mode, Label* fail) {
// ----------- S t a t e -------------
// -- eax : value
// -- ebx : target map
@@ -560,6 +567,10 @@
// -----------------------------------
Label loop, entry, convert_hole, gc_required, only_change_map, success;
+ if (mode == TRACK_ALLOCATION_SITE) {
+ masm->TestJSArrayForAllocationSiteInfo(edx, edi, fail);
+ }
+
// Check for empty arrays, which only require a map transition and no changes
// to the backing store.
__ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc
index 72a8592..029dbfb 100644
--- a/src/ia32/deoptimizer-ia32.cc
+++ b/src/ia32/deoptimizer-ia32.cc
@@ -1148,7 +1148,16 @@
__ movdbl(Operand(ebx, dst_offset), xmm0);
}
}
- __ fninit();
+
+ // Check that the TOP register is zero and clear all exceptions.
+ const int kTopMask = 0x3800;
+ __ push(eax);
+ __ fwait();
+ __ fnstsw_ax();
+ __ test(eax, Immediate(kTopMask));
+ __ Check(zero, "FPU TOP is not zero in deoptimizer.");
+ __ fnclex();
+ __ pop(eax);
// Remove the bailout id and the double registers from the stack.
if (type() == EAGER) {
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 7cb6558..23b8c66 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -1664,6 +1664,7 @@
__ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1);
FastCloneShallowArrayStub stub(
FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE,
length);
__ CallStub(&stub);
} else if (expr->depth() > 1) {
@@ -1673,21 +1674,19 @@
} else {
ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
FLAG_smi_only_arrays);
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ AllocationSiteMode allocation_site_mode = FLAG_track_allocation_sites
+ ? TRACK_ALLOCATION_SITE : DONT_TRACK_ALLOCATION_SITE;
+
// If the elements are already FAST_*_ELEMENTS, the boilerplate cannot
// change, so it's possible to specialize the stub in advance.
- FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements
- ? FastCloneShallowArrayStub::CLONE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
-
- // Tracking allocation info allows us to pre-transition later if it makes
- // sense.
- if (mode == FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS &&
- FLAG_track_allocation_sites) {
- mode = FastCloneShallowArrayStub::
- CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO;
+ if (has_constant_fast_elements) {
+ mode = FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
}
- FastCloneShallowArrayStub stub(mode, length);
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
__ CallStub(&stub);
}
@@ -3639,8 +3638,7 @@
__ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
- kIsNotStringMask | kStringEncodingMask | kAsciiDataHintMask |
- kStringRepresentationMask));
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, ASCII_STRING_TYPE);
__ j(not_equal, &bailout);
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index eb69658..3b5ef00 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -835,7 +835,9 @@
ebx,
edi,
slow);
- ElementsTransitionGenerator::GenerateSmiToDouble(masm, slow);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow);
__ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
__ jmp(&fast_double_without_map_check);
@@ -846,7 +848,9 @@
ebx,
edi,
slow);
- ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm);
+ mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode,
+ slow);
__ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
__ jmp(&finish_object_store);
@@ -860,7 +864,8 @@
ebx,
edi,
slow);
- ElementsTransitionGenerator::GenerateDoubleToObject(masm, slow);
+ mode = AllocationSiteInfo::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow);
__ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
__ jmp(&finish_object_store);
}
@@ -1650,7 +1655,9 @@
// Must return the modified receiver in eax.
if (!FLAG_trace_elements_transitions) {
Label fail;
- ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, &fail);
__ mov(eax, edx);
__ Ret();
__ bind(&fail);
@@ -1676,7 +1683,9 @@
// Must return the modified receiver in eax.
if (!FLAG_trace_elements_transitions) {
Label fail;
- ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_DOUBLE_ELEMENTS,
+ FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, &fail);
__ mov(eax, edx);
__ Ret();
__ bind(&fail);
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index e9d7509..c8fae64 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -4159,11 +4159,6 @@
}
DeoptimizeIf(below_equal, instr->environment());
} else {
- if (instr->hydrogen()->index()->representation().IsTagged() &&
- !instr->hydrogen()->index()->type().IsSmi()) {
- __ test(ToRegister(instr->index()), Immediate(kSmiTagMask));
- DeoptimizeIf(not_zero, instr->environment());
- }
__ cmp(ToRegister(instr->index()), ToOperand(instr->length()));
DeoptimizeIf(above_equal, instr->environment());
}
@@ -5300,6 +5295,8 @@
Handle<FixedArray> literals(instr->environment()->closure()->literals());
ElementsKind boilerplate_elements_kind =
instr->hydrogen()->boilerplate_elements_kind();
+ AllocationSiteMode allocation_site_mode =
+ instr->hydrogen()->allocation_site_mode();
// Deopt if the array literal boilerplate ElementsKind is of a type different
// than the expected one. The check isn't necessary if the boilerplate has
@@ -5330,7 +5327,7 @@
ASSERT(instr->hydrogen()->depth() == 1);
FastCloneShallowArrayStub::Mode mode =
FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS;
- FastCloneShallowArrayStub stub(mode, length);
+ FastCloneShallowArrayStub stub(mode, DONT_TRACK_ALLOCATION_SITE, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
} else if (instr->hydrogen()->depth() > 1) {
CallRuntime(Runtime::kCreateArrayLiteral, 3, instr);
@@ -5339,9 +5336,9 @@
} else {
FastCloneShallowArrayStub::Mode mode =
boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
- ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ELEMENTS;
- FastCloneShallowArrayStub stub(mode, length);
+ ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+ : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
}
@@ -5350,10 +5347,14 @@
void LCodeGen::EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
- int* offset) {
+ int* offset,
+ AllocationSiteMode mode) {
ASSERT(!source.is(ecx));
ASSERT(!result.is(ecx));
+ bool create_allocation_site_info = mode == TRACK_ALLOCATION_SITE &&
+ object->map()->CanTrackAllocationSite();
+
if (FLAG_debug_code) {
__ LoadHeapObject(ecx, object);
__ cmp(source, ecx);
@@ -5376,8 +5377,13 @@
// this object and its backing store.
int object_offset = *offset;
int object_size = object->map()->instance_size();
- int elements_offset = *offset + object_size;
int elements_size = has_elements ? elements->Size() : 0;
+ int elements_offset = *offset + object_size;
+ if (create_allocation_site_info) {
+ elements_offset += AllocationSiteInfo::kSize;
+ *offset += AllocationSiteInfo::kSize;
+ }
+
*offset += object_size + elements_size;
// Copy object header.
@@ -5402,7 +5408,8 @@
__ lea(ecx, Operand(result, *offset));
__ mov(FieldOperand(result, total_offset), ecx);
__ LoadHeapObject(source, value_object);
- EmitDeepCopy(value_object, result, source, offset);
+ EmitDeepCopy(value_object, result, source, offset,
+ DONT_TRACK_ALLOCATION_SITE);
} else if (value->IsHeapObject()) {
__ LoadHeapObject(ecx, Handle<HeapObject>::cast(value));
__ mov(FieldOperand(result, total_offset), ecx);
@@ -5411,6 +5418,14 @@
}
}
+ // Build Allocation Site Info if desired
+ if (create_allocation_site_info) {
+ __ mov(FieldOperand(result, object_size),
+ Immediate(Handle<Map>(isolate()->heap()->
+ allocation_site_info_map())));
+ __ mov(FieldOperand(result, object_size + kPointerSize), source);
+ }
+
if (has_elements) {
// Copy elements backing store header.
__ LoadHeapObject(source, elements);
@@ -5443,7 +5458,8 @@
__ lea(ecx, Operand(result, *offset));
__ mov(FieldOperand(result, total_offset), ecx);
__ LoadHeapObject(source, value_object);
- EmitDeepCopy(value_object, result, source, offset);
+ EmitDeepCopy(value_object, result, source, offset,
+ DONT_TRACK_ALLOCATION_SITE);
} else if (value->IsHeapObject()) {
__ LoadHeapObject(ecx, Handle<HeapObject>::cast(value));
__ mov(FieldOperand(result, total_offset), ecx);
@@ -5493,7 +5509,8 @@
__ bind(&allocated);
int offset = 0;
__ LoadHeapObject(ebx, instr->hydrogen()->boilerplate());
- EmitDeepCopy(instr->hydrogen()->boilerplate(), eax, ebx, &offset);
+ EmitDeepCopy(instr->hydrogen()->boilerplate(), eax, ebx, &offset,
+ instr->hydrogen()->allocation_site_mode());
ASSERT_EQ(size, offset);
}
@@ -5758,6 +5775,11 @@
}
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
LOperand* obj = instr->object();
LOperand* key = instr->key();
diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h
index 63d15f4..5c3d045 100644
--- a/src/ia32/lithium-codegen-ia32.h
+++ b/src/ia32/lithium-codegen-ia32.h
@@ -350,7 +350,8 @@
void EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
- int* offset);
+ int* offset,
+ AllocationSiteMode mode);
void EnsureSpaceForLazyDeopt();
void DoLoadKeyedExternalArray(LLoadKeyed* instr);
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index da1a466..8bb5740 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -726,6 +726,11 @@
}
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index 02eb6d3..1daf847 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -87,6 +87,7 @@
V(Deoptimize) \
V(DivI) \
V(DoubleToI) \
+ V(DummyUse) \
V(ElementsKind) \
V(FastLiteral) \
V(FixedArrayBaseLength) \
@@ -387,6 +388,15 @@
};
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index d5013d7..cebfb13 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -2706,17 +2706,15 @@
movzx_b(scratch2, FieldOperand(scratch2, Map::kInstanceTypeOffset));
// Check that both are flat ASCII strings.
- const int kFlatAsciiStringMask = kIsNotStringMask | kStringRepresentationMask
- | kStringEncodingMask | kAsciiDataHintTag;
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
const int kFlatAsciiStringTag = ASCII_STRING_TYPE;
// Interleave bits from both instance types and compare them in one check.
- ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 8));
- ASSERT_EQ(ASCII_STRING_TYPE, ASCII_STRING_TYPE & kFlatAsciiStringMask);
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
and_(scratch1, kFlatAsciiStringMask);
and_(scratch2, kFlatAsciiStringMask);
- shl(scratch1, 8);
- or_(scratch1, scratch2);
- cmp(scratch1, kFlatAsciiStringTag | (kFlatAsciiStringTag << 8));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
+ cmp(scratch1, kFlatAsciiStringTag | (kFlatAsciiStringTag << 3));
j(not_equal, failure);
}
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index 2129be3..9e694d2 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -1616,7 +1616,9 @@
&try_holey_map);
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm());
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
// Restore edi.
__ mov(edi, FieldOperand(edx, JSArray::kElementsOffset));
__ jmp(&fast_object);
@@ -1628,7 +1630,9 @@
edi,
&call_builtin);
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm());
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
// Restore edi.
__ mov(edi, FieldOperand(edx, JSArray::kElementsOffset));
__ bind(&fast_object);
diff --git a/src/ic.cc b/src/ic.cc
index 5c4150b..0a99772 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -47,7 +47,8 @@
case MONOMORPHIC: return '1';
case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
case POLYMORPHIC: return 'P';
- case MEGAMORPHIC: return IsGeneric() ? 'G' : 'N';
+ case MEGAMORPHIC: return 'N';
+ case GENERIC: return 'G';
// We never see the debugger states here, because the state is
// computed from the original code - not the patched code. Let
@@ -772,6 +773,7 @@
case DEBUG_STUB:
break;
case POLYMORPHIC:
+ case GENERIC:
UNREACHABLE();
break;
}
@@ -796,6 +798,7 @@
}
if (FLAG_use_ic && state != MEGAMORPHIC && object->IsHeapObject()) {
+ ASSERT(state != GENERIC);
int argc = target()->arguments_count();
Handle<Map> map =
isolate()->factory()->non_strict_arguments_elements_map();
@@ -855,6 +858,7 @@
} else if (state == MONOMORPHIC && object->IsStringWrapper()) {
stub = isolate()->builtins()->LoadIC_StringWrapperLength();
} else if (state != MEGAMORPHIC) {
+ ASSERT(state != GENERIC);
stub = megamorphic_stub();
}
if (!stub.is_null()) {
@@ -879,6 +883,7 @@
} else if (state == PREMONOMORPHIC) {
stub = isolate()->builtins()->LoadIC_ArrayLength();
} else if (state != MEGAMORPHIC) {
+ ASSERT(state != GENERIC);
stub = megamorphic_stub();
}
if (!stub.is_null()) {
@@ -900,6 +905,7 @@
} else if (state == PREMONOMORPHIC) {
stub = isolate()->builtins()->LoadIC_FunctionPrototype();
} else if (state != MEGAMORPHIC) {
+ ASSERT(state != GENERIC);
stub = megamorphic_stub();
}
if (!stub.is_null()) {
@@ -1067,6 +1073,7 @@
case DEBUG_STUB:
break;
case POLYMORPHIC:
+ case GENERIC:
UNREACHABLE();
break;
}
@@ -1336,6 +1343,7 @@
}
break;
case MEGAMORPHIC:
+ case GENERIC:
case DEBUG_STUB:
break;
case MONOMORPHIC_PROTOTYPE_FAILURE:
@@ -1614,6 +1622,7 @@
case DEBUG_STUB:
break;
case POLYMORPHIC:
+ case GENERIC:
UNREACHABLE();
break;
}
@@ -1658,6 +1667,7 @@
break;
}
case MEGAMORPHIC:
+ case GENERIC:
break;
case UNINITIALIZED:
case PREMONOMORPHIC:
@@ -1852,6 +1862,12 @@
Handle<Code> cached_stub;
Handle<Map> transitioned_map =
receiver_map->FindTransitionedMap(receiver_maps);
+
+ // TODO(mvstanton): The code below is doing pessimistic elements
+ // transitions. I would like to stop doing that and rely on Allocation Site
+ // Tracking to do a better job of ensuring the data types are what they need
+ // to be. Not all the elements are in place yet, pessimistic elements
+ // transitions are still important for performance.
if (!transitioned_map.is_null()) {
cached_stub = ElementsTransitionAndStoreStub(
receiver_map->elements_kind(), // original elements_kind
@@ -2108,6 +2124,7 @@
}
break;
case MEGAMORPHIC:
+ case GENERIC:
case DEBUG_STUB:
break;
case MONOMORPHIC_PROTOTYPE_FAILURE:
@@ -2354,7 +2371,7 @@
case HEAP_NUMBER:
return MONOMORPHIC;
case GENERIC:
- return MEGAMORPHIC;
+ return ::v8::internal::GENERIC;
}
UNREACHABLE();
return ::v8::internal::UNINITIALIZED;
@@ -2425,7 +2442,7 @@
case STRING:
return MONOMORPHIC;
case GENERIC:
- return MEGAMORPHIC;
+ return ::v8::internal::GENERIC;
}
UNREACHABLE();
return ::v8::internal::UNINITIALIZED;
diff --git a/src/ic.h b/src/ic.h
index dd81ab4..0cc6aa4 100644
--- a/src/ic.h
+++ b/src/ic.h
@@ -97,8 +97,6 @@
Code* target() const { return GetTargetAtAddress(address()); }
inline Address address() const;
- virtual bool IsGeneric() const { return false; }
-
// Compute the current IC state based on the target stub, receiver and name.
static State StateFrom(Code* target, Object* receiver, Object* name);
@@ -519,10 +517,6 @@
ElementsKind elements_kind,
KeyedAccessGrowMode grow_mode);
- virtual bool IsGeneric() const {
- return target() == *generic_stub();
- }
-
protected:
virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; }
@@ -680,11 +674,6 @@
ElementsKind elements_kind,
KeyedAccessGrowMode grow_mode);
- virtual bool IsGeneric() const {
- return target() == *generic_stub() ||
- target() == *generic_stub_strict();
- }
-
protected:
virtual Code::Kind kind() const { return Code::KEYED_STORE_IC; }
diff --git a/src/inspector.cc b/src/inspector.cc
deleted file mode 100644
index 833d338..0000000
--- a/src/inspector.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-#include "v8.h"
-#include "inspector.h"
-
-
-namespace v8 {
-namespace internal {
-
-#ifdef INSPECTOR
-
-//============================================================================
-// The Inspector.
-
-void Inspector::DumpObjectType(FILE* out, Object* obj, bool print_more) {
- // Dump the object pointer.
- OS::FPrint(out, "%p:", reinterpret_cast<void*>(obj));
- if (obj->IsHeapObject()) {
- HeapObject* hobj = HeapObject::cast(obj);
- OS::FPrint(out, " size %d :", hobj->Size());
- }
-
- // Dump each object classification that matches this object.
-#define FOR_EACH_TYPE(type) \
- if (obj->Is##type()) { \
- OS::FPrint(out, " %s", #type); \
- }
- OBJECT_TYPE_LIST(FOR_EACH_TYPE)
- HEAP_OBJECT_TYPE_LIST(FOR_EACH_TYPE)
-#undef FOR_EACH_TYPE
-}
-
-
-#endif // INSPECTOR
-
-} } // namespace v8::internal
-
diff --git a/src/inspector.h b/src/inspector.h
deleted file mode 100644
index 6962e21..0000000
--- a/src/inspector.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-#ifndef V8_INSPECTOR_H_
-#define V8_INSPECTOR_H_
-
-// Only build this code if we're configured with the INSPECTOR.
-#ifdef INSPECTOR
-
-#include "v8.h"
-
-#include "objects.h"
-
-namespace v8 {
-namespace internal {
-
-class Inspector {
- public:
- static void DumpObjectType(FILE* out, Object* obj, bool print_more);
- static void DumpObjectType(FILE* out, Object* obj) {
- DumpObjectType(out, obj, false);
- }
- static void DumpObjectType(Object* obj, bool print_more) {
- DumpObjectType(stdout, obj, print_more);
- }
- static void DumpObjectType(Object* obj) {
- DumpObjectType(stdout, obj, false);
- }
-};
-
-} } // namespace v8::internal
-
-#endif // INSPECTOR
-
-#endif // V8_INSPECTOR_H_
diff --git a/src/isolate.cc b/src/isolate.cc
index 2044805..8bcceab 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -408,9 +408,9 @@
}
-Isolate* Isolate::GetDefaultIsolateForLocking() {
+v8::Isolate* Isolate::GetDefaultIsolateForLocking() {
EnsureDefaultIsolate();
- return default_isolate_;
+ return reinterpret_cast<v8::Isolate*>(default_isolate_);
}
@@ -923,7 +923,11 @@
if (decision != UNKNOWN) return decision == YES;
// Get named access check callback
- JSFunction* constructor = JSFunction::cast(receiver->map()->constructor());
+ // TODO(dcarney): revert
+ Map* map = receiver->map();
+ CHECK(map->IsMap());
+ CHECK(map->constructor()->IsJSFunction());
+ JSFunction* constructor = JSFunction::cast(map->constructor());
if (!constructor->shared()->IsApiFunction()) return false;
Object* data_obj =
@@ -1738,7 +1742,7 @@
delete deoptimizer_data_;
deoptimizer_data_ = NULL;
if (FLAG_preemption) {
- v8::Locker locker;
+ v8::Locker locker(reinterpret_cast<v8::Isolate*>(this));
v8::Locker::StopPreemption();
}
builtins_.TearDown();
@@ -2029,7 +2033,7 @@
}
if (FLAG_preemption) {
- v8::Locker locker;
+ v8::Locker locker(reinterpret_cast<v8::Isolate*>(this));
v8::Locker::StartPreemption(100);
}
diff --git a/src/isolate.h b/src/isolate.h
index 8d0575c..d43c451 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -1070,6 +1070,11 @@
return &optimizing_compiler_thread_;
}
+ // PreInits and returns a default isolate. Needed when a new thread tries
+ // to create a Locker for the first time (the lock itself is in the isolate).
+ // TODO(svenpanne) This method is on death row...
+ static v8::Isolate* GetDefaultIsolateForLocking();
+
private:
Isolate();
@@ -1153,10 +1158,6 @@
// If one does not yet exist, allocate a new one.
PerIsolateThreadData* FindOrAllocatePerThreadDataForThisThread();
- // PreInits and returns a default isolate. Needed when a new thread tries
- // to create a Locker for the first time (the lock itself is in the isolate).
- static Isolate* GetDefaultIsolateForLocking();
-
// Initializes the current thread to run this Isolate.
// Not thread-safe. Multiple threads should not Enter/Exit the same isolate
// at the same time, this should be prevented using external locking.
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index a3efb85..f6e2e7f 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -2875,23 +2875,9 @@
if (!ignore_case) return set_replacement(NULL);
// Here, we need to check for characters whose upper and lower cases
// are outside the Latin-1 range.
- // TODO(dcarney): Replace this code with a simple
- // table lookup in unibrow::Latin-1.
- // TODO(dcarney): Test cases!.
- unibrow::uchar result;
- int chars;
- chars = unibrow::ToLowercase::Convert(quarks[j], 0, &result, NULL);
- if (chars > 1 ||
- (chars == 1 && result <= String::kMaxOneByteCharCodeU)) {
- continue;
+ if (!unibrow::Latin1::NonLatin1CanBeConvertedToLatin1(quarks[j])) {
+ return set_replacement(NULL);
}
- chars = unibrow::ToUppercase::Convert(quarks[j], 0, &result, NULL);
- if (chars > 1 ||
- (chars == 1 && result <= String::kMaxOneByteCharCodeU)) {
- continue;
- }
- // This character is definitely not in the Latin-1 range.
- return set_replacement(NULL);
#endif
}
} else {
diff --git a/src/list-inl.h b/src/list-inl.h
index 60a033d..7a84313 100644
--- a/src/list-inl.h
+++ b/src/list-inl.h
@@ -85,8 +85,9 @@
template<typename T, class P>
void List<T, P>::Resize(int new_capacity, P alloc) {
+ ASSERT_LE(length_, new_capacity);
T* new_data = NewData(new_capacity, alloc);
- memcpy(new_data, data_, capacity_ * sizeof(T));
+ memcpy(new_data, data_, length_ * sizeof(T));
List<T, P>::DeleteData(data_);
data_ = new_data;
capacity_ = new_capacity;
@@ -162,6 +163,14 @@
template<typename T, class P>
+void List<T, P>::Trim(P alloc) {
+ if (length_ < capacity_ / 4) {
+ Resize(capacity_ / 2, alloc);
+ }
+}
+
+
+template<typename T, class P>
void List<T, P>::Iterate(void (*callback)(T* x)) {
for (int i = 0; i < length_; i++) callback(&data_[i]);
}
diff --git a/src/list.h b/src/list.h
index 7fd4f5c..43d982f 100644
--- a/src/list.h
+++ b/src/list.h
@@ -149,6 +149,9 @@
// Drop the last 'count' elements from the list.
INLINE(void RewindBy(int count)) { Rewind(length_ - count); }
+ // Halve the capacity if fill level is less than a quarter.
+ INLINE(void Trim(AllocationPolicy allocator = AllocationPolicy()));
+
bool Contains(const T& elm) const;
int CountOccurrences(const T& elm, int start, int end) const;
diff --git a/src/liveobjectlist-inl.h b/src/liveobjectlist-inl.h
deleted file mode 100644
index 2bc2296..0000000
--- a/src/liveobjectlist-inl.h
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_LIVEOBJECTLIST_INL_H_
-#define V8_LIVEOBJECTLIST_INL_H_
-
-#include "v8.h"
-
-#include "liveobjectlist.h"
-
-namespace v8 {
-namespace internal {
-
-#ifdef LIVE_OBJECT_LIST
-
-void LiveObjectList::GCEpilogue() {
- if (!NeedLOLProcessing()) return;
- GCEpiloguePrivate();
-}
-
-
-void LiveObjectList::GCPrologue() {
- if (!NeedLOLProcessing()) return;
-#ifdef VERIFY_LOL
- if (FLAG_verify_lol) {
- Verify();
- }
-#endif
-}
-
-
-void LiveObjectList::IterateElements(ObjectVisitor* v) {
- if (!NeedLOLProcessing()) return;
- IterateElementsPrivate(v);
-}
-
-
-void LiveObjectList::ProcessNonLive(HeapObject* obj) {
- // Only do work if we have at least one list to process.
- if (last()) DoProcessNonLive(obj);
-}
-
-
-void LiveObjectList::UpdateReferencesForScavengeGC() {
- if (LiveObjectList::NeedLOLProcessing()) {
- UpdateLiveObjectListVisitor update_visitor;
- LiveObjectList::IterateElements(&update_visitor);
- }
-}
-
-
-LiveObjectList* LiveObjectList::FindLolForId(int id,
- LiveObjectList* start_lol) {
- if (id != 0) {
- LiveObjectList* lol = start_lol;
- while (lol != NULL) {
- if (lol->id() == id) {
- return lol;
- }
- lol = lol->prev_;
- }
- }
- return NULL;
-}
-
-
-// Iterates the elements in every lol and returns the one that matches the
-// specified key. If no matching element is found, then it returns NULL.
-template <typename T>
-inline LiveObjectList::Element*
-LiveObjectList::FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key) {
- LiveObjectList* lol = last();
- while (lol != NULL) {
- Element* elements = lol->elements_;
- for (int i = 0; i < lol->obj_count_; i++) {
- Element* element = &elements[i];
- if (GetValue(element) == key) {
- return element;
- }
- }
- lol = lol->prev_;
- }
- return NULL;
-}
-
-
-inline int LiveObjectList::GetElementId(LiveObjectList::Element* element) {
- return element->id_;
-}
-
-
-inline HeapObject*
-LiveObjectList::GetElementObj(LiveObjectList::Element* element) {
- return element->obj_;
-}
-
-#endif // LIVE_OBJECT_LIST
-
-} } // namespace v8::internal
-
-#endif // V8_LIVEOBJECTLIST_INL_H_
-
diff --git a/src/liveobjectlist.cc b/src/liveobjectlist.cc
deleted file mode 100644
index 6dbe0a8..0000000
--- a/src/liveobjectlist.cc
+++ /dev/null
@@ -1,2631 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifdef LIVE_OBJECT_LIST
-
-#include <ctype.h>
-#include <stdlib.h>
-
-#include "v8.h"
-
-#include "checks.h"
-#include "global-handles.h"
-#include "heap.h"
-#include "inspector.h"
-#include "isolate.h"
-#include "list-inl.h"
-#include "liveobjectlist-inl.h"
-#include "string-stream.h"
-#include "v8utils.h"
-#include "v8conversions.h"
-
-namespace v8 {
-namespace internal {
-
-
-typedef int (*RawComparer)(const void*, const void*);
-
-
-#ifdef CHECK_ALL_OBJECT_TYPES
-
-#define DEBUG_LIVE_OBJECT_TYPES(v) \
- v(Smi, "unexpected: Smi") \
- \
- v(CodeCache, "unexpected: CodeCache") \
- v(BreakPointInfo, "unexpected: BreakPointInfo") \
- v(DebugInfo, "unexpected: DebugInfo") \
- v(TypeSwitchInfo, "unexpected: TypeSwitchInfo") \
- v(SignatureInfo, "unexpected: SignatureInfo") \
- v(Script, "unexpected: Script") \
- v(ObjectTemplateInfo, "unexpected: ObjectTemplateInfo") \
- v(FunctionTemplateInfo, "unexpected: FunctionTemplateInfo") \
- v(CallHandlerInfo, "unexpected: CallHandlerInfo") \
- v(InterceptorInfo, "unexpected: InterceptorInfo") \
- v(AccessCheckInfo, "unexpected: AccessCheckInfo") \
- v(AccessorInfo, "unexpected: AccessorInfo") \
- v(ExternalTwoByteString, "unexpected: ExternalTwoByteString") \
- v(ExternalAsciiString, "unexpected: ExternalAsciiString") \
- v(ExternalString, "unexpected: ExternalString") \
- v(SeqTwoByteString, "unexpected: SeqTwoByteString") \
- v(SeqOneByteString, "unexpected: SeqOneByteString") \
- v(SeqString, "unexpected: SeqString") \
- v(JSFunctionResultCache, "unexpected: JSFunctionResultCache") \
- v(NativeContext, "unexpected: NativeContext") \
- v(MapCache, "unexpected: MapCache") \
- v(CodeCacheHashTable, "unexpected: CodeCacheHashTable") \
- v(CompilationCacheTable, "unexpected: CompilationCacheTable") \
- v(SymbolTable, "unexpected: SymbolTable") \
- v(Dictionary, "unexpected: Dictionary") \
- v(HashTable, "unexpected: HashTable") \
- v(DescriptorArray, "unexpected: DescriptorArray") \
- v(ExternalFloatArray, "unexpected: ExternalFloatArray") \
- v(ExternalUnsignedIntArray, "unexpected: ExternalUnsignedIntArray") \
- v(ExternalIntArray, "unexpected: ExternalIntArray") \
- v(ExternalUnsignedShortArray, "unexpected: ExternalUnsignedShortArray") \
- v(ExternalShortArray, "unexpected: ExternalShortArray") \
- v(ExternalUnsignedByteArray, "unexpected: ExternalUnsignedByteArray") \
- v(ExternalByteArray, "unexpected: ExternalByteArray") \
- v(JSValue, "unexpected: JSValue")
-
-#else
-#define DEBUG_LIVE_OBJECT_TYPES(v)
-#endif
-
-
-#define FOR_EACH_LIVE_OBJECT_TYPE(v) \
- DEBUG_LIVE_OBJECT_TYPES(v) \
- \
- v(JSArray, "JSArray") \
- v(JSRegExp, "JSRegExp") \
- v(JSFunction, "JSFunction") \
- v(JSGlobalObject, "JSGlobal") \
- v(JSBuiltinsObject, "JSBuiltins") \
- v(GlobalObject, "Global") \
- v(JSGlobalProxy, "JSGlobalProxy") \
- v(JSObject, "JSObject") \
- \
- v(Context, "meta: Context") \
- v(ByteArray, "meta: ByteArray") \
- v(ExternalPixelArray, "meta: PixelArray") \
- v(ExternalArray, "meta: ExternalArray") \
- v(FixedArray, "meta: FixedArray") \
- v(String, "String") \
- v(HeapNumber, "HeapNumber") \
- \
- v(Code, "meta: Code") \
- v(Map, "meta: Map") \
- v(Oddball, "Oddball") \
- v(Foreign, "meta: Foreign") \
- v(SharedFunctionInfo, "meta: SharedFunctionInfo") \
- v(Struct, "meta: Struct") \
- \
- v(HeapObject, "HeapObject")
-
-
-enum /* LiveObjectType */ {
-#define DECLARE_OBJECT_TYPE_ENUM(type, name) kType##type,
- FOR_EACH_LIVE_OBJECT_TYPE(DECLARE_OBJECT_TYPE_ENUM)
- kInvalidLiveObjType,
- kNumberOfTypes
-#undef DECLARE_OBJECT_TYPE_ENUM
-};
-
-
-LiveObjectType GetObjectType(HeapObject* heap_obj) {
- // TODO(mlam): investigate usint Map::instance_type() instead.
-#define CHECK_FOR_OBJECT_TYPE(type, name) \
- if (heap_obj->Is##type()) return kType##type;
- FOR_EACH_LIVE_OBJECT_TYPE(CHECK_FOR_OBJECT_TYPE)
-#undef CHECK_FOR_OBJECT_TYPE
-
- UNREACHABLE();
- return kInvalidLiveObjType;
-}
-
-
-inline const char* GetObjectTypeDesc(LiveObjectType type) {
- static const char* const name[kNumberOfTypes] = {
- #define DEFINE_OBJECT_TYPE_NAME(type, name) name,
- FOR_EACH_LIVE_OBJECT_TYPE(DEFINE_OBJECT_TYPE_NAME)
- "invalid"
- #undef DEFINE_OBJECT_TYPE_NAME
- };
- ASSERT(type < kNumberOfTypes);
- return name[type];
-}
-
-
-const char* GetObjectTypeDesc(HeapObject* heap_obj) {
- LiveObjectType type = GetObjectType(heap_obj);
- return GetObjectTypeDesc(type);
-}
-
-
-bool IsOfType(LiveObjectType type, HeapObject* obj) {
- // Note: there are types that are more general (e.g. JSObject) that would
- // have passed the Is##type_() test for more specialized types (e.g.
- // JSFunction). If we find a more specialized match but we're looking for
- // the general type, then we should reject the ones that matches the
- // specialized type.
-#define CHECK_OBJECT_TYPE(type_, name) \
- if (obj->Is##type_()) return (type == kType##type_);
-
- FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE)
-#undef CHECK_OBJECT_TYPE
-
- return false;
-}
-
-
-const AllocationSpace kInvalidSpace = static_cast<AllocationSpace>(-1);
-
-static AllocationSpace FindSpaceFor(String* space_str) {
- SmartArrayPointer<char> s =
- space_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
-
- const char* key_str = *s;
- switch (key_str[0]) {
- case 'c':
- if (strcmp(key_str, "cell") == 0) return CELL_SPACE;
- if (strcmp(key_str, "code") == 0) return CODE_SPACE;
- break;
- case 'l':
- if (strcmp(key_str, "lo") == 0) return LO_SPACE;
- break;
- case 'm':
- if (strcmp(key_str, "map") == 0) return MAP_SPACE;
- break;
- case 'n':
- if (strcmp(key_str, "new") == 0) return NEW_SPACE;
- break;
- case 'o':
- if (strcmp(key_str, "old-pointer") == 0) return OLD_POINTER_SPACE;
- if (strcmp(key_str, "old-data") == 0) return OLD_DATA_SPACE;
- break;
- }
- return kInvalidSpace;
-}
-
-
-static bool InSpace(AllocationSpace space, HeapObject* heap_obj) {
- Heap* heap = ISOLATE->heap();
- if (space != LO_SPACE) {
- return heap->InSpace(heap_obj, space);
- }
-
- // This is an optimization to speed up the check for an object in the LO
- // space by exclusion because we know that all object pointers passed in
- // here are guaranteed to be in the heap. Hence, it is safe to infer
- // using an exclusion test.
- // Note: calling Heap::InSpace(heap_obj, LO_SPACE) is too slow for our
- // filters.
- int first_space = static_cast<int>(FIRST_SPACE);
- int last_space = static_cast<int>(LO_SPACE);
- for (int sp = first_space; sp < last_space; sp++) {
- if (heap->InSpace(heap_obj, static_cast<AllocationSpace>(sp))) {
- return false;
- }
- }
- SLOW_ASSERT(heap->InSpace(heap_obj, LO_SPACE));
- return true;
-}
-
-
-static LiveObjectType FindTypeFor(String* type_str) {
- SmartArrayPointer<char> s =
- type_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
-
-#define CHECK_OBJECT_TYPE(type_, name) { \
- const char* type_desc = GetObjectTypeDesc(kType##type_); \
- const char* key_str = *s; \
- if (strstr(type_desc, key_str) != NULL) return kType##type_; \
- }
- FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE)
-#undef CHECK_OBJECT_TYPE
-
- return kInvalidLiveObjType;
-}
-
-
-class LolFilter {
- public:
- explicit LolFilter(Handle<JSObject> filter_obj);
-
- inline bool is_active() const { return is_active_; }
- inline bool Matches(HeapObject* obj) {
- return !is_active() || MatchesSlow(obj);
- }
-
- private:
- void InitTypeFilter(Handle<JSObject> filter_obj);
- void InitSpaceFilter(Handle<JSObject> filter_obj);
- void InitPropertyFilter(Handle<JSObject> filter_obj);
- bool MatchesSlow(HeapObject* obj);
-
- bool is_active_;
- LiveObjectType type_;
- AllocationSpace space_;
- Handle<String> prop_;
-};
-
-
-LolFilter::LolFilter(Handle<JSObject> filter_obj)
- : is_active_(false),
- type_(kInvalidLiveObjType),
- space_(kInvalidSpace),
- prop_() {
- if (filter_obj.is_null()) return;
-
- InitTypeFilter(filter_obj);
- InitSpaceFilter(filter_obj);
- InitPropertyFilter(filter_obj);
-}
-
-
-void LolFilter::InitTypeFilter(Handle<JSObject> filter_obj) {
- Handle<String> type_sym = FACTORY->LookupAsciiSymbol("type");
- MaybeObject* maybe_result = filter_obj->GetProperty(*type_sym);
- Object* type_obj;
- if (maybe_result->ToObject(&type_obj)) {
- if (type_obj->IsString()) {
- String* type_str = String::cast(type_obj);
- type_ = FindTypeFor(type_str);
- if (type_ != kInvalidLiveObjType) {
- is_active_ = true;
- }
- }
- }
-}
-
-
-void LolFilter::InitSpaceFilter(Handle<JSObject> filter_obj) {
- Handle<String> space_sym = FACTORY->LookupAsciiSymbol("space");
- MaybeObject* maybe_result = filter_obj->GetProperty(*space_sym);
- Object* space_obj;
- if (maybe_result->ToObject(&space_obj)) {
- if (space_obj->IsString()) {
- String* space_str = String::cast(space_obj);
- space_ = FindSpaceFor(space_str);
- if (space_ != kInvalidSpace) {
- is_active_ = true;
- }
- }
- }
-}
-
-
-void LolFilter::InitPropertyFilter(Handle<JSObject> filter_obj) {
- Handle<String> prop_sym = FACTORY->LookupAsciiSymbol("prop");
- MaybeObject* maybe_result = filter_obj->GetProperty(*prop_sym);
- Object* prop_obj;
- if (maybe_result->ToObject(&prop_obj)) {
- if (prop_obj->IsString()) {
- prop_ = Handle<String>(String::cast(prop_obj));
- is_active_ = true;
- }
- }
-}
-
-
-bool LolFilter::MatchesSlow(HeapObject* obj) {
- if ((type_ != kInvalidLiveObjType) && !IsOfType(type_, obj)) {
- return false; // Fail because obj is not of the type of interest.
- }
- if ((space_ != kInvalidSpace) && !InSpace(space_, obj)) {
- return false; // Fail because obj is not in the space of interest.
- }
- if (!prop_.is_null() && obj->IsJSObject()) {
- LookupResult result;
- obj->Lookup(*prop_, &result);
- if (!result.IsProperty()) {
- return false; // Fail because obj does not have the property of interest.
- }
- }
- return true;
-}
-
-
-class LolIterator {
- public:
- LolIterator(LiveObjectList* older, LiveObjectList* newer)
- : older_(older),
- newer_(newer),
- curr_(0),
- elements_(0),
- count_(0),
- index_(0) { }
-
- inline void Init() {
- SetCurrent(newer_);
- // If the elements_ list is empty, then move on to the next list as long
- // as we're not at the last list (indicated by done()).
- while ((elements_ == NULL) && !Done()) {
- SetCurrent(curr_->prev_);
- }
- }
-
- inline bool Done() const {
- return (curr_ == older_);
- }
-
- // Object level iteration.
- inline void Next() {
- index_++;
- if (index_ >= count_) {
- // Iterate backwards until we get to the oldest list.
- while (!Done()) {
- SetCurrent(curr_->prev_);
- // If we have elements to process, we're good to go.
- if (elements_ != NULL) break;
-
- // Else, we should advance to the next older list.
- }
- }
- }
-
- inline int Id() const {
- return elements_[index_].id_;
- }
- inline HeapObject* Obj() const {
- return elements_[index_].obj_;
- }
-
- inline int LolObjCount() const {
- if (curr_ != NULL) return curr_->obj_count_;
- return 0;
- }
-
- protected:
- inline void SetCurrent(LiveObjectList* new_curr) {
- curr_ = new_curr;
- if (curr_ != NULL) {
- elements_ = curr_->elements_;
- count_ = curr_->obj_count_;
- index_ = 0;
- }
- }
-
- LiveObjectList* older_;
- LiveObjectList* newer_;
- LiveObjectList* curr_;
- LiveObjectList::Element* elements_;
- int count_;
- int index_;
-};
-
-
-class LolForwardIterator : public LolIterator {
- public:
- LolForwardIterator(LiveObjectList* first, LiveObjectList* last)
- : LolIterator(first, last) {
- }
-
- inline void Init() {
- SetCurrent(older_);
- // If the elements_ list is empty, then move on to the next list as long
- // as we're not at the last list (indicated by Done()).
- while ((elements_ == NULL) && !Done()) {
- SetCurrent(curr_->next_);
- }
- }
-
- inline bool Done() const {
- return (curr_ == newer_);
- }
-
- // Object level iteration.
- inline void Next() {
- index_++;
- if (index_ >= count_) {
- // Done with current list. Move on to the next.
- while (!Done()) { // If not at the last list already, ...
- SetCurrent(curr_->next_);
- // If we have elements to process, we're good to go.
- if (elements_ != NULL) break;
-
- // Else, we should advance to the next list.
- }
- }
- }
-};
-
-
-// Minimizes the white space in a string. Tabs and newlines are replaced
-// with a space where appropriate.
-static int CompactString(char* str) {
- char* src = str;
- char* dst = str;
- char prev_ch = 0;
- while (*dst != '\0') {
- char ch = *src++;
- // We will treat non-ASCII chars as '?'.
- if ((ch & 0x80) != 0) {
- ch = '?';
- }
- // Compact contiguous whitespace chars into a single ' '.
- if (isspace(ch)) {
- if (prev_ch != ' ') *dst++ = ' ';
- prev_ch = ' ';
- continue;
- }
- *dst++ = ch;
- prev_ch = ch;
- }
- return (dst - str);
-}
-
-
-// Generates a custom description based on the specific type of
-// object we're looking at. We only generate specialized
-// descriptions where we can. In all other cases, we emit the
-// generic info.
-static void GenerateObjectDesc(HeapObject* obj,
- char* buffer,
- int buffer_size) {
- Vector<char> buffer_v(buffer, buffer_size);
- ASSERT(obj != NULL);
- if (obj->IsJSArray()) {
- JSArray* jsarray = JSArray::cast(obj);
- double length = jsarray->length()->Number();
- OS::SNPrintF(buffer_v,
- "%p <%s> len %g",
- reinterpret_cast<void*>(obj),
- GetObjectTypeDesc(obj),
- length);
-
- } else if (obj->IsString()) {
- String* str = String::cast(obj);
- // Only grab up to 160 chars in case they are double byte.
- // We'll only dump 80 of them after we compact them.
- const int kMaxCharToDump = 80;
- const int kMaxBufferSize = kMaxCharToDump * 2;
- SmartArrayPointer<char> str_sp = str->ToCString(DISALLOW_NULLS,
- ROBUST_STRING_TRAVERSAL,
- 0,
- kMaxBufferSize);
- char* str_cstr = *str_sp;
- int length = CompactString(str_cstr);
- OS::SNPrintF(buffer_v,
- "%p <%s> '%.80s%s'",
- reinterpret_cast<void*>(obj),
- GetObjectTypeDesc(obj),
- str_cstr,
- (length > kMaxCharToDump) ? "..." : "");
-
- } else if (obj->IsJSFunction() || obj->IsSharedFunctionInfo()) {
- SharedFunctionInfo* sinfo;
- if (obj->IsJSFunction()) {
- JSFunction* func = JSFunction::cast(obj);
- sinfo = func->shared();
- } else {
- sinfo = SharedFunctionInfo::cast(obj);
- }
-
- String* name = sinfo->DebugName();
- SmartArrayPointer<char> name_sp =
- name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
- char* name_cstr = *name_sp;
-
- HeapStringAllocator string_allocator;
- StringStream stream(&string_allocator);
- sinfo->SourceCodePrint(&stream, 50);
- SmartArrayPointer<const char> source_sp = stream.ToCString();
- const char* source_cstr = *source_sp;
-
- OS::SNPrintF(buffer_v,
- "%p <%s> '%s' %s",
- reinterpret_cast<void*>(obj),
- GetObjectTypeDesc(obj),
- name_cstr,
- source_cstr);
-
- } else if (obj->IsFixedArray()) {
- FixedArray* fixed = FixedArray::cast(obj);
-
- OS::SNPrintF(buffer_v,
- "%p <%s> len %d",
- reinterpret_cast<void*>(obj),
- GetObjectTypeDesc(obj),
- fixed->length());
-
- } else {
- OS::SNPrintF(buffer_v,
- "%p <%s>",
- reinterpret_cast<void*>(obj),
- GetObjectTypeDesc(obj));
- }
-}
-
-
-// Utility function for filling in a line of detail in a verbose dump.
-static bool AddObjDetail(Handle<FixedArray> arr,
- int index,
- int obj_id,
- Handle<HeapObject> target,
- const char* desc_str,
- Handle<String> id_sym,
- Handle<String> desc_sym,
- Handle<String> size_sym,
- Handle<JSObject> detail,
- Handle<String> desc,
- Handle<Object> error) {
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- detail = factory->NewJSObject(isolate->object_function());
- if (detail->IsFailure()) {
- error = detail;
- return false;
- }
-
- int size = 0;
- char buffer[512];
- if (desc_str == NULL) {
- ASSERT(!target.is_null());
- HeapObject* obj = *target;
- GenerateObjectDesc(obj, buffer, sizeof(buffer));
- desc_str = buffer;
- size = obj->Size();
- }
- desc = factory->NewStringFromAscii(CStrVector(desc_str));
- if (desc->IsFailure()) {
- error = desc;
- return false;
- }
-
- { MaybeObject* maybe_result = detail->SetProperty(*id_sym,
- Smi::FromInt(obj_id),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return false;
- }
- { MaybeObject* maybe_result = detail->SetProperty(*desc_sym,
- *desc,
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return false;
- }
- { MaybeObject* maybe_result = detail->SetProperty(*size_sym,
- Smi::FromInt(size),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return false;
- }
-
- arr->set(index, *detail);
- return true;
-}
-
-
-class DumpWriter {
- public:
- virtual ~DumpWriter() {}
-
- virtual void ComputeTotalCountAndSize(LolFilter* filter,
- int* count,
- int* size) = 0;
- virtual bool Write(Handle<FixedArray> elements_arr,
- int start,
- int dump_limit,
- LolFilter* filter,
- Handle<Object> error) = 0;
-};
-
-
-class LolDumpWriter: public DumpWriter {
- public:
- LolDumpWriter(LiveObjectList* older, LiveObjectList* newer)
- : older_(older), newer_(newer) {
- }
-
- void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
- *count = 0;
- *size = 0;
-
- LolIterator it(older_, newer_);
- for (it.Init(); !it.Done(); it.Next()) {
- HeapObject* heap_obj = it.Obj();
- if (!filter->Matches(heap_obj)) {
- continue;
- }
-
- *size += heap_obj->Size();
- (*count)++;
- }
- }
-
- bool Write(Handle<FixedArray> elements_arr,
- int start,
- int dump_limit,
- LolFilter* filter,
- Handle<Object> error) {
- // The lols are listed in latest to earliest. We want to dump from
- // earliest to latest. So, compute the last element to start with.
- int index = 0;
- int count = 0;
-
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
-
- // Prefetch some needed symbols.
- Handle<String> id_sym = factory->LookupAsciiSymbol("id");
- Handle<String> desc_sym = factory->LookupAsciiSymbol("desc");
- Handle<String> size_sym = factory->LookupAsciiSymbol("size");
-
- // Fill the array with the lol object details.
- Handle<JSObject> detail;
- Handle<String> desc;
- Handle<HeapObject> target;
-
- LiveObjectList* first_lol = (older_ != NULL) ?
- older_->next_ : LiveObjectList::first_;
- LiveObjectList* last_lol = (newer_ != NULL) ? newer_->next_ : NULL;
-
- LolForwardIterator it(first_lol, last_lol);
- for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
- HeapObject* heap_obj = it.Obj();
-
- // Skip objects that have been filtered out.
- if (!filter->Matches(heap_obj)) {
- continue;
- }
-
- // Only report objects that are in the section of interest.
- if (count >= start) {
- target = Handle<HeapObject>(heap_obj);
- bool success = AddObjDetail(elements_arr,
- index++,
- it.Id(),
- target,
- NULL,
- id_sym,
- desc_sym,
- size_sym,
- detail,
- desc,
- error);
- if (!success) return false;
- }
- count++;
- }
- return true;
- }
-
- private:
- LiveObjectList* older_;
- LiveObjectList* newer_;
-};
-
-
-class RetainersDumpWriter: public DumpWriter {
- public:
- RetainersDumpWriter(Handle<HeapObject> target,
- Handle<JSObject> instance_filter,
- Handle<JSFunction> args_function)
- : target_(target),
- instance_filter_(instance_filter),
- args_function_(args_function) {
- }
-
- void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) {
- Handle<FixedArray> retainers_arr;
- Handle<Object> error;
-
- *size = -1;
- LiveObjectList::GetRetainers(target_,
- instance_filter_,
- retainers_arr,
- 0,
- Smi::kMaxValue,
- count,
- filter,
- NULL,
- *args_function_,
- error);
- }
-
- bool Write(Handle<FixedArray> elements_arr,
- int start,
- int dump_limit,
- LolFilter* filter,
- Handle<Object> error) {
- int dummy;
- int count;
-
- // Fill the retainer objects.
- count = LiveObjectList::GetRetainers(target_,
- instance_filter_,
- elements_arr,
- start,
- dump_limit,
- &dummy,
- filter,
- NULL,
- *args_function_,
- error);
- if (count < 0) {
- return false;
- }
- return true;
- }
-
- private:
- Handle<HeapObject> target_;
- Handle<JSObject> instance_filter_;
- Handle<JSFunction> args_function_;
-};
-
-
-class LiveObjectSummary {
- public:
- explicit LiveObjectSummary(LolFilter* filter)
- : total_count_(0),
- total_size_(0),
- found_root_(false),
- found_weak_root_(false),
- filter_(filter) {
- memset(counts_, 0, sizeof(counts_[0]) * kNumberOfEntries);
- memset(sizes_, 0, sizeof(sizes_[0]) * kNumberOfEntries);
- }
-
- void Add(HeapObject* heap_obj) {
- int size = heap_obj->Size();
- LiveObjectType type = GetObjectType(heap_obj);
- ASSERT(type != kInvalidLiveObjType);
- counts_[type]++;
- sizes_[type] += size;
- total_count_++;
- total_size_ += size;
- }
-
- void set_found_root() { found_root_ = true; }
- void set_found_weak_root() { found_weak_root_ = true; }
-
- inline int Count(LiveObjectType type) {
- return counts_[type];
- }
- inline int Size(LiveObjectType type) {
- return sizes_[type];
- }
- inline int total_count() {
- return total_count_;
- }
- inline int total_size() {
- return total_size_;
- }
- inline bool found_root() {
- return found_root_;
- }
- inline bool found_weak_root() {
- return found_weak_root_;
- }
- int GetNumberOfEntries() {
- int entries = 0;
- for (int i = 0; i < kNumberOfEntries; i++) {
- if (counts_[i]) entries++;
- }
- return entries;
- }
-
- inline LolFilter* filter() { return filter_; }
-
- static const int kNumberOfEntries = kNumberOfTypes;
-
- private:
- int counts_[kNumberOfEntries];
- int sizes_[kNumberOfEntries];
- int total_count_;
- int total_size_;
- bool found_root_;
- bool found_weak_root_;
-
- LolFilter* filter_;
-};
-
-
-// Abstraction for a summary writer.
-class SummaryWriter {
- public:
- virtual ~SummaryWriter() {}
- virtual void Write(LiveObjectSummary* summary) = 0;
-};
-
-
-// A summary writer for filling in a summary of lol lists and diffs.
-class LolSummaryWriter: public SummaryWriter {
- public:
- LolSummaryWriter(LiveObjectList* older_lol,
- LiveObjectList* newer_lol)
- : older_(older_lol), newer_(newer_lol) {
- }
-
- void Write(LiveObjectSummary* summary) {
- LolFilter* filter = summary->filter();
-
- // Fill the summary with the lol object details.
- LolIterator it(older_, newer_);
- for (it.Init(); !it.Done(); it.Next()) {
- HeapObject* heap_obj = it.Obj();
- if (!filter->Matches(heap_obj)) {
- continue;
- }
- summary->Add(heap_obj);
- }
- }
-
- private:
- LiveObjectList* older_;
- LiveObjectList* newer_;
-};
-
-
-// A summary writer for filling in a retainers list.
-class RetainersSummaryWriter: public SummaryWriter {
- public:
- RetainersSummaryWriter(Handle<HeapObject> target,
- Handle<JSObject> instance_filter,
- Handle<JSFunction> args_function)
- : target_(target),
- instance_filter_(instance_filter),
- args_function_(args_function) {
- }
-
- void Write(LiveObjectSummary* summary) {
- Handle<FixedArray> retainers_arr;
- Handle<Object> error;
- int dummy_total_count;
- LiveObjectList::GetRetainers(target_,
- instance_filter_,
- retainers_arr,
- 0,
- Smi::kMaxValue,
- &dummy_total_count,
- summary->filter(),
- summary,
- *args_function_,
- error);
- }
-
- private:
- Handle<HeapObject> target_;
- Handle<JSObject> instance_filter_;
- Handle<JSFunction> args_function_;
-};
-
-
-uint32_t LiveObjectList::next_element_id_ = 1;
-int LiveObjectList::list_count_ = 0;
-int LiveObjectList::last_id_ = 0;
-LiveObjectList* LiveObjectList::first_ = NULL;
-LiveObjectList* LiveObjectList::last_ = NULL;
-
-
-LiveObjectList::LiveObjectList(LiveObjectList* prev, int capacity)
- : prev_(prev),
- next_(NULL),
- capacity_(capacity),
- obj_count_(0) {
- elements_ = NewArray<Element>(capacity);
- id_ = ++last_id_;
-
- list_count_++;
-}
-
-
-LiveObjectList::~LiveObjectList() {
- DeleteArray<Element>(elements_);
- delete prev_;
-}
-
-
-int LiveObjectList::GetTotalObjCountAndSize(int* size_p) {
- int size = 0;
- int count = 0;
- LiveObjectList* lol = this;
- do {
- // Only compute total size if requested i.e. when size_p is not null.
- if (size_p != NULL) {
- Element* elements = lol->elements_;
- for (int i = 0; i < lol->obj_count_; i++) {
- HeapObject* heap_obj = elements[i].obj_;
- size += heap_obj->Size();
- }
- }
- count += lol->obj_count_;
- lol = lol->prev_;
- } while (lol != NULL);
-
- if (size_p != NULL) {
- *size_p = size;
- }
- return count;
-}
-
-
-// Adds an object to the lol.
-// Returns true if successful, else returns false.
-bool LiveObjectList::Add(HeapObject* obj) {
- // If the object is already accounted for in the prev list which we inherit
- // from, then no need to add it to this list.
- if ((prev() != NULL) && (prev()->Find(obj) != NULL)) {
- return true;
- }
- ASSERT(obj_count_ <= capacity_);
- if (obj_count_ == capacity_) {
- // The heap must have grown and we have more objects than capacity to store
- // them.
- return false; // Fail this addition.
- }
- Element& element = elements_[obj_count_++];
- element.id_ = next_element_id_++;
- element.obj_ = obj;
- return true;
-}
-
-
-// Comparator used for sorting and searching the lol.
-int LiveObjectList::CompareElement(const Element* a, const Element* b) {
- const HeapObject* obj1 = a->obj_;
- const HeapObject* obj2 = b->obj_;
- // For lol elements, it doesn't matter which comes first if 2 elements point
- // to the same object (which gets culled later). Hence, we only care about
- // the the greater than / less than relationships.
- return (obj1 > obj2) ? 1 : (obj1 == obj2) ? 0 : -1;
-}
-
-
-// Looks for the specified object in the lol, and returns its element if found.
-LiveObjectList::Element* LiveObjectList::Find(HeapObject* obj) {
- LiveObjectList* lol = this;
- Element key;
- Element* result = NULL;
-
- key.obj_ = obj;
- // Iterate through the chain of lol's to look for the object.
- while ((result == NULL) && (lol != NULL)) {
- result = reinterpret_cast<Element*>(
- bsearch(&key, lol->elements_, lol->obj_count_,
- sizeof(Element),
- reinterpret_cast<RawComparer>(CompareElement)));
- lol = lol->prev_;
- }
- return result;
-}
-
-
-// "Nullifies" (convert the HeapObject* into an SMI) so that it will get cleaned
-// up in the GCEpilogue, while preserving the sort order of the lol.
-// NOTE: the lols need to be already sorted before NullifyMostRecent() is
-// called.
-void LiveObjectList::NullifyMostRecent(HeapObject* obj) {
- LiveObjectList* lol = last();
- Element key;
- Element* result = NULL;
-
- key.obj_ = obj;
- // Iterate through the chain of lol's to look for the object.
- while (lol != NULL) {
- result = reinterpret_cast<Element*>(
- bsearch(&key, lol->elements_, lol->obj_count_,
- sizeof(Element),
- reinterpret_cast<RawComparer>(CompareElement)));
- if (result != NULL) {
- // Since there may be more than one (we are nullifying dup's after all),
- // find the first in the current lol, and nullify that. The lol should
- // be sorted already to make this easy (see the use of SortAll()).
- int i = result - lol->elements_;
-
- // NOTE: we sort the lol in increasing order. So, if an object has been
- // "nullified" (its lowest bit will be cleared to make it look like an
- // SMI), it would/should show up before the equivalent dups that have not
- // yet been "nullified". Hence, we should be searching backwards for the
- // first occurence of a matching object and nullify that instance. This
- // will ensure that we preserve the expected sorting order.
- for (i--; i > 0; i--) {
- Element* element = &lol->elements_[i];
- HeapObject* curr_obj = element->obj_;
- if (curr_obj != obj) {
- break; // No more matches. Let's move on.
- }
- result = element; // Let this earlier match be the result.
- }
-
- // Nullify the object.
- NullifyNonLivePointer(&result->obj_);
- return;
- }
- lol = lol->prev_;
- }
-}
-
-
-// Sorts the lol.
-void LiveObjectList::Sort() {
- if (obj_count_ > 0) {
- Vector<Element> elements_v(elements_, obj_count_);
- elements_v.Sort(CompareElement);
- }
-}
-
-
-// Sorts all captured lols starting from the latest.
-void LiveObjectList::SortAll() {
- LiveObjectList* lol = last();
- while (lol != NULL) {
- lol->Sort();
- lol = lol->prev_;
- }
-}
-
-
-// Counts the number of objects in the heap.
-static int CountHeapObjects() {
- int count = 0;
- // Iterate over all the heap spaces and count the number of objects.
- HeapIterator iterator;
- HeapObject* heap_obj = NULL;
- while ((heap_obj = iterator.next()) != NULL) {
- count++;
- }
- return count;
-}
-
-
-// Captures a current snapshot of all objects in the heap.
-MaybeObject* LiveObjectList::Capture() {
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- HandleScope scope(isolate);
-
- // Count the number of objects in the heap.
- int total_count = CountHeapObjects();
- int count = total_count;
- int size = 0;
-
- LiveObjectList* last_lol = last();
- if (last_lol != NULL) {
- count -= last_lol->TotalObjCount();
- }
-
- LiveObjectList* lol;
-
- // Create a lol large enough to track all the objects.
- lol = new LiveObjectList(last_lol, count);
- if (lol == NULL) {
- return NULL; // No memory to proceed.
- }
-
- // The HeapIterator needs to be in its own scope because it disables
- // allocation, and we need allocate below.
- {
- // Iterate over all the heap spaces and add the objects.
- HeapIterator iterator;
- HeapObject* heap_obj = NULL;
- bool failed = false;
- while (!failed && (heap_obj = iterator.next()) != NULL) {
- failed = !lol->Add(heap_obj);
- size += heap_obj->Size();
- }
- ASSERT(!failed);
-
- lol->Sort();
-
- // Add the current lol to the list of lols.
- if (last_ != NULL) {
- last_->next_ = lol;
- } else {
- first_ = lol;
- }
- last_ = lol;
-
-#ifdef VERIFY_LOL
- if (FLAG_verify_lol) {
- Verify(true);
- }
-#endif
- }
-
- Handle<String> id_sym = factory->LookupAsciiSymbol("id");
- Handle<String> count_sym = factory->LookupAsciiSymbol("count");
- Handle<String> size_sym = factory->LookupAsciiSymbol("size");
-
- Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
- if (result->IsFailure()) return Object::cast(*result);
-
- { MaybeObject* maybe_result = result->SetProperty(*id_sym,
- Smi::FromInt(lol->id()),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- }
- { MaybeObject* maybe_result = result->SetProperty(*count_sym,
- Smi::FromInt(total_count),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- }
- { MaybeObject* maybe_result = result->SetProperty(*size_sym,
- Smi::FromInt(size),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- }
-
- return *result;
-}
-
-
-// Delete doesn't actually deletes an lol. It just marks it as invisible since
-// its contents are considered to be part of subsequent lists as well. The
-// only time we'll actually delete the lol is when we Reset() or if the lol is
-// invisible, and its element count reaches 0.
-bool LiveObjectList::Delete(int id) {
- LiveObjectList* lol = last();
- while (lol != NULL) {
- if (lol->id() == id) {
- break;
- }
- lol = lol->prev_;
- }
-
- // If no lol is found for this id, then we fail to delete.
- if (lol == NULL) return false;
-
- // Else, mark the lol as invisible i.e. id == 0.
- lol->id_ = 0;
- list_count_--;
- ASSERT(list_count_ >= 0);
- if (lol->obj_count_ == 0) {
- // Point the next lol's prev to this lol's prev.
- LiveObjectList* next = lol->next_;
- LiveObjectList* prev = lol->prev_;
- // Point next's prev to prev.
- if (next != NULL) {
- next->prev_ = lol->prev_;
- } else {
- last_ = lol->prev_;
- }
- // Point prev's next to next.
- if (prev != NULL) {
- prev->next_ = lol->next_;
- } else {
- first_ = lol->next_;
- }
-
- lol->prev_ = NULL;
- lol->next_ = NULL;
-
- // Delete this now empty and invisible lol.
- delete lol;
- }
-
- // Just in case we've marked everything invisible, then clean up completely.
- if (list_count_ == 0) {
- Reset();
- }
-
- return true;
-}
-
-
-MaybeObject* LiveObjectList::Dump(int older_id,
- int newer_id,
- int start_idx,
- int dump_limit,
- Handle<JSObject> filter_obj) {
- if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) {
- return Failure::Exception(); // Fail: 0 is not a valid lol id.
- }
- if (newer_id < older_id) {
- // They are not in the expected order. Swap them.
- int temp = older_id;
- older_id = newer_id;
- newer_id = temp;
- }
-
- LiveObjectList* newer_lol = FindLolForId(newer_id, last());
- LiveObjectList* older_lol = FindLolForId(older_id, newer_lol);
-
- // If the id is defined, and we can't find a LOL for it, then we have an
- // invalid id.
- if ((newer_id != 0) && (newer_lol == NULL)) {
- return Failure::Exception(); // Fail: the newer lol id is invalid.
- }
- if ((older_id != 0) && (older_lol == NULL)) {
- return Failure::Exception(); // Fail: the older lol id is invalid.
- }
-
- LolFilter filter(filter_obj);
- LolDumpWriter writer(older_lol, newer_lol);
- return DumpPrivate(&writer, start_idx, dump_limit, &filter);
-}
-
-
-MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer,
- int start,
- int dump_limit,
- LolFilter* filter) {
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
-
- HandleScope scope(isolate);
-
- // Calculate the number of entries of the dump.
- int count = -1;
- int size = -1;
- writer->ComputeTotalCountAndSize(filter, &count, &size);
-
- // Adjust for where to start the dump.
- if ((start < 0) || (start >= count)) {
- return Failure::Exception(); // invalid start.
- }
-
- int remaining_count = count - start;
- if (dump_limit > remaining_count) {
- dump_limit = remaining_count;
- }
-
- // Allocate an array to hold the result.
- Handle<FixedArray> elements_arr = factory->NewFixedArray(dump_limit);
- if (elements_arr->IsFailure()) return Object::cast(*elements_arr);
-
- // Fill in the dump.
- Handle<Object> error;
- bool success = writer->Write(elements_arr,
- start,
- dump_limit,
- filter,
- error);
- if (!success) return Object::cast(*error);
-
- MaybeObject* maybe_result;
-
- // Allocate the result body.
- Handle<JSObject> body = factory->NewJSObject(isolate->object_function());
- if (body->IsFailure()) return Object::cast(*body);
-
- // Set the updated body.count.
- Handle<String> count_sym = factory->LookupAsciiSymbol("count");
- maybe_result = body->SetProperty(*count_sym,
- Smi::FromInt(count),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- // Set the updated body.size if appropriate.
- if (size >= 0) {
- Handle<String> size_sym = factory->LookupAsciiSymbol("size");
- maybe_result = body->SetProperty(*size_sym,
- Smi::FromInt(size),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- }
-
- // Set body.first_index.
- Handle<String> first_sym = factory->LookupAsciiSymbol("first_index");
- maybe_result = body->SetProperty(*first_sym,
- Smi::FromInt(start),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- // Allocate the JSArray of the elements.
- Handle<JSObject> elements = factory->NewJSObject(isolate->array_function());
- if (elements->IsFailure()) return Object::cast(*elements);
-
- maybe_result = Handle<JSArray>::cast(elements)->SetContent(*elements_arr);
- if (maybe_result->IsFailure()) return maybe_result;
-
- // Set body.elements.
- Handle<String> elements_sym = factory->LookupAsciiSymbol("elements");
- maybe_result = body->SetProperty(*elements_sym,
- *elements,
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- return *body;
-}
-
-
-MaybeObject* LiveObjectList::Summarize(int older_id,
- int newer_id,
- Handle<JSObject> filter_obj) {
- if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) {
- return Failure::Exception(); // Fail: 0 is not a valid lol id.
- }
- if (newer_id < older_id) {
- // They are not in the expected order. Swap them.
- int temp = older_id;
- older_id = newer_id;
- newer_id = temp;
- }
-
- LiveObjectList* newer_lol = FindLolForId(newer_id, last());
- LiveObjectList* older_lol = FindLolForId(older_id, newer_lol);
-
- // If the id is defined, and we can't find a LOL for it, then we have an
- // invalid id.
- if ((newer_id != 0) && (newer_lol == NULL)) {
- return Failure::Exception(); // Fail: the newer lol id is invalid.
- }
- if ((older_id != 0) && (older_lol == NULL)) {
- return Failure::Exception(); // Fail: the older lol id is invalid.
- }
-
- LolFilter filter(filter_obj);
- LolSummaryWriter writer(older_lol, newer_lol);
- return SummarizePrivate(&writer, &filter, false);
-}
-
-
-// Creates a summary report for the debugger.
-// Note: the SummaryWriter takes care of iterating over objects and filling in
-// the summary.
-MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer,
- LolFilter* filter,
- bool is_tracking_roots) {
- HandleScope scope;
- MaybeObject* maybe_result;
-
- LiveObjectSummary summary(filter);
- writer->Write(&summary);
-
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
-
- // The result body will look like this:
- // body: {
- // count: <total_count>,
- // size: <total_size>,
- // found_root: <boolean>, // optional.
- // found_weak_root: <boolean>, // optional.
- // summary: [
- // {
- // desc: "<object type name>",
- // count: <count>,
- // size: size
- // },
- // ...
- // ]
- // }
-
- // Prefetch some needed symbols.
- Handle<String> desc_sym = factory->LookupAsciiSymbol("desc");
- Handle<String> count_sym = factory->LookupAsciiSymbol("count");
- Handle<String> size_sym = factory->LookupAsciiSymbol("size");
- Handle<String> summary_sym = factory->LookupAsciiSymbol("summary");
-
- // Allocate the summary array.
- int entries_count = summary.GetNumberOfEntries();
- Handle<FixedArray> summary_arr =
- factory->NewFixedArray(entries_count);
- if (summary_arr->IsFailure()) return Object::cast(*summary_arr);
-
- int idx = 0;
- for (int i = 0; i < LiveObjectSummary::kNumberOfEntries; i++) {
- // Allocate the summary record.
- Handle<JSObject> detail = factory->NewJSObject(isolate->object_function());
- if (detail->IsFailure()) return Object::cast(*detail);
-
- // Fill in the summary record.
- LiveObjectType type = static_cast<LiveObjectType>(i);
- int count = summary.Count(type);
- if (count) {
- const char* desc_cstr = GetObjectTypeDesc(type);
- Handle<String> desc = factory->LookupAsciiSymbol(desc_cstr);
- int size = summary.Size(type);
-
- maybe_result = detail->SetProperty(*desc_sym,
- *desc,
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- maybe_result = detail->SetProperty(*count_sym,
- Smi::FromInt(count),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- maybe_result = detail->SetProperty(*size_sym,
- Smi::FromInt(size),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- summary_arr->set(idx++, *detail);
- }
- }
-
- // Wrap the summary fixed array in a JS array.
- Handle<JSObject> summary_obj =
- factory->NewJSObject(isolate->array_function());
- if (summary_obj->IsFailure()) return Object::cast(*summary_obj);
-
- maybe_result = Handle<JSArray>::cast(summary_obj)->SetContent(*summary_arr);
- if (maybe_result->IsFailure()) return maybe_result;
-
- // Create the body object.
- Handle<JSObject> body = factory->NewJSObject(isolate->object_function());
- if (body->IsFailure()) return Object::cast(*body);
-
- // Fill out the body object.
- int total_count = summary.total_count();
- int total_size = summary.total_size();
- maybe_result = body->SetProperty(*count_sym,
- Smi::FromInt(total_count),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- maybe_result = body->SetProperty(*size_sym,
- Smi::FromInt(total_size),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- if (is_tracking_roots) {
- int found_root = summary.found_root();
- int found_weak_root = summary.found_weak_root();
- Handle<String> root_sym = factory->LookupAsciiSymbol("found_root");
- Handle<String> weak_root_sym =
- factory->LookupAsciiSymbol("found_weak_root");
- maybe_result = body->SetProperty(*root_sym,
- Smi::FromInt(found_root),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- maybe_result = body->SetProperty(*weak_root_sym,
- Smi::FromInt(found_weak_root),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- }
-
- maybe_result = body->SetProperty(*summary_sym,
- *summary_obj,
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- return *body;
-}
-
-
-// Returns an array listing the captured lols.
-// Note: only dumps the section starting at start_idx and only up to
-// dump_limit entries.
-MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) {
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
-
- HandleScope scope(isolate);
- MaybeObject* maybe_result;
-
- int total_count = LiveObjectList::list_count();
- int dump_count = total_count;
-
- // Adjust for where to start the dump.
- if (total_count == 0) {
- start_idx = 0; // Ensure this to get an empty list.
- } else if ((start_idx < 0) || (start_idx >= total_count)) {
- return Failure::Exception(); // invalid start.
- }
- dump_count -= start_idx;
-
- // Adjust for the dump limit.
- if (dump_count > dump_limit) {
- dump_count = dump_limit;
- }
-
- // Allocate an array to hold the result.
- Handle<FixedArray> list = factory->NewFixedArray(dump_count);
- if (list->IsFailure()) return Object::cast(*list);
-
- // Prefetch some needed symbols.
- Handle<String> id_sym = factory->LookupAsciiSymbol("id");
- Handle<String> count_sym = factory->LookupAsciiSymbol("count");
- Handle<String> size_sym = factory->LookupAsciiSymbol("size");
-
- // Fill the array with the lol details.
- int idx = 0;
- LiveObjectList* lol = first_;
- while ((lol != NULL) && (idx < start_idx)) { // Skip tail entries.
- if (lol->id() != 0) {
- idx++;
- }
- lol = lol->next();
- }
- idx = 0;
- while ((lol != NULL) && (dump_limit != 0)) {
- if (lol->id() != 0) {
- int count;
- int size;
- count = lol->GetTotalObjCountAndSize(&size);
-
- Handle<JSObject> detail =
- factory->NewJSObject(isolate->object_function());
- if (detail->IsFailure()) return Object::cast(*detail);
-
- maybe_result = detail->SetProperty(*id_sym,
- Smi::FromInt(lol->id()),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- maybe_result = detail->SetProperty(*count_sym,
- Smi::FromInt(count),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- maybe_result = detail->SetProperty(*size_sym,
- Smi::FromInt(size),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
- list->set(idx++, *detail);
- dump_limit--;
- }
- lol = lol->next();
- }
-
- // Return the result as a JS array.
- Handle<JSObject> lols = factory->NewJSObject(isolate->array_function());
-
- maybe_result = Handle<JSArray>::cast(lols)->SetContent(*list);
- if (maybe_result->IsFailure()) return maybe_result;
-
- Handle<JSObject> result = factory->NewJSObject(isolate->object_function());
- if (result->IsFailure()) return Object::cast(*result);
-
- maybe_result = result->SetProperty(*count_sym,
- Smi::FromInt(total_count),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- Handle<String> first_sym = factory->LookupAsciiSymbol("first_index");
- maybe_result = result->SetProperty(*first_sym,
- Smi::FromInt(start_idx),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- Handle<String> lists_sym = factory->LookupAsciiSymbol("lists");
- maybe_result = result->SetProperty(*lists_sym,
- *lols,
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- return *result;
-}
-
-
-// Deletes all captured lols.
-void LiveObjectList::Reset() {
- LiveObjectList* lol = last();
- // Just delete the last. Each lol will delete it's prev automatically.
- delete lol;
-
- next_element_id_ = 1;
- list_count_ = 0;
- last_id_ = 0;
- first_ = NULL;
- last_ = NULL;
-}
-
-
-// Gets the object for the specified obj id.
-Object* LiveObjectList::GetObj(int obj_id) {
- Element* element = FindElementFor<int>(GetElementId, obj_id);
- if (element != NULL) {
- return Object::cast(element->obj_);
- }
- return HEAP->undefined_value();
-}
-
-
-// Gets the obj id for the specified address if valid.
-int LiveObjectList::GetObjId(Object* obj) {
- // Make a heap object pointer from the address.
- HeapObject* hobj = HeapObject::cast(obj);
- Element* element = FindElementFor<HeapObject*>(GetElementObj, hobj);
- if (element != NULL) {
- return element->id_;
- }
- return 0; // Invalid address.
-}
-
-
-// Gets the obj id for the specified address if valid.
-Object* LiveObjectList::GetObjId(Handle<String> address) {
- SmartArrayPointer<char> addr_str =
- address->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
-
- Isolate* isolate = Isolate::Current();
-
- // Extract the address value from the string.
- int value =
- static_cast<int>(StringToInt(isolate->unicode_cache(), *address, 16));
- Object* obj = reinterpret_cast<Object*>(value);
- return Smi::FromInt(GetObjId(obj));
-}
-
-
-// Helper class for copying HeapObjects.
-class LolVisitor: public ObjectVisitor {
- public:
- LolVisitor(HeapObject* target, Handle<HeapObject> handle_to_skip)
- : target_(target), handle_to_skip_(handle_to_skip), found_(false) {}
-
- void VisitPointer(Object** p) { CheckPointer(p); }
-
- void VisitPointers(Object** start, Object** end) {
- // Check all HeapObject pointers in [start, end).
- for (Object** p = start; !found() && p < end; p++) CheckPointer(p);
- }
-
- inline bool found() const { return found_; }
- inline bool reset() { return found_ = false; }
-
- private:
- inline void CheckPointer(Object** p) {
- Object* object = *p;
- if (HeapObject::cast(object) == target_) {
- // We may want to skip this handle because the handle may be a local
- // handle in a handle scope in one of our callers. Once we return,
- // that handle will be popped. Hence, we don't want to count it as
- // a root that would have kept the target object alive.
- if (!handle_to_skip_.is_null() &&
- handle_to_skip_.location() == reinterpret_cast<HeapObject**>(p)) {
- return; // Skip this handle.
- }
- found_ = true;
- }
- }
-
- HeapObject* target_;
- Handle<HeapObject> handle_to_skip_;
- bool found_;
-};
-
-
-inline bool AddRootRetainerIfFound(const LolVisitor& visitor,
- LolFilter* filter,
- LiveObjectSummary* summary,
- void (*SetRootFound)(LiveObjectSummary* s),
- int start,
- int dump_limit,
- int* total_count,
- Handle<FixedArray> retainers_arr,
- int* count,
- int* index,
- const char* root_name,
- Handle<String> id_sym,
- Handle<String> desc_sym,
- Handle<String> size_sym,
- Handle<Object> error) {
- HandleScope scope;
-
- // Scratch handles.
- Handle<JSObject> detail;
- Handle<String> desc;
- Handle<HeapObject> retainer;
-
- if (visitor.found()) {
- if (!filter->is_active()) {
- (*total_count)++;
- if (summary) {
- SetRootFound(summary);
- } else if ((*total_count > start) && ((*index) < dump_limit)) {
- (*count)++;
- if (!retainers_arr.is_null()) {
- return AddObjDetail(retainers_arr,
- (*index)++,
- 0,
- retainer,
- root_name,
- id_sym,
- desc_sym,
- size_sym,
- detail,
- desc,
- error);
- }
- }
- }
- }
- return true;
-}
-
-
-inline void SetFoundRoot(LiveObjectSummary* summary) {
- summary->set_found_root();
-}
-
-
-inline void SetFoundWeakRoot(LiveObjectSummary* summary) {
- summary->set_found_weak_root();
-}
-
-
-int LiveObjectList::GetRetainers(Handle<HeapObject> target,
- Handle<JSObject> instance_filter,
- Handle<FixedArray> retainers_arr,
- int start,
- int dump_limit,
- int* total_count,
- LolFilter* filter,
- LiveObjectSummary* summary,
- JSFunction* arguments_function,
- Handle<Object> error) {
- HandleScope scope;
-
- // Scratch handles.
- Handle<JSObject> detail;
- Handle<String> desc;
- Handle<HeapObject> retainer;
-
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
-
- // Prefetch some needed symbols.
- Handle<String> id_sym = factory->LookupAsciiSymbol("id");
- Handle<String> desc_sym = factory->LookupAsciiSymbol("desc");
- Handle<String> size_sym = factory->LookupAsciiSymbol("size");
-
- NoHandleAllocation ha;
- int count = 0;
- int index = 0;
- Handle<JSObject> last_obj;
-
- *total_count = 0;
-
- // Iterate roots.
- LolVisitor lol_visitor(*target, target);
- isolate->heap()->IterateStrongRoots(&lol_visitor, VISIT_ALL);
- if (!AddRootRetainerIfFound(lol_visitor,
- filter,
- summary,
- SetFoundRoot,
- start,
- dump_limit,
- total_count,
- retainers_arr,
- &count,
- &index,
- "<root>",
- id_sym,
- desc_sym,
- size_sym,
- error)) {
- return -1;
- }
-
- lol_visitor.reset();
- isolate->heap()->IterateWeakRoots(&lol_visitor, VISIT_ALL);
- if (!AddRootRetainerIfFound(lol_visitor,
- filter,
- summary,
- SetFoundWeakRoot,
- start,
- dump_limit,
- total_count,
- retainers_arr,
- &count,
- &index,
- "<weak root>",
- id_sym,
- desc_sym,
- size_sym,
- error)) {
- return -1;
- }
-
- // Iterate the live object lists.
- LolIterator it(NULL, last());
- for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) {
- HeapObject* heap_obj = it.Obj();
-
- // Only look at all JSObjects.
- if (heap_obj->IsJSObject()) {
- // Skip context extension objects and argument arrays as these are
- // checked in the context of functions using them.
- JSObject* obj = JSObject::cast(heap_obj);
- if (obj->IsJSContextExtensionObject() ||
- obj->map()->constructor() == arguments_function) {
- continue;
- }
-
- // Check if the JS object has a reference to the object looked for.
- if (obj->ReferencesObject(*target)) {
- // Check instance filter if supplied. This is normally used to avoid
- // references from mirror objects (see Runtime_IsInPrototypeChain).
- if (!instance_filter->IsUndefined()) {
- Object* V = obj;
- while (true) {
- Object* prototype = V->GetPrototype();
- if (prototype->IsNull()) {
- break;
- }
- if (*instance_filter == prototype) {
- obj = NULL; // Don't add this object.
- break;
- }
- V = prototype;
- }
- }
-
- if (obj != NULL) {
- // Skip objects that have been filtered out.
- if (filter->Matches(heap_obj)) {
- continue;
- }
-
- // Valid reference found add to instance array if supplied an update
- // count.
- last_obj = Handle<JSObject>(obj);
- (*total_count)++;
-
- if (summary != NULL) {
- summary->Add(heap_obj);
- } else if ((*total_count > start) && (index < dump_limit)) {
- count++;
- if (!retainers_arr.is_null()) {
- retainer = Handle<HeapObject>(heap_obj);
- bool success = AddObjDetail(retainers_arr,
- index++,
- it.Id(),
- retainer,
- NULL,
- id_sym,
- desc_sym,
- size_sym,
- detail,
- desc,
- error);
- if (!success) return -1;
- }
- }
- }
- }
- }
- }
-
- // Check for circular reference only. This can happen when the object is only
- // referenced from mirrors and has a circular reference in which case the
- // object is not really alive and would have been garbage collected if not
- // referenced from the mirror.
-
- if (*total_count == 1 && !last_obj.is_null() && *last_obj == *target) {
- count = 0;
- *total_count = 0;
- }
-
- return count;
-}
-
-
-MaybeObject* LiveObjectList::GetObjRetainers(int obj_id,
- Handle<JSObject> instance_filter,
- bool verbose,
- int start,
- int dump_limit,
- Handle<JSObject> filter_obj) {
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- Heap* heap = isolate->heap();
-
- HandleScope scope(isolate);
-
- // Get the target object.
- HeapObject* heap_obj = HeapObject::cast(GetObj(obj_id));
- if (heap_obj == heap->undefined_value()) {
- return heap_obj;
- }
-
- Handle<HeapObject> target = Handle<HeapObject>(heap_obj);
-
- // Get the constructor function for context extension and arguments array.
- JSObject* arguments_boilerplate =
- isolate->context()->native_context()->arguments_boilerplate();
- JSFunction* arguments_function =
- JSFunction::cast(arguments_boilerplate->map()->constructor());
-
- Handle<JSFunction> args_function = Handle<JSFunction>(arguments_function);
- LolFilter filter(filter_obj);
-
- if (!verbose) {
- RetainersSummaryWriter writer(target, instance_filter, args_function);
- return SummarizePrivate(&writer, &filter, true);
-
- } else {
- RetainersDumpWriter writer(target, instance_filter, args_function);
- Object* body_obj;
- MaybeObject* maybe_result =
- DumpPrivate(&writer, start, dump_limit, &filter);
- if (!maybe_result->ToObject(&body_obj)) {
- return maybe_result;
- }
-
- // Set body.id.
- Handle<JSObject> body = Handle<JSObject>(JSObject::cast(body_obj));
- Handle<String> id_sym = factory->LookupAsciiSymbol("id");
- maybe_result = body->SetProperty(*id_sym,
- Smi::FromInt(obj_id),
- NONE,
- kNonStrictMode);
- if (maybe_result->IsFailure()) return maybe_result;
-
- return *body;
- }
-}
-
-
-Object* LiveObjectList::PrintObj(int obj_id) {
- Object* obj = GetObj(obj_id);
- if (!obj) {
- return HEAP->undefined_value();
- }
-
- EmbeddedVector<char, 128> temp_filename;
- static int temp_count = 0;
- const char* path_prefix = ".";
-
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- Heap* heap = isolate->heap();
-
- if (FLAG_lol_workdir) {
- path_prefix = FLAG_lol_workdir;
- }
- OS::SNPrintF(temp_filename, "%s/lol-print-%d", path_prefix, ++temp_count);
-
- FILE* f = OS::FOpen(temp_filename.start(), "w+");
-
- PrintF(f, "@%d ", LiveObjectList::GetObjId(obj));
-#ifdef OBJECT_PRINT
-#ifdef INSPECTOR
- Inspector::DumpObjectType(f, obj);
-#endif // INSPECTOR
- PrintF(f, "\n");
- obj->Print(f);
-#else // !OBJECT_PRINT
- obj->ShortPrint(f);
-#endif // !OBJECT_PRINT
- PrintF(f, "\n");
- Flush(f);
- fclose(f);
-
- // Create a string from the temp_file.
- // Note: the mmapped resource will take care of closing the file.
- MemoryMappedExternalResource* resource =
- new MemoryMappedExternalResource(temp_filename.start(), true);
- if (resource->exists() && !resource->is_empty()) {
- ASSERT(resource->IsAscii());
- Handle<String> dump_string =
- factory->NewExternalStringFromAscii(resource);
- heap->external_string_table()->AddString(*dump_string);
- return *dump_string;
- } else {
- delete resource;
- }
- return HEAP->undefined_value();
-}
-
-
-class LolPathTracer: public PathTracer {
- public:
- LolPathTracer(FILE* out,
- Object* search_target,
- WhatToFind what_to_find)
- : PathTracer(search_target, what_to_find, VISIT_ONLY_STRONG), out_(out) {}
-
- private:
- void ProcessResults();
-
- FILE* out_;
-};
-
-
-void LolPathTracer::ProcessResults() {
- if (found_target_) {
- PrintF(out_, "=====================================\n");
- PrintF(out_, "==== Path to object ====\n");
- PrintF(out_, "=====================================\n\n");
-
- ASSERT(!object_stack_.is_empty());
- Object* prev = NULL;
- for (int i = 0, index = 0; i < object_stack_.length(); i++) {
- Object* obj = object_stack_[i];
-
- // Skip this object if it is basically the internals of the
- // previous object (which would have dumped its details already).
- if (prev && prev->IsJSObject() &&
- (obj != search_target_)) {
- JSObject* jsobj = JSObject::cast(prev);
- if (obj->IsFixedArray() &&
- jsobj->properties() == FixedArray::cast(obj)) {
- // Skip this one because it would have been printed as the
- // properties of the last object already.
- continue;
- } else if (obj->IsHeapObject() &&
- jsobj->elements() == HeapObject::cast(obj)) {
- // Skip this one because it would have been printed as the
- // elements of the last object already.
- continue;
- }
- }
-
- // Print a connecting arrow.
- if (i > 0) PrintF(out_, "\n |\n |\n V\n\n");
-
- // Print the object index.
- PrintF(out_, "[%d] ", ++index);
-
- // Print the LOL object ID:
- int id = LiveObjectList::GetObjId(obj);
- if (id > 0) PrintF(out_, "@%d ", id);
-
-#ifdef OBJECT_PRINT
-#ifdef INSPECTOR
- Inspector::DumpObjectType(out_, obj);
-#endif // INSPECTOR
- PrintF(out_, "\n");
- obj->Print(out_);
-#else // !OBJECT_PRINT
- obj->ShortPrint(out_);
- PrintF(out_, "\n");
-#endif // !OBJECT_PRINT
- Flush(out_);
- }
- PrintF(out_, "\n");
- PrintF(out_, "=====================================\n\n");
- Flush(out_);
- }
-}
-
-
-Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) {
- EmbeddedVector<char, 128> temp_filename;
- static int temp_count = 0;
- const char* path_prefix = ".";
-
- if (FLAG_lol_workdir) {
- path_prefix = FLAG_lol_workdir;
- }
- OS::SNPrintF(temp_filename, "%s/lol-getpath-%d", path_prefix, ++temp_count);
-
- FILE* f = OS::FOpen(temp_filename.start(), "w+");
-
- Isolate* isolate = Isolate::Current();
- Factory* factory = isolate->factory();
- Heap* heap = isolate->heap();
-
- // Save the previous verbosity.
- bool prev_verbosity = FLAG_use_verbose_printer;
- FLAG_use_verbose_printer = false;
-
- // Dump the paths.
- {
- // The tracer needs to be scoped because its usage asserts no allocation,
- // and we need to allocate the result string below.
- LolPathTracer tracer(f, obj2, LolPathTracer::FIND_FIRST);
-
- bool found = false;
- if (obj1 == NULL) {
- // Check for ObjectGroups that references this object.
- // TODO(mlam): refactor this to be more modular.
- {
- List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
- for (int i = 0; i < groups->length(); i++) {
- ObjectGroup* group = groups->at(i);
- if (group == NULL) continue;
-
- bool found_group = false;
- for (size_t j = 0; j < group->length_; j++) {
- Object* object = *(group->objects_[j]);
- HeapObject* hobj = HeapObject::cast(object);
- if (obj2 == hobj) {
- found_group = true;
- break;
- }
- }
-
- if (found_group) {
- PrintF(f,
- "obj %p is a member of object group %p {\n",
- reinterpret_cast<void*>(obj2),
- reinterpret_cast<void*>(group));
- for (size_t j = 0; j < group->length_; j++) {
- Object* object = *(group->objects_[j]);
- if (!object->IsHeapObject()) continue;
-
- HeapObject* hobj = HeapObject::cast(object);
- int id = GetObjId(hobj);
- if (id != 0) {
- PrintF(f, " @%d:", id);
- } else {
- PrintF(f, " <no id>:");
- }
-
- char buffer[512];
- GenerateObjectDesc(hobj, buffer, sizeof(buffer));
- PrintF(f, " %s", buffer);
- if (hobj == obj2) {
- PrintF(f, " <===");
- }
- PrintF(f, "\n");
- }
- PrintF(f, "}\n");
- }
- }
- }
-
- PrintF(f, "path from roots to obj %p\n", reinterpret_cast<void*>(obj2));
- heap->IterateRoots(&tracer, VISIT_ONLY_STRONG);
- found = tracer.found();
-
- if (!found) {
- PrintF(f, " No paths found. Checking symbol tables ...\n");
- SymbolTable* symbol_table = HEAP->raw_unchecked_symbol_table();
- tracer.VisitPointers(reinterpret_cast<Object**>(&symbol_table),
- reinterpret_cast<Object**>(&symbol_table)+1);
- found = tracer.found();
- if (!found) {
- symbol_table->IteratePrefix(&tracer);
- found = tracer.found();
- }
- }
-
- if (!found) {
- PrintF(f, " No paths found. Checking weak roots ...\n");
- // Check weak refs next.
- isolate->global_handles()->IterateWeakRoots(&tracer);
- found = tracer.found();
- }
-
- } else {
- PrintF(f, "path from obj %p to obj %p:\n",
- reinterpret_cast<void*>(obj1), reinterpret_cast<void*>(obj2));
- tracer.TracePathFrom(reinterpret_cast<Object**>(&obj1));
- found = tracer.found();
- }
-
- if (!found) {
- PrintF(f, " No paths found\n\n");
- }
- }
-
- // Flush and clean up the dumped file.
- Flush(f);
- fclose(f);
-
- // Restore the previous verbosity.
- FLAG_use_verbose_printer = prev_verbosity;
-
- // Create a string from the temp_file.
- // Note: the mmapped resource will take care of closing the file.
- MemoryMappedExternalResource* resource =
- new MemoryMappedExternalResource(temp_filename.start(), true);
- if (resource->exists() && !resource->is_empty()) {
- ASSERT(resource->IsAscii());
- Handle<String> path_string =
- factory->NewExternalStringFromAscii(resource);
- heap->external_string_table()->AddString(*path_string);
- return *path_string;
- } else {
- delete resource;
- }
- return heap->undefined_value();
-}
-
-
-Object* LiveObjectList::GetPath(int obj_id1,
- int obj_id2,
- Handle<JSObject> instance_filter) {
- HandleScope scope;
-
- // Get the target object.
- HeapObject* obj1 = NULL;
- if (obj_id1 != 0) {
- obj1 = HeapObject::cast(GetObj(obj_id1));
- if (obj1 == HEAP->undefined_value()) {
- return obj1;
- }
- }
-
- HeapObject* obj2 = HeapObject::cast(GetObj(obj_id2));
- if (obj2 == HEAP->undefined_value()) {
- return obj2;
- }
-
- return GetPathPrivate(obj1, obj2);
-}
-
-
-void LiveObjectList::DoProcessNonLive(HeapObject* obj) {
- // We should only be called if we have at least one lol to search.
- ASSERT(last() != NULL);
- Element* element = last()->Find(obj);
- if (element != NULL) {
- NullifyNonLivePointer(&element->obj_);
- }
-}
-
-
-void LiveObjectList::IterateElementsPrivate(ObjectVisitor* v) {
- LiveObjectList* lol = last();
- while (lol != NULL) {
- Element* elements = lol->elements_;
- int count = lol->obj_count_;
- for (int i = 0; i < count; i++) {
- HeapObject** p = &elements[i].obj_;
- v->VisitPointer(reinterpret_cast<Object** >(p));
- }
- lol = lol->prev_;
- }
-}
-
-
-// Purpose: Called by GCEpilogue to purge duplicates. Not to be called by
-// anyone else.
-void LiveObjectList::PurgeDuplicates() {
- bool is_sorted = false;
- LiveObjectList* lol = last();
- if (!lol) {
- return; // Nothing to purge.
- }
-
- int total_count = lol->TotalObjCount();
- if (!total_count) {
- return; // Nothing to purge.
- }
-
- Element* elements = NewArray<Element>(total_count);
- int count = 0;
-
- // Copy all the object elements into a consecutive array.
- while (lol) {
- memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
- count += lol->obj_count_;
- lol = lol->prev_;
- }
- qsort(elements, total_count, sizeof(Element),
- reinterpret_cast<RawComparer>(CompareElement));
-
- ASSERT(count == total_count);
-
- // Iterate over all objects in the consolidated list and check for dups.
- total_count--;
- for (int i = 0; i < total_count; ) {
- Element* curr = &elements[i];
- HeapObject* curr_obj = curr->obj_;
- int j = i+1;
- bool done = false;
-
- while (!done && (j < total_count)) {
- // Process if the element's object is still live after the current GC.
- // Non-live objects will be converted to SMIs i.e. not HeapObjects.
- if (curr_obj->IsHeapObject()) {
- Element* next = &elements[j];
- HeapObject* next_obj = next->obj_;
- if (next_obj->IsHeapObject()) {
- if (curr_obj != next_obj) {
- done = true;
- continue; // Live object but no match. Move on.
- }
-
- // NOTE: we've just GCed the LOLs. Hence, they are no longer sorted.
- // Since we detected at least one need to search for entries, we'll
- // sort it to enable the use of NullifyMostRecent() below. We only
- // need to sort it once (except for one exception ... see below).
- if (!is_sorted) {
- SortAll();
- is_sorted = true;
- }
-
- // We have a match. Need to nullify the most recent ref to this
- // object. We'll keep the oldest ref:
- // Note: we will nullify the element record in the LOL
- // database, not in the local sorted copy of the elements.
- NullifyMostRecent(curr_obj);
- }
- }
- // Either the object was already marked for purging, or we just marked
- // it. Either way, if there's more than one dup, then we need to check
- // the next element for another possible dup against the current as well
- // before we move on. So, here we go.
- j++;
- }
-
- // We can move on to checking the match on the next element.
- i = j;
- }
-
- DeleteArray<Element>(elements);
-}
-
-
-// Purpose: Purges dead objects and resorts the LOLs.
-void LiveObjectList::GCEpiloguePrivate() {
- // Note: During the GC, ConsStrings may be collected and pointers may be
- // forwarded to its constituent string. As a result, we may find dupes of
- // objects references in the LOL list.
- // Another common way we get dups is that free chunks that have been swept
- // in the oldGen heap may be kept as ByteArray objects in a free list.
- //
- // When we promote live objects from the youngGen, the object may be moved
- // to the start of these free chunks. Since there is no free or move event
- // for the free chunks, their addresses will show up 2 times: once for their
- // original free ByteArray selves, and once for the newly promoted youngGen
- // object. Hence, we can get a duplicate address in the LOL again.
- //
- // We need to eliminate these dups because the LOL implementation expects to
- // only have at most one unique LOL reference to any object at any time.
- PurgeDuplicates();
-
- // After the GC, sweep away all free'd Elements and compact.
- LiveObjectList* prev = NULL;
- LiveObjectList* next = NULL;
-
- // Iterating from the youngest lol to the oldest lol.
- for (LiveObjectList* lol = last(); lol; lol = prev) {
- Element* elements = lol->elements_;
- prev = lol->prev(); // Save the prev.
-
- // Remove any references to collected objects.
- int i = 0;
- while (i < lol->obj_count_) {
- Element& element = elements[i];
- if (!element.obj_->IsHeapObject()) {
- // If the HeapObject address was converted into a SMI, then this
- // is a dead object. Copy the last element over this one.
- element = elements[lol->obj_count_ - 1];
- lol->obj_count_--;
- // We've just moved the last element into this index. We'll revisit
- // this index again. Hence, no need to increment the iterator.
- } else {
- i++; // Look at the next element next.
- }
- }
-
- int new_count = lol->obj_count_;
-
- // Check if there are any more elements to keep after purging the dead ones.
- if (new_count == 0) {
- DeleteArray<Element>(elements);
- lol->elements_ = NULL;
- lol->capacity_ = 0;
- ASSERT(lol->obj_count_ == 0);
-
- // If the list is also invisible, the clean up the list as well.
- if (lol->id_ == 0) {
- // Point the next lol's prev to this lol's prev.
- if (next) {
- next->prev_ = lol->prev_;
- } else {
- last_ = lol->prev_;
- }
-
- // Delete this now empty and invisible lol.
- delete lol;
-
- // Don't point the next to this lol since it is now deleted.
- // Leave the next pointer pointing to the current lol.
- continue;
- }
-
- } else {
- // If the obj_count_ is less than the capacity and the difference is
- // greater than a specified threshold, then we should shrink the list.
- int diff = lol->capacity_ - new_count;
- const int kMaxUnusedSpace = 64;
- if (diff > kMaxUnusedSpace) { // Threshold for shrinking.
- // Shrink the list.
- Element* new_elements = NewArray<Element>(new_count);
- memcpy(new_elements, elements, new_count * sizeof(Element));
-
- DeleteArray<Element>(elements);
- lol->elements_ = new_elements;
- lol->capacity_ = new_count;
- }
- ASSERT(lol->obj_count_ == new_count);
-
- lol->Sort(); // We've moved objects. Re-sort in case.
- }
-
- // Save the next (for the previous link) in case we need it later.
- next = lol;
- }
-
-#ifdef VERIFY_LOL
- if (FLAG_verify_lol) {
- Verify();
- }
-#endif
-}
-
-
-#ifdef VERIFY_LOL
-void LiveObjectList::Verify(bool match_heap_exactly) {
- OS::Print("Verifying the LiveObjectList database:\n");
-
- LiveObjectList* lol = last();
- if (lol == NULL) {
- OS::Print(" No lol database to verify\n");
- return;
- }
-
- OS::Print(" Preparing the lol database ...\n");
- int total_count = lol->TotalObjCount();
-
- Element* elements = NewArray<Element>(total_count);
- int count = 0;
-
- // Copy all the object elements into a consecutive array.
- OS::Print(" Copying the lol database ...\n");
- while (lol != NULL) {
- memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element));
- count += lol->obj_count_;
- lol = lol->prev_;
- }
- qsort(elements, total_count, sizeof(Element),
- reinterpret_cast<RawComparer>(CompareElement));
-
- ASSERT(count == total_count);
-
- // Iterate over all objects in the heap and check for:
- // 1. object in LOL but not in heap i.e. error.
- // 2. object in heap but not in LOL (possibly not an error). Usually
- // just means that we don't have the a capture of the latest heap.
- // That is unless we did this verify immediately after a capture,
- // and specified match_heap_exactly = true.
-
- int number_of_heap_objects = 0;
- int number_of_matches = 0;
- int number_not_in_heap = total_count;
- int number_not_in_lol = 0;
-
- OS::Print(" Start verify ...\n");
- OS::Print(" Verifying ...");
- Flush();
- HeapIterator iterator;
- HeapObject* heap_obj = NULL;
- while ((heap_obj = iterator.next()) != NULL) {
- number_of_heap_objects++;
-
- // Check if the heap_obj is in the lol.
- Element key;
- key.obj_ = heap_obj;
-
- Element* result = reinterpret_cast<Element*>(
- bsearch(&key, elements, total_count, sizeof(Element),
- reinterpret_cast<RawComparer>(CompareElement)));
-
- if (result != NULL) {
- number_of_matches++;
- number_not_in_heap--;
- // Mark it as found by changing it into a SMI (mask off low bit).
- // Note: we cannot use HeapObject::cast() here because it asserts that
- // the HeapObject bit is set on the address, but we're unsetting it on
- // purpose here for our marking.
- result->obj_ = reinterpret_cast<HeapObject*>(heap_obj->address());
-
- } else {
- number_not_in_lol++;
- if (match_heap_exactly) {
- OS::Print("heap object %p NOT in lol database\n", heap_obj);
- }
- }
- // Show some sign of life.
- if (number_of_heap_objects % 1000 == 0) {
- OS::Print(".");
- fflush(stdout);
- }
- }
- OS::Print("\n");
-
- // Reporting lol objects not found in the heap.
- if (number_not_in_heap) {
- int found = 0;
- for (int i = 0; (i < total_count) && (found < number_not_in_heap); i++) {
- Element& element = elements[i];
- if (element.obj_->IsHeapObject()) {
- OS::Print("lol database object [%d of %d] %p NOT in heap\n",
- i, total_count, element.obj_);
- found++;
- }
- }
- }
-
- DeleteArray<Element>(elements);
-
- OS::Print("number of objects in lol database %d\n", total_count);
- OS::Print("number of heap objects .......... %d\n", number_of_heap_objects);
- OS::Print("number of matches ............... %d\n", number_of_matches);
- OS::Print("number NOT in heap .............. %d\n", number_not_in_heap);
- OS::Print("number NOT in lol database ...... %d\n", number_not_in_lol);
-
- if (number_of_matches != total_count) {
- OS::Print(" *** ERROR: "
- "NOT all lol database objects match heap objects.\n");
- }
- if (number_not_in_heap != 0) {
- OS::Print(" *** ERROR: %d lol database objects not found in heap.\n",
- number_not_in_heap);
- }
- if (match_heap_exactly) {
- if (!(number_not_in_lol == 0)) {
- OS::Print(" *** ERROR: %d heap objects NOT found in lol database.\n",
- number_not_in_lol);
- }
- }
-
- ASSERT(number_of_matches == total_count);
- ASSERT(number_not_in_heap == 0);
- ASSERT(number_not_in_lol == (number_of_heap_objects - total_count));
- if (match_heap_exactly) {
- ASSERT(total_count == number_of_heap_objects);
- ASSERT(number_not_in_lol == 0);
- }
-
- OS::Print(" Verify the lol database is sorted ...\n");
- lol = last();
- while (lol != NULL) {
- Element* elements = lol->elements_;
- for (int i = 0; i < lol->obj_count_ - 1; i++) {
- if (elements[i].obj_ >= elements[i+1].obj_) {
- OS::Print(" *** ERROR: lol %p obj[%d] %p > obj[%d] %p\n",
- lol, i, elements[i].obj_, i+1, elements[i+1].obj_);
- }
- }
- lol = lol->prev_;
- }
-
- OS::Print(" DONE verifying.\n\n\n");
-}
-
-
-void LiveObjectList::VerifyNotInFromSpace() {
- OS::Print("VerifyNotInFromSpace() ...\n");
- LolIterator it(NULL, last());
- Heap* heap = ISOLATE->heap();
- int i = 0;
- for (it.Init(); !it.Done(); it.Next()) {
- HeapObject* heap_obj = it.Obj();
- if (heap->InFromSpace(heap_obj)) {
- OS::Print(" ERROR: VerifyNotInFromSpace: [%d] obj %p in From space %p\n",
- i++, heap_obj, Heap::new_space()->FromSpaceStart());
- }
- }
-}
-#endif // VERIFY_LOL
-
-
-} } // namespace v8::internal
-
-#endif // LIVE_OBJECT_LIST
diff --git a/src/liveobjectlist.h b/src/liveobjectlist.h
deleted file mode 100644
index 1aa9196..0000000
--- a/src/liveobjectlist.h
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright 2011 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived
-// from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef V8_LIVEOBJECTLIST_H_
-#define V8_LIVEOBJECTLIST_H_
-
-#include "v8.h"
-
-#include "checks.h"
-#include "heap.h"
-#include "objects.h"
-#include "globals.h"
-
-namespace v8 {
-namespace internal {
-
-#ifdef LIVE_OBJECT_LIST
-
-#ifdef DEBUG
-// The following symbol when defined enables thorough verification of lol data.
-// FLAG_verify_lol will also need to set to true to enable the verification.
-#define VERIFY_LOL
-#endif
-
-
-typedef int LiveObjectType;
-class LolFilter;
-class LiveObjectSummary;
-class DumpWriter;
-class SummaryWriter;
-
-
-// The LiveObjectList is both a mechanism for tracking a live capture of
-// objects in the JS heap, as well as is the data structure which represents
-// each of those captures. Unlike a snapshot, the lol is live. For example,
-// if an object in a captured lol dies and is collected by the GC, the lol
-// will reflect that the object is no longer available. The term
-// LiveObjectList (and lol) is used to describe both the mechanism and the
-// data structure depending on context of use.
-//
-// In captured lols, objects are tracked using their address and an object id.
-// The object id is unique. Once assigned to an object, the object id can never
-// be assigned to another object. That is unless all captured lols are deleted
-// which allows the user to start over with a fresh set of lols and object ids.
-// The uniqueness of the object ids allows the user to track specific objects
-// and inspect its longevity while debugging JS code in execution.
-//
-// The lol comes with utility functions to capture, dump, summarize, and diff
-// captured lols amongst other functionality. These functionality are
-// accessible via the v8 debugger interface.
-class LiveObjectList {
- public:
- inline static void GCEpilogue();
- inline static void GCPrologue();
- inline static void IterateElements(ObjectVisitor* v);
- inline static void ProcessNonLive(HeapObject* obj);
- inline static void UpdateReferencesForScavengeGC();
-
- // Note: LOLs can be listed by calling Dump(0, <lol id>), and 2 LOLs can be
- // compared/diff'ed using Dump(<lol id1>, <lol id2>, ...). This will yield
- // a verbose dump of all the objects in the resultant lists.
- // Similarly, a summarized result of a LOL listing or a diff can be
- // attained using the Summarize(0, <lol id>) and Summarize(<lol id1,
- // <lol id2>, ...) respectively.
-
- static MaybeObject* Capture();
- static bool Delete(int id);
- static MaybeObject* Dump(int id1,
- int id2,
- int start_idx,
- int dump_limit,
- Handle<JSObject> filter_obj);
- static MaybeObject* Info(int start_idx, int dump_limit);
- static MaybeObject* Summarize(int id1, int id2, Handle<JSObject> filter_obj);
-
- static void Reset();
- static Object* GetObj(int obj_id);
- static int GetObjId(Object* obj);
- static Object* GetObjId(Handle<String> address);
- static MaybeObject* GetObjRetainers(int obj_id,
- Handle<JSObject> instance_filter,
- bool verbose,
- int start,
- int count,
- Handle<JSObject> filter_obj);
-
- static Object* GetPath(int obj_id1,
- int obj_id2,
- Handle<JSObject> instance_filter);
- static Object* PrintObj(int obj_id);
-
- private:
- struct Element {
- int id_;
- HeapObject* obj_;
- };
-
- explicit LiveObjectList(LiveObjectList* prev, int capacity);
- ~LiveObjectList();
-
- static void GCEpiloguePrivate();
- static void IterateElementsPrivate(ObjectVisitor* v);
-
- static void DoProcessNonLive(HeapObject* obj);
-
- static int CompareElement(const Element* a, const Element* b);
-
- static Object* GetPathPrivate(HeapObject* obj1, HeapObject* obj2);
-
- static int GetRetainers(Handle<HeapObject> target,
- Handle<JSObject> instance_filter,
- Handle<FixedArray> retainers_arr,
- int start,
- int dump_limit,
- int* total_count,
- LolFilter* filter,
- LiveObjectSummary* summary,
- JSFunction* arguments_function,
- Handle<Object> error);
-
- static MaybeObject* DumpPrivate(DumpWriter* writer,
- int start,
- int dump_limit,
- LolFilter* filter);
- static MaybeObject* SummarizePrivate(SummaryWriter* writer,
- LolFilter* filter,
- bool is_tracking_roots);
-
- static bool NeedLOLProcessing() { return (last() != NULL); }
- static void NullifyNonLivePointer(HeapObject** p) {
- // Mask out the low bit that marks this as a heap object. We'll use this
- // cleared bit as an indicator that this pointer needs to be collected.
- //
- // Meanwhile, we still preserve its approximate value so that we don't
- // have to resort the elements list all the time.
- //
- // Note: Doing so also makes this HeapObject* look like an SMI. Hence,
- // GC pointer updater will ignore it when it gets scanned.
- *p = reinterpret_cast<HeapObject*>((*p)->address());
- }
-
- LiveObjectList* prev() { return prev_; }
- LiveObjectList* next() { return next_; }
- int id() { return id_; }
-
- static int list_count() { return list_count_; }
- static LiveObjectList* last() { return last_; }
-
- inline static LiveObjectList* FindLolForId(int id, LiveObjectList* start_lol);
- int TotalObjCount() { return GetTotalObjCountAndSize(NULL); }
- int GetTotalObjCountAndSize(int* size_p);
-
- bool Add(HeapObject* obj);
- Element* Find(HeapObject* obj);
- static void NullifyMostRecent(HeapObject* obj);
- void Sort();
- static void SortAll();
-
- static void PurgeDuplicates(); // Only to be called by GCEpilogue.
-
-#ifdef VERIFY_LOL
- static void Verify(bool match_heap_exactly = false);
- static void VerifyNotInFromSpace();
-#endif
-
- // Iterates the elements in every lol and returns the one that matches the
- // specified key. If no matching element is found, then it returns NULL.
- template <typename T>
- inline static LiveObjectList::Element*
- FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key);
-
- inline static int GetElementId(Element* element);
- inline static HeapObject* GetElementObj(Element* element);
-
- // Instance fields.
- LiveObjectList* prev_;
- LiveObjectList* next_;
- int id_;
- int capacity_;
- int obj_count_;
- Element* elements_;
-
- // Statics for managing all the lists.
- static uint32_t next_element_id_;
- static int list_count_;
- static int last_id_;
- static LiveObjectList* first_;
- static LiveObjectList* last_;
-
- friend class LolIterator;
- friend class LolForwardIterator;
- friend class LolDumpWriter;
- friend class RetainersDumpWriter;
- friend class RetainersSummaryWriter;
- friend class UpdateLiveObjectListVisitor;
-};
-
-
-// Helper class for updating the LiveObjectList HeapObject pointers.
-class UpdateLiveObjectListVisitor: public ObjectVisitor {
- public:
- void VisitPointer(Object** p) { UpdatePointer(p); }
-
- void VisitPointers(Object** start, Object** end) {
- // Copy all HeapObject pointers in [start, end).
- for (Object** p = start; p < end; p++) UpdatePointer(p);
- }
-
- private:
- // Based on Heap::ScavengeObject() but only does forwarding of pointers
- // to live new space objects, and not actually keep them alive.
- void UpdatePointer(Object** p) {
- Object* object = *p;
- if (!HEAP->InNewSpace(object)) return;
-
- HeapObject* heap_obj = HeapObject::cast(object);
- ASSERT(HEAP->InFromSpace(heap_obj));
-
- // We use the first word (where the map pointer usually is) of a heap
- // object to record the forwarding pointer. A forwarding pointer can
- // point to an old space, the code space, or the to space of the new
- // generation.
- MapWord first_word = heap_obj->map_word();
-
- // If the first word is a forwarding address, the object has already been
- // copied.
- if (first_word.IsForwardingAddress()) {
- *p = first_word.ToForwardingAddress();
- return;
-
- // Else, it's a dead object.
- } else {
- LiveObjectList::NullifyNonLivePointer(reinterpret_cast<HeapObject**>(p));
- }
- }
-};
-
-
-#else // !LIVE_OBJECT_LIST
-
-
-class LiveObjectList {
- public:
- inline static void GCEpilogue() {}
- inline static void GCPrologue() {}
- inline static void IterateElements(ObjectVisitor* v) {}
- inline static void ProcessNonLive(HeapObject* obj) {}
- inline static void UpdateReferencesForScavengeGC() {}
-
- inline static MaybeObject* Capture() { return HEAP->undefined_value(); }
- inline static bool Delete(int id) { return false; }
- inline static MaybeObject* Dump(int id1,
- int id2,
- int start_idx,
- int dump_limit,
- Handle<JSObject> filter_obj) {
- return HEAP->undefined_value();
- }
- inline static MaybeObject* Info(int start_idx, int dump_limit) {
- return HEAP->undefined_value();
- }
- inline static MaybeObject* Summarize(int id1,
- int id2,
- Handle<JSObject> filter_obj) {
- return HEAP->undefined_value();
- }
-
- inline static void Reset() {}
- inline static Object* GetObj(int obj_id) { return HEAP->undefined_value(); }
- inline static Object* GetObjId(Handle<String> address) {
- return HEAP->undefined_value();
- }
- inline static MaybeObject* GetObjRetainers(int obj_id,
- Handle<JSObject> instance_filter,
- bool verbose,
- int start,
- int count,
- Handle<JSObject> filter_obj) {
- return HEAP->undefined_value();
- }
-
- inline static Object* GetPath(int obj_id1,
- int obj_id2,
- Handle<JSObject> instance_filter) {
- return HEAP->undefined_value();
- }
- inline static Object* PrintObj(int obj_id) { return HEAP->undefined_value(); }
-};
-
-
-#endif // LIVE_OBJECT_LIST
-
-} } // namespace v8::internal
-
-#endif // V8_LIVEOBJECTLIST_H_
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index d867197..9606092 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -36,7 +36,6 @@
#include "heap-profiler.h"
#include "ic-inl.h"
#include "incremental-marking.h"
-#include "liveobjectlist-inl.h"
#include "mark-compact.h"
#include "objects-visiting.h"
#include "objects-visiting-inl.h"
@@ -835,8 +834,6 @@
// GC, because it relies on the new address of certain old space
// objects (empty string, illegal builtin).
heap()->isolate()->stub_cache()->Clear();
-
- heap()->external_string_table_.CleanUp();
}
@@ -935,6 +932,16 @@
}
+bool CodeFlusher::ContainsCandidate(SharedFunctionInfo* shared_info) {
+ SharedFunctionInfo* candidate = shared_function_info_candidates_head_;
+ while (candidate != NULL) {
+ if (candidate == shared_info) return true;
+ candidate = GetNextCandidate(candidate);
+ }
+ return false;
+}
+
+
void CodeFlusher::EvictCandidate(SharedFunctionInfo* shared_info) {
// The function is no longer a candidate, make sure it gets visited
// again so that previous flushing decisions are revisited.
@@ -2059,6 +2066,7 @@
symbol_table->ElementsRemoved(v.PointersRemoved());
heap()->external_string_table_.Iterate(&v);
heap()->external_string_table_.CleanUp();
+ heap()->error_object_list_.RemoveUnmarked(heap());
// Process the weak references.
MarkCompactWeakObjectRetainer mark_compact_object_retainer;
@@ -2583,9 +2591,6 @@
size,
NEW_SPACE);
} else {
- // Process the dead object before we write a NULL into its header.
- LiveObjectList::ProcessNonLive(object);
-
// Mark dead objects in the new space with null in their map field.
Memory::Address_at(object->address()) = NULL;
}
@@ -2985,7 +2990,6 @@
GCTracer::Scope::MC_UPDATE_ROOT_TO_NEW_POINTERS);
// Update roots.
heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE);
- LiveObjectList::IterateElements(&updating_visitor);
}
{ GCTracer::Scope gc_scope(tracer_,
@@ -3098,6 +3102,9 @@
heap_->UpdateReferencesInExternalStringTable(
&UpdateReferenceInExternalStringTableEntry);
+ // Update pointers in the new error object list.
+ heap_->error_object_list()->UpdateReferences();
+
if (!FLAG_watch_ic_patching) {
// Update JSFunction pointers from the runtime profiler.
heap()->isolate()->runtime_profiler()->UpdateSamplesAfterCompact(
diff --git a/src/mark-compact.h b/src/mark-compact.h
index 8821c3d..b34be6b 100644
--- a/src/mark-compact.h
+++ b/src/mark-compact.h
@@ -423,6 +423,10 @@
if (GetNextCandidate(shared_info) == NULL) {
SetNextCandidate(shared_info, shared_function_info_candidates_head_);
shared_function_info_candidates_head_ = shared_info;
+ } else {
+ // TODO(mstarzinger): Active in release mode to flush out problems.
+ // Should be turned back into an ASSERT or removed completely.
+ CHECK(ContainsCandidate(shared_info));
}
}
@@ -434,6 +438,8 @@
}
}
+ bool ContainsCandidate(SharedFunctionInfo* shared_info);
+
void EvictCandidate(SharedFunctionInfo* shared_info);
void EvictCandidate(JSFunction* function);
diff --git a/src/messages.js b/src/messages.js
index d238e3f..4a8143e 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -820,7 +820,7 @@
%_CallFunction(this.receiver,
ownName,
ObjectLookupSetter) === this.fun ||
- this.receiver[ownName] === this.fun)) {
+ %GetDataProperty(this.receiver, ownName) === this.fun)) {
// To handle DontEnum properties we guess that the method has
// the same name as the function.
return ownName;
@@ -829,8 +829,7 @@
for (var prop in this.receiver) {
if (%_CallFunction(this.receiver, prop, ObjectLookupGetter) === this.fun ||
%_CallFunction(this.receiver, prop, ObjectLookupSetter) === this.fun ||
- (!%_CallFunction(this.receiver, prop, ObjectLookupGetter) &&
- this.receiver[prop] === this.fun)) {
+ %GetDataProperty(this.receiver, prop) === this.fun) {
// If we find more than one match bail out to avoid confusion.
if (name) {
return null;
@@ -883,7 +882,8 @@
}
function CallSiteIsConstructor() {
- var constructor = this.receiver ? this.receiver.constructor : null;
+ var receiver = this.receiver;
+ var constructor = receiver ? %GetDataProperty(receiver, "constructor") : null;
if (!constructor) {
return false;
}
@@ -933,12 +933,14 @@
var typeName = GetTypeName(this, true);
var methodName = this.getMethodName();
if (functionName) {
- if (typeName && functionName.indexOf(typeName) != 0) {
+ if (typeName &&
+ %_CallFunction(functionName, typeName, StringIndexOf) != 0) {
line += typeName + ".";
}
line += functionName;
- if (methodName && functionName.lastIndexOf("." + methodName) !=
- functionName.length - methodName.length - 1) {
+ if (methodName &&
+ (%_CallFunction(functionName, "." + methodName, StringIndexOf) !=
+ functionName.length - methodName.length - 1)) {
line += " [as " + methodName + "]";
}
} else {
@@ -1016,17 +1018,37 @@
return eval_origin;
}
-function FormatStackTrace(error, frames) {
- var lines = [];
+
+function FormatErrorString(error) {
try {
- lines.push(error.toString());
+ return %_CallFunction(error, ErrorToString);
} catch (e) {
try {
- lines.push("<error: " + e + ">");
+ return "<error: " + e + ">";
} catch (ee) {
- lines.push("<error>");
+ return "<error>";
}
}
+}
+
+
+function GetStackFrames(raw_stack) {
+ var frames = new InternalArray();
+ for (var i = 0; i < raw_stack.length; i += 4) {
+ var recv = raw_stack[i];
+ var fun = raw_stack[i + 1];
+ var code = raw_stack[i + 2];
+ var pc = raw_stack[i + 3];
+ var pos = %FunctionGetPositionForOffset(code, pc);
+ frames.push(new CallSite(recv, fun, pos));
+ }
+ return frames;
+}
+
+
+function FormatStackTrace(error_string, frames) {
+ var lines = new InternalArray();
+ lines.push(error_string);
for (var i = 0; i < frames.length; i++) {
var frame = frames[i];
var line;
@@ -1042,25 +1064,9 @@
}
lines.push(" at " + line);
}
- return lines.join("\n");
+ return %_CallFunction(lines, "\n", ArrayJoin);
}
-function FormatRawStackTrace(error, raw_stack) {
- var frames = [ ];
- for (var i = 0; i < raw_stack.length; i += 4) {
- var recv = raw_stack[i];
- var fun = raw_stack[i + 1];
- var code = raw_stack[i + 2];
- var pc = raw_stack[i + 3];
- var pos = %FunctionGetPositionForOffset(code, pc);
- frames.push(new CallSite(recv, fun, pos));
- }
- if (IS_FUNCTION($Error.prepareStackTrace)) {
- return $Error.prepareStackTrace(error, frames);
- } else {
- return FormatStackTrace(error, frames);
- }
-}
function GetTypeName(obj, requireConstructor) {
var constructor = obj.receiver.constructor;
@@ -1076,24 +1082,51 @@
return constructorName;
}
+
+// Flag to prevent recursive call of Error.prepareStackTrace.
+var formatting_custom_stack_trace = false;
+
+
function captureStackTrace(obj, cons_opt) {
var stackTraceLimit = $Error.stackTraceLimit;
if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
if (stackTraceLimit < 0 || stackTraceLimit > 10000) {
stackTraceLimit = 10000;
}
- var raw_stack = %CollectStackTrace(obj,
- cons_opt ? cons_opt : captureStackTrace,
- stackTraceLimit);
+ var stack = %CollectStackTrace(obj,
+ cons_opt ? cons_opt : captureStackTrace,
+ stackTraceLimit);
+
+ // Don't be lazy if the error stack formatting is custom (observable).
+ if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
+ var array = [];
+ %MoveArrayContents(GetStackFrames(stack), array);
+ formatting_custom_stack_trace = true;
+ try {
+ obj.stack = $Error.prepareStackTrace(obj, array);
+ } catch (e) {
+ throw e; // The custom formatting function threw. Rethrow.
+ } finally {
+ formatting_custom_stack_trace = false;
+ }
+ return;
+ }
+
+ var error_string = FormatErrorString(obj);
// Note that 'obj' and 'this' maybe different when called on objects that
// have the error object on its prototype chain. The getter replaces itself
// with a data property as soon as the stack trace has been formatted.
+ // The getter must not change the object layout as it may be called after GC.
var getter = function() {
- var value = FormatRawStackTrace(obj, raw_stack);
- raw_stack = void 0;
- %DefineOrRedefineDataProperty(obj, 'stack', value, NONE);
- return value;
+ if (IS_STRING(stack)) return stack;
+ // Stack is still a raw array awaiting to be formatted.
+ stack = FormatStackTrace(error_string, GetStackFrames(stack));
+ // Release context value.
+ error_string = void 0;
+ return stack;
};
+ %MarkOneShotGetter(getter);
+
// The 'stack' property of the receiver is set as data property. If
// the receiver is the same as holder, this accessor pair is replaced.
var setter = function(v) {
@@ -1240,23 +1273,32 @@
// error object copy, but can be found on the prototype chain of 'this'.
// When the stack trace is formatted, this accessor property is replaced by
// a data property.
+ var error_string = boilerplate.name + ": " + boilerplate.message;
+
+ // The getter must not change the object layout as it may be called after GC.
function getter() {
var holder = this;
while (!IS_ERROR(holder)) {
holder = %GetPrototype(holder);
if (holder == null) return MakeSyntaxError('illegal_access', []);
}
- var raw_stack = %GetOverflowedRawStackTrace(holder);
- var result = IS_ARRAY(raw_stack) ? FormatRawStackTrace(holder, raw_stack)
- : void 0;
- %DefineOrRedefineDataProperty(holder, 'stack', result, NONE);
- return result;
+ var stack = %GetOverflowedStackTrace(holder);
+ if (IS_STRING(stack)) return stack;
+ if (IS_ARRAY(stack)) {
+ var result = FormatStackTrace(error_string, GetStackFrames(stack));
+ %SetOverflowedStackTrace(holder, result);
+ return result;
+ }
+ return void 0;
}
+ %MarkOneShotGetter(getter);
// The 'stack' property of the receiver is set as data property. If
// the receiver is the same as holder, this accessor pair is replaced.
function setter(v) {
%DefineOrRedefineDataProperty(this, 'stack', v, NONE);
+ // Release the stack trace that is stored as hidden property, if exists.
+ %SetOverflowedStackTrace(this, void 0);
}
%DefineOrRedefineAccessorProperty(
diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc
index 3d01be6..bcd068b 100644
--- a/src/mips/code-stubs-mips.cc
+++ b/src/mips/code-stubs-mips.cc
@@ -3965,6 +3965,17 @@
}
+static void JumpIfOOM(MacroAssembler* masm,
+ Register value,
+ Register scratch,
+ Label* oom_label) {
+ STATIC_ASSERT(Failure::OUT_OF_MEMORY_EXCEPTION == 3);
+ STATIC_ASSERT(kFailureTag == 3);
+ __ andi(scratch, value, 0xf);
+ __ Branch(oom_label, eq, scratch, Operand(0xf));
+}
+
+
void CEntryStub::GenerateCore(MacroAssembler* masm,
Label* throw_normal_exception,
Label* throw_termination_exception,
@@ -4071,14 +4082,7 @@
__ Branch(&retry, eq, t0, Operand(zero_reg));
// Special handling of out of memory exceptions.
- Failure* out_of_memory = Failure::OutOfMemoryException();
- __ Branch(USE_DELAY_SLOT,
- throw_out_of_memory_exception,
- eq,
- v0,
- Operand(reinterpret_cast<int32_t>(out_of_memory)));
- // If we throw the OOM exception, the value of a3 doesn't matter.
- // Any instruction can be in the delay slot that's not a jump.
+ JumpIfOOM(masm, v0, t0, throw_out_of_memory_exception);
// Retrieve the pending exception and clear the variable.
__ LoadRoot(a3, Heap::kTheHoleValueRootIndex);
@@ -4170,8 +4174,11 @@
__ sw(a0, MemOperand(a2));
// Set pending exception and v0 to out of memory exception.
- Failure* out_of_memory = Failure::OutOfMemoryException();
+ Label already_have_failure;
+ JumpIfOOM(masm, v0, t0, &already_have_failure);
+ Failure* out_of_memory = Failure::OutOfMemoryException(0x1);
__ li(v0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
+ __ bind(&already_have_failure);
__ li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
isolate)));
__ sw(v0, MemOperand(a2));
@@ -6684,6 +6691,11 @@
__ And(at, t0, Operand(kAsciiDataHintMask));
__ and_(at, at, t1);
__ Branch(&ascii_data, ne, at, Operand(zero_reg));
+ __ Xor(t0, t0, Operand(t1));
+ STATIC_ASSERT(kOneByteStringTag != 0 && kAsciiDataHintTag != 0);
+ __ And(t0, t0, Operand(kOneByteStringTag | kAsciiDataHintTag));
+ __ Branch(&ascii_data, eq, t0,
+ Operand(kOneByteStringTag | kAsciiDataHintTag));
// Allocate a two byte cons string.
__ AllocateTwoByteConsString(v0, t2, t0, t1, &call_runtime);
diff --git a/src/mips/codegen-mips.cc b/src/mips/codegen-mips.cc
index 27835af..1ea8fd9 100644
--- a/src/mips/codegen-mips.cc
+++ b/src/mips/codegen-mips.cc
@@ -533,9 +533,9 @@
__ Check(eq, "Non-smi value", at, Operand(zero_reg));
__ lw(at, FieldMemOperand(string, String::kLengthOffset));
- __ Check(lt, "Index is too large", at, Operand(index));
+ __ Check(lt, "Index is too large", index, Operand(at));
- __ Check(ge, "Index is negative", index, Operand(Smi::FromInt(0)));
+ __ Check(ge, "Index is negative", index, Operand(zero_reg));
__ lw(at, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(at, FieldMemOperand(at, Map::kInstanceTypeOffset));
@@ -543,9 +543,9 @@
__ And(at, at, Operand(kStringRepresentationMask | kStringEncodingMask));
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
- __ Check(eq, "Unexpected string type", at,
- Operand(encoding == String::ONE_BYTE_ENCODING
- ? one_byte_seq_type : two_byte_seq_type));
+ __ Subu(at, at, Operand(encoding == String::ONE_BYTE_ENCODING
+ ? one_byte_seq_type : two_byte_seq_type));
+ __ Check(eq, "Unexpected string type", at, Operand(zero_reg));
}
__ Addu(at,
diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc
index 02ca145..4484ed3 100644
--- a/src/mips/lithium-codegen-mips.cc
+++ b/src/mips/lithium-codegen-mips.cc
@@ -4218,7 +4218,7 @@
__ Branch(¬_applicable, ne, scratch, Operand(from_map));
__ li(new_map_reg, Operand(to_map));
- if (IsFastSmiElementsKind(from_kind) && IsFastObjectElementsKind(to_kind)) {
+ if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
__ sw(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset));
// Write barrier.
__ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
@@ -5620,6 +5620,11 @@
}
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
Register object = ToRegister(instr->object());
Register key = ToRegister(instr->key());
diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc
index 8ce576d..4d9603e 100644
--- a/src/mips/lithium-mips.cc
+++ b/src/mips/lithium-mips.cc
@@ -682,6 +682,11 @@
}
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h
index 7f87b02..a9116ef 100644
--- a/src/mips/lithium-mips.h
+++ b/src/mips/lithium-mips.h
@@ -93,6 +93,7 @@
V(Deoptimize) \
V(DivI) \
V(DoubleToI) \
+ V(DummyUse) \
V(ElementsKind) \
V(FastLiteral) \
V(FixedArrayBaseLength) \
@@ -401,6 +402,15 @@
};
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc
index e0c5787..64f3631 100644
--- a/src/mips/macro-assembler-mips.cc
+++ b/src/mips/macro-assembler-mips.cc
@@ -4944,10 +4944,8 @@
Register scratch2,
Label* failure) {
int kFlatAsciiStringMask =
- kIsNotStringMask | kStringEncodingMask | kAsciiDataHintMask |
- kStringRepresentationMask;
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
int kFlatAsciiStringTag = ASCII_STRING_TYPE;
- ASSERT_EQ(ASCII_STRING_TYPE, ASCII_STRING_TYPE & kFlatAsciiStringMask);
ASSERT(kFlatAsciiStringTag <= 0xffff); // Ensure this fits 16-bit immed.
andi(scratch1, first, kFlatAsciiStringMask);
Branch(failure, ne, scratch1, Operand(kFlatAsciiStringTag));
@@ -4960,10 +4958,8 @@
Register scratch,
Label* failure) {
int kFlatAsciiStringMask =
- kIsNotStringMask | kStringEncodingMask | kAsciiDataHintMask |
- kStringRepresentationMask;
+ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
int kFlatAsciiStringTag = ASCII_STRING_TYPE;
- ASSERT_EQ(ASCII_STRING_TYPE, ASCII_STRING_TYPE & kFlatAsciiStringMask);
And(scratch, type, Operand(kFlatAsciiStringMask));
Branch(failure, ne, scratch, Operand(kFlatAsciiStringTag));
}
diff --git a/src/mips/regexp-macro-assembler-mips.cc b/src/mips/regexp-macro-assembler-mips.cc
index 6ace471..1ae2a7a 100644
--- a/src/mips/regexp-macro-assembler-mips.cc
+++ b/src/mips/regexp-macro-assembler-mips.cc
@@ -341,7 +341,17 @@
__ Or(t0, t0, Operand(0x20)); // Also convert input character.
__ Branch(&fail, ne, t0, Operand(a3));
__ Subu(a3, a3, Operand('a'));
+#ifndef ENABLE_LATIN_1
__ Branch(&fail, hi, a3, Operand('z' - 'a')); // Is a3 a lowercase letter?
+#else
+ __ Branch(&loop_check, ls, a3, Operand('z' - 'a'));
+ // Latin-1: Check for values in range [224,254] but not 247.
+ __ Subu(a3, a3, Operand(224 - 'a'));
+ // Weren't Latin-1 letters.
+ __ Branch(&fail, hi, a3, Operand(254 - 224));
+ // Check for 247.
+ __ Branch(&fail, eq, a3, Operand(247 - 224));
+#endif
__ bind(&loop_check);
__ Branch(&loop, lt, a0, Operand(a1));
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index e99c45b..593e955 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -474,7 +474,7 @@
void SeqOneByteString::SeqOneByteStringVerify() {
#ifndef ENABLE_LATIN_1
- CHECK(!HasOnlyAsciiChars() || String::IsAscii(GetChars(), length()));
+ CHECK(String::IsAscii(GetChars(), length()));
#endif
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 34983cd..a865edc 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -341,7 +341,12 @@
bool String::HasOnlyAsciiChars() {
uint32_t type = map()->instance_type();
+#ifndef ENABLE_LATIN_1
+ return (type & kStringEncodingMask) == kOneByteStringTag ||
+ (type & kAsciiDataHintMask) == kAsciiDataHintTag;
+#else
return (type & kAsciiDataHintMask) == kAsciiDataHintTag;
+#endif
}
@@ -3373,6 +3378,11 @@
}
+inline bool Map::CanTrackAllocationSite() {
+ return instance_type() == JS_ARRAY_TYPE;
+}
+
+
void Map::set_owns_descriptors(bool is_shared) {
set_bit_field3(OwnsDescriptors::update(bit_field3(), is_shared));
}
diff --git a/src/objects-visiting-inl.h b/src/objects-visiting-inl.h
index 309cddf..33af403 100644
--- a/src/objects-visiting-inl.h
+++ b/src/objects-visiting-inl.h
@@ -211,7 +211,7 @@
// when they might be keeping a Context alive, or when the heap is about
// to be serialized.
if (FLAG_cleanup_code_caches_at_gc && target->is_inline_cache_stub()
- && (target->ic_state() == MEGAMORPHIC ||
+ && (target->ic_state() == MEGAMORPHIC || target->ic_state() == GENERIC ||
target->ic_state() == POLYMORPHIC || heap->flush_monomorphic_ics() ||
Serializer::enabled() || target->ic_age() != heap->global_ic_age())) {
IC::Clear(rinfo->pc());
diff --git a/src/objects.cc b/src/objects.cc
index 2f2bd64..1ef8f58 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -627,6 +627,8 @@
// that even though we may not actually end up loading the named
// property from the current object, we still check that we have
// access to it.
+ // TODO(dcarney): revert.
+ CHECK(current->IsJSObject());
JSObject* checked = JSObject::cast(current);
if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) {
return checked->GetPropertyWithFailedAccessCheck(receiver,
@@ -7508,6 +7510,31 @@
}
+// Heuristic: We only need to create allocation site info if the boilerplate
+// elements kind is the initial elements kind.
+AllocationSiteMode AllocationSiteInfo::GetMode(
+ ElementsKind boilerplate_elements_kind) {
+ if (FLAG_track_allocation_sites &&
+ IsFastSmiElementsKind(boilerplate_elements_kind)) {
+ return TRACK_ALLOCATION_SITE;
+ }
+
+ return DONT_TRACK_ALLOCATION_SITE;
+}
+
+
+AllocationSiteMode AllocationSiteInfo::GetMode(ElementsKind from,
+ ElementsKind to) {
+ if (FLAG_track_allocation_sites &&
+ IsFastSmiElementsKind(from) &&
+ (IsFastObjectElementsKind(to) || IsFastDoubleElementsKind(to))) {
+ return TRACK_ALLOCATION_SITE;
+ }
+
+ return DONT_TRACK_ALLOCATION_SITE;
+}
+
+
uint32_t StringHasher::MakeArrayIndexHash(uint32_t value, int length) {
// For array indexes mix the length into the hash as an array index could
// be zero.
@@ -9036,6 +9063,7 @@
case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
case POLYMORPHIC: return "POLYMORPHIC";
case MEGAMORPHIC: return "MEGAMORPHIC";
+ case GENERIC: return "GENERIC";
case DEBUG_STUB: return "DEBUG_STUB";
}
UNREACHABLE();
@@ -9875,6 +9903,10 @@
ElementsKind kind = HasFastHoleyElements()
? FAST_HOLEY_ELEMENTS
: FAST_ELEMENTS;
+
+ MaybeObject* trans = PossiblyTransitionArrayBoilerplate(kind);
+ if (trans->IsFailure()) return trans;
+
MaybeObject* maybe_new_map = GetElementsTransitionMap(GetIsolate(),
kind);
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
@@ -10406,15 +10438,31 @@
MaybeObject* JSObject::PossiblyTransitionArrayBoilerplate(
ElementsKind to_kind) {
MaybeObject* ret = NULL;
- if (IsJSArray()) {
- AllocationSiteInfo* info = AllocationSiteInfo::FindForJSObject(this);
- if (info != NULL) {
- JSObject* payload = JSObject::cast(info->payload());
- if (payload->GetElementsKind() != to_kind) {
- if (IsMoreGeneralElementsKindTransition(payload->GetElementsKind(),
- to_kind)) {
- ret = payload->TransitionElementsKind(to_kind);
- }
+ if (!FLAG_track_allocation_sites || !IsJSArray()) {
+ return ret;
+ }
+
+ AllocationSiteInfo* info = AllocationSiteInfo::FindForJSObject(this);
+ if (info == NULL) {
+ return ret;
+ }
+
+ ASSERT(info->payload()->IsJSArray());
+ JSArray* payload = JSArray::cast(info->payload());
+ ElementsKind kind = payload->GetElementsKind();
+ if (IsMoreGeneralElementsKindTransition(kind, to_kind)) {
+ // If the array is huge, it's not likely to be defined in a local
+ // function, so we shouldn't make new instances of it very often.
+ uint32_t length = 0;
+ CHECK(payload->length()->ToArrayIndex(&length));
+ if (length <= 8 * 1024) {
+ ret = payload->TransitionElementsKind(to_kind);
+ if (FLAG_trace_track_allocation_sites) {
+ PrintF(
+ "AllocationSiteInfo: JSArray %p boilerplate updated %s->%s\n",
+ reinterpret_cast<void*>(this),
+ ElementsKindToString(kind),
+ ElementsKindToString(to_kind));
}
}
}
diff --git a/src/objects.h b/src/objects.h
index 8b3bbd4..f9178cc 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -538,46 +538,39 @@
enum InstanceType {
// String types.
SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kSeqStringTag,
- ASCII_SYMBOL_TYPE = kOneByteStringTag | kAsciiDataHintTag | kSymbolTag |
- kSeqStringTag,
+ ASCII_SYMBOL_TYPE = kOneByteStringTag | kSymbolTag | kSeqStringTag,
CONS_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kConsStringTag,
- CONS_ASCII_SYMBOL_TYPE = kOneByteStringTag | kAsciiDataHintTag | kSymbolTag |
- kConsStringTag,
+ CONS_ASCII_SYMBOL_TYPE = kOneByteStringTag | kSymbolTag | kConsStringTag,
SHORT_EXTERNAL_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag |
kExternalStringTag | kShortExternalStringTag,
SHORT_EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE =
kTwoByteStringTag | kSymbolTag | kExternalStringTag |
kAsciiDataHintTag | kShortExternalStringTag,
- SHORT_EXTERNAL_ASCII_SYMBOL_TYPE = kOneByteStringTag | kAsciiDataHintTag |
- kExternalStringTag | kSymbolTag |
- kShortExternalStringTag,
+ SHORT_EXTERNAL_ASCII_SYMBOL_TYPE = kOneByteStringTag | kExternalStringTag |
+ kSymbolTag | kShortExternalStringTag,
EXTERNAL_SYMBOL_TYPE = kTwoByteStringTag | kSymbolTag | kExternalStringTag,
EXTERNAL_SYMBOL_WITH_ASCII_DATA_TYPE =
kTwoByteStringTag | kSymbolTag | kExternalStringTag | kAsciiDataHintTag,
EXTERNAL_ASCII_SYMBOL_TYPE =
- kOneByteStringTag | kAsciiDataHintTag | kSymbolTag | kExternalStringTag,
+ kOneByteStringTag | kSymbolTag | kExternalStringTag,
STRING_TYPE = kTwoByteStringTag | kSeqStringTag,
- ASCII_STRING_TYPE = kOneByteStringTag | kAsciiDataHintTag | kSeqStringTag,
+ ASCII_STRING_TYPE = kOneByteStringTag | kSeqStringTag,
CONS_STRING_TYPE = kTwoByteStringTag | kConsStringTag,
- CONS_ASCII_STRING_TYPE =
- kOneByteStringTag | kAsciiDataHintTag | kConsStringTag,
+ CONS_ASCII_STRING_TYPE = kOneByteStringTag | kConsStringTag,
SLICED_STRING_TYPE = kTwoByteStringTag | kSlicedStringTag,
- SLICED_ASCII_STRING_TYPE =
- kOneByteStringTag | kAsciiDataHintTag | kSlicedStringTag,
+ SLICED_ASCII_STRING_TYPE = kOneByteStringTag | kSlicedStringTag,
SHORT_EXTERNAL_STRING_TYPE =
kTwoByteStringTag | kExternalStringTag | kShortExternalStringTag,
SHORT_EXTERNAL_STRING_WITH_ASCII_DATA_TYPE =
kTwoByteStringTag | kExternalStringTag |
kAsciiDataHintTag | kShortExternalStringTag,
SHORT_EXTERNAL_ASCII_STRING_TYPE =
- kOneByteStringTag | kAsciiDataHintTag |
- kExternalStringTag | kShortExternalStringTag,
+ kOneByteStringTag | kExternalStringTag | kShortExternalStringTag,
EXTERNAL_STRING_TYPE = kTwoByteStringTag | kExternalStringTag,
EXTERNAL_STRING_WITH_ASCII_DATA_TYPE =
kTwoByteStringTag | kExternalStringTag | kAsciiDataHintTag,
// LAST_STRING_TYPE
- EXTERNAL_ASCII_STRING_TYPE =
- kOneByteStringTag | kAsciiDataHintTag | kExternalStringTag,
+ EXTERNAL_ASCII_STRING_TYPE = kOneByteStringTag | kExternalStringTag,
PRIVATE_EXTERNAL_ASCII_STRING_TYPE = EXTERNAL_ASCII_STRING_TYPE,
// Objects allocated in their own spaces (never in new space).
@@ -4988,7 +4981,7 @@
set_bit_field3(EnumLengthBits::update(bit_field3(), length));
}
-
+ inline bool CanTrackAllocationSite();
inline bool owns_descriptors();
inline void set_owns_descriptors(bool is_shared);
inline bool is_observed();
@@ -6912,9 +6905,10 @@
};
-enum AllocationSiteInfoMode {
- DONT_TRACK_ALLOCATION_SITE_INFO,
- TRACK_ALLOCATION_SITE_INFO
+enum AllocationSiteMode {
+ DONT_TRACK_ALLOCATION_SITE,
+ TRACK_ALLOCATION_SITE,
+ LAST_ALLOCATION_SITE_MODE = TRACK_ALLOCATION_SITE
};
@@ -6930,6 +6924,9 @@
// Returns NULL if no AllocationSiteInfo is available for object.
static AllocationSiteInfo* FindForJSObject(JSObject* object);
+ static AllocationSiteMode GetMode(ElementsKind boilerplate_elements_kind);
+ static AllocationSiteMode GetMode(ElementsKind from, ElementsKind to);
+
static const int kPayloadOffset = HeapObject::kHeaderSize;
static const int kSize = kPayloadOffset + kPointerSize;
diff --git a/src/platform-linux.cc b/src/platform-linux.cc
index ce45bab..e89d2cc 100644
--- a/src/platform-linux.cc
+++ b/src/platform-linux.cc
@@ -535,19 +535,20 @@
// the beginning of the filename or the end of the line.
do {
c = getc(fp);
- } while ((c != EOF) && (c != '\n') && (c != '/'));
+ } while ((c != EOF) && (c != '\n') && (c != '/') && (c != '['));
if (c == EOF) break; // EOF: Was unexpected, just exit.
// Process the filename if found.
- if (c == '/') {
- ungetc(c, fp); // Push the '/' back into the stream to be read below.
+ if ((c == '/') || (c == '[')) {
+ // Push the '/' or '[' back into the stream to be read below.
+ ungetc(c, fp);
// Read to the end of the line. Exit if the read fails.
if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
// Drop the newline character read by fgets. We do not need to check
// for a zero-length string because we know that we at least read the
- // '/' character.
+ // '/' or '[' character.
lib_name[strlen(lib_name) - 1] = '\0';
} else {
// No library name found, just record the raw address range.
@@ -1207,7 +1208,9 @@
#if defined(ANDROID)
syscall(__NR_tgkill, vm_tgid_, tid, SIGPROF);
#else
- syscall(SYS_tgkill, vm_tgid_, tid, SIGPROF);
+ int result = syscall(SYS_tgkill, vm_tgid_, tid, SIGPROF);
+ USE(result);
+ ASSERT(result == 0);
#endif
}
diff --git a/src/runtime.cc b/src/runtime.cc
index b508ad3..b5f04e3 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -48,7 +48,6 @@
#include "json-parser.h"
#include "json-stringifier.h"
#include "liveedit.h"
-#include "liveobjectlist-inl.h"
#include "misc-intrinsics.h"
#include "parser.h"
#include "platform.h"
@@ -668,7 +667,11 @@
isolate->heap()->fixed_cow_array_map()) {
isolate->counters()->cow_arrays_created_runtime()->Increment();
}
- return isolate->heap()->CopyJSObject(JSObject::cast(*boilerplate));
+
+ JSObject* boilerplate_object = JSObject::cast(*boilerplate);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(
+ boilerplate_object->GetElementsKind());
+ return isolate->heap()->CopyJSObject(boilerplate_object, mode);
}
@@ -1098,14 +1101,15 @@
PropertyAttributes attrs = obj->GetLocalPropertyAttribute(*name);
if (attrs == ABSENT) return heap->undefined_value();
- AccessorPair* accessors = obj->GetLocalPropertyAccessorPair(*name);
+ AccessorPair* raw_accessors = obj->GetLocalPropertyAccessorPair(*name);
+ Handle<AccessorPair> accessors(raw_accessors, isolate);
Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE);
elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0));
elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) == 0));
- elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(accessors != NULL));
+ elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(raw_accessors != NULL));
- if (accessors == NULL) {
+ if (raw_accessors == NULL) {
elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0));
// GetProperty does access check.
Handle<Object> value = GetProperty(obj, name);
@@ -5803,7 +5807,9 @@
namespace {
static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF;
-
+#ifdef ENABLE_LATIN_1
+static const uintptr_t kAsciiMask = kOneInEveryByte << 7;
+#endif
// Given a word and two range boundaries returns a word with high bit
// set in every byte iff the corresponding input byte was strictly in
@@ -5817,7 +5823,10 @@
ASSERT((w & (kOneInEveryByte * 0x7F)) == w);
// Use strict inequalities since in edge cases the function could be
// further simplified.
- ASSERT(0 < m && m < n && n < 0x7F);
+ ASSERT(0 < m && m < n);
+#ifndef ENABLE_LATIN_1
+ ASSERT(n < 0x7F);
+#endif
// Has high bit set in every w byte less than n.
uintptr_t tmp1 = kOneInEveryByte * (0x7F + n) - w;
// Has high bit set in every w byte greater than m.
@@ -5834,7 +5843,11 @@
template <AsciiCaseConversion dir>
struct FastAsciiConverter {
+#ifdef ENABLE_LATIN_1
+ static bool Convert(char* dst, char* src, int length, bool* changed_out) {
+#else
static bool Convert(char* dst, char* src, int length) {
+#endif
#ifdef DEBUG
char* saved_dst = dst;
char* saved_src = src;
@@ -5846,12 +5859,18 @@
const char lo = (dir == ASCII_TO_LOWER) ? 'A' - 1 : 'a' - 1;
const char hi = (dir == ASCII_TO_LOWER) ? 'Z' + 1 : 'z' + 1;
bool changed = false;
+#ifdef ENABLE_LATIN_1
+ uintptr_t or_acc = 0;
+#endif
char* const limit = src + length;
#ifdef V8_HOST_CAN_READ_UNALIGNED
// Process the prefix of the input that requires no conversion one
// (machine) word at a time.
while (src <= limit - sizeof(uintptr_t)) {
uintptr_t w = *reinterpret_cast<uintptr_t*>(src);
+#ifdef ENABLE_LATIN_1
+ or_acc |= w;
+#endif
if (AsciiRangeMask(w, lo, hi) != 0) {
changed = true;
break;
@@ -5864,6 +5883,9 @@
// required one word at a time.
while (src <= limit - sizeof(uintptr_t)) {
uintptr_t w = *reinterpret_cast<uintptr_t*>(src);
+#ifdef ENABLE_LATIN_1
+ or_acc |= w;
+#endif
uintptr_t m = AsciiRangeMask(w, lo, hi);
// The mask has high (7th) bit set in every byte that needs
// conversion and we know that the distance between cases is
@@ -5877,6 +5899,9 @@
// unaligned access is not supported).
while (src < limit) {
char c = *src;
+#ifdef ENABLE_LATIN_1
+ or_acc |= c;
+#endif
if (lo < c && c < hi) {
c ^= (1 << 5);
changed = true;
@@ -5885,10 +5910,20 @@
++src;
++dst;
}
+#ifdef ENABLE_LATIN_1
+ if ((or_acc & kAsciiMask) != 0) {
+ return false;
+ }
+#endif
#ifdef DEBUG
CheckConvert(saved_dst, saved_src, length, changed);
#endif
+#ifdef ENABLE_LATIN_1
+ *changed_out = changed;
+ return true;
+#else
return changed;
+#endif
}
#ifdef DEBUG
@@ -5941,7 +5976,6 @@
// Assume that the string is not empty; we need this assumption later
if (length == 0) return s;
-#ifndef ENABLE_LATIN_1
// Simpler handling of ASCII strings.
//
// NOTE: This assumes that the upper/lower case of an ASCII
@@ -5954,13 +5988,25 @@
if (!maybe_o->ToObject(&o)) return maybe_o;
}
SeqOneByteString* result = SeqOneByteString::cast(o);
+#ifndef ENABLE_LATIN_1
bool has_changed_character = ConvertTraits::AsciiConverter::Convert(
reinterpret_cast<char*>(result->GetChars()),
reinterpret_cast<char*>(SeqOneByteString::cast(s)->GetChars()),
length);
return has_changed_character ? result : s;
- }
+#else
+ bool has_changed_character;
+ bool is_ascii = ConvertTraits::AsciiConverter::Convert(
+ reinterpret_cast<char*>(result->GetChars()),
+ reinterpret_cast<char*>(SeqOneByteString::cast(s)->GetChars()),
+ length,
+ &has_changed_character);
+ // If not ASCII, we discard the result and take the 2 byte path.
+ if (is_ascii) {
+ return has_changed_character ? result : s;
+ }
#endif
+ }
Object* answer;
{ MaybeObject* maybe_answer =
@@ -12934,206 +12980,6 @@
return Smi::FromInt(usage);
}
-
-// Captures a live object list from the present heap.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_HasLOLEnabled) {
-#ifdef LIVE_OBJECT_LIST
- return isolate->heap()->true_value();
-#else
- return isolate->heap()->false_value();
-#endif
-}
-
-
-// Captures a live object list from the present heap.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_CaptureLOL) {
-#ifdef LIVE_OBJECT_LIST
- return LiveObjectList::Capture();
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Deletes the specified live object list.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteLOL) {
-#ifdef LIVE_OBJECT_LIST
- CONVERT_SMI_ARG_CHECKED(id, 0);
- bool success = LiveObjectList::Delete(id);
- return isolate->heap()->ToBoolean(success);
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Generates the response to a debugger request for a dump of the objects
-// contained in the difference between the captured live object lists
-// specified by id1 and id2.
-// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be
-// dumped.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_DumpLOL) {
-#ifdef LIVE_OBJECT_LIST
- HandleScope scope;
- CONVERT_SMI_ARG_CHECKED(id1, 0);
- CONVERT_SMI_ARG_CHECKED(id2, 1);
- CONVERT_SMI_ARG_CHECKED(start, 2);
- CONVERT_SMI_ARG_CHECKED(count, 3);
- CONVERT_ARG_HANDLE_CHECKED(JSObject, filter_obj, 4);
- EnterDebugger enter_debugger;
- return LiveObjectList::Dump(id1, id2, start, count, filter_obj);
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Gets the specified object as requested by the debugger.
-// This is only used for obj ids shown in live object lists.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLObj) {
-#ifdef LIVE_OBJECT_LIST
- CONVERT_SMI_ARG_CHECKED(obj_id, 0);
- Object* result = LiveObjectList::GetObj(obj_id);
- return result;
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Gets the obj id for the specified address if valid.
-// This is only used for obj ids shown in live object lists.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLObjId) {
-#ifdef LIVE_OBJECT_LIST
- HandleScope scope;
- CONVERT_ARG_HANDLE_CHECKED(String, address, 0);
- Object* result = LiveObjectList::GetObjId(address);
- return result;
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Gets the retainers that references the specified object alive.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLObjRetainers) {
-#ifdef LIVE_OBJECT_LIST
- HandleScope scope;
- CONVERT_SMI_ARG_CHECKED(obj_id, 0);
- RUNTIME_ASSERT(args[1]->IsUndefined() || args[1]->IsJSObject());
- RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsBoolean());
- RUNTIME_ASSERT(args[3]->IsUndefined() || args[3]->IsSmi());
- RUNTIME_ASSERT(args[4]->IsUndefined() || args[4]->IsSmi());
- CONVERT_ARG_HANDLE_CHECKED(JSObject, filter_obj, 5);
-
- Handle<JSObject> instance_filter;
- if (args[1]->IsJSObject()) {
- instance_filter = args.at<JSObject>(1);
- }
- bool verbose = false;
- if (args[2]->IsBoolean()) {
- verbose = args[2]->IsTrue();
- }
- int start = 0;
- if (args[3]->IsSmi()) {
- start = args.smi_at(3);
- }
- int limit = Smi::kMaxValue;
- if (args[4]->IsSmi()) {
- limit = args.smi_at(4);
- }
-
- return LiveObjectList::GetObjRetainers(obj_id,
- instance_filter,
- verbose,
- start,
- limit,
- filter_obj);
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Gets the reference path between 2 objects.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLOLPath) {
-#ifdef LIVE_OBJECT_LIST
- HandleScope scope;
- CONVERT_SMI_ARG_CHECKED(obj_id1, 0);
- CONVERT_SMI_ARG_CHECKED(obj_id2, 1);
- RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsJSObject());
-
- Handle<JSObject> instance_filter;
- if (args[2]->IsJSObject()) {
- instance_filter = args.at<JSObject>(2);
- }
-
- Object* result =
- LiveObjectList::GetPath(obj_id1, obj_id2, instance_filter);
- return result;
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Generates the response to a debugger request for a list of all
-// previously captured live object lists.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_InfoLOL) {
-#ifdef LIVE_OBJECT_LIST
- CONVERT_SMI_ARG_CHECKED(start, 0);
- CONVERT_SMI_ARG_CHECKED(count, 1);
- return LiveObjectList::Info(start, count);
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Gets a dump of the specified object as requested by the debugger.
-// This is only used for obj ids shown in live object lists.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_PrintLOLObj) {
-#ifdef LIVE_OBJECT_LIST
- HandleScope scope;
- CONVERT_SMI_ARG_CHECKED(obj_id, 0);
- Object* result = LiveObjectList::PrintObj(obj_id);
- return result;
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Resets and releases all previously captured live object lists.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_ResetLOL) {
-#ifdef LIVE_OBJECT_LIST
- LiveObjectList::Reset();
- return isolate->heap()->undefined_value();
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
-
-// Generates the response to a debugger request for a summary of the types
-// of objects in the difference between the captured live object lists
-// specified by id1 and id2.
-// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be
-// summarized.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_SummarizeLOL) {
-#ifdef LIVE_OBJECT_LIST
- HandleScope scope;
- CONVERT_SMI_ARG_CHECKED(id1, 0);
- CONVERT_SMI_ARG_CHECKED(id2, 1);
- CONVERT_ARG_HANDLE_CHECKED(JSObject, filter_obj, 2);
-
- EnterDebugger enter_debugger;
- return LiveObjectList::Summarize(id1, id2, filter_obj);
-#else
- return isolate->heap()->undefined_value();
-#endif
-}
-
#endif // ENABLE_DEBUGGER_SUPPORT
@@ -13219,19 +13065,49 @@
}
-// Retrieve the raw stack trace collected on stack overflow and delete
-// it since it is used only once to avoid keeping it alive.
-RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOverflowedRawStackTrace) {
+// Mark a function to recognize when called after GC to format the stack trace.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MarkOneShotGetter) {
+ ASSERT_EQ(args.length(), 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0);
+ HandleScope scope(isolate);
+ Handle<String> key = isolate->factory()->hidden_stack_trace_symbol();
+ JSObject::SetHiddenProperty(fun, key, key);
+ return *fun;
+}
+
+
+// Retrieve the stack trace. This could be the raw stack trace collected
+// on stack overflow or the already formatted stack trace string.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetOverflowedStackTrace) {
+ HandleScope scope(isolate);
ASSERT_EQ(args.length(), 1);
CONVERT_ARG_CHECKED(JSObject, error_object, 0);
String* key = isolate->heap()->hidden_stack_trace_symbol();
Object* result = error_object->GetHiddenProperty(key);
- RUNTIME_ASSERT(result->IsJSArray() || result->IsUndefined());
- error_object->DeleteHiddenProperty(key);
+ RUNTIME_ASSERT(result->IsJSArray() ||
+ result->IsString() ||
+ result->IsUndefined());
return result;
}
+// Set or clear the stack trace attached to an stack overflow error object.
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetOverflowedStackTrace) {
+ HandleScope scope(isolate);
+ ASSERT_EQ(args.length(), 2);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, error_object, 0);
+ CONVERT_ARG_HANDLE_CHECKED(HeapObject, value, 1);
+ Handle<String> key = isolate->factory()->hidden_stack_trace_symbol();
+ if (value->IsUndefined()) {
+ error_object->DeleteHiddenProperty(*key);
+ } else {
+ RUNTIME_ASSERT(value->IsString());
+ JSObject::SetHiddenProperty(error_object, key, value);
+ }
+ return *error_object;
+}
+
+
// Returns V8 version as a string.
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetV8Version) {
ASSERT_EQ(args.length(), 0);
diff --git a/src/runtime.h b/src/runtime.h
index b72813d..0bdc811 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -238,7 +238,9 @@
F(FunctionIsBuiltin, 1, 1) \
F(GetScript, 1, 1) \
F(CollectStackTrace, 3, 1) \
- F(GetOverflowedRawStackTrace, 1, 1) \
+ F(MarkOneShotGetter, 1, 1) \
+ F(GetOverflowedStackTrace, 1, 1) \
+ F(SetOverflowedStackTrace, 2, 1) \
F(GetV8Version, 0, 1) \
\
F(ClassOf, 1, 1) \
@@ -476,20 +478,6 @@
F(SetFlags, 1, 1) \
F(CollectGarbage, 1, 1) \
F(GetHeapUsage, 0, 1) \
- \
- /* LiveObjectList support*/ \
- F(HasLOLEnabled, 0, 1) \
- F(CaptureLOL, 0, 1) \
- F(DeleteLOL, 1, 1) \
- F(DumpLOL, 5, 1) \
- F(GetLOLObj, 1, 1) \
- F(GetLOLObjId, 1, 1) \
- F(GetLOLObjRetainers, 6, 1) \
- F(GetLOLPath, 3, 1) \
- F(InfoLOL, 2, 1) \
- F(PrintLOLObj, 1, 1) \
- F(ResetLOL, 0, 1) \
- F(SummarizeLOL, 3, 1)
#else
#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
diff --git a/src/spaces.cc b/src/spaces.cc
index 35619d1..b004dfa 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -27,7 +27,6 @@
#include "v8.h"
-#include "liveobjectlist-inl.h"
#include "macro-assembler.h"
#include "mark-compact.h"
#include "platform.h"
diff --git a/src/type-info.cc b/src/type-info.cc
index 00e8ee8..75e915b 100644
--- a/src/type-info.cc
+++ b/src/type-info.cc
@@ -488,7 +488,7 @@
isolate_->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) {
// TODO(fschneider): We could collect the maps and signal that
// we need a generic store (or load) here.
- ASSERT(Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC);
+ ASSERT(Handle<Code>::cast(object)->ic_state() == GENERIC);
} else if (object->IsMap()) {
types->Add(Handle<Map>::cast(object), zone());
} else if (FLAG_collect_megamorphic_maps_from_stub_cache &&
diff --git a/src/unicode-inl.h b/src/unicode-inl.h
index c3a00ed..b4e2cb5 100644
--- a/src/unicode-inl.h
+++ b/src/unicode-inl.h
@@ -79,6 +79,36 @@
}
+bool Latin1::NonLatin1CanBeConvertedToLatin1(uint16_t c) {
+ ASSERT(c > Latin1::kMaxChar);
+ switch (c) {
+ case 0x130:
+ case 0x131:
+ case 0x149:
+ case 0x178:
+ case 0x17f:
+ case 0x1f0:
+ case 0x1e96:
+ case 0x1e97:
+ case 0x1e98:
+ case 0x1e99:
+ case 0x1e9a:
+ case 0x1e9e:
+ case 0x212a:
+ case 0x212b:
+ case 0xfb00:
+ case 0xfb01:
+ case 0xfb02:
+ case 0xfb03:
+ case 0xfb04:
+ case 0xfb05:
+ case 0xfb06:
+ return true;
+ }
+ return false;
+}
+
+
unsigned Utf8::Encode(char* str, uchar c, int previous) {
static const int kMask = ~(1 << 6);
if (c <= kMaxOneByteChar) {
diff --git a/src/unicode.h b/src/unicode.h
index 61dbab4..0278576 100644
--- a/src/unicode.h
+++ b/src/unicode.h
@@ -140,6 +140,7 @@
#else
static const unsigned kMaxChar = 0xff;
#endif
+ static inline bool NonLatin1CanBeConvertedToLatin1(uint16_t);
};
class Utf8 {
diff --git a/src/v8globals.h b/src/v8globals.h
index 807be30..65e8455 100644
--- a/src/v8globals.h
+++ b/src/v8globals.h
@@ -263,6 +263,8 @@
POLYMORPHIC,
// Many receiver types have been seen.
MEGAMORPHIC,
+ // A generic handler is installed and no extra typefeedback is recorded.
+ GENERIC,
// Special state for debug break or step in prepare stubs.
DEBUG_STUB
};
diff --git a/src/v8threads.cc b/src/v8threads.cc
index 32ea5e1..925e198 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -42,15 +42,18 @@
bool Locker::active_ = false;
-// Constructor for the Locker object. Once the Locker is constructed the
-// current thread will be guaranteed to have the lock for a given isolate.
-Locker::Locker(v8::Isolate* isolate)
- : has_lock_(false),
- top_level_(true),
- isolate_(reinterpret_cast<i::Isolate*>(isolate)) {
- if (isolate_ == NULL) {
- isolate_ = i::Isolate::GetDefaultIsolateForLocking();
- }
+Locker::Locker() {
+ Initialize(i::Isolate::GetDefaultIsolateForLocking());
+}
+
+
+// Once the Locker is initialized, the current thread will be guaranteed to have
+// the lock for a given isolate.
+void Locker::Initialize(v8::Isolate* isolate) {
+ ASSERT(isolate != NULL);
+ has_lock_= false;
+ top_level_ = true;
+ isolate_ = reinterpret_cast<i::Isolate*>(isolate);
// Record that the Locker has been used at least once.
active_ = true;
// Get the big lock if necessary.
@@ -86,10 +89,8 @@
bool Locker::IsLocked(v8::Isolate* isolate) {
+ ASSERT(isolate != NULL);
i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
- if (internal_isolate == NULL) {
- internal_isolate = i::Isolate::GetDefaultIsolateForLocking();
- }
return internal_isolate->thread_manager()->IsLockedByCurrentThread();
}
@@ -115,11 +116,14 @@
}
-Unlocker::Unlocker(v8::Isolate* isolate)
- : isolate_(reinterpret_cast<i::Isolate*>(isolate)) {
- if (isolate_ == NULL) {
- isolate_ = i::Isolate::GetDefaultIsolateForLocking();
- }
+Unlocker::Unlocker() {
+ Initialize(i::Isolate::GetDefaultIsolateForLocking());
+}
+
+
+void Unlocker::Initialize(v8::Isolate* isolate) {
+ ASSERT(isolate != NULL);
+ isolate_ = reinterpret_cast<i::Isolate*>(isolate);
ASSERT(isolate_->thread_manager()->IsLockedByCurrentThread());
if (isolate_->IsDefaultIsolate()) {
isolate_->Exit();
@@ -479,7 +483,7 @@
// Acknowledge the preemption by the receiving thread.
void ContextSwitcher::PreemptionReceived() {
- ASSERT(Locker::IsLocked());
+ ASSERT(Locker::IsLocked(i::Isolate::GetDefaultIsolateForLocking()));
// There is currently no accounting being done for this. But could be in the
// future, which is why we leave this in.
}
diff --git a/src/v8utils.cc b/src/v8utils.cc
index 627169e..58ad4e5 100644
--- a/src/v8utils.cc
+++ b/src/v8utils.cc
@@ -273,97 +273,4 @@
}
}
-
-MemoryMappedExternalResource::MemoryMappedExternalResource(const char* filename)
- : filename_(NULL),
- data_(NULL),
- length_(0),
- remove_file_on_cleanup_(false) {
- Init(filename);
-}
-
-
-MemoryMappedExternalResource::
- MemoryMappedExternalResource(const char* filename,
- bool remove_file_on_cleanup)
- : filename_(NULL),
- data_(NULL),
- length_(0),
- remove_file_on_cleanup_(remove_file_on_cleanup) {
- Init(filename);
-}
-
-
-MemoryMappedExternalResource::~MemoryMappedExternalResource() {
- // Release the resources if we had successfully acquired them:
- if (file_ != NULL) {
- delete file_;
- if (remove_file_on_cleanup_) {
- OS::Remove(filename_);
- }
- DeleteArray<char>(filename_);
- }
-}
-
-
-void MemoryMappedExternalResource::Init(const char* filename) {
- file_ = OS::MemoryMappedFile::open(filename);
- if (file_ != NULL) {
- filename_ = StrDup(filename);
- data_ = reinterpret_cast<char*>(file_->memory());
- length_ = file_->size();
- }
-}
-
-
-bool MemoryMappedExternalResource::EnsureIsAscii(bool abort_if_failed) const {
- bool is_ascii = true;
-
- int line_no = 1;
- const char* start_of_line = data_;
- const char* end = data_ + length_;
- for (const char* p = data_; p < end; p++) {
- char c = *p;
- if ((c & 0x80) != 0) {
- // Non-ASCII detected:
- is_ascii = false;
-
- // Report the error and abort if appropriate:
- if (abort_if_failed) {
- int char_no = static_cast<int>(p - start_of_line) - 1;
-
- ASSERT(filename_ != NULL);
- PrintF("\n\n\n"
- "Abort: Non-Ascii character 0x%.2x in file %s line %d char %d",
- c, filename_, line_no, char_no);
-
- // Allow for some context up to kNumberOfLeadingContextChars chars
- // before the offending non-ASCII char to help the user see where
- // the offending char is.
- const int kNumberOfLeadingContextChars = 10;
- const char* err_context = p - kNumberOfLeadingContextChars;
- if (err_context < data_) {
- err_context = data_;
- }
- // Compute the length of the error context and print it.
- int err_context_length = static_cast<int>(p - err_context);
- if (err_context_length != 0) {
- PrintF(" after \"%.*s\"", err_context_length, err_context);
- }
- PrintF(".\n\n\n");
- OS::Abort();
- }
-
- break; // Non-ASCII detected. No need to continue scanning.
- }
- if (c == '\n') {
- start_of_line = p;
- line_no++;
- }
- }
-
- return is_ascii;
-}
-
-
} } // namespace v8::internal
diff --git a/src/v8utils.h b/src/v8utils.h
index 2064c5a..793d34d 100644
--- a/src/v8utils.h
+++ b/src/v8utils.h
@@ -249,7 +249,8 @@
}
// Number of characters in a uintptr_t.
static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest); // NOLINT
- while (dest <= limit - kStepSize) {
+ ASSERT(dest + kStepSize > dest); // Check for overflow.
+ while (dest + kStepSize <= limit) {
*reinterpret_cast<uintptr_t*>(dest) =
*reinterpret_cast<const uintptr_t*>(src);
dest += kStepSize;
@@ -263,37 +264,6 @@
}
-// A resource for using mmapped files to back external strings that are read
-// from files.
-class MemoryMappedExternalResource: public
- v8::String::ExternalAsciiStringResource {
- public:
- explicit MemoryMappedExternalResource(const char* filename);
- MemoryMappedExternalResource(const char* filename,
- bool remove_file_on_cleanup);
- virtual ~MemoryMappedExternalResource();
-
- virtual const char* data() const { return data_; }
- virtual size_t length() const { return length_; }
-
- bool exists() const { return file_ != NULL; }
- bool is_empty() const { return length_ == 0; }
-
- bool EnsureIsAscii(bool abort_if_failed) const;
- bool EnsureIsAscii() const { return EnsureIsAscii(true); }
- bool IsAscii() const { return EnsureIsAscii(false); }
-
- private:
- void Init(const char* filename);
-
- char* filename_;
- OS::MemoryMappedFile* file_;
-
- const char* data_;
- size_t length_;
- bool remove_file_on_cleanup_;
-};
-
class StringBuilder : public SimpleStringBuilder {
public:
explicit StringBuilder(int size) : SimpleStringBuilder(size) { }
diff --git a/src/version.cc b/src/version.cc
index e996622..a95ba1a 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,8 +34,8 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
#define MINOR_VERSION 16
-#define BUILD_NUMBER 4
-#define PATCH_LEVEL 2
+#define BUILD_NUMBER 6
+#define PATCH_LEVEL 0
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
#define IS_CANDIDATE_VERSION 0
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 160a0df..b989a88 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -1646,6 +1646,15 @@
}
+void Assembler::movzxwl(Register dst, Register src) {
+ EnsureSpace ensure_space(this);
+ emit_optional_rex_32(dst, src);
+ emit(0x0F);
+ emit(0xB7);
+ emit_modrm(dst, src);
+}
+
+
void Assembler::repmovsb() {
EnsureSpace ensure_space(this);
emit(0xF3);
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index f2243d3..406df27 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -732,6 +732,7 @@
void movzxbl(Register dst, const Operand& src);
void movzxwq(Register dst, const Operand& src);
void movzxwl(Register dst, const Operand& src);
+ void movzxwl(Register dst, Register src);
// Repeated moves.
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 3f17d03..82f9100 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -316,7 +316,7 @@
MacroAssembler* masm,
int length,
FastCloneShallowArrayStub::Mode mode,
- AllocationSiteInfoMode allocation_site_info_mode,
+ AllocationSiteMode allocation_site_mode,
Label* fail) {
// Registers on entry:
//
@@ -332,7 +332,7 @@
}
int size = JSArray::kSize;
int allocation_info_start = size;
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
size += AllocationSiteInfo::kSize;
}
size += elements_size;
@@ -345,7 +345,7 @@
}
__ AllocateInNewSpace(size, rax, rbx, rdx, fail, flags);
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
__ LoadRoot(kScratchRegister, Heap::kAllocationSiteInfoMapRootIndex);
__ movq(FieldOperand(rax, allocation_info_start), kScratchRegister);
__ movq(FieldOperand(rax, allocation_info_start + kPointerSize), rcx);
@@ -363,7 +363,7 @@
// Get hold of the elements array of the boilerplate and setup the
// elements pointer in the resulting object.
__ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset));
- if (allocation_site_info_mode == TRACK_ALLOCATION_SITE_INFO) {
+ if (allocation_site_mode == TRACK_ALLOCATION_SITE) {
__ lea(rdx, Operand(rax, JSArray::kSize + AllocationSiteInfo::kSize));
} else {
__ lea(rdx, Operand(rax, JSArray::kSize));
@@ -414,22 +414,14 @@
FastCloneShallowArrayStub::Mode mode = mode_;
// rcx is boilerplate object.
Factory* factory = masm->isolate()->factory();
- AllocationSiteInfoMode allocation_site_info_mode =
- DONT_TRACK_ALLOCATION_SITE_INFO;
- if (mode == CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO) {
- mode = CLONE_ANY_ELEMENTS;
- allocation_site_info_mode = TRACK_ALLOCATION_SITE_INFO;
- }
-
if (mode == CLONE_ANY_ELEMENTS) {
Label double_elements, check_fast_elements;
__ movq(rbx, FieldOperand(rcx, JSArray::kElementsOffset));
__ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
factory->fixed_cow_array_map());
__ j(not_equal, &check_fast_elements);
- GenerateFastCloneShallowArrayCommon(masm, 0,
- COPY_ON_WRITE_ELEMENTS,
- allocation_site_info_mode,
+ GenerateFastCloneShallowArrayCommon(masm, 0, COPY_ON_WRITE_ELEMENTS,
+ allocation_site_mode_,
&slow_case);
__ ret(3 * kPointerSize);
@@ -437,9 +429,8 @@
__ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
factory->fixed_array_map());
__ j(not_equal, &double_elements);
- GenerateFastCloneShallowArrayCommon(masm, length_,
- CLONE_ELEMENTS,
- allocation_site_info_mode,
+ GenerateFastCloneShallowArrayCommon(masm, length_, CLONE_ELEMENTS,
+ allocation_site_mode_,
&slow_case);
__ ret(3 * kPointerSize);
@@ -471,7 +462,8 @@
}
GenerateFastCloneShallowArrayCommon(masm, length_, mode,
- allocation_site_info_mode, &slow_case);
+ allocation_site_mode_,
+ &slow_case);
__ ret(3 * kPointerSize);
__ bind(&slow_case);
@@ -4782,6 +4774,11 @@
// r9: second instance type.
__ testb(rcx, Immediate(kAsciiDataHintMask));
__ j(not_zero, &ascii_data);
+ __ xor_(r8, r9);
+ STATIC_ASSERT(kOneByteStringTag != 0 && kAsciiDataHintTag != 0);
+ __ andb(r8, Immediate(kOneByteStringTag | kAsciiDataHintTag));
+ __ cmpb(r8, Immediate(kOneByteStringTag | kAsciiDataHintTag));
+ __ j(equal, &ascii_data);
// Allocate a two byte cons string.
__ AllocateTwoByteConsString(rcx, rdi, no_reg, &call_runtime);
__ jmp(&allocated);
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index 0b2480a..36880bd 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -251,7 +251,8 @@
#define __ ACCESS_MASM(masm)
void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
- MacroAssembler* masm) {
+ MacroAssembler* masm, AllocationSiteMode mode,
+ Label* allocation_site_info_found) {
// ----------- S t a t e -------------
// -- rax : value
// -- rbx : target map
@@ -259,6 +260,12 @@
// -- rdx : receiver
// -- rsp[0] : return address
// -----------------------------------
+ if (mode == TRACK_ALLOCATION_SITE) {
+ ASSERT(allocation_site_info_found != NULL);
+ masm->TestJSArrayForAllocationSiteInfo(rdx, rdi,
+ allocation_site_info_found);
+ }
+
// Set transitioned map.
__ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx);
__ RecordWriteField(rdx,
@@ -272,7 +279,7 @@
void ElementsTransitionGenerator::GenerateSmiToDouble(
- MacroAssembler* masm, Label* fail) {
+ MacroAssembler* masm, AllocationSiteMode mode, Label* fail) {
// ----------- S t a t e -------------
// -- rax : value
// -- rbx : target map
@@ -283,7 +290,7 @@
// The fail label is not actually used since we do not allocate.
Label allocated, new_backing_store, only_change_map, done;
- if (FLAG_track_allocation_sites) {
+ if (mode == TRACK_ALLOCATION_SITE) {
masm->TestJSArrayForAllocationSiteInfo(rdx, rdi, fail);
}
@@ -398,7 +405,7 @@
void ElementsTransitionGenerator::GenerateDoubleToObject(
- MacroAssembler* masm, Label* fail) {
+ MacroAssembler* masm, AllocationSiteMode mode, Label* fail) {
// ----------- S t a t e -------------
// -- rax : value
// -- rbx : target map
@@ -408,6 +415,10 @@
// -----------------------------------
Label loop, entry, convert_hole, gc_required, only_change_map;
+ if (mode == TRACK_ALLOCATION_SITE) {
+ masm->TestJSArrayForAllocationSiteInfo(rdx, rdi, fail);
+ }
+
// Check for empty arrays, which only require a map transition and no changes
// to the backing store.
__ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset));
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 6f18c19..46447de 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -1689,6 +1689,7 @@
__ IncrementCounter(isolate()->counters()->cow_arrays_created_stub(), 1);
FastCloneShallowArrayStub stub(
FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS,
+ DONT_TRACK_ALLOCATION_SITE,
length);
__ CallStub(&stub);
} else if (expr->depth() > 1) {
@@ -1698,21 +1699,19 @@
} else {
ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
FLAG_smi_only_arrays);
+ FastCloneShallowArrayStub::Mode mode =
+ FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
+ AllocationSiteMode allocation_site_mode = FLAG_track_allocation_sites
+ ? TRACK_ALLOCATION_SITE : DONT_TRACK_ALLOCATION_SITE;
+
// If the elements are already FAST_*_ELEMENTS, the boilerplate cannot
// change, so it's possible to specialize the stub in advance.
- FastCloneShallowArrayStub::Mode mode = has_constant_fast_elements
- ? FastCloneShallowArrayStub::CLONE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
-
- // Tracking allocation info allows us to pre-transition later if it makes
- // sense.
- if (mode == FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS &&
- FLAG_track_allocation_sites) {
- mode = FastCloneShallowArrayStub::
- CLONE_ANY_ELEMENTS_WITH_ALLOCATION_SITE_INFO;
+ if (has_constant_fast_elements) {
+ mode = FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ allocation_site_mode = DONT_TRACK_ALLOCATION_SITE;
}
- FastCloneShallowArrayStub stub(mode, length);
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
__ CallStub(&stub);
}
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index f3061af..657c88a 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -709,7 +709,9 @@
rbx,
rdi,
slow);
- ElementsTransitionGenerator::GenerateSmiToDouble(masm, slow);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, slow);
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ jmp(&fast_double_without_map_check);
@@ -720,7 +722,9 @@
rbx,
rdi,
slow);
- ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm);
+ mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm, mode,
+ slow);
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ jmp(&finish_object_store);
@@ -734,7 +738,8 @@
rbx,
rdi,
slow);
- ElementsTransitionGenerator::GenerateDoubleToObject(masm, slow);
+ mode = AllocationSiteInfo::GetMode(FAST_DOUBLE_ELEMENTS, FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, slow);
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
__ jmp(&finish_object_store);
}
@@ -1670,7 +1675,9 @@
// Must return the modified receiver in eax.
if (!FLAG_trace_elements_transitions) {
Label fail;
- ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_SMI_ELEMENTS,
+ FAST_DOUBLE_ELEMENTS);
+ ElementsTransitionGenerator::GenerateSmiToDouble(masm, mode, &fail);
__ movq(rax, rdx);
__ Ret();
__ bind(&fail);
@@ -1693,7 +1700,9 @@
// Must return the modified receiver in eax.
if (!FLAG_trace_elements_transitions) {
Label fail;
- ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail);
+ AllocationSiteMode mode = AllocationSiteInfo::GetMode(FAST_DOUBLE_ELEMENTS,
+ FAST_ELEMENTS);
+ ElementsTransitionGenerator::GenerateDoubleToObject(masm, mode, &fail);
__ movq(rax, rdx);
__ Ret();
__ bind(&fail);
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index 8c4dafa..87eb954 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -3603,8 +3603,7 @@
// state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
// Only operate on the lower 32 bit of rax.
- __ movl(rdx, rax);
- __ andl(rdx, Immediate(0xFFFF));
+ __ movzxwl(rdx, rax);
__ imull(rdx, rdx, Immediate(18273));
__ shrl(rax, Immediate(16));
__ addl(rax, rdx);
@@ -3612,8 +3611,7 @@
__ movl(FieldOperand(rbx, ByteArray::kHeaderSize), rax);
// state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
- __ movl(rdx, rcx);
- __ andl(rdx, Immediate(0xFFFF));
+ __ movzxwl(rdx, rcx);
__ imull(rdx, rdx, Immediate(36969));
__ shrl(rcx, Immediate(16));
__ addl(rcx, rdx);
@@ -3629,10 +3627,10 @@
// Convert 32 random bits in rax to 0.(32 random bits) in a double
// by computing:
// ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
- __ movl(rcx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
- __ movd(xmm2, rcx);
+ __ movq(rcx, V8_INT64_C(0x4130000000000000),
+ RelocInfo::NONE64); // 1.0 x 2^20 as double
+ __ movq(xmm2, rcx);
__ movd(xmm1, rax);
- __ cvtss2sd(xmm2, xmm2);
__ xorps(xmm1, xmm2);
__ subsd(xmm1, xmm2);
}
@@ -4903,6 +4901,8 @@
Handle<FixedArray> literals(instr->environment()->closure()->literals());
ElementsKind boilerplate_elements_kind =
instr->hydrogen()->boilerplate_elements_kind();
+ AllocationSiteMode allocation_site_mode =
+ instr->hydrogen()->allocation_site_mode();
// Deopt if the array literal boilerplate ElementsKind is of a type different
// than the expected one. The check isn't necessary if the boilerplate has
@@ -4933,7 +4933,7 @@
ASSERT(instr->hydrogen()->depth() == 1);
FastCloneShallowArrayStub::Mode mode =
FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS;
- FastCloneShallowArrayStub stub(mode, length);
+ FastCloneShallowArrayStub stub(mode, DONT_TRACK_ALLOCATION_SITE, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
} else if (instr->hydrogen()->depth() > 1) {
CallRuntime(Runtime::kCreateArrayLiteral, 3, instr);
@@ -4942,9 +4942,9 @@
} else {
FastCloneShallowArrayStub::Mode mode =
boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
- ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
- : FastCloneShallowArrayStub::CLONE_ELEMENTS;
- FastCloneShallowArrayStub stub(mode, length);
+ ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
+ : FastCloneShallowArrayStub::CLONE_ELEMENTS;
+ FastCloneShallowArrayStub stub(mode, allocation_site_mode, length);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
}
}
@@ -4953,10 +4953,14 @@
void LCodeGen::EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
- int* offset) {
+ int* offset,
+ AllocationSiteMode mode) {
ASSERT(!source.is(rcx));
ASSERT(!result.is(rcx));
+ bool create_allocation_site_info = mode == TRACK_ALLOCATION_SITE &&
+ object->map()->CanTrackAllocationSite();
+
// Only elements backing stores for non-COW arrays need to be copied.
Handle<FixedArrayBase> elements(object->elements());
bool has_elements = elements->length() > 0 &&
@@ -4966,8 +4970,13 @@
// this object and its backing store.
int object_offset = *offset;
int object_size = object->map()->instance_size();
- int elements_offset = *offset + object_size;
int elements_size = has_elements ? elements->Size() : 0;
+ int elements_offset = *offset + object_size;
+ if (create_allocation_site_info) {
+ elements_offset += AllocationSiteInfo::kSize;
+ *offset += AllocationSiteInfo::kSize;
+ }
+
*offset += object_size + elements_size;
// Copy object header.
@@ -4992,7 +5001,8 @@
__ lea(rcx, Operand(result, *offset));
__ movq(FieldOperand(result, total_offset), rcx);
__ LoadHeapObject(source, value_object);
- EmitDeepCopy(value_object, result, source, offset);
+ EmitDeepCopy(value_object, result, source, offset,
+ DONT_TRACK_ALLOCATION_SITE);
} else if (value->IsHeapObject()) {
__ LoadHeapObject(rcx, Handle<HeapObject>::cast(value));
__ movq(FieldOperand(result, total_offset), rcx);
@@ -5002,6 +5012,13 @@
}
}
+ // Build Allocation Site Info if desired
+ if (create_allocation_site_info) {
+ __ LoadRoot(kScratchRegister, Heap::kAllocationSiteInfoMapRootIndex);
+ __ movq(FieldOperand(result, object_size), kScratchRegister);
+ __ movq(FieldOperand(result, object_size + kPointerSize), source);
+ }
+
if (has_elements) {
// Copy elements backing store header.
__ LoadHeapObject(source, elements);
@@ -5032,7 +5049,8 @@
__ lea(rcx, Operand(result, *offset));
__ movq(FieldOperand(result, total_offset), rcx);
__ LoadHeapObject(source, value_object);
- EmitDeepCopy(value_object, result, source, offset);
+ EmitDeepCopy(value_object, result, source, offset,
+ DONT_TRACK_ALLOCATION_SITE);
} else if (value->IsHeapObject()) {
__ LoadHeapObject(rcx, Handle<HeapObject>::cast(value));
__ movq(FieldOperand(result, total_offset), rcx);
@@ -5082,7 +5100,8 @@
__ bind(&allocated);
int offset = 0;
__ LoadHeapObject(rbx, instr->hydrogen()->boilerplate());
- EmitDeepCopy(instr->hydrogen()->boilerplate(), rax, rbx, &offset);
+ EmitDeepCopy(instr->hydrogen()->boilerplate(), rax, rbx, &offset,
+ instr->hydrogen()->allocation_site_mode());
ASSERT_EQ(size, offset);
}
@@ -5360,6 +5379,11 @@
}
+void LCodeGen::DoDummyUse(LDummyUse* instr) {
+ // Nothing to see here, move on!
+}
+
+
void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
LOperand* obj = instr->object();
LOperand* key = instr->key();
diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h
index 2fa10e1..08557ab 100644
--- a/src/x64/lithium-codegen-x64.h
+++ b/src/x64/lithium-codegen-x64.h
@@ -334,7 +334,8 @@
void EmitDeepCopy(Handle<JSObject> object,
Register result,
Register source,
- int* offset);
+ int* offset,
+ AllocationSiteMode mode);
struct JumpTableEntry {
inline JumpTableEntry(Address entry, bool frame, bool is_lazy)
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index 85eeee1..c31de81 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -690,6 +690,11 @@
}
+LInstruction* LChunkBuilder::DoDummyUse(HDummyUse* instr) {
+ return DefineAsRegister(new(zone()) LDummyUse(UseAny(instr->value())));
+}
+
+
LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
return AssignEnvironment(new(zone()) LDeoptimize);
}
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index 9def5c5..ffec14a 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -93,6 +93,7 @@
V(Deoptimize) \
V(DivI) \
V(DoubleToI) \
+ V(DummyUse) \
V(ElementsKind) \
V(FastLiteral) \
V(FixedArrayBaseLength) \
@@ -404,6 +405,15 @@
};
+class LDummyUse: public LTemplateInstruction<1, 1, 0> {
+ public:
+ explicit LDummyUse(LOperand* value) {
+ inputs_[0] = value;
+ }
+ DECLARE_CONCRETE_INSTRUCTION(DummyUse, "dummy-use")
+};
+
+
class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
public:
DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 6e9e78e..52699e7 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -2218,19 +2218,16 @@
// Check that both are flat ASCII strings.
ASSERT(kNotStringTag != 0);
const int kFlatAsciiStringMask =
- kIsNotStringMask | kStringEncodingMask | kAsciiDataHintMask |
- kStringRepresentationMask;
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
const int kFlatAsciiStringTag = ASCII_STRING_TYPE;
andl(scratch1, Immediate(kFlatAsciiStringMask));
andl(scratch2, Immediate(kFlatAsciiStringMask));
// Interleave the bits to check both scratch1 and scratch2 in one test.
- ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 8));
- ASSERT_EQ(ASCII_STRING_TYPE, ASCII_STRING_TYPE & kFlatAsciiStringMask);
- shl(scratch1, Immediate(8));
- orl(scratch1, scratch2);
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
cmpl(scratch1,
- Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 8)));
+ Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
j(not_equal, on_fail, near_jump);
}
@@ -2266,19 +2263,17 @@
// Check that both are flat ASCII strings.
ASSERT(kNotStringTag != 0);
- const int kFlatAsciiStringMask = kIsNotStringMask | kStringRepresentationMask
- | kStringEncodingMask | kAsciiDataHintTag;
+ const int kFlatAsciiStringMask =
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask;
const int kFlatAsciiStringTag = ASCII_STRING_TYPE;
andl(scratch1, Immediate(kFlatAsciiStringMask));
andl(scratch2, Immediate(kFlatAsciiStringMask));
// Interleave the bits to check both scratch1 and scratch2 in one test.
- ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 8));
- ASSERT_EQ(ASCII_STRING_TYPE, ASCII_STRING_TYPE & kFlatAsciiStringMask);
- shl(scratch1, Immediate(8));
- orl(scratch1, scratch2);
+ ASSERT_EQ(0, kFlatAsciiStringMask & (kFlatAsciiStringMask << 3));
+ lea(scratch1, Operand(scratch1, scratch2, times_8, 0));
cmpl(scratch1,
- Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 8)));
+ Immediate(kFlatAsciiStringTag + (kFlatAsciiStringTag << 3)));
j(not_equal, on_fail, near_jump);
}
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 82727ef..46d74b6 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -1579,7 +1579,9 @@
&try_holey_map);
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm());
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
// Restore edi.
__ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset));
__ jmp(&fast_object);
@@ -1591,7 +1593,9 @@
rdi,
&call_builtin);
ElementsTransitionGenerator::
- GenerateMapChangeElementsTransition(masm());
+ GenerateMapChangeElementsTransition(masm(),
+ DONT_TRACK_ALLOCATION_SITE,
+ NULL);
__ movq(rdi, FieldOperand(rdx, JSArray::kElementsOffset));
__ bind(&fast_object);
} else {