mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
66 changed files with 2564 additions and 569 deletions
@ -0,0 +1,548 @@ |
|||||
|
// Copyright 2009 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 "heap-profiler.h" |
||||
|
#include "string-stream.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
|
||||
|
#ifdef ENABLE_LOGGING_AND_PROFILING |
||||
|
namespace { |
||||
|
|
||||
|
// Clusterizer is a set of helper functions for converting
|
||||
|
// object references into clusters.
|
||||
|
class Clusterizer : public AllStatic { |
||||
|
public: |
||||
|
static JSObjectsCluster Clusterize(HeapObject* obj) { |
||||
|
return Clusterize(obj, true); |
||||
|
} |
||||
|
static void InsertIntoTree(JSObjectsClusterTree* tree, |
||||
|
HeapObject* obj, bool fine_grain); |
||||
|
static void InsertReferenceIntoTree(JSObjectsClusterTree* tree, |
||||
|
const JSObjectsCluster& cluster) { |
||||
|
InsertIntoTree(tree, cluster, 0); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
static JSObjectsCluster Clusterize(HeapObject* obj, bool fine_grain); |
||||
|
static int CalculateNetworkSize(JSObject* obj); |
||||
|
static int GetObjectSize(HeapObject* obj) { |
||||
|
return obj->IsJSObject() ? |
||||
|
CalculateNetworkSize(JSObject::cast(obj)) : obj->Size(); |
||||
|
} |
||||
|
static void InsertIntoTree(JSObjectsClusterTree* tree, |
||||
|
const JSObjectsCluster& cluster, int size); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) { |
||||
|
if (obj->IsJSObject()) { |
||||
|
JSObject* js_obj = JSObject::cast(obj); |
||||
|
String* constructor = JSObject::cast(js_obj)->constructor_name(); |
||||
|
// Differentiate Array, Function, and Object instances.
|
||||
|
if (fine_grain && (constructor == Heap::Object_symbol() || |
||||
|
constructor == Heap::Array_symbol() || |
||||
|
constructor == Heap::function_class_symbol())) { |
||||
|
return JSObjectsCluster(constructor, obj); |
||||
|
} else { |
||||
|
return JSObjectsCluster(constructor); |
||||
|
} |
||||
|
} else if (obj->IsString()) { |
||||
|
return JSObjectsCluster(Heap::String_symbol()); |
||||
|
} |
||||
|
return JSObjectsCluster(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree, |
||||
|
HeapObject* obj, bool fine_grain) { |
||||
|
JSObjectsCluster cluster = Clusterize(obj, fine_grain); |
||||
|
if (cluster.is_null()) return; |
||||
|
InsertIntoTree(tree, cluster, GetObjectSize(obj)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree, |
||||
|
const JSObjectsCluster& cluster, int size) { |
||||
|
JSObjectsClusterTree::Locator loc; |
||||
|
tree->Insert(cluster, &loc); |
||||
|
NumberAndSizeInfo number_and_size = loc.value(); |
||||
|
number_and_size.increment_number(1); |
||||
|
number_and_size.increment_bytes(size); |
||||
|
loc.set_value(number_and_size); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int Clusterizer::CalculateNetworkSize(JSObject* obj) { |
||||
|
int size = obj->Size(); |
||||
|
// If 'properties' and 'elements' are non-empty (thus, non-shared),
|
||||
|
// take their size into account.
|
||||
|
if (FixedArray::cast(obj->properties())->length() != 0) { |
||||
|
size += obj->properties()->Size(); |
||||
|
} |
||||
|
if (FixedArray::cast(obj->elements())->length() != 0) { |
||||
|
size += obj->elements()->Size(); |
||||
|
} |
||||
|
return size; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// A helper class for recording back references.
|
||||
|
class ReferencesExtractor : public ObjectVisitor { |
||||
|
public: |
||||
|
ReferencesExtractor(const JSObjectsCluster& cluster, |
||||
|
RetainerHeapProfile* profile) |
||||
|
: cluster_(cluster), |
||||
|
profile_(profile), |
||||
|
inside_array_(false) { |
||||
|
} |
||||
|
|
||||
|
void VisitPointer(Object** o) { |
||||
|
if ((*o)->IsJSObject() || (*o)->IsString()) { |
||||
|
profile_->StoreReference(cluster_, HeapObject::cast(*o)); |
||||
|
} else if ((*o)->IsFixedArray() && !inside_array_) { |
||||
|
// Traverse one level deep for data members that are fixed arrays.
|
||||
|
// This covers the case of 'elements' and 'properties' of JSObject,
|
||||
|
// and function contexts.
|
||||
|
inside_array_ = true; |
||||
|
FixedArray::cast(*o)->Iterate(this); |
||||
|
inside_array_ = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void VisitPointers(Object** start, Object** end) { |
||||
|
for (Object** p = start; p < end; p++) VisitPointer(p); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
const JSObjectsCluster& cluster_; |
||||
|
RetainerHeapProfile* profile_; |
||||
|
bool inside_array_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// A printer interface implementation for the Retainers profile.
|
||||
|
class RetainersPrinter : public RetainerHeapProfile::Printer { |
||||
|
public: |
||||
|
void PrintRetainers(const JSObjectsCluster& cluster, |
||||
|
const StringStream& retainers) { |
||||
|
HeapStringAllocator allocator; |
||||
|
StringStream stream(&allocator); |
||||
|
cluster.Print(&stream); |
||||
|
LOG(HeapSampleJSRetainersEvent( |
||||
|
*(stream.ToCString()), *(retainers.ToCString()))); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class RetainerTreePrinter BASE_EMBEDDED { |
||||
|
public: |
||||
|
explicit RetainerTreePrinter(StringStream* stream) : stream_(stream) {} |
||||
|
void Call(const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size) { |
||||
|
Print(stream_, cluster, number_and_size); |
||||
|
} |
||||
|
static void Print(StringStream* stream, |
||||
|
const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& numNNber_and_size); |
||||
|
|
||||
|
private: |
||||
|
StringStream* stream_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
void RetainerTreePrinter::Print(StringStream* stream, |
||||
|
const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size) { |
||||
|
stream->Put(','); |
||||
|
cluster.Print(stream); |
||||
|
stream->Add(";%d", number_and_size.number()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} // namespace
|
||||
|
|
||||
|
|
||||
|
const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey; |
||||
|
const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue; |
||||
|
|
||||
|
|
||||
|
ConstructorHeapProfile::ConstructorHeapProfile() |
||||
|
: zscope_(DELETE_ON_EXIT) { |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ConstructorHeapProfile::Call(const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size) { |
||||
|
HeapStringAllocator allocator; |
||||
|
StringStream stream(&allocator); |
||||
|
cluster.Print(&stream); |
||||
|
LOG(HeapSampleJSConstructorEvent(*(stream.ToCString()), |
||||
|
number_and_size.number(), |
||||
|
number_and_size.bytes())); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ConstructorHeapProfile::CollectStats(HeapObject* obj) { |
||||
|
Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ConstructorHeapProfile::PrintStats() { |
||||
|
js_objects_info_tree_.ForEach(this); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JSObjectsCluster::Print(StringStream* accumulator) const { |
||||
|
ASSERT(!is_null()); |
||||
|
if (constructor_ == FromSpecialCase(ROOTS)) { |
||||
|
accumulator->Add("(roots)"); |
||||
|
} else if (constructor_ == FromSpecialCase(GLOBAL_PROPERTY)) { |
||||
|
accumulator->Add("(global property)"); |
||||
|
} else { |
||||
|
SmartPointer<char> s_name( |
||||
|
constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); |
||||
|
accumulator->Add("%s", (*s_name)[0] != '\0' ? *s_name : "(anonymous)"); |
||||
|
if (instance_ != NULL) { |
||||
|
accumulator->Add(":%p", static_cast<void*>(instance_)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void JSObjectsCluster::DebugPrint(StringStream* accumulator) const { |
||||
|
if (!is_null()) { |
||||
|
Print(accumulator); |
||||
|
} else { |
||||
|
accumulator->Add("(null cluster)"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs( |
||||
|
const JSObjectsCluster& cluster_) |
||||
|
: cluster(cluster_), refs(kInitialBackrefsListCapacity) { |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs( |
||||
|
const ClustersCoarser::ClusterBackRefs& src) |
||||
|
: cluster(src.cluster), refs(src.refs.capacity()) { |
||||
|
refs.AddAll(src.refs); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline ClustersCoarser::ClusterBackRefs& |
||||
|
ClustersCoarser::ClusterBackRefs::operator=( |
||||
|
const ClustersCoarser::ClusterBackRefs& src) { |
||||
|
if (this == &src) return *this; |
||||
|
cluster = src.cluster; |
||||
|
refs.Clear(); |
||||
|
refs.AddAll(src.refs); |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline int ClustersCoarser::ClusterBackRefs::Compare( |
||||
|
const ClustersCoarser::ClusterBackRefs& a, |
||||
|
const ClustersCoarser::ClusterBackRefs& b) { |
||||
|
int cmp = JSObjectsCluster::CompareConstructors(a.cluster, b.cluster); |
||||
|
if (cmp != 0) return cmp; |
||||
|
if (a.refs.length() < b.refs.length()) return -1; |
||||
|
if (a.refs.length() > b.refs.length()) return 1; |
||||
|
for (int i = 0; i < a.refs.length(); ++i) { |
||||
|
int cmp = JSObjectsCluster::Compare(a.refs[i], b.refs[i]); |
||||
|
if (cmp != 0) return cmp; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
ClustersCoarser::ClustersCoarser() |
||||
|
: zscope_(DELETE_ON_EXIT), |
||||
|
sim_list_(ClustersCoarser::kInitialSimilarityListCapacity), |
||||
|
current_pair_(NULL) { |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ClustersCoarser::Call(const JSObjectsCluster& cluster, |
||||
|
JSObjectsClusterTree* tree) { |
||||
|
if (!cluster.can_be_coarsed()) return; |
||||
|
ClusterBackRefs pair(cluster); |
||||
|
ASSERT(current_pair_ == NULL); |
||||
|
current_pair_ = &pair; |
||||
|
current_set_ = new JSObjectsRetainerTree(); |
||||
|
tree->ForEach(this); |
||||
|
sim_list_.Add(pair); |
||||
|
current_pair_ = NULL; |
||||
|
current_set_ = NULL; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ClustersCoarser::Call(const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size) { |
||||
|
ASSERT(current_pair_ != NULL); |
||||
|
ASSERT(current_set_ != NULL); |
||||
|
JSObjectsCluster eq = GetCoarseEquivalent(cluster); |
||||
|
JSObjectsRetainerTree::Locator loc; |
||||
|
if (!eq.is_null()) { |
||||
|
if (current_set_->Find(eq, &loc)) return; |
||||
|
current_pair_->refs.Add(eq); |
||||
|
current_set_->Insert(eq, &loc); |
||||
|
} else { |
||||
|
current_pair_->refs.Add(cluster); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ClustersCoarser::Process(JSObjectsRetainerTree* tree) { |
||||
|
int last_eq_clusters = -1; |
||||
|
for (int i = 0; i < kMaxPassesCount; ++i) { |
||||
|
sim_list_.Clear(); |
||||
|
const int curr_eq_clusters = DoProcess(tree); |
||||
|
// If no new cluster equivalents discovered, abort processing.
|
||||
|
if (last_eq_clusters == curr_eq_clusters) break; |
||||
|
last_eq_clusters = curr_eq_clusters; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) { |
||||
|
tree->ForEach(this); |
||||
|
// To sort similarity list properly, references list of a cluster is
|
||||
|
// required to be sorted, thus 'O1 <- A, B' and 'O2 <- B, A' would
|
||||
|
// be considered equivalent. But we don't sort them explicitly
|
||||
|
// because we know that they come from a splay tree traversal, so
|
||||
|
// they are already sorted.
|
||||
|
sim_list_.Sort(ClusterBackRefsCmp); |
||||
|
return FillEqualityTree(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
JSObjectsCluster ClustersCoarser::GetCoarseEquivalent( |
||||
|
const JSObjectsCluster& cluster) { |
||||
|
if (!cluster.can_be_coarsed()) return JSObjectsCluster(); |
||||
|
EqualityTree::Locator loc; |
||||
|
return eq_tree_.Find(cluster, &loc) ? loc.value() : JSObjectsCluster(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
bool ClustersCoarser::HasAnEquivalent(const JSObjectsCluster& cluster) { |
||||
|
// Return true for coarsible clusters that have a non-identical equivalent.
|
||||
|
return cluster.can_be_coarsed() && |
||||
|
JSObjectsCluster::Compare(cluster, GetCoarseEquivalent(cluster)) != 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int ClustersCoarser::FillEqualityTree() { |
||||
|
int eq_clusters_count = 0; |
||||
|
int eq_to = 0; |
||||
|
bool first_added = false; |
||||
|
for (int i = 1; i < sim_list_.length(); ++i) { |
||||
|
if (ClusterBackRefs::Compare(sim_list_[i], sim_list_[eq_to]) == 0) { |
||||
|
EqualityTree::Locator loc; |
||||
|
if (!first_added) { |
||||
|
// Add self-equivalence, if we have more than one item in this
|
||||
|
// equivalence class.
|
||||
|
eq_tree_.Insert(sim_list_[eq_to].cluster, &loc); |
||||
|
loc.set_value(sim_list_[eq_to].cluster); |
||||
|
first_added = true; |
||||
|
} |
||||
|
eq_tree_.Insert(sim_list_[i].cluster, &loc); |
||||
|
loc.set_value(sim_list_[eq_to].cluster); |
||||
|
++eq_clusters_count; |
||||
|
} else { |
||||
|
eq_to = i; |
||||
|
first_added = false; |
||||
|
} |
||||
|
} |
||||
|
return eq_clusters_count; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey; |
||||
|
const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue; |
||||
|
const JSObjectsRetainerTreeConfig::Key JSObjectsRetainerTreeConfig::kNoKey; |
||||
|
const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue = |
||||
|
NULL; |
||||
|
|
||||
|
|
||||
|
RetainerHeapProfile::RetainerHeapProfile() |
||||
|
: zscope_(DELETE_ON_EXIT), |
||||
|
coarse_cluster_tree_(NULL), |
||||
|
current_printer_(NULL), |
||||
|
current_stream_(NULL) { |
||||
|
JSObjectsCluster roots(JSObjectsCluster::ROOTS); |
||||
|
ReferencesExtractor extractor(roots, this); |
||||
|
Heap::IterateRoots(&extractor); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void RetainerHeapProfile::StoreReference(const JSObjectsCluster& cluster, |
||||
|
HeapObject* ref) { |
||||
|
JSObjectsCluster ref_cluster = Clusterizer::Clusterize(ref); |
||||
|
JSObjectsRetainerTree::Locator ref_loc; |
||||
|
if (retainers_tree_.Insert(ref_cluster, &ref_loc)) { |
||||
|
ref_loc.set_value(new JSObjectsClusterTree()); |
||||
|
} |
||||
|
JSObjectsClusterTree* referenced_by = ref_loc.value(); |
||||
|
Clusterizer::InsertReferenceIntoTree(referenced_by, cluster); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void RetainerHeapProfile::CollectStats(HeapObject* obj) { |
||||
|
if (obj->IsJSObject()) { |
||||
|
const JSObjectsCluster cluster = Clusterizer::Clusterize(obj); |
||||
|
ReferencesExtractor extractor(cluster, this); |
||||
|
obj->Iterate(&extractor); |
||||
|
} else if (obj->IsJSGlobalPropertyCell()) { |
||||
|
JSObjectsCluster global_prop(JSObjectsCluster::GLOBAL_PROPERTY); |
||||
|
ReferencesExtractor extractor(global_prop, this); |
||||
|
obj->Iterate(&extractor); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void RetainerHeapProfile::DebugPrintStats( |
||||
|
RetainerHeapProfile::Printer* printer) { |
||||
|
coarser_.Process(&retainers_tree_); |
||||
|
ASSERT(current_printer_ == NULL); |
||||
|
current_printer_ = printer; |
||||
|
retainers_tree_.ForEach(this); |
||||
|
current_printer_ = NULL; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void RetainerHeapProfile::PrintStats() { |
||||
|
RetainersPrinter printer; |
||||
|
DebugPrintStats(&printer); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void RetainerHeapProfile::Call(const JSObjectsCluster& cluster, |
||||
|
JSObjectsClusterTree* tree) { |
||||
|
// First level of retainer graph.
|
||||
|
if (coarser_.HasAnEquivalent(cluster)) return; |
||||
|
ASSERT(current_stream_ == NULL); |
||||
|
HeapStringAllocator allocator; |
||||
|
StringStream stream(&allocator); |
||||
|
current_stream_ = &stream; |
||||
|
ASSERT(coarse_cluster_tree_ == NULL); |
||||
|
coarse_cluster_tree_ = new JSObjectsClusterTree(); |
||||
|
tree->ForEach(this); |
||||
|
// Print aggregated counts and sizes.
|
||||
|
RetainerTreePrinter printer(current_stream_); |
||||
|
coarse_cluster_tree_->ForEach(&printer); |
||||
|
coarse_cluster_tree_ = NULL; |
||||
|
current_printer_->PrintRetainers(cluster, stream); |
||||
|
current_stream_ = NULL; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void RetainerHeapProfile::Call(const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size) { |
||||
|
ASSERT(coarse_cluster_tree_ != NULL); |
||||
|
ASSERT(current_stream_ != NULL); |
||||
|
JSObjectsCluster eq = coarser_.GetCoarseEquivalent(cluster); |
||||
|
if (eq.is_null()) { |
||||
|
RetainerTreePrinter::Print(current_stream_, cluster, number_and_size); |
||||
|
} else { |
||||
|
// Aggregate counts and sizes for equivalent clusters.
|
||||
|
JSObjectsClusterTree::Locator loc; |
||||
|
coarse_cluster_tree_->Insert(eq, &loc); |
||||
|
NumberAndSizeInfo eq_number_and_size = loc.value(); |
||||
|
eq_number_and_size.increment_number(number_and_size.number()); |
||||
|
loc.set_value(eq_number_and_size); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//
|
||||
|
// HeapProfiler class implementation.
|
||||
|
//
|
||||
|
void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) { |
||||
|
InstanceType type = obj->map()->instance_type(); |
||||
|
ASSERT(0 <= type && type <= LAST_TYPE); |
||||
|
info[type].increment_number(1); |
||||
|
info[type].increment_bytes(obj->Size()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void HeapProfiler::WriteSample() { |
||||
|
LOG(HeapSampleBeginEvent("Heap", "allocated")); |
||||
|
LOG(HeapSampleStats( |
||||
|
"Heap", "allocated", Heap::Capacity(), Heap::SizeOfObjects())); |
||||
|
|
||||
|
HistogramInfo info[LAST_TYPE+1]; |
||||
|
#define DEF_TYPE_NAME(name) info[name].set_name(#name); |
||||
|
INSTANCE_TYPE_LIST(DEF_TYPE_NAME) |
||||
|
#undef DEF_TYPE_NAME |
||||
|
|
||||
|
ConstructorHeapProfile js_cons_profile; |
||||
|
RetainerHeapProfile js_retainer_profile; |
||||
|
HeapIterator iterator; |
||||
|
while (iterator.has_next()) { |
||||
|
HeapObject* obj = iterator.next(); |
||||
|
CollectStats(obj, info); |
||||
|
js_cons_profile.CollectStats(obj); |
||||
|
js_retainer_profile.CollectStats(obj); |
||||
|
} |
||||
|
|
||||
|
// Lump all the string types together.
|
||||
|
int string_number = 0; |
||||
|
int string_bytes = 0; |
||||
|
#define INCREMENT_SIZE(type, size, name, camel_name) \ |
||||
|
string_number += info[type].number(); \ |
||||
|
string_bytes += info[type].bytes(); |
||||
|
STRING_TYPE_LIST(INCREMENT_SIZE) |
||||
|
#undef INCREMENT_SIZE |
||||
|
if (string_bytes > 0) { |
||||
|
LOG(HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes)); |
||||
|
} |
||||
|
|
||||
|
for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) { |
||||
|
if (info[i].bytes() > 0) { |
||||
|
LOG(HeapSampleItemEvent(info[i].name(), info[i].number(), |
||||
|
info[i].bytes())); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
js_cons_profile.PrintStats(); |
||||
|
js_retainer_profile.PrintStats(); |
||||
|
|
||||
|
LOG(HeapSampleEndEvent("Heap", "allocated")); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
||||
|
|
||||
|
|
||||
|
} } // namespace v8::internal
|
@ -0,0 +1,266 @@ |
|||||
|
// Copyright 2009 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_HEAP_PROFILER_H_ |
||||
|
#define V8_HEAP_PROFILER_H_ |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
#ifdef ENABLE_LOGGING_AND_PROFILING |
||||
|
|
||||
|
// The HeapProfiler writes data to the log files, which can be postprocessed
|
||||
|
// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
|
||||
|
class HeapProfiler { |
||||
|
public: |
||||
|
// Write a single heap sample to the log file.
|
||||
|
static void WriteSample(); |
||||
|
|
||||
|
private: |
||||
|
// Update the array info with stats from obj.
|
||||
|
static void CollectStats(HeapObject* obj, HistogramInfo* info); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// JSObjectsCluster describes a group of JS objects that are
|
||||
|
// considered equivalent in terms of a particular profile.
|
||||
|
class JSObjectsCluster BASE_EMBEDDED { |
||||
|
public: |
||||
|
// These special cases are used in retainer profile.
|
||||
|
enum SpecialCase { |
||||
|
ROOTS = 1, |
||||
|
GLOBAL_PROPERTY = 2 |
||||
|
}; |
||||
|
|
||||
|
JSObjectsCluster() : constructor_(NULL), instance_(NULL) {} |
||||
|
explicit JSObjectsCluster(String* constructor) |
||||
|
: constructor_(constructor), instance_(NULL) {} |
||||
|
explicit JSObjectsCluster(SpecialCase special) |
||||
|
: constructor_(FromSpecialCase(special)), instance_(NULL) {} |
||||
|
JSObjectsCluster(String* constructor, Object* instance) |
||||
|
: constructor_(constructor), instance_(instance) {} |
||||
|
|
||||
|
static int CompareConstructors(const JSObjectsCluster& a, |
||||
|
const JSObjectsCluster& b) { |
||||
|
// Strings are unique, so it is sufficient to compare their pointers.
|
||||
|
return a.constructor_ == b.constructor_ ? 0 |
||||
|
: (a.constructor_ < b.constructor_ ? -1 : 1); |
||||
|
} |
||||
|
static int Compare(const JSObjectsCluster& a, const JSObjectsCluster& b) { |
||||
|
// Strings are unique, so it is sufficient to compare their pointers.
|
||||
|
const int cons_cmp = CompareConstructors(a, b); |
||||
|
return cons_cmp == 0 ? |
||||
|
(a.instance_ == b.instance_ ? 0 : (a.instance_ < b.instance_ ? -1 : 1)) |
||||
|
: cons_cmp; |
||||
|
} |
||||
|
|
||||
|
bool is_null() const { return constructor_ == NULL; } |
||||
|
bool can_be_coarsed() const { return instance_ != NULL; } |
||||
|
String* constructor() const { return constructor_; } |
||||
|
|
||||
|
void Print(StringStream* accumulator) const; |
||||
|
// Allows null clusters to be printed.
|
||||
|
void DebugPrint(StringStream* accumulator) const; |
||||
|
|
||||
|
private: |
||||
|
static String* FromSpecialCase(SpecialCase special) { |
||||
|
// We use symbols that are illegal JS identifiers to identify special cases.
|
||||
|
// Their actual value is irrelevant for us.
|
||||
|
switch (special) { |
||||
|
case ROOTS: return Heap::result_symbol(); |
||||
|
case GLOBAL_PROPERTY: return Heap::code_symbol(); |
||||
|
default: |
||||
|
UNREACHABLE(); |
||||
|
return NULL; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
String* constructor_; |
||||
|
Object* instance_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
struct JSObjectsClusterTreeConfig { |
||||
|
typedef JSObjectsCluster Key; |
||||
|
typedef NumberAndSizeInfo Value; |
||||
|
static const Key kNoKey; |
||||
|
static const Value kNoValue; |
||||
|
static int Compare(const Key& a, const Key& b) { |
||||
|
return Key::Compare(a, b); |
||||
|
} |
||||
|
}; |
||||
|
typedef ZoneSplayTree<JSObjectsClusterTreeConfig> JSObjectsClusterTree; |
||||
|
|
||||
|
|
||||
|
// ConstructorHeapProfile is responsible for gathering and logging
|
||||
|
// "constructor profile" of JS objects allocated on heap.
|
||||
|
// It is run during garbage collection cycle, thus it doesn't need
|
||||
|
// to use handles.
|
||||
|
class ConstructorHeapProfile BASE_EMBEDDED { |
||||
|
public: |
||||
|
ConstructorHeapProfile(); |
||||
|
virtual ~ConstructorHeapProfile() {} |
||||
|
void CollectStats(HeapObject* obj); |
||||
|
void PrintStats(); |
||||
|
// Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests.
|
||||
|
virtual void Call(const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size); |
||||
|
|
||||
|
private: |
||||
|
ZoneScope zscope_; |
||||
|
JSObjectsClusterTree js_objects_info_tree_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// JSObjectsRetainerTree is used to represent retainer graphs using
|
||||
|
// adjacency list form:
|
||||
|
//
|
||||
|
// Cluster -> (Cluster -> NumberAndSizeInfo)
|
||||
|
//
|
||||
|
// Subordinate splay trees are stored by pointer. They are zone-allocated,
|
||||
|
// so it isn't needed to manage their lifetime.
|
||||
|
//
|
||||
|
struct JSObjectsRetainerTreeConfig { |
||||
|
typedef JSObjectsCluster Key; |
||||
|
typedef JSObjectsClusterTree* Value; |
||||
|
static const Key kNoKey; |
||||
|
static const Value kNoValue; |
||||
|
static int Compare(const Key& a, const Key& b) { |
||||
|
return Key::Compare(a, b); |
||||
|
} |
||||
|
}; |
||||
|
typedef ZoneSplayTree<JSObjectsRetainerTreeConfig> JSObjectsRetainerTree; |
||||
|
|
||||
|
|
||||
|
class ClustersCoarser BASE_EMBEDDED { |
||||
|
public: |
||||
|
ClustersCoarser(); |
||||
|
|
||||
|
// Processes a given retainer graph.
|
||||
|
void Process(JSObjectsRetainerTree* tree); |
||||
|
|
||||
|
// Returns an equivalent cluster (can be the cluster itself).
|
||||
|
// If the given cluster doesn't have an equivalent, returns null cluster.
|
||||
|
JSObjectsCluster GetCoarseEquivalent(const JSObjectsCluster& cluster); |
||||
|
// Returns whether a cluster can be substitued with an equivalent and thus,
|
||||
|
// skipped in some cases.
|
||||
|
bool HasAnEquivalent(const JSObjectsCluster& cluster); |
||||
|
|
||||
|
// Used by JSObjectsRetainerTree::ForEach.
|
||||
|
void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); |
||||
|
void Call(const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size); |
||||
|
|
||||
|
private: |
||||
|
// Stores a list of back references for a cluster.
|
||||
|
struct ClusterBackRefs { |
||||
|
explicit ClusterBackRefs(const JSObjectsCluster& cluster_); |
||||
|
ClusterBackRefs(const ClusterBackRefs& src); |
||||
|
ClusterBackRefs& operator=(const ClusterBackRefs& src); |
||||
|
|
||||
|
static int Compare(const ClusterBackRefs& a, const ClusterBackRefs& b); |
||||
|
|
||||
|
JSObjectsCluster cluster; |
||||
|
ZoneList<JSObjectsCluster> refs; |
||||
|
}; |
||||
|
typedef ZoneList<ClusterBackRefs> SimilarityList; |
||||
|
|
||||
|
// A tree for storing a list of equivalents for a cluster.
|
||||
|
struct ClusterEqualityConfig { |
||||
|
typedef JSObjectsCluster Key; |
||||
|
typedef JSObjectsCluster Value; |
||||
|
static const Key kNoKey; |
||||
|
static const Value kNoValue; |
||||
|
static int Compare(const Key& a, const Key& b) { |
||||
|
return Key::Compare(a, b); |
||||
|
} |
||||
|
}; |
||||
|
typedef ZoneSplayTree<ClusterEqualityConfig> EqualityTree; |
||||
|
|
||||
|
static int ClusterBackRefsCmp(const ClusterBackRefs* a, |
||||
|
const ClusterBackRefs* b) { |
||||
|
return ClusterBackRefs::Compare(*a, *b); |
||||
|
} |
||||
|
int DoProcess(JSObjectsRetainerTree* tree); |
||||
|
int FillEqualityTree(); |
||||
|
|
||||
|
static const int kInitialBackrefsListCapacity = 2; |
||||
|
static const int kInitialSimilarityListCapacity = 2000; |
||||
|
// Number of passes for finding equivalents. Limits the length of paths
|
||||
|
// that can be considered equivalent.
|
||||
|
static const int kMaxPassesCount = 10; |
||||
|
|
||||
|
ZoneScope zscope_; |
||||
|
SimilarityList sim_list_; |
||||
|
EqualityTree eq_tree_; |
||||
|
ClusterBackRefs* current_pair_; |
||||
|
JSObjectsRetainerTree* current_set_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// RetainerHeapProfile is responsible for gathering and logging
|
||||
|
// "retainer profile" of JS objects allocated on heap.
|
||||
|
// It is run during garbage collection cycle, thus it doesn't need
|
||||
|
// to use handles.
|
||||
|
class RetainerHeapProfile BASE_EMBEDDED { |
||||
|
public: |
||||
|
class Printer { |
||||
|
public: |
||||
|
virtual ~Printer() {} |
||||
|
virtual void PrintRetainers(const JSObjectsCluster& cluster, |
||||
|
const StringStream& retainers) = 0; |
||||
|
}; |
||||
|
|
||||
|
RetainerHeapProfile(); |
||||
|
void CollectStats(HeapObject* obj); |
||||
|
void PrintStats(); |
||||
|
void DebugPrintStats(Printer* printer); |
||||
|
void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref); |
||||
|
|
||||
|
private: |
||||
|
// Limit on the number of retainers to be printed per cluster.
|
||||
|
static const int kMaxRetainersToPrint = 50; |
||||
|
ZoneScope zscope_; |
||||
|
JSObjectsRetainerTree retainers_tree_; |
||||
|
ClustersCoarser coarser_; |
||||
|
// TODO(mnaganov): Use some helper class to hold these state variables.
|
||||
|
JSObjectsClusterTree* coarse_cluster_tree_; |
||||
|
Printer* current_printer_; |
||||
|
StringStream* current_stream_; |
||||
|
public: |
||||
|
// Used by JSObjectsRetainerTree::ForEach.
|
||||
|
void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); |
||||
|
void Call(const JSObjectsCluster& cluster, |
||||
|
const NumberAndSizeInfo& number_and_size); |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
||||
|
|
||||
|
} } // namespace v8::internal
|
||||
|
|
||||
|
#endif // V8_HEAP_PROFILER_H_
|
@ -0,0 +1,330 @@ |
|||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
|
//
|
||||
|
// Tests for heap profiler
|
||||
|
|
||||
|
#ifdef ENABLE_LOGGING_AND_PROFILING |
||||
|
|
||||
|
#include "v8.h" |
||||
|
#include "heap-profiler.h" |
||||
|
#include "string-stream.h" |
||||
|
#include "cctest.h" |
||||
|
|
||||
|
namespace i = v8::internal; |
||||
|
using i::ClustersCoarser; |
||||
|
using i::JSObjectsCluster; |
||||
|
using i::JSObjectsRetainerTree; |
||||
|
using i::JSObjectsClusterTree; |
||||
|
using i::RetainerHeapProfile; |
||||
|
|
||||
|
|
||||
|
static void CompileAndRunScript(const char *src) { |
||||
|
v8::Script::Compile(v8::String::New(src))->Run(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
class ConstructorHeapProfileTestHelper : public i::ConstructorHeapProfile { |
||||
|
public: |
||||
|
ConstructorHeapProfileTestHelper() |
||||
|
: i::ConstructorHeapProfile(), |
||||
|
f_name_(i::Factory::NewStringFromAscii(i::CStrVector("F"))), |
||||
|
f_count_(0) { |
||||
|
} |
||||
|
|
||||
|
void Call(const JSObjectsCluster& cluster, |
||||
|
const i::NumberAndSizeInfo& number_and_size) { |
||||
|
if (f_name_->Equals(cluster.constructor())) { |
||||
|
CHECK_EQ(f_count_, 0); |
||||
|
f_count_ = number_and_size.number(); |
||||
|
CHECK_GT(f_count_, 0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int f_count() { return f_count_; } |
||||
|
|
||||
|
private: |
||||
|
i::Handle<i::String> f_name_; |
||||
|
int f_count_; |
||||
|
}; |
||||
|
|
||||
|
} // namespace
|
||||
|
|
||||
|
|
||||
|
TEST(ConstructorProfile) { |
||||
|
v8::HandleScope scope; |
||||
|
v8::Handle<v8::Context> env = v8::Context::New(); |
||||
|
env->Enter(); |
||||
|
|
||||
|
CompileAndRunScript( |
||||
|
"function F() {} // A constructor\n" |
||||
|
"var f1 = new F();\n" |
||||
|
"var f2 = new F();\n"); |
||||
|
|
||||
|
ConstructorHeapProfileTestHelper cons_profile; |
||||
|
i::AssertNoAllocation no_alloc; |
||||
|
i::HeapIterator iterator; |
||||
|
while (iterator.has_next()) { |
||||
|
i::HeapObject* obj = iterator.next(); |
||||
|
cons_profile.CollectStats(obj); |
||||
|
} |
||||
|
CHECK_EQ(0, cons_profile.f_count()); |
||||
|
cons_profile.PrintStats(); |
||||
|
CHECK_EQ(2, cons_profile.f_count()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static JSObjectsCluster AddHeapObjectToTree( |
||||
|
JSObjectsRetainerTree* tree, |
||||
|
i::String* constructor, |
||||
|
int instance, |
||||
|
JSObjectsCluster* ref1 = NULL, |
||||
|
JSObjectsCluster* ref2 = NULL, |
||||
|
JSObjectsCluster* ref3 = NULL) { |
||||
|
JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance)); |
||||
|
JSObjectsClusterTree* o_tree = new JSObjectsClusterTree(); |
||||
|
JSObjectsClusterTree::Locator o_loc; |
||||
|
if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc); |
||||
|
if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc); |
||||
|
if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc); |
||||
|
JSObjectsRetainerTree::Locator loc; |
||||
|
tree->Insert(o, &loc); |
||||
|
loc.set_value(o_tree); |
||||
|
return o; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static inline void CheckEqualsHelper(const char* file, int line, |
||||
|
const char* expected_source, |
||||
|
const JSObjectsCluster& expected, |
||||
|
const char* value_source, |
||||
|
const JSObjectsCluster& value) { |
||||
|
if (JSObjectsCluster::Compare(expected, value) != 0) { |
||||
|
i::HeapStringAllocator allocator; |
||||
|
i::StringStream stream(&allocator); |
||||
|
stream.Add("# Expected: "); |
||||
|
expected.DebugPrint(&stream); |
||||
|
stream.Add("\n# Found: "); |
||||
|
value.DebugPrint(&stream); |
||||
|
V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n%s", |
||||
|
expected_source, value_source, |
||||
|
*stream.ToCString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static inline void CheckNonEqualsHelper(const char* file, int line, |
||||
|
const char* expected_source, |
||||
|
const JSObjectsCluster& expected, |
||||
|
const char* value_source, |
||||
|
const JSObjectsCluster& value) { |
||||
|
if (JSObjectsCluster::Compare(expected, value) == 0) { |
||||
|
i::HeapStringAllocator allocator; |
||||
|
i::StringStream stream(&allocator); |
||||
|
stream.Add("# Expected: "); |
||||
|
expected.DebugPrint(&stream); |
||||
|
stream.Add("\n# Found: "); |
||||
|
value.DebugPrint(&stream); |
||||
|
V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n%s", |
||||
|
expected_source, value_source, |
||||
|
*stream.ToCString()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST(ClustersCoarserSimple) { |
||||
|
v8::HandleScope scope; |
||||
|
v8::Handle<v8::Context> env = v8::Context::New(); |
||||
|
env->Enter(); |
||||
|
|
||||
|
i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
||||
|
|
||||
|
JSObjectsRetainerTree tree; |
||||
|
JSObjectsCluster function(i::Heap::function_class_symbol()); |
||||
|
JSObjectsCluster a(*i::Factory::NewStringFromAscii(i::CStrVector("A"))); |
||||
|
JSObjectsCluster b(*i::Factory::NewStringFromAscii(i::CStrVector("B"))); |
||||
|
|
||||
|
// o1 <- Function
|
||||
|
JSObjectsCluster o1 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); |
||||
|
// o2 <- Function
|
||||
|
JSObjectsCluster o2 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); |
||||
|
// o3 <- A, B
|
||||
|
JSObjectsCluster o3 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &a, &b); |
||||
|
// o4 <- B, A
|
||||
|
JSObjectsCluster o4 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x400, &b, &a); |
||||
|
// o5 <- A, B, Function
|
||||
|
JSObjectsCluster o5 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x500, |
||||
|
&a, &b, &function); |
||||
|
|
||||
|
ClustersCoarser coarser; |
||||
|
coarser.Process(&tree); |
||||
|
|
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); |
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(o3), coarser.GetCoarseEquivalent(o4)); |
||||
|
CHECK_NE(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o3)); |
||||
|
CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o5)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST(ClustersCoarserMultipleConstructors) { |
||||
|
v8::HandleScope scope; |
||||
|
v8::Handle<v8::Context> env = v8::Context::New(); |
||||
|
env->Enter(); |
||||
|
|
||||
|
i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
||||
|
|
||||
|
JSObjectsRetainerTree tree; |
||||
|
JSObjectsCluster function(i::Heap::function_class_symbol()); |
||||
|
|
||||
|
// o1 <- Function
|
||||
|
JSObjectsCluster o1 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100, &function); |
||||
|
// a1 <- Function
|
||||
|
JSObjectsCluster a1 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x1000, &function); |
||||
|
// o2 <- Function
|
||||
|
JSObjectsCluster o2 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x200, &function); |
||||
|
// a2 <- Function
|
||||
|
JSObjectsCluster a2 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Array_symbol(), 0x2000, &function); |
||||
|
|
||||
|
ClustersCoarser coarser; |
||||
|
coarser.Process(&tree); |
||||
|
|
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(o1), coarser.GetCoarseEquivalent(o2)); |
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(a1), coarser.GetCoarseEquivalent(a2)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST(ClustersCoarserPathsTraversal) { |
||||
|
v8::HandleScope scope; |
||||
|
v8::Handle<v8::Context> env = v8::Context::New(); |
||||
|
env->Enter(); |
||||
|
|
||||
|
i::ZoneScope zn_scope(i::DELETE_ON_EXIT); |
||||
|
|
||||
|
JSObjectsRetainerTree tree; |
||||
|
|
||||
|
// On the following graph:
|
||||
|
//
|
||||
|
// p
|
||||
|
// <- o21 <- o11 <-
|
||||
|
// q o
|
||||
|
// <- o22 <- o12 <-
|
||||
|
// r
|
||||
|
//
|
||||
|
// we expect that coarser will deduce equivalences: p ~ q ~ r,
|
||||
|
// o21 ~ o22, and o11 ~ o12.
|
||||
|
|
||||
|
JSObjectsCluster o = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x100); |
||||
|
JSObjectsCluster o11 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x110, &o); |
||||
|
JSObjectsCluster o12 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x120, &o); |
||||
|
JSObjectsCluster o21 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x210, &o11); |
||||
|
JSObjectsCluster o22 = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x220, &o12); |
||||
|
JSObjectsCluster p = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x300, &o21); |
||||
|
JSObjectsCluster q = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x310, &o21, &o22); |
||||
|
JSObjectsCluster r = |
||||
|
AddHeapObjectToTree(&tree, i::Heap::Object_symbol(), 0x320, &o22); |
||||
|
|
||||
|
ClustersCoarser coarser; |
||||
|
coarser.Process(&tree); |
||||
|
|
||||
|
CHECK_EQ(JSObjectsCluster(), coarser.GetCoarseEquivalent(o)); |
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o12)); |
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(o22)); |
||||
|
CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(o21)); |
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(p), coarser.GetCoarseEquivalent(q)); |
||||
|
CHECK_EQ(coarser.GetCoarseEquivalent(q), coarser.GetCoarseEquivalent(r)); |
||||
|
CHECK_NE(coarser.GetCoarseEquivalent(o11), coarser.GetCoarseEquivalent(p)); |
||||
|
CHECK_NE(coarser.GetCoarseEquivalent(o21), coarser.GetCoarseEquivalent(p)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
class RetainerProfilePrinter : public RetainerHeapProfile::Printer { |
||||
|
public: |
||||
|
RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {} |
||||
|
|
||||
|
void PrintRetainers(const JSObjectsCluster& cluster, |
||||
|
const i::StringStream& retainers) { |
||||
|
cluster.Print(&stream_); |
||||
|
stream_.Add("%s", *(retainers.ToCString())); |
||||
|
stream_.Put('\0'); |
||||
|
} |
||||
|
|
||||
|
const char* GetRetainers(const char* constructor) { |
||||
|
FillLines(); |
||||
|
const size_t cons_len = strlen(constructor); |
||||
|
for (int i = 0; i < lines_.length(); ++i) { |
||||
|
if (strncmp(constructor, lines_[i], cons_len) == 0 && |
||||
|
lines_[i][cons_len] == ',') { |
||||
|
return lines_[i] + cons_len + 1; |
||||
|
} |
||||
|
} |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
void FillLines() { |
||||
|
if (lines_.length() > 0) return; |
||||
|
stream_.Put('\0'); |
||||
|
stream_str_ = stream_.ToCString(); |
||||
|
const char* pos = *stream_str_; |
||||
|
while (pos != NULL && *pos != '\0') { |
||||
|
lines_.Add(pos); |
||||
|
pos = strchr(pos, '\0'); |
||||
|
if (pos != NULL) ++pos; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
i::HeapStringAllocator allocator_; |
||||
|
i::StringStream stream_; |
||||
|
i::SmartPointer<const char> stream_str_; |
||||
|
i::List<const char*> lines_; |
||||
|
}; |
||||
|
|
||||
|
} // namespace
|
||||
|
|
||||
|
|
||||
|
TEST(RetainerProfile) { |
||||
|
v8::HandleScope scope; |
||||
|
v8::Handle<v8::Context> env = v8::Context::New(); |
||||
|
env->Enter(); |
||||
|
|
||||
|
CompileAndRunScript( |
||||
|
"function A() {}\n" |
||||
|
"function B(x) { this.x = x; }\n" |
||||
|
"function C(x) { this.x1 = x; this.x2 = x; }\n" |
||||
|
"var a = new A();\n" |
||||
|
"var b1 = new B(a), b2 = new B(a);\n" |
||||
|
"var c = new C(a);"); |
||||
|
|
||||
|
RetainerHeapProfile ret_profile; |
||||
|
i::AssertNoAllocation no_alloc; |
||||
|
i::HeapIterator iterator; |
||||
|
while (iterator.has_next()) { |
||||
|
i::HeapObject* obj = iterator.next(); |
||||
|
ret_profile.CollectStats(obj); |
||||
|
} |
||||
|
RetainerProfilePrinter printer; |
||||
|
ret_profile.DebugPrintStats(&printer); |
||||
|
CHECK_EQ("(global property);1,B;2,C;2", printer.GetRetainers("A")); |
||||
|
CHECK_EQ("(global property);2", printer.GetRetainers("B")); |
||||
|
CHECK_EQ("(global property);1", printer.GetRetainers("C")); |
||||
|
} |
||||
|
|
||||
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
@ -0,0 +1,14 @@ |
|||||
|
This directory contains code for binding the es5conform test suite |
||||
|
into the v8 test harness. To use the tests check out the es5conform |
||||
|
tests from |
||||
|
|
||||
|
https://es5conform.svn.codeplex.com/svn |
||||
|
|
||||
|
in revision 59101 as 'data' in this directory. Using later version |
||||
|
may be possible but the tests are only known to pass (and indeed run) |
||||
|
with that revision. |
||||
|
|
||||
|
If you do update to a newer revision you may have to change the test |
||||
|
harness adapter code since it uses internal functionality from the |
||||
|
harness that comes bundled with the tests. You will most likely also |
||||
|
have to update the test expectation file. |
@ -0,0 +1,68 @@ |
|||||
|
# Copyright 2009 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. |
||||
|
|
||||
|
prefix es5conform |
||||
|
def UNIMPLEMENTED = PASS || FAIL |
||||
|
def FAIL_OK = FAIL, OKAY |
||||
|
|
||||
|
chapter07: UNIMPLEMENTED |
||||
|
chapter08: UNIMPLEMENTED |
||||
|
chapter10: UNIMPLEMENTED |
||||
|
chapter11: UNIMPLEMENTED |
||||
|
chapter12: UNIMPLEMENTED |
||||
|
chapter13: UNIMPLEMENTED |
||||
|
chapter14: UNIMPLEMENTED |
||||
|
chapter15/15.1: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.1: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.2: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.3: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.4: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.5: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.6: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.7: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.8: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.9: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.10: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.11: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.12: UNIMPLEMENTED |
||||
|
chapter15/15.2/15.2.3/15.2.3.13: UNIMPLEMENTED |
||||
|
|
||||
|
# Object.keys |
||||
|
chapter15/15.2/15.2.3/15.2.3.14: PASS |
||||
|
|
||||
|
# We fail this because Object.keys returns numbers for element indices |
||||
|
# rather than strings. |
||||
|
chapter15/15.2/15.2.3/15.2.3.14/15.2.3.14-3-3: FAIL_OK |
||||
|
|
||||
|
chapter15/15.3: UNIMPLEMENTED |
||||
|
chapter15/15.4: UNIMPLEMENTED |
||||
|
chapter15/15.5: UNIMPLEMENTED |
||||
|
chapter15/15.6: UNIMPLEMENTED |
||||
|
chapter15/15.7: UNIMPLEMENTED |
||||
|
chapter15/15.9: UNIMPLEMENTED |
||||
|
chapter15/15.10: UNIMPLEMENTED |
||||
|
chapter15/15.12: UNIMPLEMENTED |
@ -0,0 +1,108 @@ |
|||||
|
# Copyright 2008 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. |
||||
|
|
||||
|
|
||||
|
import test |
||||
|
import os |
||||
|
from os.path import join, exists |
||||
|
|
||||
|
|
||||
|
HARNESS_FILES = ['sth.js'] |
||||
|
|
||||
|
|
||||
|
class ES5ConformTestCase(test.TestCase): |
||||
|
|
||||
|
def __init__(self, filename, path, context, root, mode, framework): |
||||
|
super(ES5ConformTestCase, self).__init__(context, path) |
||||
|
self.filename = filename |
||||
|
self.mode = mode |
||||
|
self.framework = framework |
||||
|
self.root = root |
||||
|
|
||||
|
def IsNegative(self): |
||||
|
return self.filename.endswith('-n.js') |
||||
|
|
||||
|
def GetLabel(self): |
||||
|
return "%s es5conform %s" % (self.mode, self.GetName()) |
||||
|
|
||||
|
def IsFailureOutput(self, output): |
||||
|
if output.exit_code != 0: |
||||
|
return True |
||||
|
return 'FAILED!' in output.stdout |
||||
|
|
||||
|
def GetCommand(self): |
||||
|
result = [self.context.GetVm(self.mode)] |
||||
|
result += ['-e', 'var window = this'] |
||||
|
result += self.framework |
||||
|
result.append(self.filename) |
||||
|
result += ['-e', 'ES5Harness.startTesting()'] |
||||
|
return result |
||||
|
|
||||
|
def GetName(self): |
||||
|
return self.path[-1] |
||||
|
|
||||
|
def GetSource(self): |
||||
|
return open(self.filename).read() |
||||
|
|
||||
|
|
||||
|
class ES5ConformTestConfiguration(test.TestConfiguration): |
||||
|
|
||||
|
def __init__(self, context, root): |
||||
|
super(ES5ConformTestConfiguration, self).__init__(context, root) |
||||
|
|
||||
|
def ListTests(self, current_path, path, mode): |
||||
|
tests = [] |
||||
|
current_root = join(self.root, 'data', 'TestCases') |
||||
|
harness = [] |
||||
|
harness += [join(self.root, 'data', 'SimpleTestHarness', f) for f in HARNESS_FILES] |
||||
|
harness += [join(self.root, 'harness-adapt.js')] |
||||
|
for root, dirs, files in os.walk(current_root): |
||||
|
for dotted in [x for x in dirs if x.startswith('.')]: |
||||
|
dirs.remove(dotted) |
||||
|
root_path = root[len(self.root):].split(os.path.sep) |
||||
|
root_path = current_path + [x for x in root_path if x] |
||||
|
for file in files: |
||||
|
if file.endswith('.js'): |
||||
|
full_path = root_path + [file[:-3]] |
||||
|
full_path = [x for x in full_path if not (x in ['data', 'TestCases'])] |
||||
|
if self.Contains(path, full_path): |
||||
|
test = ES5ConformTestCase(join(root, file), full_path, self.context, |
||||
|
self.root, mode, harness) |
||||
|
tests.append(test) |
||||
|
return tests |
||||
|
|
||||
|
def GetBuildRequirements(self): |
||||
|
return ['sample', 'sample=shell'] |
||||
|
|
||||
|
def GetTestStatus(self, sections, defs): |
||||
|
status_file = join(self.root, 'es5conform.status') |
||||
|
if exists(status_file): |
||||
|
test.ReadConfigurationInto(status_file, sections, defs) |
||||
|
|
||||
|
|
||||
|
def GetConfiguration(context, root): |
||||
|
return ES5ConformTestConfiguration(context, root) |
@ -0,0 +1,119 @@ |
|||||
|
// Copyright 2008 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.
|
||||
|
|
||||
|
|
||||
|
var loop_count = 5 |
||||
|
|
||||
|
|
||||
|
for (var i = 0; i < loop_count; i++) { |
||||
|
var a = new Array(); |
||||
|
var b = Array(); |
||||
|
assertEquals(0, a.length); |
||||
|
assertEquals(0, b.length); |
||||
|
for (var k = 0; k < 10; k++) { |
||||
|
assertEquals('undefined', typeof a[k]); |
||||
|
assertEquals('undefined', typeof b[k]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
for (var i = 0; i < loop_count; i++) { |
||||
|
for (var j = 0; j < 100; j++) { |
||||
|
var a = new Array(j); |
||||
|
var b = Array(j); |
||||
|
assertEquals(j, a.length); |
||||
|
assertEquals(j, b.length); |
||||
|
for (var k = 0; k < j; k++) { |
||||
|
assertEquals('undefined', typeof a[k]); |
||||
|
assertEquals('undefined', typeof b[k]); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
for (var i = 0; i < loop_count; i++) { |
||||
|
a = new Array(0, 1); |
||||
|
assertArrayEquals([0, 1], a); |
||||
|
a = new Array(0, 1, 2); |
||||
|
assertArrayEquals([0, 1, 2], a); |
||||
|
a = new Array(0, 1, 2, 3); |
||||
|
assertArrayEquals([0, 1, 2, 3], a); |
||||
|
a = new Array(0, 1, 2, 3, 4); |
||||
|
assertArrayEquals([0, 1, 2, 3, 4], a); |
||||
|
a = new Array(0, 1, 2, 3, 4, 5); |
||||
|
assertArrayEquals([0, 1, 2, 3, 4, 5], a); |
||||
|
a = new Array(0, 1, 2, 3, 4, 5, 6); |
||||
|
assertArrayEquals([0, 1, 2, 3, 4, 5, 6], a); |
||||
|
a = new Array(0, 1, 2, 3, 4, 5, 6, 7); |
||||
|
assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7], a); |
||||
|
a = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8); |
||||
|
assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7, 8], a); |
||||
|
a = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); |
||||
|
assertArrayEquals([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], a); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function innerArrayLiteral(n) { |
||||
|
var a = new Array(n); |
||||
|
for (var i = 0; i < n; i++) { |
||||
|
a[i] = i * 2 + 7; |
||||
|
} |
||||
|
return a.join(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function testConstructOfSizeSize(n) { |
||||
|
var str = innerArrayLiteral(n); |
||||
|
var a = eval('[' + str + ']'); |
||||
|
var b = eval('new Array(' + str + ')') |
||||
|
var c = eval('Array(' + str + ')') |
||||
|
assertEquals(n, a.length); |
||||
|
assertArrayEquals(a, b); |
||||
|
assertArrayEquals(a, c); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
for (var i = 0; i < loop_count; i++) { |
||||
|
// JSObject::kInitialMaxFastElementArray is 10000.
|
||||
|
for (var j = 1000; j < 12000; j += 1000) { |
||||
|
testConstructOfSizeSize(j); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
for (var i = 0; i < loop_count; i++) { |
||||
|
assertArrayEquals(['xxx'], new Array('xxx')); |
||||
|
assertArrayEquals(['xxx'], Array('xxx')); |
||||
|
assertArrayEquals([true], new Array(true)); |
||||
|
assertArrayEquals([false], Array(false)); |
||||
|
assertArrayEquals([{a:1}], new Array({a:1})); |
||||
|
assertArrayEquals([{b:2}], Array({b:2})); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
assertThrows('new Array(3.14)'); |
||||
|
assertThrows('Array(2.72)'); |
@ -0,0 +1,62 @@ |
|||||
|
// Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
|
||||
|
//
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions
|
||||
|
// are met:
|
||||
|
//
|
||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
//
|
||||
|
// 2. 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.
|
||||
|
//
|
||||
|
// 3. Neither the name of the copyright holder(s) nor the names of any
|
||||
|
// 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.
|
||||
|
|
||||
|
// Simple splice tests based on webkit layout tests.
|
||||
|
var arr = ['a','b','c','d']; |
||||
|
assertArrayEquals(['a','b','c','d'], arr); |
||||
|
assertArrayEquals(['c','d'], arr.splice(2)); |
||||
|
assertArrayEquals(['a','b'], arr); |
||||
|
assertArrayEquals(['a','b'], arr.splice(0)); |
||||
|
assertArrayEquals([], arr) |
||||
|
|
||||
|
arr = ['a','b','c','d']; |
||||
|
assertEquals(undefined, arr.splice()) |
||||
|
assertArrayEquals(['a','b','c','d'], arr); |
||||
|
assertArrayEquals(['a','b','c','d'], arr.splice(undefined)) |
||||
|
assertArrayEquals([], arr); |
||||
|
|
||||
|
arr = ['a','b','c','d']; |
||||
|
assertArrayEquals(['a','b','c','d'], arr.splice(null)) |
||||
|
assertArrayEquals([], arr); |
||||
|
|
||||
|
arr = ['a','b','c','d']; |
||||
|
assertArrayEquals([], arr.splice(100)) |
||||
|
assertArrayEquals(['a','b','c','d'], arr); |
||||
|
assertArrayEquals(['d'], arr.splice(-1)) |
||||
|
assertArrayEquals(['a','b','c'], arr); |
||||
|
|
||||
|
assertArrayEquals([], arr.splice(2, undefined)) |
||||
|
assertArrayEquals([], arr.splice(2, null)) |
||||
|
assertArrayEquals([], arr.splice(2, -1)) |
||||
|
assertArrayEquals([], arr.splice(2, 0)) |
||||
|
assertArrayEquals(['a','b','c'], arr); |
||||
|
assertArrayEquals(['c'], arr.splice(2, 100)) |
||||
|
assertArrayEquals(['a','b'], arr); |
@ -0,0 +1,66 @@ |
|||||
|
// Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
|
||||
|
//
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions
|
||||
|
// are met:
|
||||
|
//
|
||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
//
|
||||
|
// 2. 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.
|
||||
|
//
|
||||
|
// 3. Neither the name of the copyright holder(s) nor the names of any
|
||||
|
// 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.
|
||||
|
|
||||
|
// Based on LayoutTests/fast/js/Object-keys.html
|
||||
|
|
||||
|
assertThrows(function () { Object.keys(2) }, TypeError); |
||||
|
assertThrows(function () { Object.keys("foo") }, TypeError); |
||||
|
assertThrows(function () { Object.keys(null) }, TypeError); |
||||
|
assertThrows(function () { Object.keys(undefined) }, TypeError); |
||||
|
|
||||
|
assertEquals(Object.keys({}), []); |
||||
|
assertEquals(Object.keys({a:null}), ['a']); |
||||
|
assertEquals(Object.keys({a:null, b:null}), ['a', 'b']); |
||||
|
assertEquals(Object.keys({b:null, a:null}), ['b', 'a']); |
||||
|
assertEquals(Object.keys([]), []); |
||||
|
assertEquals(Object.keys([null]), ['0']); |
||||
|
assertEquals(Object.keys([null,null]), ['0', '1']); |
||||
|
assertEquals(Object.keys([null,null,,,,null]), ['0', '1', '5']); |
||||
|
assertEquals(Object.keys({__proto__:{a:null}}), []); |
||||
|
assertEquals(Object.keys({__proto__:[1,2,3]}), []); |
||||
|
var x = []; |
||||
|
x.__proto__ = [1, 2, 3]; |
||||
|
assertEquals(Object.keys(x), []); |
||||
|
assertEquals(Object.keys(function () {}), []); |
||||
|
|
||||
|
function argsTest(a, b, c) { |
||||
|
assertEquals([0, 1, 2], Object.keys(arguments)); |
||||
|
} |
||||
|
|
||||
|
argsTest(1, 2, 3); |
||||
|
|
||||
|
var literal = {a: 1, b: 2, c: 3}; |
||||
|
var keysBefore = Object.keys(literal); |
||||
|
assertEquals(['a', 'b', 'c'], keysBefore); |
||||
|
keysBefore[0] = 'x'; |
||||
|
var keysAfter = Object.keys(literal); |
||||
|
assertEquals(['a', 'b', 'c'], keysAfter); |
||||
|
assertEquals(['x', 'b', 'c'], keysBefore); |
Loading…
Reference in new issue