Skip to content

Commit

Permalink
[WTF] Shrink sizeof(JSON::Value)
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=259384
rdar://112637340

Reviewed by Devin Rousso and Darin Adler.

It makes sizeof(JSON::Value) optimized, and wipe all virtual functions for that.
We would like to use this class more widely, for easy-to-use JSON without any JSC contexts.
One target is many toolings, and for example, JSC bytecode profiler is now using this.

* Source/WTF/wtf/JSONValues.cpp:
(WTF::JSONImpl::Value::visitDerived):
(WTF::JSONImpl::Value::visitDerived const):
(WTF::JSONImpl::Value::operator delete):
(WTF::JSONImpl::Value::asObject):
(WTF::JSONImpl::Value::asObject const):
(WTF::JSONImpl::Value::asArray):
(WTF::JSONImpl::Value::writeJSON const):
(WTF::JSONImpl::Value::writeJSONImpl const):
(WTF::JSONImpl::Value::memoryCost const):
(WTF::JSONImpl::Value::memoryCostImpl const):
(WTF::JSONImpl::ObjectBase::memoryCostImpl const):
(WTF::JSONImpl::ObjectBase::writeJSONImpl const):
(WTF::JSONImpl::ArrayBase::writeJSONImpl const):
(WTF::JSONImpl::ArrayBase::memoryCostImpl const):
(WTF::JSONImpl::ObjectBase::~ObjectBase): Deleted.
(WTF::JSONImpl::ObjectBase::asObject): Deleted.
(WTF::JSONImpl::ObjectBase::asObject const): Deleted.
(WTF::JSONImpl::ObjectBase::memoryCost const): Deleted.
(WTF::JSONImpl::ObjectBase::writeJSON const): Deleted.
(WTF::JSONImpl::ArrayBase::~ArrayBase): Deleted.
(WTF::JSONImpl::ArrayBase::asArray): Deleted.
(WTF::JSONImpl::ArrayBase::writeJSON const): Deleted.
(WTF::JSONImpl::ArrayBase::memoryCost const): Deleted.
* Source/WTF/wtf/JSONValues.h:

Canonical link: https://commits.webkit.org/266204@main
  • Loading branch information
Constellation committed Jul 21, 2023
1 parent 6881e7b commit 1e2385d
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 90 deletions.
112 changes: 59 additions & 53 deletions Source/WTF/wtf/JSONValues.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2010 Google Inc. All rights reserved.
* Copyright (C) 2014 University of Washington. All rights reserved.
* Copyright (C) 2017-2019 Apple Inc. All rights reserved.
* Copyright (C) 2017-2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
Expand Down Expand Up @@ -33,6 +33,7 @@
#include "config.h"
#include <wtf/JSONValues.h>

#include <functional>
#include <wtf/CommaPrinter.h>
#include <wtf/text/StringBuilder.h>

Expand Down Expand Up @@ -460,49 +461,61 @@ RefPtr<JSON::Value> buildValue(const CodeUnit* start, const CodeUnit* end, const

} // anonymous namespace

Ref<Value> Value::null()
template<typename Visitor> constexpr decltype(auto) Value::visitDerived(Visitor&& visitor)
{
return adoptRef(*new Value);
switch (m_type) {
case Type::Null:
case Type::Boolean:
case Type::Double:
case Type::Integer:
case Type::String:
return std::invoke(std::forward<Visitor>(visitor), static_cast<Value&>(*this));
case Type::Object:
return std::invoke(std::forward<Visitor>(visitor), static_cast<Object&>(*this));
case Type::Array:
return std::invoke(std::forward<Visitor>(visitor), static_cast<Array&>(*this));
}
RELEASE_ASSERT_NOT_REACHED();
}

Ref<Value> Value::create(bool value)
template<typename Visitor> constexpr decltype(auto) Value::visitDerived(Visitor&& visitor) const
{
return adoptRef(*new Value(value));
return const_cast<Value&>(*this).visitDerived([&](auto& derived) {
return std::invoke(std::forward<Visitor>(visitor), std::as_const(derived));
});
}

Ref<Value> Value::create(int value)
void Value::operator delete(Value* value, std::destroying_delete_t)
{
return adoptRef(*new Value(value));
value->visitDerived([](auto& derived) {
std::destroy_at(&derived);
std::decay_t<decltype(derived)>::freeAfterDestruction(&derived);
});
}

