Import Tint changes from Dawn

Changes:
  - be967e31fc7e839d6152b5c703631c538e7f1d1c Add JSON output to tint_info. by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: be967e31fc7e839d6152b5c703631c538e7f1d1c
Change-Id: I0abcfa8dd09cda35e4bb807d42cf5da8e6ac583c
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/120320
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/cmd/helper.cc b/src/tint/cmd/helper.cc
index 702050e..f3760da 100644
--- a/src/tint/cmd/helper.cc
+++ b/src/tint/cmd/helper.cc
@@ -20,125 +20,6 @@
 namespace tint::cmd {
 namespace {
 
-std::string TextureDimensionToString(tint::inspector::ResourceBinding::TextureDimension dim) {
-    switch (dim) {
-        case tint::inspector::ResourceBinding::TextureDimension::kNone:
-            return "None";
-        case tint::inspector::ResourceBinding::TextureDimension::k1d:
-            return "1d";
-        case tint::inspector::ResourceBinding::TextureDimension::k2d:
-            return "2d";
-        case tint::inspector::ResourceBinding::TextureDimension::k2dArray:
-            return "2dArray";
-        case tint::inspector::ResourceBinding::TextureDimension::k3d:
-            return "3d";
-        case tint::inspector::ResourceBinding::TextureDimension::kCube:
-            return "Cube";
-        case tint::inspector::ResourceBinding::TextureDimension::kCubeArray:
-            return "CubeArray";
-    }
-
-    return "Unknown";
-}
-
-std::string SampledKindToString(tint::inspector::ResourceBinding::SampledKind kind) {
-    switch (kind) {
-        case tint::inspector::ResourceBinding::SampledKind::kFloat:
-            return "Float";
-        case tint::inspector::ResourceBinding::SampledKind::kUInt:
-            return "UInt";
-        case tint::inspector::ResourceBinding::SampledKind::kSInt:
-            return "SInt";
-        case tint::inspector::ResourceBinding::SampledKind::kUnknown:
-            break;
-    }
-
-    return "Unknown";
-}
-
-std::string TexelFormatToString(tint::inspector::ResourceBinding::TexelFormat format) {
-    switch (format) {
-        case tint::inspector::ResourceBinding::TexelFormat::kR32Uint:
-            return "R32Uint";
-        case tint::inspector::ResourceBinding::TexelFormat::kR32Sint:
-            return "R32Sint";
-        case tint::inspector::ResourceBinding::TexelFormat::kR32Float:
-            return "R32Float";
-        case tint::inspector::ResourceBinding::TexelFormat::kBgra8Unorm:
-            return "Bgra8Unorm";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Unorm:
-            return "Rgba8Unorm";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Snorm:
-            return "Rgba8Snorm";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Uint:
-            return "Rgba8Uint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Sint:
-            return "Rgba8Sint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRg32Uint:
-            return "Rg32Uint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRg32Sint:
-            return "Rg32Sint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRg32Float:
-            return "Rg32Float";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba16Uint:
-            return "Rgba16Uint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba16Sint:
-            return "Rgba16Sint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba16Float:
-            return "Rgba16Float";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba32Uint:
-            return "Rgba32Uint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba32Sint:
-            return "Rgba32Sint";
-        case tint::inspector::ResourceBinding::TexelFormat::kRgba32Float:
-            return "Rgba32Float";
-        case tint::inspector::ResourceBinding::TexelFormat::kNone:
-            return "None";
-    }
-    return "Unknown";
-}
-
-std::string ResourceTypeToString(tint::inspector::ResourceBinding::ResourceType type) {
-    switch (type) {
-        case tint::inspector::ResourceBinding::ResourceType::kUniformBuffer:
-            return "UniformBuffer";
-        case tint::inspector::ResourceBinding::ResourceType::kStorageBuffer:
-            return "StorageBuffer";
-        case tint::inspector::ResourceBinding::ResourceType::kReadOnlyStorageBuffer:
-            return "ReadOnlyStorageBuffer";
-        case tint::inspector::ResourceBinding::ResourceType::kSampler:
-            return "Sampler";
-        case tint::inspector::ResourceBinding::ResourceType::kComparisonSampler:
-            return "ComparisonSampler";
-        case tint::inspector::ResourceBinding::ResourceType::kSampledTexture:
-            return "SampledTexture";
-        case tint::inspector::ResourceBinding::ResourceType::kMultisampledTexture:
-            return "MultisampledTexture";
-        case tint::inspector::ResourceBinding::ResourceType::kWriteOnlyStorageTexture:
-            return "WriteOnlyStorageTexture";
-        case tint::inspector::ResourceBinding::ResourceType::kDepthTexture:
-            return "DepthTexture";
-        case tint::inspector::ResourceBinding::ResourceType::kDepthMultisampledTexture:
-            return "DepthMultisampledTexture";
-        case tint::inspector::ResourceBinding::ResourceType::kExternalTexture:
-            return "ExternalTexture";
-    }
-
-    return "Unknown";
-}
-
-std::string EntryPointStageToString(tint::inspector::PipelineStage stage) {
-    switch (stage) {
-        case tint::inspector::PipelineStage::kVertex:
-            return "Vertex Shader";
-        case tint::inspector::PipelineStage::kFragment:
-            return "Fragment Shader";
-        case tint::inspector::PipelineStage::kCompute:
-            return "Compute Shader";
-    }
-    return "Unknown";
-}
-
 enum class InputFormat {
     kUnknown,
     kWgsl,
@@ -432,4 +313,199 @@
     std::cout << std::string(80, '-') << std::endl;
 }
 
+std::string EntryPointStageToString(tint::inspector::PipelineStage stage) {
+    switch (stage) {
+        case tint::inspector::PipelineStage::kVertex:
+            return "vertex";
+        case tint::inspector::PipelineStage::kFragment:
+            return "fragment";
+        case tint::inspector::PipelineStage::kCompute:
+            return "compute";
+    }
+    return "Unknown";
+}
+
+std::string TextureDimensionToString(tint::inspector::ResourceBinding::TextureDimension dim) {
+    switch (dim) {
+        case tint::inspector::ResourceBinding::TextureDimension::kNone:
+            return "None";
+        case tint::inspector::ResourceBinding::TextureDimension::k1d:
+            return "1d";
+        case tint::inspector::ResourceBinding::TextureDimension::k2d:
+            return "2d";
+        case tint::inspector::ResourceBinding::TextureDimension::k2dArray:
+            return "2dArray";
+        case tint::inspector::ResourceBinding::TextureDimension::k3d:
+            return "3d";
+        case tint::inspector::ResourceBinding::TextureDimension::kCube:
+            return "Cube";
+        case tint::inspector::ResourceBinding::TextureDimension::kCubeArray:
+            return "CubeArray";
+    }
+
+    return "Unknown";
+}
+
+std::string SampledKindToString(tint::inspector::ResourceBinding::SampledKind kind) {
+    switch (kind) {
+        case tint::inspector::ResourceBinding::SampledKind::kFloat:
+            return "Float";
+        case tint::inspector::ResourceBinding::SampledKind::kUInt:
+            return "UInt";
+        case tint::inspector::ResourceBinding::SampledKind::kSInt:
+            return "SInt";
+        case tint::inspector::ResourceBinding::SampledKind::kUnknown:
+            break;
+    }
+
+    return "Unknown";
+}
+
+std::string TexelFormatToString(tint::inspector::ResourceBinding::TexelFormat format) {
+    switch (format) {
+        case tint::inspector::ResourceBinding::TexelFormat::kR32Uint:
+            return "R32Uint";
+        case tint::inspector::ResourceBinding::TexelFormat::kR32Sint:
+            return "R32Sint";
+        case tint::inspector::ResourceBinding::TexelFormat::kR32Float:
+            return "R32Float";
+        case tint::inspector::ResourceBinding::TexelFormat::kBgra8Unorm:
+            return "Bgra8Unorm";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Unorm:
+            return "Rgba8Unorm";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Snorm:
+            return "Rgba8Snorm";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Uint:
+            return "Rgba8Uint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba8Sint:
+            return "Rgba8Sint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRg32Uint:
+            return "Rg32Uint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRg32Sint:
+            return "Rg32Sint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRg32Float:
+            return "Rg32Float";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba16Uint:
+            return "Rgba16Uint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba16Sint:
+            return "Rgba16Sint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba16Float:
+            return "Rgba16Float";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba32Uint:
+            return "Rgba32Uint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba32Sint:
+            return "Rgba32Sint";
+        case tint::inspector::ResourceBinding::TexelFormat::kRgba32Float:
+            return "Rgba32Float";
+        case tint::inspector::ResourceBinding::TexelFormat::kNone:
+            return "None";
+    }
+    return "Unknown";
+}
+
+std::string ResourceTypeToString(tint::inspector::ResourceBinding::ResourceType type) {
+    switch (type) {
+        case tint::inspector::ResourceBinding::ResourceType::kUniformBuffer:
+            return "UniformBuffer";
+        case tint::inspector::ResourceBinding::ResourceType::kStorageBuffer:
+            return "StorageBuffer";
+        case tint::inspector::ResourceBinding::ResourceType::kReadOnlyStorageBuffer:
+            return "ReadOnlyStorageBuffer";
+        case tint::inspector::ResourceBinding::ResourceType::kSampler:
+            return "Sampler";
+        case tint::inspector::ResourceBinding::ResourceType::kComparisonSampler:
+            return "ComparisonSampler";
+        case tint::inspector::ResourceBinding::ResourceType::kSampledTexture:
+            return "SampledTexture";
+        case tint::inspector::ResourceBinding::ResourceType::kMultisampledTexture:
+            return "MultisampledTexture";
+        case tint::inspector::ResourceBinding::ResourceType::kWriteOnlyStorageTexture:
+            return "WriteOnlyStorageTexture";
+        case tint::inspector::ResourceBinding::ResourceType::kDepthTexture:
+            return "DepthTexture";
+        case tint::inspector::ResourceBinding::ResourceType::kDepthMultisampledTexture:
+            return "DepthMultisampledTexture";
+        case tint::inspector::ResourceBinding::ResourceType::kExternalTexture:
+            return "ExternalTexture";
+    }
+
+    return "Unknown";
+}
+
+std::string ComponentTypeToString(tint::inspector::ComponentType type) {
+    switch (type) {
+        case tint::inspector::ComponentType::kUnknown:
+            return "unknown";
+        case tint::inspector::ComponentType::kF32:
+            return "f32";
+        case tint::inspector::ComponentType::kU32:
+            return "u32";
+        case tint::inspector::ComponentType::kI32:
+            return "i32";
+        case tint::inspector::ComponentType::kF16:
+            return "f16";
+    }
+    return "unknown";
+}
+
+std::string CompositionTypeToString(tint::inspector::CompositionType type) {
+    switch (type) {
+        case tint::inspector::CompositionType::kUnknown:
+            return "unknown";
+        case tint::inspector::CompositionType::kScalar:
+            return "scalar";
+        case tint::inspector::CompositionType::kVec2:
+            return "vec2";
+        case tint::inspector::CompositionType::kVec3:
+            return "vec3";
+        case tint::inspector::CompositionType::kVec4:
+            return "vec4";
+    }
+    return "unknown";
+}
+
+std::string InterpolationTypeToString(tint::inspector::InterpolationType type) {
+    switch (type) {
+        case tint::inspector::InterpolationType::kUnknown:
+            return "unknown";
+        case tint::inspector::InterpolationType::kPerspective:
+            return "perspective";
+        case tint::inspector::InterpolationType::kLinear:
+            return "linear";
+        case tint::inspector::InterpolationType::kFlat:
+            return "flat";
+    }
+    return "unknown";
+}
+
+std::string InterpolationSamplingToString(tint::inspector::InterpolationSampling type) {
+    switch (type) {
+        case tint::inspector::InterpolationSampling::kUnknown:
+            return "unknown";
+        case tint::inspector::InterpolationSampling::kNone:
+            return "none";
+        case tint::inspector::InterpolationSampling::kCenter:
+            return "center";
+        case tint::inspector::InterpolationSampling::kCentroid:
+            return "centroid";
+        case tint::inspector::InterpolationSampling::kSample:
+            return "sample";
+    }
+    return "unknown";
+}
+
+std::string OverrideTypeToString(tint::inspector::Override::Type type) {
+    switch (type) {
+        case tint::inspector::Override::Type::kBool:
+            return "bool";
+        case tint::inspector::Override::Type::kFloat32:
+            return "f32";
+        case tint::inspector::Override::Type::kUint32:
+            return "u32";
+        case tint::inspector::Override::Type::kInt32:
+            return "i32";
+    }
+    return "unknown";
+}
+
 }  // namespace tint::cmd
diff --git a/src/tint/cmd/helper.h b/src/tint/cmd/helper.h
index 8ce0c7b..3a48ea4 100644
--- a/src/tint/cmd/helper.h
+++ b/src/tint/cmd/helper.h
@@ -62,6 +62,46 @@
 /// @param opts the loading options
 ProgramInfo LoadProgramInfo(const LoadProgramOptions& opts);
 
+/// @param stage the pipeline stage
+/// @returns the string representation
+std::string EntryPointStageToString(tint::inspector::PipelineStage stage);
+
+/// @param dim the dimension
+/// @returns the text name
+std::string TextureDimensionToString(tint::inspector::ResourceBinding::TextureDimension dim);
+
+/// @param kind the sample kind
+/// @returns the text name
+std::string SampledKindToString(tint::inspector::ResourceBinding::SampledKind kind);
+
+/// @param format the texel format
+/// @returns the text name
+std::string TexelFormatToString(tint::inspector::ResourceBinding::TexelFormat format);
+
+/// @param type the resource type
+/// @returns the text name
+std::string ResourceTypeToString(tint::inspector::ResourceBinding::ResourceType type);
+
+/// @param type the composition type
+/// @return the text name
+std::string CompositionTypeToString(tint::inspector::CompositionType type);
+
+/// @param type the component type
+/// @return the text name
+std::string ComponentTypeToString(tint::inspector::ComponentType type);
+
+/// @param type the interpolation sampling type
+/// @return the text name
+std::string InterpolationSamplingToString(tint::inspector::InterpolationSampling type);
+
+/// @param type the interpolation type
+/// @return the text name
+std::string InterpolationTypeToString(tint::inspector::InterpolationType type);
+
+/// @param type the override type
+/// @return the text name
+std::string OverrideTypeToString(tint::inspector::Override::Type type);
+
 }  // namespace tint::cmd
 
 #endif  // SRC_TINT_CMD_HELPER_H_
