Add Dawn Wire Server LPM Fuzzer [2/N]

Add generators for protobuf files.

This CL contains the basic logic required to generate the protobuf
files from dawn.json and the newly added dawn_lpm.json for
libprotobuf-mutator.

Bug: chromium:1374747
Change-Id: I5dd207ed94ecdac365306c26e79b6cc18d3978f6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/114640
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Brendon Tiszka <tiszka@chromium.org>
diff --git a/generator/dawn_json_generator.py b/generator/dawn_json_generator.py
index 8eb0d6c..705948c 100644
--- a/generator/dawn_json_generator.py
+++ b/generator/dawn_json_generator.py
@@ -566,6 +566,76 @@
 
     return wire_params
 
+############################################################
+# DAWN LPM FUZZ STUFF
+############################################################
+
+
+def compute_lpm_params(api_and_wire_params, lpm_json):
+    # Start with all commands in dawn.json and dawn_wire.json
+    lpm_params = api_and_wire_params.copy()
+
+    # Commands that are built through generation
+    proto_generated_commands = []
+
+    # All commands, including hand written commands that we can't generate
+    # through codegen
+    proto_all_commands = []
+
+    # Remove blocklisted commands from protobuf generation params
+    blocklisted_cmds_proto = lpm_json.get('blocklisted_cmds')
+    custom_cmds_proto = lpm_json.get('custom_cmds')
+    for command in lpm_params['cmd_records']['command']:
+        blocklisted = command.name.get() in blocklisted_cmds_proto
+        custom = command.name.get() in custom_cmds_proto
+
+        if not blocklisted and not custom:
+            proto_generated_commands.append(command)
+        proto_all_commands.append(command)
+
+    lpm_params['cmd_records'] = {
+        'proto_generated_commands': proto_generated_commands,
+        'proto_all_commands': proto_all_commands,
+    }
+
+    return lpm_params
+
+
+def as_protobufTypeLPM(member):
+    assert 'type' in member.json_data
+
+    if member.type.name.native:
+        typ = member.json_data['type']
+        cpp_to_protobuf_type = {
+            "bool": "bool",
+            "float": "float",
+            "double": "double",
+            "int8_t": "int32",
+            "int16_t": "int32",
+            "int32_t": "int32",
+            "int64_t": "int64",
+            "uint8_t": "uint32",
+            "uint16_t": "uint32",
+            "uint32_t": "uint32",
+            "uint64_t": "uint64",
+        }
+
+        assert typ in cpp_to_protobuf_type
+
+        return cpp_to_protobuf_type[typ]
+
+    return member.type.name.CamelCase()
+
+
+def as_protobufNameLPM(*names):
+    # `descriptor` is a reserved keyword in lpm
+    if (names[0].concatcase() == "descriptor"):
+        return "desc"
+    return as_varName(*names)
+
+
+def unreachable_code():
+    assert False
 
 #############################################################
 # Generator
@@ -1039,11 +1109,17 @@
             params_dawn_wire = parse_json(loaded_json,
                                           enabled_tags=['dawn', 'deprecated'],
                                           disabled_tags=['native'])
-            additional_params = compute_wire_params(params_dawn_wire,
-                                                    wire_json)
+            api_and_wire_params = compute_wire_params(params_dawn_wire,
+                                                      wire_json)
+
+            fuzzer_params = compute_lpm_params(api_and_wire_params, lpm_json)
 
             lpm_params = [
-                RENDER_PARAMS_BASE, params_dawn_wire, {}, additional_params
+                RENDER_PARAMS_BASE, params_dawn_wire, {
+                    'as_protobufTypeLPM': as_protobufTypeLPM,
+                    'as_protobufNameLPM': as_protobufNameLPM,
+                    'unreachable': unreachable_code
+                }, api_and_wire_params, fuzzer_params
             ]
 
             renders.append(
@@ -1055,11 +1131,11 @@
             params_dawn_wire = parse_json(loaded_json,
                                           enabled_tags=['dawn', 'deprecated'],
                                           disabled_tags=['native'])
-            additional_params = compute_wire_params(params_dawn_wire,
-                                                    wire_json)
+            api_and_wire_params = compute_wire_params(params_dawn_wire,
+                                                      wire_json)
 
             lpm_params = [
-                RENDER_PARAMS_BASE, params_dawn_wire, {}, additional_params
+                RENDER_PARAMS_BASE, params_dawn_wire, {}, api_and_wire_params
             ]
 
             renders.append(
@@ -1074,6 +1150,12 @@
                     'src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h',
                     lpm_params))
 
