Read and write module names in the names section (#831)

diff --git a/src/binary-reader-ir.cc b/src/binary-reader-ir.cc
index 9c9c520..33c2ff5 100644
--- a/src/binary-reader-ir.cc
+++ b/src/binary-reader-ir.cc
@@ -200,6 +200,7 @@
                            const void* data,
                            Address size) override;
 
+  Result OnModuleName(string_view module_name) override;
   Result OnFunctionNamesCount(Index num_functions) override;
   Result OnFunctionName(Index function_index,
                         string_view function_name) override;
@@ -935,13 +936,26 @@
   return Result::Ok;
 }
 
+static std::string MakeDollarName(string_view name) {
+  return std::string("$") + name.to_string();
+}
+
+Result BinaryReaderIR::OnModuleName(string_view name) {
+  if (name.empty()) {
+    return Result::Ok;
+  }
+
+  module_->name = MakeDollarName(name);
+  return Result::Ok;
+}
+
 Result BinaryReaderIR::OnFunctionName(Index index, string_view name) {
   if (name.empty()) {
     return Result::Ok;
   }
 
   Func* func = module_->funcs[index];
-  std::string dollar_name = std::string("$") + name.to_string();
+  std::string dollar_name = MakeDollarName(name);
   int counter = 1;
   std::string orig_name = dollar_name;
   while (module_->func_bindings.count(dollar_name) != 0) {
@@ -1028,7 +1042,7 @@
     bindings = &func->local_bindings;
     index = local_index - num_params;
   }
-  bindings->emplace(std::string("$") + name.to_string(), Binding(index));
+  bindings->emplace(MakeDollarName(name), Binding(index));
   return Result::Ok;
 }
 
diff --git a/src/binary-reader-logging.cc b/src/binary-reader-logging.cc
index 03a17cd..9aa14f7 100644
--- a/src/binary-reader-logging.cc
+++ b/src/binary-reader-logging.cc
@@ -372,6 +372,21 @@
   return reader_->OnDataSegmentData(index, data, size);
 }
 