diff --git a/src/tint/cmd/info.cc b/src/tint/cmd/info.cc
index 50df90f..6c244ad 100644
--- a/src/tint/cmd/info.cc
+++ b/src/tint/cmd/info.cc
@@ -36,11 +36,13 @@
 #endif
 
     std::string input_filename;
+    bool emit_json = false;
 };
 
 const char kUsage[] = R"(Usage: tint [options] <input-file>
 
  options:
+   --json                    -- Emit JSON
    -h                        -- This help text
 
 )";
@@ -50,6 +52,8 @@
         const std::string& arg = args[i];
         if (arg == "-h" || arg == "--help") {
             opts->show_help = true;
+        } else if (arg == "--json") {
+            opts->emit_json = true;
         } else if (!arg.empty()) {
             if (arg[0] == '-') {
                 std::cerr << "Unrecognized option: " << arg << std::endl;
@@ -66,6 +70,246 @@
     return true;
 }
 
+void EmitJson(const tint::Program* program) {
+    tint::inspector::Inspector inspector(program);
+
+    std::cout << "{" << std::endl;
+    std::cout << "\"extensions\": [" << std::endl;
+
+    if (!inspector.GetUsedExtensionNames().empty()) {
+        bool first = true;
+        for (const auto& name : inspector.GetUsedExtensionNames()) {
+            if (!first) {
+                std::cout << ",";
+            }
+            first = false;
+            std::cout << "\"" << name << "\"" << std::endl;
+        }
+    }
+    std::cout << "]," << std::endl;
+
+    std::cout << "\"entry_points\": [";
+
+    auto stage_var = [&](const tint::inspector::StageVariable& var) {
+        std::cout << std::endl;
+        std::cout << "{" << std::endl;
+        std::cout << "\"name\": \"" << var.name << "\"";
+        if (var.has_location_attribute) {
+            std::cout << "," << std::endl;
+            std::cout << "\"location\": " << var.location_attribute << "," << std::endl;
+            std::cout << "\"component_type\": \""
+                      << tint::cmd::ComponentTypeToString(var.component_type) << "\"," << std::endl;
+            std::cout << "\"composition_type\": \""
+                      << tint::cmd::CompositionTypeToString(var.composition_type) << "\","
+                      << std::endl;
+            std::cout << "\"interpolation\": {" << std::endl;
+            std::cout << "\"type\": \""
+                      << tint::cmd::InterpolationTypeToString(var.interpolation_type) << "\","
+                      << std::endl;
+            std::cout << "\"sampling\": \""
+                      << tint::cmd::InterpolationSamplingToString(var.interpolation_sampling)
+                      << "\"" << std::endl;
+            std::cout << "}" << std::endl;
+        }
+        std::cout << std::endl;
+        std::cout << "}";
+    };
+
+    auto entry_points = inspector.GetEntryPoints();
+    bool first = true;
+    for (auto& entry_point : entry_points) {
+        if (!first) {
+            std::cout << ",";
+        }
+        first = false;
+
+        std::cout << std::endl;
+        std::cout << "{" << std::endl;
+
+        std::cout << "\"name\": \"" << entry_point.name << "\""
+                  << "," << std::endl;
+        std::cout << "\"stage\": \"" << tint::cmd::EntryPointStageToString(entry_point.stage)
+                  << "\""
+                  << "," << std::endl;
+
+        if (entry_point.workgroup_size) {
+            std::cout << "\"workgroup_size\": [";
+            std::cout << entry_point.workgroup_size->x << ", " << entry_point.workgroup_size->y
+                      << ", " << entry_point.workgroup_size->z << "]"
+                      << "," << std::endl;
+        }
+
+        std::cout << "\"input_variables\": [";
+        bool input_first = true;
+        for (const auto& var : entry_point.input_variables) {
+            if (!input_first) {
+                std::cout << ",";
+            }
+            input_first = false;
+            stage_var(var);
+        }
+        std::cout << std::endl
+                  << "]"
+                  << "," << std::endl;
+
+        std::cout << "\"output_variables\": [";
+        bool output_first = true;
+        for (const auto& var : entry_point.output_variables) {
+            if (!output_first) {
+                std::cout << ",";
+            }
+            output_first = false;
+            stage_var(var);
+        }
+        std::cout << std::endl
+                  << "]"
+                  << "," << std::endl;
+
+        std::cout << "\"overrides\": [";
+
+        bool override_first = true;
+        for (const auto& var : entry_point.overrides) {
+            if (!override_first) {
+                std::cout << ",";
+            }
+            override_first = false;
+
+            std::cout << std::endl;
+            std::cout << "{" << std::endl;
+            std::cout << "\"name\": \"" << var.name << "\"," << std::endl;
+            std::cout << "\"id\": " << var.id.value << "," << std::endl;
+            std::cout << "\"type\": \"" << tint::cmd::OverrideTypeToString(var.type) << "\","
+                      << std::endl;
+            std::cout << "\"is_initialized\": " << (var.is_initialized ? "true" : "false") << ","
+                      << std::endl;
+            std::cout << "\"is_id_specified\": " << (var.is_id_specified ? "true" : "false")
+                      << std::endl;
+            std::cout << "}";
+        }
+        std::cout << std::endl
+                  << "]"
+                  << "," << std::endl;
+
+        std::cout << "\"bindings\": [";
+        auto bindings = inspector.GetResourceBindings(entry_point.name);
+        bool ep_first = true;
+        for (auto& binding : bindings) {
+            if (!ep_first) {
+                std::cout << ",";
+            }
+            ep_first = false;
+
+            std::cout << std::endl;
+            std::cout << "{" << std::endl;
+            std::cout << "\"binding\": " << binding.binding << "," << std::endl;
+            std::cout << "\"group\": " << binding.bind_group << "," << std::endl;
+            std::cout << "\"size\": " << binding.size << "," << std::endl;
+            std::cout << "\"resource_type\": \""
+                      << tint::cmd::ResourceTypeToString(binding.resource_type) << "\","
+                      << std::endl;
+            std::cout << "\"dimemsions\": \"" << tint::cmd::TextureDimensionToString(binding.dim)
+                      << "\"," << std::endl;
+            std::cout << "\"sampled_kind\": \""
+                      << tint::cmd::SampledKindToString(binding.sampled_kind) << "\"," << std::endl;
+            std::cout << "\"image_format\": \""
+                      << tint::cmd::TexelFormatToString(binding.image_format) << "\"" << std::endl;
+            std::cout << "}";
+        }
+        std::cout << std::endl << "]" << std::endl;
+        std::cout << "}";
+    }
+    std::cout << std::endl << "]," << std::endl;
+    std::cout << "\"structures\": [";
+
+    bool struct_first = true;
+    for (const auto* ty : program->Types()) {
+        if (!ty->Is<tint::type::Struct>()) {
+            continue;
+        }
+        const auto* s = ty->As<tint::type::Struct>();
+
+        if (!struct_first) {
+            std::cout << ",";
+        }
+        struct_first = false;
+
+        std::cout << std::endl;
+        std::cout << "{" << std::endl;
+        std::cout << "\"name\": \"" << s->FriendlyName(program->Symbols()) << "\"," << std::endl;
+        std::cout << "\"align\": " << s->Align() << "," << std::endl;
+        std::cout << "\"size\": " << s->Size() << "," << std::endl;
+        std::cout << "\"members\": [";
+        for (size_t i = 0; i < s->Members().Length(); ++i) {
+            auto* const m = s->Members()[i];
+
+            if (i != 0) {
+                std::cout << ",";
+            }
+            std::cout << std::endl;
+
+            // Output field alignment padding, if any
+            auto* const prev_member = (i == 0) ? nullptr : s->Members()[i - 1];
+            if (prev_member) {
+                uint32_t padding = m->Offset() - (prev_member->Offset() + prev_member->Size());
+                if (padding > 0) {
+                    size_t padding_offset = m->Offset() - padding;
+                    std::cout << "{" << std::endl;
+                    std::cout << "\"name\": \"implicit_padding\"," << std::endl;
+                    std::cout << "\"offset\": " << padding_offset << "," << std::endl;
+                    std::cout << "\"align\": 1," << std::endl;
+                    std::cout << "\"size\": " << padding << std::endl;
+                    std::cout << "}," << std::endl;
+                }
+            }
+
+            std::cout << "{" << std::endl;
+            std::cout << "\"name\": \"" << program->Symbols().NameFor(m->Name()) << "\","
+                      << std::endl;
+            std::cout << "\"offset\": " << m->Offset() << "," << std::endl;
+            std::cout << "\"align\": " << m->Align() << "," << std::endl;
+            std::cout << "\"size\": " << m->Size() << std::endl;
+            std::cout << "}";
+        }
+        std::cout << std::endl << "]" << std::endl;
+        std::cout << "}";
+    }
+    std::cout << std::endl << "]" << std::endl;
+    std::cout << "}" << std::endl;
+}
+
+void EmitText(const tint::Program* program) {
+    tint::inspector::Inspector inspector(program);
+    if (!inspector.GetUsedExtensionNames().empty()) {
+        std::cout << "Extensions:" << std::endl;
+        for (const auto& name : inspector.GetUsedExtensionNames()) {
+            std::cout << "\t" << name << std::endl;
+        }
+    }
+    std::cout << std::endl;
+
+    tint::cmd::PrintInspectorData(inspector);
+
+    bool has_struct = false;
+    for (const auto* ty : program->Types()) {
+        if (!ty->Is<tint::type::Struct>()) {
+            continue;
+        }
+        has_struct = true;
+        break;
+    }
+
+    if (has_struct) {
+        std::cout << "Structures" << std::endl;
+        for (const auto* ty : program->Types()) {
+            if (!ty->Is<tint::type::Struct>()) {
+                continue;
+            }
+            const auto* s = ty->As<tint::type::Struct>();
+            std::cout << s->Layout(program->Symbols()) << std::endl << std::endl;
+        }
+    }
+}
+
 }  // namespace
 
 int main(int argc, const char** argv) {
@@ -102,36 +346,10 @@
         source_file = std::move(info.source_file);
     }
 
-    tint::inspector::Inspector inspector(program.get());
-
-    if (!inspector.GetUsedExtensionNames().empty()) {
-        std::cout << "Extensions:" << std::endl;
-        for (const auto& name : inspector.GetUsedExtensionNames()) {
-            std::cout << "\t" << name << std::endl;
-        }
-    }
-    std::cout << std::endl;
-
-    tint::cmd::PrintInspectorData(inspector);
-
-    bool has_struct = false;
-    for (const auto* ty : program->Types()) {
-        if (!ty->Is<tint::type::Struct>()) {
-            continue;
-        }
-        has_struct = true;
-        break;
-    }
-
-    if (has_struct) {
-        std::cout << "Structures" << std::endl;
-        for (const auto* ty : program->Types()) {
-            if (!ty->Is<tint::type::Struct>()) {
-                continue;
-            }
-            const auto* s = ty->As<tint::type::Struct>();
-            std::cout << s->Layout(program->Symbols()) << std::endl << std::endl;
-        }
+    if (options.emit_json) {
+        EmitJson(program.get());
+    } else {
+        EmitText(program.get());
     }
 
     return 0;