blob: 96e4010eea705c80a66227f83369063a9e257dbc [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "platform/TracedValue.h"
#include "platform/Decimal.h"
#include "platform/JSONValues.h"
#include "wtf/HashMap.h"
#include "wtf/text/StringBuilder.h"
#include "wtf/text/StringHash.h"
namespace blink {
namespace {
String threadSafeCopy(const String& string)
{
RefPtr<StringImpl> copy(string.impl());
if (string.isSafeToSendToAnotherThread())
return string;
return string.isolatedCopy();
}
} // namespace
// InternalValue and its subclasses are JSON like objects which TracedValue
// internally uses. We don't use JSONValues because we want to count how much
// memory TracedValue uses.
// TODO(bashi): InternalValue should use an allocator which counts allocation
// size as tracing overhead.
class InternalValue : public RefCounted<InternalValue> {
public:
typedef enum {
TypeNone,
TypeNumber,
TypeBoolean,
TypeString,
TypeArray,
TypeDictionary,
} Type;
virtual ~InternalValue() { }
virtual Type type()
{
ASSERT_NOT_REACHED();
return TypeNone;
}
virtual void toJSON(StringBuilder* builder)
{
ASSERT_NOT_REACHED();
}
};
class TracedNumberValue : public InternalValue {
public:
static PassRefPtr<TracedNumberValue> create(double value)
{
return adoptRef(new TracedNumberValue(value));
}
virtual ~TracedNumberValue() { }
virtual Type type() { return TypeNumber; }
virtual void toJSON(StringBuilder* builder)
{
builder->append(Decimal::fromDouble(m_value).toString());
}
private:
explicit TracedNumberValue(double value) : m_value(value) { }
double m_value;
};
class TracedBooleanValue : public InternalValue {
public:
static PassRefPtr<TracedBooleanValue> create(bool value)
{
return adoptRef(new TracedBooleanValue(value));
}
virtual ~TracedBooleanValue() { }
virtual Type type() { return TypeBoolean; }
virtual void toJSON(StringBuilder* builder)
{
builder->append(m_value ? "true" : "false");
}
private:
explicit TracedBooleanValue(bool value) : m_value(value) { }
bool m_value;
};
class TracedStringValue : public InternalValue {
public:
static PassRefPtr<TracedStringValue> create(String value)
{
return adoptRef(new TracedStringValue(value));
}
virtual ~TracedStringValue() { }
virtual Type type() { return TypeString; }
virtual void toJSON(StringBuilder* builder)
{
doubleQuoteStringForJSON(m_value, builder);
}
private:
explicit TracedStringValue(String value) : m_value(threadSafeCopy(value)) { }
String m_value;
};
class TracedArrayValue : public InternalValue {
public:
static PassRefPtr<TracedArrayValue> create()
{
return adoptRef(new TracedArrayValue());
}
virtual ~TracedArrayValue() { }
virtual Type type() { return TypeArray; }
virtual void toJSON(StringBuilder* builder)
{
builder->append('[');
for (TracedValueVector::const_iterator it = m_value.begin(); it != m_value.end(); ++it) {
if (it != m_value.begin())
builder->append(',');
(*it)->toJSON(builder);
}
builder->append(']');
}
void push(PassRefPtr<InternalValue> value) { m_value.append(value); }
private:
TracedArrayValue() { }
TracedValueVector m_value;
};
class TracedDictionaryValue : public InternalValue {
public:
static PassRefPtr<TracedDictionaryValue> create()
{
return adoptRef(new TracedDictionaryValue());
}
virtual ~TracedDictionaryValue() { }
virtual Type type() { return TypeDictionary; }
virtual void toJSON(StringBuilder* builder)
{
builder->append('{');
for (size_t i = 0; i < m_order.size(); ++i) {
TracedValueHashMap::const_iterator it = m_value.find(m_order[i]);
if (i)
builder->append(',');
doubleQuoteStringForJSON(it->key, builder);
builder->append(':');
it->value->toJSON(builder);
}
builder->append('}');
}
void set(const char* name, PassRefPtr<InternalValue> value)
{
String nameString = String(name);
if (m_value.set(nameString, value).isNewEntry)
m_order.append(nameString);
}
private:
TracedDictionaryValue() { }
TracedValueHashMap m_value;
Vector<String> m_order;
};
PassRefPtr<TracedValue> TracedValue::create()
{
return adoptRef(new TracedValue());
}
TracedValue::TracedValue()
{
m_stack.append(TracedDictionaryValue::create());
}
TracedValue::~TracedValue()
{
ASSERT(m_stack.size() == 1);
}
void TracedValue::setInteger(const char* name, int value)
{
currentDictionary()->set(name, TracedNumberValue::create(value));
}
void TracedValue::setDouble(const char* name, double value)
{
currentDictionary()->set(name, TracedNumberValue::create(value));
}
void TracedValue::setBoolean(const char* name, bool value)
{
currentDictionary()->set(name, TracedBooleanValue::create(value));
}
void TracedValue::setString(const char* name, const String& value)
{
currentDictionary()->set(name, TracedStringValue::create(value));
}
void TracedValue::beginDictionary(const char* name)
{
RefPtr<TracedDictionaryValue> dictionary = TracedDictionaryValue::create();
currentDictionary()->set(name, dictionary);
m_stack.append(dictionary);
}
void TracedValue::beginArray(const char* name)
{
RefPtr<TracedArrayValue> array = TracedArrayValue::create();
currentDictionary()->set(name, array);
m_stack.append(array);
}
void TracedValue::endDictionary()
{
ASSERT(m_stack.size() > 1);
ASSERT(currentDictionary());
m_stack.removeLast();
}
void TracedValue::pushInteger(int value)
{
currentArray()->push(TracedNumberValue::create(value));
}
void TracedValue::pushDouble(double value)
{
currentArray()->push(TracedNumberValue::create(value));
}
void TracedValue::pushBoolean(bool value)
{
currentArray()->push(TracedBooleanValue::create(value));
}
void TracedValue::pushString(const String& value)
{
currentArray()->push(TracedStringValue::create(value));
}
void TracedValue::beginArray()
{
RefPtr<TracedArrayValue> array = TracedArrayValue::create();
currentArray()->push(array);
m_stack.append(array);
}
void TracedValue::beginDictionary()
{
RefPtr<TracedDictionaryValue> dictionary = TracedDictionaryValue::create();
currentArray()->push(dictionary);
m_stack.append(dictionary);
}
void TracedValue::endArray()
{
ASSERT(m_stack.size() > 1);
ASSERT(currentArray());
m_stack.removeLast();
}
String TracedValue::asTraceFormat() const
{
ASSERT(m_stack.size() == 1);
StringBuilder builder;
m_stack.first()->toJSON(&builder);
return builder.toString();
}
TracedDictionaryValue* TracedValue::currentDictionary() const
{
ASSERT(!m_stack.isEmpty());
ASSERT(m_stack.last()->type() == InternalValue::TypeDictionary);
return static_cast<TracedDictionaryValue*>(m_stack.last().get());
}
TracedArrayValue* TracedValue::currentArray() const
{
ASSERT(!m_stack.isEmpty());
ASSERT(m_stack.last()->type() == InternalValue::TypeArray);
return static_cast<TracedArrayValue*>(m_stack.last().get());
}
}