Date: Saturday, September 18, 2021 @ 18:35:34 Author: foutrelis Revision: 1017057
upgpkg: electron9 9.4.4-2: harfbuzz 3.0.0 rebuild Added: electron9/trunk/chromium-harfbuzz-3.0.0.patch electron9/trunk/chromium-include-limits.patch electron9/trunk/disk_data_allocator-Metadata-constructor.patch electron9/trunk/skia-harfbuzz-3.0.0.patch electron9/trunk/sql-make-VirtualCursor-standard-layout-type.patch Modified: electron9/trunk/PKGBUILD ---------------------------------------------------+ PKGBUILD | 17 + chromium-harfbuzz-3.0.0.patch | 20 + chromium-include-limits.patch | 31 ++ disk_data_allocator-Metadata-constructor.patch | 18 + skia-harfbuzz-3.0.0.patch | 99 ++++++++ sql-make-VirtualCursor-standard-layout-type.patch | 238 ++++++++++++++++++++ 6 files changed, 422 insertions(+), 1 deletion(-) Modified: PKGBUILD =================================================================== --- PKGBUILD 2021-09-18 17:21:12 UTC (rev 1017056) +++ PKGBUILD 2021-09-18 18:35:34 UTC (rev 1017057) @@ -4,7 +4,7 @@ pkgver=9.4.4 _commit=f981f3a7c41767fa5ec13cbc8e2242129db96e7a _chromiumver=83.0.4103.122 -pkgrel=1 +pkgrel=2 pkgdesc='Build cross platform desktop apps with web technologies' arch=('x86_64') url='https://electronjs.org/' @@ -28,7 +28,12 @@ 'chromium-83-gcc-10.patch' 'chromium-ffmpeg-4.3.patch' 'qt5-webengine-glibc-2.33.patch' + 'chromium-harfbuzz-3.0.0.patch' + 'skia-harfbuzz-3.0.0.patch' + 'chromium-include-limits.patch' 'chromium-skia-harmony.patch' + 'sql-make-VirtualCursor-standard-layout-type.patch' + 'disk_data_allocator-Metadata-constructor.patch' 'clean-up-a-call-to-set_utf8.patch' 'include-memory-header-to-get-the-definition-of-std-u.patch' 'iwyu-std-numeric_limits-is-defined-in-limits.patch' @@ -47,7 +52,12 @@ '3e5ba8c0a70a4bc673deec0c61eb2b58f05a4c784cbdb7c8118be1eb6580db6d' '5390304b5f544868985ce00a3ec082d4ece2dacb1c73cdb35dd4facfea12449a' '5600cfa40254fa3fa2cb541d3b55cc8f7a9231de8d2830c25a7651aa392de16f' + '7ce947944a139e66774dfc7249bf7c3069f07f83a0f1b2c1a1b14287a7e15928' + 'fffbdd7479292462a1b4e3846c610ee5a0f0d409ded11426f21dd47051604dfd' + 'cb8e4c347169f145d2f08329e25125eb2a3682bfc3530c674cf1ffad89431bdf' '771292942c0901092a402cc60ee883877a99fb804cb54d568c8c6c94565a48e1' + 'dd317f85e5abfdcfc89c6f23f4c8edbcdebdd5e083dcec770e5da49ee647d150' + '52a63a84a10dc501a0712a76170cfa12abc02ed4efe93486a1e6139b1670f90c' '58c41713eb6fb33b6eef120f4324fa1fb8123b1fbc4ecbe5662f1f9779b9b6af' '071326135bc25226aa165639dff80a03670a17548f2d2ff5cc4f40982b39c52a' '675fb3d6276cce569a641436465f58d5709d1d4a5f62b7052989479fd4aaea24' @@ -140,8 +150,13 @@ patch -Np1 -i ../avoid-double-destruction-of-ServiceWorkerObjectHost.patch patch -Np1 -i ../chromium-83-gcc-10.patch patch -Np1 -i ../chromium-ffmpeg-4.3.patch + patch -Np1 -i ../chromium-include-limits.patch patch -Np0 -i ../chromium-skia-harmony.patch + patch -Np1 -i ../sql-make-VirtualCursor-standard-layout-type.patch + patch -Np0 -i ../disk_data_allocator-Metadata-constructor.patch patch -Np4 -i ../qt5-webengine-glibc-2.33.patch + patch -Np1 -i ../chromium-harfbuzz-3.0.0.patch + patch -Np1 -d third_party/skia <../skia-harfbuzz-3.0.0.patch patch -Np1 -i ../clean-up-a-call-to-set_utf8.patch patch -Np1 -i ../include-memory-header-to-get-the-definition-of-std-u.patch patch -Np1 -F3 -i ../iwyu-std-numeric_limits-is-defined-in-limits.patch Added: chromium-harfbuzz-3.0.0.patch =================================================================== --- chromium-harfbuzz-3.0.0.patch (rev 0) +++ chromium-harfbuzz-3.0.0.patch 2021-09-18 18:35:34 UTC (rev 1017057) @@ -0,0 +1,20 @@ +# https://github.com/chromium/chromium/commit/b289f6f3fcbc + +diff --git a/components/paint_preview/common/subset_font.cc b/components/paint_preview/common/subset_font.cc +index 8ff0540d9a..20a7d37474 100644 +--- a/components/paint_preview/common/subset_font.cc ++++ b/components/paint_preview/common/subset_font.cc +@@ -72,9 +72,11 @@ sk_sp<SkData> SubsetFont(SkTypeface* typeface, const GlyphUsage& usage) { + hb_set_t* glyphs = + hb_subset_input_glyph_set(input.get()); // Owned by |input|. + usage.ForEach(base::BindRepeating(&AddGlyphs, base::Unretained(glyphs))); +- hb_subset_input_set_retain_gids(input.get(), true); ++ hb_subset_input_set_flags(input.get(), HB_SUBSET_FLAGS_RETAIN_GIDS); + +- HbScoped<hb_face_t> subset_face(hb_subset(face.get(), input.get())); ++ HbScoped<hb_face_t> subset_face(hb_subset_or_fail(face.get(), input.get())); ++ if (!subset_face) ++ return nullptr; + HbScoped<hb_blob_t> subset_blob(hb_face_reference_blob(subset_face.get())); + if (!subset_blob) + return nullptr; Added: chromium-include-limits.patch =================================================================== --- chromium-include-limits.patch (rev 0) +++ chromium-include-limits.patch 2021-09-18 18:35:34 UTC (rev 1017057) @@ -0,0 +1,31 @@ +--- a/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc ++++ b/third_party/abseil-cpp/absl/synchronization/internal/graphcycles.cc +@@ -37,6 +37,7 @@ + + #include <algorithm> + #include <array> ++#include <limits> + #include "absl/base/internal/hide_ptr.h" + #include "absl/base/internal/raw_logging.h" + #include "absl/base/internal/spinlock.h" +--- a/third_party/angle/src/libANGLE/HandleAllocator.cpp ++++ b/third_party/angle/src/libANGLE/HandleAllocator.cpp +@@ -11,6 +11,7 @@ + + #include <algorithm> + #include <functional> ++#include <limits> + + #include "common/debug.h" + +--- a/third_party/perfetto/src/trace_processor/containers/string_pool.h ++++ b/third_party/perfetto/src/trace_processor/containers/string_pool.h +@@ -17,6 +17,8 @@ + #ifndef SRC_TRACE_PROCESSOR_CONTAINERS_STRING_POOL_H_ + #define SRC_TRACE_PROCESSOR_CONTAINERS_STRING_POOL_H_ + ++#include <limits> ++ + #include <stddef.h> + #include <stdint.h> + Added: disk_data_allocator-Metadata-constructor.patch =================================================================== --- disk_data_allocator-Metadata-constructor.patch (rev 0) +++ disk_data_allocator-Metadata-constructor.patch 2021-09-18 18:35:34 UTC (rev 1017057) @@ -0,0 +1,18 @@ +# Modeled after https://github.com/chromium/chromium/commit/d62de64e2fd2 + +--- third_party/blink/renderer/platform/disk_data_allocator.h.orig 2021-09-18 15:20:46.550131515 +0000 ++++ third_party/blink/renderer/platform/disk_data_allocator.h 2021-09-18 15:21:20.868676978 +0000 +@@ -36,12 +36,12 @@ class PLATFORM_EXPORT DiskDataAllocator + public: + int64_t start_offset() const { return start_offset_; } + size_t size() const { return size_; } +- Metadata(Metadata&& other) = delete; + + private: + Metadata(int64_t start_offset, size_t size) + : start_offset_(start_offset), size_(size) {} + Metadata(const Metadata& other) = default; ++ Metadata(Metadata&& other) = default; + Metadata& operator=(const Metadata& other) = default; + + int64_t start_offset_; Added: skia-harfbuzz-3.0.0.patch =================================================================== --- skia-harfbuzz-3.0.0.patch (rev 0) +++ skia-harfbuzz-3.0.0.patch 2021-09-18 18:35:34 UTC (rev 1017057) @@ -0,0 +1,99 @@ +# Minimal diff for harfbuzz 3.0.0 support; based on: +# https://github.com/google/skia/commit/66684b17b382 +# https://github.com/google/skia/commit/51d83abcd24a + +diff --git a/gn/skia.gni b/gn/skia.gni +index d98fdc19ee..199335d5c4 100644 +--- a/gn/skia.gni ++++ b/gn/skia.gni +@@ -34,7 +34,6 @@ declare_args() { + skia_include_multiframe_procs = false + skia_lex = false + skia_libgifcodec_path = "third_party/externals/libgifcodec" +- skia_pdf_subset_harfbuzz = false # TODO: set skia_pdf_subset_harfbuzz to skia_use_harfbuzz. + skia_qt_path = getenv("QT_PATH") + skia_skqp_global_error_tolerance = 0 + skia_tools_require_resources = false +@@ -99,6 +97,10 @@ declare_args() { + skia_use_libfuzzer_defaults = true + } + ++declare_args() { ++ skia_pdf_subset_harfbuzz = skia_use_harfbuzz ++} ++ + declare_args() { + skia_compile_sksl_tests = skia_compile_processors + skia_enable_fontmgr_android = skia_use_expat && skia_use_freetype +diff --git a/src/pdf/SkPDFSubsetFont.cpp b/src/pdf/SkPDFSubsetFont.cpp +index 81c37eef3a..2340a7937b 100644 +--- a/src/pdf/SkPDFSubsetFont.cpp ++++ b/src/pdf/SkPDFSubsetFont.cpp +@@ -49,6 +49,37 @@ static sk_sp<SkData> to_data(HBBlob blob) { + blob.release()); + } + ++template<typename...> using void_t = void; ++template<typename T, typename = void> ++struct SkPDFHarfBuzzSubset { ++ // This is the HarfBuzz 3.0 interface. ++ // hb_subset_flags_t does not exist in 2.0. It isn't dependent on T, so inline the value of ++ // HB_SUBSET_FLAGS_RETAIN_GIDS until 2.0 is no longer supported. ++ static HBFace Make(T input, hb_face_t* face) { ++ // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. ++ // If it isn't known if a font is 'tricky', retain the hints. ++ hb_subset_input_set_flags(input, 2/*HB_SUBSET_FLAGS_RETAIN_GIDS*/); ++ return HBFace(hb_subset_or_fail(face, input)); ++ } ++}; ++template<typename T> ++struct SkPDFHarfBuzzSubset<T, void_t< ++ decltype(hb_subset_input_set_retain_gids(std::declval<T>(), std::declval<bool>())), ++ decltype(hb_subset_input_set_drop_hints(std::declval<T>(), std::declval<bool>())), ++ decltype(hb_subset(std::declval<hb_face_t*>(), std::declval<T>())) ++ >> ++{ ++ // This is the HarfBuzz 2.0 (non-public) interface, used if it exists. ++ // This code should be removed as soon as all users are migrated to the newer API. ++ static HBFace Make(T input, hb_face_t* face) { ++ hb_subset_input_set_retain_gids(input, true); ++ // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. ++ // If it isn't known if a font is 'tricky', retain the hints. ++ hb_subset_input_set_drop_hints(input, false); ++ return HBFace(hb_subset(face, input)); ++ } ++}; ++ + static sk_sp<SkData> subset_harfbuzz(sk_sp<SkData> fontData, + const SkPDFGlyphUse& glyphUsage, + int ttcIndex) { +@@ -71,11 +102,10 @@ static sk_sp<SkData> subset_harfbuzz(sk_sp<SkData> fontData, + hb_set_t* glyphs = hb_subset_input_glyph_set(input.get()); + glyphUsage.getSetValues([&glyphs](unsigned gid) { hb_set_add(glyphs, gid);}); + +- hb_subset_input_set_retain_gids(input.get(), true); +- // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. +- // If it isn't known if a font is 'tricky', retain the hints. +- hb_subset_input_set_drop_hints(input.get(), false); +- HBFace subset(hb_subset(face.get(), input.get())); ++ HBFace subset = SkPDFHarfBuzzSubset<hb_subset_input_t*>::Make(input.get(), face.get()); ++ if (!subset) { ++ return nullptr; ++ } + HBBlob result(hb_face_reference_blob(subset.get())); + return to_data(std::move(result)); + } +diff --git a/third_party/harfbuzz/BUILD.gn b/third_party/harfbuzz/BUILD.gn +index 173830de62..4156607ef9 100644 +--- a/third_party/harfbuzz/BUILD.gn ++++ b/third_party/harfbuzz/BUILD.gn +@@ -14,6 +14,9 @@ if (skia_use_system_harfbuzz) { + "harfbuzz", + "harfbuzz-icu", + ] ++ if (skia_pdf_subset_harfbuzz) { ++ libs += [ "harfbuzz-subset" ] ++ } + } + } else { + third_party("harfbuzz") { Added: sql-make-VirtualCursor-standard-layout-type.patch =================================================================== --- sql-make-VirtualCursor-standard-layout-type.patch (rev 0) +++ sql-make-VirtualCursor-standard-layout-type.patch 2021-09-18 18:35:34 UTC (rev 1017057) @@ -0,0 +1,238 @@ +From 80368f8ba7a8bab13440463a254888311efe3986 Mon Sep 17 00:00:00 2001 +From: Stephan Hartmann <sth...@googlemail.com> +Date: Tue, 4 May 2021 15:00:19 +0000 +Subject: [PATCH] sql: make VirtualCursor standard layout type + +sql::recover::VirtualCursor needs to be a standard layout type, but +has members of type std::unique_ptr. However, std::unique_ptr is not +guaranteed to be standard layout. Compiling with clang combined with +gcc-11 libstdc++ fails because of this. Replace std::unique_ptr with +raw pointers. + +Bug: 1189788 +Change-Id: Ia6dc388cc5ef1c0f2afc75f8ca45b9f12687ca9c +--- + sql/recover_module/btree.cc | 21 +++++++++++++++------ + sql/recover_module/btree.h | 17 +++++++++++++---- + sql/recover_module/cursor.cc | 24 ++++++++++++------------ + sql/recover_module/cursor.h | 2 +- + sql/recover_module/pager.cc | 7 +++---- + sql/recover_module/pager.h | 5 +++-- + 6 files changed, 47 insertions(+), 29 deletions(-) + +diff --git a/sql/recover_module/btree.cc b/sql/recover_module/btree.cc +index 9ecaafe8a3..839318abf9 100644 +--- a/sql/recover_module/btree.cc ++++ b/sql/recover_module/btree.cc +@@ -135,16 +135,25 @@ static_assert(std::is_trivially_destructible<LeafPageDecoder>::value, + "Move the destructor to the .cc file if it's non-trival"); + #endif // !DCHECK_IS_ON() + +-LeafPageDecoder::LeafPageDecoder(DatabasePageReader* db_reader) noexcept +- : page_id_(db_reader->page_id()), +- db_reader_(db_reader), +- cell_count_(ComputeCellCount(db_reader)), +- next_read_index_(0), +- last_record_size_(0) { ++void LeafPageDecoder::Initialize(DatabasePageReader* db_reader) { ++ DCHECK(db_reader); + DCHECK(IsOnValidPage(db_reader)); ++ page_id_ = db_reader->page_id(); ++ db_reader_ = db_reader; ++ cell_count_ = ComputeCellCount(db_reader); ++ next_read_index_ = 0; ++ last_record_size_ = 0; + DCHECK(DatabasePageReader::IsValidPageId(page_id_)); + } + ++void LeafPageDecoder::Reset() { ++ db_reader_ = nullptr; ++ page_id_ = 0; ++ cell_count_ = 0; ++ next_read_index_ = 0; ++ last_record_size_ = 0; ++} ++ + bool LeafPageDecoder::TryAdvance() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(CanAdvance()); +diff --git a/sql/recover_module/btree.h b/sql/recover_module/btree.h +index d76d076bf6..33114b01fa 100644 +--- a/sql/recover_module/btree.h ++++ b/sql/recover_module/btree.h +@@ -102,7 +102,7 @@ class LeafPageDecoder { + // + // |db_reader| must have been used to read an inner page of a table B-tree. + // |db_reader| must outlive this instance. +- explicit LeafPageDecoder(DatabasePageReader* db_reader) noexcept; ++ explicit LeafPageDecoder() noexcept = default; + ~LeafPageDecoder() noexcept = default; + + LeafPageDecoder(const LeafPageDecoder&) = delete; +@@ -150,6 +150,15 @@ class LeafPageDecoder { + // read as long as CanAdvance() returns true. + bool TryAdvance(); + ++ // Initialize with DatabasePageReader ++ void Initialize(DatabasePageReader* db_reader); ++ ++ // Reset internal DatabasePageReader ++ void Reset(); ++ ++ // True if DatabasePageReader is valid ++ bool IsValid() { return (db_reader_ != nullptr); } ++ + // True if the given reader may point to an inner page in a table B-tree. + // + // The last ReadPage() call on |db_reader| must have succeeded. +@@ -163,14 +172,14 @@ class LeafPageDecoder { + static int ComputeCellCount(DatabasePageReader* db_reader); + + // The number of the B-tree page this reader is reading. +- const int64_t page_id_; ++ int64_t page_id_; + // Used to read the tree page. + // + // Raw pointer usage is acceptable because this instance's owner is expected + // to ensure that the DatabasePageReader outlives this. +- DatabasePageReader* const db_reader_; ++ DatabasePageReader* db_reader_; + // Caches the ComputeCellCount() value for this reader's page. +- const int cell_count_ = ComputeCellCount(db_reader_); ++ int cell_count_; + + // The reader's cursor state. + // +diff --git a/sql/recover_module/cursor.cc b/sql/recover_module/cursor.cc +index 0029ff9295..42548bc4b5 100644 +--- a/sql/recover_module/cursor.cc ++++ b/sql/recover_module/cursor.cc +@@ -26,7 +26,7 @@ VirtualCursor::~VirtualCursor() { + int VirtualCursor::First() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + inner_decoders_.clear(); +- leaf_decoder_ = nullptr; ++ leaf_decoder_.Reset(); + + AppendPageDecoder(table_->root_page_id()); + return Next(); +@@ -36,18 +36,18 @@ int VirtualCursor::Next() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + record_reader_.Reset(); + +- while (!inner_decoders_.empty() || leaf_decoder_.get()) { +- if (leaf_decoder_.get()) { +- if (!leaf_decoder_->CanAdvance()) { ++ while (!inner_decoders_.empty() || leaf_decoder_.IsValid()) { ++ if (leaf_decoder_.IsValid()) { ++ if (!leaf_decoder_.CanAdvance()) { + // The leaf has been exhausted. Remove it from the DFS stack. +- leaf_decoder_ = nullptr; ++ leaf_decoder_.Reset(); + continue; + } +- if (!leaf_decoder_->TryAdvance()) ++ if (!leaf_decoder_.TryAdvance()) + continue; + +- if (!payload_reader_.Initialize(leaf_decoder_->last_record_size(), +- leaf_decoder_->last_record_offset())) { ++ if (!payload_reader_.Initialize(leaf_decoder_.last_record_size(), ++ leaf_decoder_.last_record_offset())) { + continue; + } + if (!record_reader_.Initialize()) +@@ -99,13 +99,13 @@ int VirtualCursor::ReadColumn(int column_index, + int64_t VirtualCursor::RowId() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(record_reader_.IsInitialized()); +- DCHECK(leaf_decoder_.get()); +- return leaf_decoder_->last_record_rowid(); ++ DCHECK(leaf_decoder_.IsValid()); ++ return leaf_decoder_.last_record_rowid(); + } + + void VirtualCursor::AppendPageDecoder(int page_id) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +- DCHECK(leaf_decoder_.get() == nullptr) ++ DCHECK(!leaf_decoder_.IsValid()) + << __func__ + << " must only be called when the current path has no leaf decoder"; + +@@ -113,7 +113,7 @@ void VirtualCursor::AppendPageDecoder(int page_id) { + return; + + if (LeafPageDecoder::IsOnValidPage(&db_reader_)) { +- leaf_decoder_ = std::make_unique<LeafPageDecoder>(&db_reader_); ++ leaf_decoder_.Initialize(&db_reader_); + return; + } + +diff --git a/sql/recover_module/cursor.h b/sql/recover_module/cursor.h +index afcd6900e1..b15c31d425 100644 +--- a/sql/recover_module/cursor.h ++++ b/sql/recover_module/cursor.h +@@ -129,7 +129,7 @@ class VirtualCursor { + std::vector<std::unique_ptr<InnerPageDecoder>> inner_decoders_; + + // Decodes the leaf page containing records. +- std::unique_ptr<LeafPageDecoder> leaf_decoder_; ++ LeafPageDecoder leaf_decoder_; + + SEQUENCE_CHECKER(sequence_checker_); + }; +diff --git a/sql/recover_module/pager.cc b/sql/recover_module/pager.cc +index 58e75de270..5fe96204e5 100644 +--- a/sql/recover_module/pager.cc ++++ b/sql/recover_module/pager.cc +@@ -23,8 +23,7 @@ static_assert(DatabasePageReader::kMaxPageId <= std::numeric_limits<int>::max(), + "ints are not appropriate for representing page IDs"); + + DatabasePageReader::DatabasePageReader(VirtualTable* table) +- : page_data_(std::make_unique<uint8_t[]>(table->page_size())), +- table_(table) { ++ : page_data_(), table_(table) { + DCHECK(table != nullptr); + DCHECK(IsValidPageSize(table->page_size())); + } +@@ -57,8 +56,8 @@ int DatabasePageReader::ReadPage(int page_id) { + std::numeric_limits<int64_t>::max(), + "The |read_offset| computation above may overflow"); + +- int sqlite_status = +- RawRead(sqlite_file, read_size, read_offset, page_data_.get()); ++ int sqlite_status = RawRead(sqlite_file, read_size, read_offset, ++ const_cast<uint8_t*>(page_data_.data())); + + // |page_id_| needs to be set to kInvalidPageId if the read failed. + // Otherwise, future ReadPage() calls with the previous |page_id_| value +diff --git a/sql/recover_module/pager.h b/sql/recover_module/pager.h +index 0e388ddc3b..99314e30ff 100644 +--- a/sql/recover_module/pager.h ++++ b/sql/recover_module/pager.h +@@ -5,6 +5,7 @@ + #ifndef SQL_RECOVER_MODULE_PAGER_H_ + #define SQL_RECOVER_MODULE_PAGER_H_ + ++#include <array> + #include <cstdint> + #include <memory> + +@@ -70,7 +71,7 @@ class DatabasePageReader { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_NE(page_id_, kInvalidPageId) + << "Successful ReadPage() required before accessing pager state"; +- return page_data_.get(); ++ return page_data_.data(); + } + + // The number of bytes in the page read by the last ReadPage() call. +@@ -137,7 +138,7 @@ class DatabasePageReader { + int page_id_ = kInvalidPageId; + // Stores the bytes of the last page successfully read by ReadPage(). + // The content is undefined if the last call to ReadPage() did not succeed. +- const std::unique_ptr<uint8_t[]> page_data_; ++ const std::array<uint8_t, kMaxPageSize> page_data_; + // Raw pointer usage is acceptable because this instance's owner is expected + // to ensure that the VirtualTable outlives this. + VirtualTable* const table_;