+            renders.append(
+                FileRender(
+                    'dawn/fuzzers/lpmfuzz/DawnLPMConstants.h',
+                    'src/dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h',
+                    lpm_params))
+
         return renders
 
     def get_dependencies(self, args):
diff --git a/src/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h b/generator/templates/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h
similarity index 85%
rename from src/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h
rename to generator/templates/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h
index 2d173da..f8cc150 100644
--- a/src/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h
+++ b/generator/templates/dawn/fuzzers/lpmfuzz/DawnLPMConstants.h
@@ -12,4 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#define INSTANCE_OBJECT_ID 1
+namespace DawnLPMFuzzer {
+
+static constexpr int kInstanceObjectId = 1;
+
+} // namespace DawnLPMFuzzer
diff --git a/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto b/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto
index 3c30312..e9ada2e 100644
--- a/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto
+++ b/generator/templates/dawn/fuzzers/lpmfuzz/dawn_lpm.proto
@@ -15,13 +15,134 @@
 syntax = "proto2";
 package fuzzing;
 
-{% for command in cmd_records["command"] %}
-    message {{command.name.CamelCase()}} {}
+
+{% for type in by_category["enum"] %}
+    enum {{as_cppType(type.name)}} {
+        {% for value in type.values %}
+            {{ as_cppType(type.name) }}{{as_cppEnum(value.name)}} = {{ value.value }};
+        {% endfor %}
+    };
 {% endfor %}
 
+
+{% for type in by_category["bitmask"] %}
+    enum {{as_cppType(type.name)}} {
+        {% for value in type.values %}
+            {{ as_cppType(type.name) }}{{as_cppEnum(value.name)}} = {{ value.value }};
+        {% endfor %}
+    };
+{% endfor %}
+
+
+{% macro lift_string_proto_member(member, count) -%}
+    required string {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+    {% set count.value = count.value + 1 %}
+{%- endmacro %}
+
+{% macro lift_float_array_proto_member(member, count) -%}
+    repeated float {{ as_varName(member.name) }} = {{ count.value }};
+    {% set count.value = count.value + 1 %}
+{%- endmacro %}
+
+
+{% macro lift_varlength_proto_member(member, count) -%}
+    {% if member.type in by_category["object"] %}
+        repeated uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type.name.get() == "object id" %}
+        repeated uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type.name.get() == "uint8_t" %}
+        // Skip over byte arrays in protobuf, handled by DawnSerializer
+    {% else %}
+        repeated {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% endif %}
+{%- endmacro %}
+
+
+{% macro lift_dawn_member_pass_by_value(record, name, member, count) %}
+    {% if member.type in by_category["structure"] %}
+        required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type in by_category["bitmask"] %}
+        repeated {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type in by_category["enum"] %}
+        required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type in by_category["object"] %}
+        required uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type.name.get() == "ObjectId" %}
+        required uint32 {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type.name.get() == "ObjectHandle" %}
+        // Skips object handles while lifting dawn.json to protobuf because
+        // ObjectHandles are created and managed in DawnLPMSerializer. Passing
+        // arbitrary ObjectHandles from the fuzzer's bytestream isn't the
+        // strategy for this fuzzer.
+    {% else %}
+        required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% endif %}
+{% endmacro %}
+
+{% macro lift_dawn_member_pass_by_pointer(record, name, member, count) %}
+    {% if member.type in by_category["structure"] and member.length == "constant" and member.constant_length == 1 %}
+        required {{ as_protobufTypeLPM(member) }} {{ as_protobufNameLPM(member.name) }} = {{ count.value }};
+        {% set count.value = count.value + 1 %}
+    {% elif member.type.name.get() == "char" and member.length == 'strlen' %}
+        {{ lift_string_proto_member(member, count) }}
+    {% elif member.type.name.get() == "float" %}
+        {{ lift_float_array_proto_member(member, count) }}
+    {% elif member.type.name.get() == "uint8_t" %}
+        // Skip over byte arrays in protobuf, handled by DawnLPMSerializer
+        // with a hardcoded bytes and length.
+    {% elif member.length != 'constant' %}
+        {{ lift_varlength_proto_member(member, count) }}
+    {% else %}
+        // There shouldn't be any other pass-by-pointer types in
+        // dawn*.json, if any are added we would like to know at compile time
+        {{ unreachable_code }}
+    {% endif %}
+{% endmacro %}
+
+{% macro lift_proto_members_helper(record, name, members) %}
+    {% set count = namespace(value=1) %}
+    {% for member in members %}
+        {% if member.skip_serialize == True %}
+            //  {{ member.name.camelCase()}}.skip_serialize
+        {% elif member.annotation == 'value' %}
+            {{ lift_dawn_member_pass_by_value(record, name, member, count) }}
+        {% elif member.annotation == 'const*' %}
+            {{ lift_dawn_member_pass_by_pointer(record, name, member, count) }}
+        {% endif %}
+    {% endfor %}
+{% endmacro %}
+
+
+{% for structure in by_category["structure"] %}
+    message {{structure.name.CamelCase()}} {
+        {{ lift_proto_members_helper(structure, structure.name, structure.members) }}
+    }
+{% endfor %}
+
+
+{% for command in cmd_records["proto_all_commands"] %}
+    {% if command not in cmd_records["proto_generated_commands"] %}
+        message {{command.name.CamelCase()}} {}
+    {% else %}
+        message {{command.name.CamelCase()}} {
+            {{ lift_proto_members_helper(command, command.name, command.members) }}
+        }
+    {% endif %}
+{% endfor %}
+
+
 message Command {
     oneof command {
-        {% for command in cmd_records["command"] %}
+        {% for command in cmd_records["proto_all_commands"] %}
             {{command.name.CamelCase()}} {{command.name.camelCase()}} = {{ loop.index }};
         {% endfor %}
     }
@@ -29,4 +150,4 @@
 
 message Program {
     repeated Command commands = 1;
-}
\ No newline at end of file
+}
diff --git a/src/dawn/fuzzers/BUILD.gn b/src/dawn/fuzzers/BUILD.gn
index 875a115..161bbc4 100644
--- a/src/dawn/fuzzers/BUILD.gn
+++ b/src/dawn/fuzzers/BUILD.gn
@@ -122,6 +122,7 @@
     outputs = [
       "src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.cpp",
       "src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h",
+      "src/dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h",
     ]
   }
 