Ref<Value> Value::create(double value)
Ref<Value> Value::null()
{
return adoptRef(*new Value(value));
return adoptRef(*new Value);
}

Ref<Value> Value::create(const String& value)
Ref<Value> Value::create(bool value)
{
return adoptRef(*new Value(value));
}

RefPtr<Value> Value::asValue()
{
return this;
}

RefPtr<Object> Value::asObject()
Ref<Value> Value::create(int value)
{
return nullptr;
return adoptRef(*new Value(value));
}

RefPtr<const Object> Value::asObject() const
Ref<Value> Value::create(double value)
{
return nullptr;
return adoptRef(*new Value(value));
}

RefPtr<Array> Value::asArray()
Ref<Value> Value::create(const String& value)
{
return nullptr;
return adoptRef(*new Value(value));
}

RefPtr<Value> Value::parseJSON(StringView json)
Expand Down Expand Up @@ -624,6 +637,13 @@ void Value::dump(PrintStream& out) const
}

void Value::writeJSON(StringBuilder& output) const
{
visitDerived([&](auto& derived) {
derived.writeJSONImpl(output);
});
}

void Value::writeJSONImpl(StringBuilder& output) const
{
switch (m_type) {
case Type::Null:
Expand All @@ -646,38 +666,32 @@ void Value::writeJSON(StringBuilder& output) const
output.append(m_value.number);
break;
}
default:
case Type::Object:
case Type::Array:
ASSERT_NOT_REACHED();
}
}

size_t Value::memoryCost() const
{
size_t memoryCost = sizeof(this);
if (m_type == Type::String && m_value.string)
memoryCost += m_value.string->sizeInBytes();
return memoryCost;
return visitDerived([&](auto& derived) {
return derived.memoryCostImpl();
});
}

ObjectBase::~ObjectBase()
size_t Value::memoryCostImpl() const
{
size_t memoryCost = sizeof(*this);
if (m_type == Type::String && m_value.string)
memoryCost += m_value.string->sizeInBytes();
return memoryCost;
}

RefPtr<Object> ObjectBase::asObject()
{
static_assert(sizeof(Object) == sizeof(ObjectBase), "cannot cast");
return static_cast<Object*>(this);
}

RefPtr<const Object> ObjectBase::asObject() const
{
static_assert(sizeof(Object) == sizeof(ObjectBase), "cannot cast");
return static_cast<const Object*>(this);
}
ObjectBase::~ObjectBase() = default;

size_t ObjectBase::memoryCost() const
size_t ObjectBase::memoryCostImpl() const
{
size_t memoryCost = Value::memoryCost();
size_t memoryCost = sizeof(*this);
for (const auto& entry : m_map)
memoryCost += entry.key.sizeInBytes() + entry.value->memoryCost();
return memoryCost;
Expand Down Expand Up @@ -745,7 +759,7 @@ void ObjectBase::remove(const String& name)
m_order.removeFirst(name);
}

void ObjectBase::writeJSON(StringBuilder& output) const
void ObjectBase::writeJSONImpl(StringBuilder& output) const
{
output.append('{');
for (size_t i = 0; i < m_order.size(); ++i) {
Expand All @@ -765,17 +779,9 @@ ObjectBase::ObjectBase()
{
}

ArrayBase::~ArrayBase()
{
}

RefPtr<Array> ArrayBase::asArray()
{
static_assert(sizeof(ArrayBase) == sizeof(Array), "cannot cast");
return static_cast<Array*>(this);
}
ArrayBase::~ArrayBase() = default;

void ArrayBase::writeJSON(StringBuilder& output) const
void ArrayBase::writeJSONImpl(StringBuilder& output) const
{
output.append('[');
for (auto it = m_map.begin(); it != m_map.end(); ++it) {
Expand Down Expand Up @@ -807,9 +813,9 @@ Ref<Array> Array::create()
return adoptRef(*new Array);
}

size_t ArrayBase::memoryCost() const
size_t ArrayBase::memoryCostImpl() const
{
size_t memoryCost = Value::memoryCost();
size_t memoryCost = sizeof(*this);
for (const auto& item : m_map)
memoryCost += item->memoryCost();
return memoryCost;
Expand Down
Loading

0 comments on commit 1e2385d

Please sign in to comment.