+Result BinaryReaderLogging::OnModuleNameSubsection(Index index,
+                                                   uint32_t name_type,
+                                                   Offset subsection_size) {
+  LOGF("OnModuleNameSubsection(index:%" PRIindex ", nametype:%u, size:%" PRIzd
+       ")\n",
+       index, name_type, subsection_size);
+  return reader_->OnModuleNameSubsection(index, name_type, subsection_size);
+}
+
+Result BinaryReaderLogging::OnModuleName(string_view name) {
+  LOGF("OnModuleName(name: \"" PRIstringview "\")\n",
+       WABT_PRINTF_STRING_VIEW_ARG(name));
+  return reader_->OnModuleName(name);
+}
+
 Result BinaryReaderLogging::OnFunctionNameSubsection(Index index,
                                                      uint32_t name_type,
                                                      Offset subsection_size) {
diff --git a/src/binary-reader-logging.h b/src/binary-reader-logging.h
index 2acf7ae..37893f8 100644
--- a/src/binary-reader-logging.h
+++ b/src/binary-reader-logging.h
@@ -227,6 +227,10 @@
   Result EndDataSection() override;
 
   Result BeginNamesSection(Offset size) override;
+  Result OnModuleNameSubsection(Index index,
+                                uint32_t name_type,
+                                Offset subsection_size) override;
+  Result OnModuleName(string_view name) override;
   Result OnFunctionNameSubsection(Index index,
                                   uint32_t name_type,
                                   Offset subsection_size) override;
diff --git a/src/binary-reader-nop.h b/src/binary-reader-nop.h
index f0a2e4e..e2c80d5 100644
--- a/src/binary-reader-nop.h
+++ b/src/binary-reader-nop.h
@@ -310,6 +310,12 @@
 
   /* Names section */
   Result BeginNamesSection(Offset size) override { return Result::Ok; }
+  Result OnModuleNameSubsection(Index index,
+                                uint32_t name_type,
+                                Offset subsection_size) override {
+    return Result::Ok;
+  }
+  Result OnModuleName(string_view name) override { return Result::Ok; }
   Result OnFunctionNameSubsection(Index index,
                                   uint32_t name_type,
                                   Offset subsection_size) override {
diff --git a/src/binary-reader.cc b/src/binary-reader.cc
index e377ae0..620a0c7 100644
--- a/src/binary-reader.cc
+++ b/src/binary-reader.cc
@@ -1330,6 +1330,14 @@
     read_end_ = subsection_end;
 
     switch (static_cast<NameSectionSubsection>(name_type)) {
+      case NameSectionSubsection::Module:
+        CALLBACK(OnModuleNameSubsection, i, name_type, subsection_size);
+        if (subsection_size) {
+          string_view name;
+          CHECK_RESULT(ReadStr(&name, "module name"));
+          CALLBACK(OnModuleName, name);
+        }
+        break;
       case NameSectionSubsection::Function:
         CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size);
         if (subsection_size) {
diff --git a/src/binary-reader.h b/src/binary-reader.h
index 8916927..5dea6c7 100644
--- a/src/binary-reader.h
+++ b/src/binary-reader.h
@@ -280,6 +280,10 @@
 
   /* Names section */
   virtual Result BeginNamesSection(Offset size) = 0;
+  virtual Result OnModuleNameSubsection(Index index,
+                                        uint32_t name_type,
+                                        Offset subsection_size) = 0;
+  virtual Result OnModuleName(string_view name) = 0;
   virtual Result OnFunctionNameSubsection(Index index,
                                           uint32_t name_type,
                                           Offset subsection_size) = 0;
diff --git a/src/binary-writer.cc b/src/binary-writer.cc
index 7136c60..a64522b 100644
--- a/src/binary-writer.cc
+++ b/src/binary-writer.cc
@@ -977,6 +977,13 @@
       }
     }
 
+    if (!module_->name.empty()) {
+      WriteU32Leb128(stream_, 0, "module name type");
+      BeginSubsection("module name subsection");
+      WriteDebugName(stream_, module_->name, "module name");
+      EndSubsection();
+    }
+
     if (named_functions > 0) {
       WriteU32Leb128(stream_, 1, "function name type");
       BeginSubsection("function name subsection");
diff --git a/src/binary.h b/src/binary.h
index 9f21c0a..12e1481 100644
--- a/src/binary.h
+++ b/src/binary.h
@@ -59,6 +59,7 @@
 static const int kBinarySectionCount = WABT_ENUM_COUNT(BinarySection);
 
 enum class NameSectionSubsection {
+  Module = 0,
   Function = 1,
   Local = 2,
 };
diff --git a/src/wat-writer.cc b/src/wat-writer.cc
index 0f52e6a..ba68113 100644
--- a/src/wat-writer.cc
+++ b/src/wat-writer.cc
@@ -1458,7 +1458,12 @@
   module_ = &module;
   BuildInlineExportMap();
   BuildInlineImportMap();
-  WriteOpenNewline("module");
+  WriteOpenSpace("module");
+  if (module.name.empty()) {
+    WriteNewline(NO_FORCE_NEWLINE);
+  } else {
+    WriteName(module.name, NextChar::Newline);
+  }
   for (const ModuleField& field : module.fields) {
     switch (field.type()) {
       case ModuleFieldType::Func:
diff --git a/test/binary/names.txt b/test/binary/names.txt
index a6110d3..d48762f 100644
--- a/test/binary/names.txt
+++ b/test/binary/names.txt
@@ -11,6 +11,10 @@
   }
 }
 section("name") {
+  section(NAME_MODULE) {
+    str("M0")
+  }
+
   section(NAME_FUNCTION) {
     func_count[1]
     index[0]
@@ -26,7 +30,7 @@
   }
 }
 (;; STDOUT ;;;
-(module
+(module $M0
   (type (;0;) (func (result i32)))
   (func $F0 (type 0) (result i32)
     (local $L0 i32)
diff --git a/test/gen-wasm.py b/test/gen-wasm.py
index 0424792..4ecc5a5 100755
--- a/test/gen-wasm.py
+++ b/test/gen-wasm.py
@@ -64,6 +64,7 @@
     'DATA': 11,
 
     # name subsection codes
+    'NAME_MODULE': 0,
     'NAME_FUNCTION': 1,
     'NAME_LOCALS': 2,
 
diff --git a/test/roundtrip/debug-names.txt b/test/roundtrip/debug-names.txt
index 4c77754..f890ba1 100644
--- a/test/roundtrip/debug-names.txt
+++ b/test/roundtrip/debug-names.txt
@@ -1,6 +1,6 @@
 ;;; TOOL: run-roundtrip
 ;;; ARGS: --debug-names
-(module
+(module $m1
   (func $f1)
   (func $f2 (param $p1 i32) (param $p2 i64)
     (local $l1 f32)