diff --git a/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp b/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp
index 3b7533d..930cd5a 100644
--- a/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp
+++ b/src/dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.cpp
@@ -20,7 +20,7 @@
 #include "dawn/common/Log.h"
 #include "dawn/common/SystemUtils.h"
 #include "dawn/dawn_proc.h"
-#include "dawn/fuzzers/lpmfuzz/DawnLPMConstants.h"
+#include "dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h"
 #include "dawn/fuzzers/lpmfuzz/DawnLPMFuzzer.h"
 #include "dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h"
 #include "dawn/fuzzers/lpmfuzz/dawn_lpm_autogen.pb.h"
@@ -68,7 +68,9 @@
 
 }  // namespace
 
-int DawnLPMFuzzer::Initialize(int* argc, char*** argv) {
+namespace DawnLPMFuzzer {
+
+int Initialize(int* argc, char*** argv) {
     // TODO(crbug.com/1038952): The Instance must be static because destructing the vkInstance with
     // Swiftshader crashes libFuzzer. When this is fixed, move this into Run so that error injection
     // for adapter discovery can be fuzzed.
@@ -78,8 +80,7 @@
     return 0;
 }
 
-int DawnLPMFuzzer::Run(const fuzzing::Program& program,
-                       bool (*AdapterSupported)(const dawn::native::Adapter&)) {
+int Run(const fuzzing::Program& program, bool (*AdapterSupported)(const dawn::native::Adapter&)) {
     sAdapterSupported = AdapterSupported;
 
     DawnProcTable procs = dawn::native::GetProcs();
@@ -115,7 +116,7 @@
     serverDesc.serializer = &devNull;
 
     std::unique_ptr<dawn::wire::WireServer> wireServer(new dawn_wire::WireServer(serverDesc));
-    wireServer->InjectInstance(sInstance->Get(), INSTANCE_OBJECT_ID, 0);
+    wireServer->InjectInstance(sInstance->Get(), kInstanceObjectId, 0);
 
     static utils::TerribleCommandBuffer* mCommandBuffer = new utils::TerribleCommandBuffer();
     static dawn::wire::ChunkedCommandSerializer mSerializer =
@@ -131,3 +132,5 @@
     wireServer = nullptr;
     return 0;
 }
+
+}  // namespace DawnLPMFuzzer
diff --git a/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json b/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json
index 992426c..a22ca12 100644
--- a/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json
+++ b/src/dawn/fuzzers/lpmfuzz/dawn_lpm.json
@@ -15,5 +15,18 @@
         "limitations under the License."
     ],
 
-    "_doc": "See docs/dawn/codegen.md"
+    "_doc": "See docs/dawn/codegen.md",
+
+    "custom_cmds": [],
+
+    "blocklisted_cmds": [
+        "surface descriptor from windows core window",
+        "surface descriptor from windows swap chain panel",
+        "surface descriptor from canvas html selector",
+        "device create render pipeline",
+        "device create render pipeline async",
+        "device create shader module",
+        "destroy object"
+    ]
+
 }
\ No newline at end of file