chromium/mojo/public/tools/bindings/generators/mojolpm_templates/mojolpm.cc.tmpl

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "{{module.path}}-mojolpm.h"

#include <functional>

#include "base/functional/bind.h"
#include "mojo/public/cpp/bindings/lib/default_construct_tag_internal.h"

{% for extra_traits_header in all_extra_traits_headers %}
#include "{{extra_traits_header}}"
{%- endfor %}

{%- import "mojolpm_macros.tmpl" as util %}
{%- import "mojolpm_from_proto_macros.tmpl" as from_proto %}
{%- import "mojolpm_to_proto_macros.tmpl" as to_proto %}
{%- import "mojolpm_traits_specialization_macros.tmpl" as traits_specialization %}

namespace mojo {
{%- for struct in structs %}
{{- traits_specialization.define_struct(struct) }}
{%- endfor %}

{%- for union in unions %}
{{ traits_specialization.define_union(union) }}
{%- endfor %}
} // namespace mojo

namespace mojolpm {
{%- for enum in all_enums %}
{{- from_proto.define_enum(enum) }}
{{- to_proto.define_enum(enum) }}
{%- endfor %}

{%- for struct in structs %}
{%-   set proto_type = "::mojolpm" ~ (struct|get_qualified_name_for_kind(flatten_nested_kind=True)) %}
{%-   set struct_type = proto_type ~ "_ProtoStruct" %}
{%-   for field in struct.fields %}
{%-       set name = field.name|camel_to_under %}
{%-       set kind = field.kind %}
{%-       if kind|is_array_kind or kind|is_map_kind %}
{{- from_proto.define(struct_type, kind, name) }}
{{- to_proto.define(struct_type, kind, name) }}
{%-       endif %}
{%-   endfor %}
{{- from_proto.define_struct(struct) }}
{{- to_proto.define_struct(struct) }}
{%- endfor %}

{%- for union in unions %}
{%-   set proto_type = "::mojolpm" ~ (union|get_qualified_name_for_kind(flatten_nested_kind=True)) %}
{%-   set union_type = proto_type ~ "_ProtoUnion" %}
{%-     for field in union.fields %}
{%-       set name = field.name|camel_to_under %}
{%-       set kind = field.kind %}
{%-       if kind|is_array_kind or kind|is_map_kind %}
{{- from_proto.define(union_type, kind, name)}}
{{- to_proto.define(union_type, kind, name)}}
{%-       endif %}
{%-     endfor %}
{{- from_proto.define_union(union) }}
{{- to_proto.define_union(union) }}
{%- endfor %}

{%- for interface in interfaces %}
{%-   set mojom_type = interface|get_qualified_name_for_kind(flatten_nested_kind=True) %}
{%-   set proto_type = "::mojolpm" ~ (interface|get_qualified_name_for_kind(flatten_nested_kind=True)) %}
class {{interface.name}}Impl : public {{mojom_type}} {
 public:
  {{interface.name}}Impl() {
  }

{%-   for method in interface.methods -%}{{"\n"}}
  void {{method.name}}({{ "\n" }}
{%-     for param in method.parameters -%}
{%-       set name = param.name|camel_to_under -%}
{%-       set kind = param.kind -%}
{%-       set param_mojom_type = kind|cpp_wrapper_param_type(add_same_module_namespaces=true) -%}
{{ ",\n" if not loop.first }}      {{param_mojom_type}} {{name}}
{%-     endfor -%}
{%-     if method.response_parameters != None -%}
{{ ",\n" if method.parameters }}    {{mojom_type}}::{{method.name}}Callback callback
{%-     endif -%}
) override {
{%-     for param in method.parameters -%}
{%-       set name = param.name|camel_to_under -%}
{%-       set kind = param.kind -%}
{{ util.add_instance(kind, name, False)|indent(2, True) }}
{%-     endfor %}
    mojolpmdbg("{{interface.name}}Impl.{{method.name}}\n");
{%-       if method.response_parameters != None %}
    mojolpm::GetContext()->AddInstance<{{mojom_type}}::{{method.name}}Callback>(std::move(callback));
{%-       endif %}
  }
{%-   endfor %}
};

bool FromProto(uint32_t input,
               ::mojo::PendingRemote<{{mojom_type}}>& output) {
  bool result = false;
  ::mojo::Remote<{{mojom_type}}>* output_ptr = nullptr;

  if (input) {
    output_ptr = mojolpm::GetContext()->GetInstance<::mojo::Remote<{{mojom_type}}>>(input);
    if (output_ptr) {
      // TODO(markbrand): look for a cleaner way to handle this check.
      if (!output_ptr->is_bound() || (output_ptr->internal_state()
          && output_ptr->internal_state()->has_pending_callbacks())) {
        // not safe to Unbind, so fail instead.
        output_ptr = nullptr;
      } else {
        output = output_ptr->Unbind();
        result = true;
      }
    }
  } else {
    auto impl = std::make_unique<{{interface.name}}Impl>();
    auto receiver_ptr =
      std::make_unique<::mojo::Receiver<{{mojom_type}}>>(
        impl.get(), output.InitWithNewPipeAndPassReceiver());
    mojolpm::GetContext()->AddInstance(std::move(impl));
    mojolpm::GetContext()->AddInstance(std::move(receiver_ptr));
    result = true;
  }

  return result;
}

bool ToProto(::mojo::PendingRemote<{{mojom_type}}>&& input,
             uint32_t& output) {
  ::mojo::Remote<{{mojom_type}}> remote(std::move(input));
  int next_id = NextId<{{mojom_type}}>();
  output = mojolpm::GetContext()->AddInstance(next_id, std::move(remote));
  return true;
}

bool FromProto(uint32_t input,
               ::mojo::PendingReceiver<{{mojom_type}}>& output) {
  ::mojo::Remote<{{mojom_type}}> remote = ::mojo::Remote<{{mojom_type}}>();
  output = remote.BindNewPipeAndPassReceiver();
  mojolpm::GetContext()->AddInstance(input, std::move(remote));
  return true;
}

bool ToProto(::mojo::PendingReceiver<{{mojom_type}}>&& input,
             uint32_t& output) {
  // This should only get called from callbacks into the fuzzer, ie from one of
  // the XxxImpls or from a return callback. Since that is the case, we want to
  // bind the receiver and store it.

  auto impl = std::make_unique<{{interface.name}}Impl>();
  auto receiver_ptr = std::make_unique<::mojo::Receiver<{{mojom_type}}>>(
    impl.get(), std::move(input));
  mojolpm::GetContext()->AddInstance(std::move(impl));
  output = mojolpm::GetContext()->AddInstance(std::move(receiver_ptr));
  return true;
}

bool FromProto(uint32_t input,
               ::mojo::PendingAssociatedRemote<{{mojom_type}}>& output) {
  mojolpmdbg("PendingAssociatedRemote {{interface.name}}\n");
  bool result = false;
  ::mojo::AssociatedRemote<{{mojom_type}}>* output_ptr;

  if (input) {
    output_ptr = mojolpm::GetContext()->GetInstance<::mojo::AssociatedRemote<{{mojom_type}}>>(input);
    if (output_ptr) {
      // TODO(markbrand): look for a cleaner way to handle this check.
      if (!output_ptr->is_bound() || (output_ptr->internal_state()
          && output_ptr->internal_state()->has_pending_callbacks())) {
        // not safe to Unbind, so fail instead.
        output_ptr = nullptr;
      } else {
        output = output_ptr->Unbind();
        result = true;
      }
    }
  } else {
    auto impl = std::make_unique<{{interface.name}}Impl>();
    auto receiver_ptr =
      std::make_unique<::mojo::AssociatedReceiver<{{mojom_type}}>>(
        impl.get(), output.InitWithNewEndpointAndPassReceiver());
    mojolpm::GetContext()->AddInstance(std::move(impl));
    mojolpm::GetContext()->AddInstance(std::move(receiver_ptr));
    result = true;
  }

  return result;
}

bool ToProto(::mojo::PendingAssociatedRemote<{{mojom_type}}>&& input,
             uint32_t& output) {
  ::mojo::AssociatedRemote<{{mojom_type}}> remote(std::move(input));
  int next_id = NextId<{{mojom_type}}>();
  output = mojolpm::GetContext()->AddInstance(next_id, std::move(remote));
  return true;
}

bool FromProto(uint32_t input,
               ::mojo::PendingAssociatedReceiver<{{mojom_type}}>& output) {
  ::mojo::AssociatedRemote<{{mojom_type}}> remote = ::mojo::AssociatedRemote<{{mojom_type}}>();
  output = remote.BindNewEndpointAndPassReceiver();
  mojolpm::GetContext()->AddInstance(input, std::move(remote));
  return true;
}

bool ToProto(::mojo::PendingAssociatedReceiver<{{mojom_type}}>&& input,
             uint32_t& output) {
  // This should only get called from callbacks into the fuzzer, ie from one of
  // the XxxImpls or from a return callback. Since that is the case, we want to
  // bind the receiver and store it.

  auto impl = std::make_unique<{{interface.name}}Impl>();
  auto receiver_ptr = std::make_unique<::mojo::AssociatedReceiver<{{mojom_type}}>>(
    impl.get(), std::move(input));
  mojolpm::GetContext()->AddInstance(std::move(impl));
  output = mojolpm::GetContext()->AddInstance(std::move(receiver_ptr));
  return true;
}{{"\n"-}}

{%-   for method in interface.methods %}
{%-     set method_type = proto_type ~ "::" ~ interface.name ~ "_" ~ method.name %}
{%-     for param in method.parameters %}
{%-       set name = param.name|camel_to_under %}
{%-       set kind = param.kind %}
{%-       if kind|is_array_kind or kind|is_map_kind -%}
{{ from_proto.define(method_type, kind, name) }}
{{ to_proto.define(method_type, kind, name) }}
{%-       endif %}
{%-     endfor %}
{%-   endfor %}
{%-   for method in interface.methods %}
{%-     if method.response_parameters != None %}
{%-       set method_type = proto_type ~ "::" ~ interface.name ~ "_" ~ method.name ~ "Response" %}
{%-       for param in method.response_parameters %}
{%-         set name = param.name %}
{%-         set kind = param.kind %}
{%-         if kind|is_array_kind or kind|is_map_kind -%}
{{- from_proto.define(method_type, kind, name)}}
{{- to_proto.define(method_type, kind, name)}}
{%-         endif %}
{%-       endfor %}
{%-     endif %}
{%-   endfor %}
{%- endfor %}

{%- for interface in interfaces %}
{%-   set mojom_type = interface|get_qualified_name_for_kind(flatten_nested_kind=True) %}
{%-   set proto_type = "::mojolpm" ~ (interface|get_qualified_name_for_kind(flatten_nested_kind=True)) %}
{%-   if interface.methods %}
bool HandleRemoteAction(const {{proto_type}}::RemoteAction& input) {
  bool result = true;

  switch (input.method_case()) {
{%-     for method in interface.methods %}
    case {{proto_type}}::RemoteAction::k{{("m_" ~ method.name)|under_to_camel}}: {
      result = HandleRemoteCall(input.id(), input.{{("m" ~ method.name)|camel_to_under}}());
    } break;
{%-     endfor %}
    case {{proto_type}}::RemoteAction::kReset: {
      mojolpm::GetContext()->GetAndRemoveInstance<::mojo::Remote<{{mojom_type}}>>(input.id());
    } break;

    default: {
      result = false;
    }
  }

  return result;
}

bool HandleAssociatedRemoteAction(const {{proto_type}}::AssociatedRemoteAction& input) {
  bool result = true;

  switch (input.method_case()) {
{%-     for method in interface.methods %}
    case {{proto_type}}::AssociatedRemoteAction::k{{("m_" ~ method.name)|under_to_camel}}: {
      result = HandleAssociatedRemoteCall(input.id(), input.{{("m" ~ method.name)|camel_to_under}}());
    } break;
{%-     endfor %}
    case {{proto_type}}::AssociatedRemoteAction::kReset: {
      mojolpm::GetContext()->GetAndRemoveInstance<::mojo::AssociatedRemote<{{mojom_type}}>>(input.id());
    } break;

    default: {
      result = false;
    }
  }

  return result;
}

bool HandleReceiverAction(
    const {{proto_type}}::ReceiverAction& input) {
  bool result = true;
  switch (input.response_case()) {
{%-     for method in interface.methods %}
    case {{proto_type}}::ReceiverAction::k{{("m_" ~ method.name ~ "_response")|under_to_camel}}: {
      result = HandleResponse(input.id(), input.{{("m" ~ method.name ~ "_response")|camel_to_under}}());
    } break;
{%-     endfor %}

    default: {
      result = false;
    }
  }

  return result;
}{{"\n"-}}
{%-     for method in interface.methods %}
{%-       if method.response_parameters != None %}
static void {{interface.name}}_{{method.name}}Callback(
{%-         for param in method.response_parameters %}
{%-           set name = param.name|camel_to_under %}
{%-           set kind = param.kind %}
{%-           set param_mojom_type = kind|cpp_wrapper_param_type(add_same_module_namespaces=true) %}{{ ',' if not loop.first }}
  {{param_mojom_type}} param_{{name}}
{%-         endfor -%}
) {
{%-         for param in method.response_parameters %}
{%-           set name = param.name|camel_to_under %}
{%-           set kind = param.kind %}
{{ util.add_instance(kind, 'param_' ~ name, False) }}
{%-         endfor %}
  mojolpmdbg("{{interface.name}}.{{method.name}}Callback\n");
}{{"\n"-}}
{%-       endif %}
template <typename T>
bool HandleCall(uint32_t instance_id,
                      const {{proto_type}}::{{interface.name}}_{{method.name}}& input) {
  T* instance =
    mojolpm::GetContext()->GetInstance<T>(instance_id);
  if (!instance || !instance->is_bound() || !instance->is_connected()) {
    return false;
  }

  mojolpm::GetContext()->StartDeserialization();

  bool mojolpm_result = true;
{%-       for param in method.parameters %}
{%-         set name = param.name|camel_to_under %}
{%-         set kind = param.kind %}
{%-         set param_mojom_type = kind|cpp_wrapper_type(add_same_module_namespaces=true) %}
  {{param_mojom_type}} local_{{name}}{ {{- kind|default_constructor_args -}} };
{%-         if kind|is_nullable_kind %}
{%-           set unnullable_kind = kind|to_unnullable_kind %}
{%-           set param_maybe_mojom_type = unnullable_kind|cpp_wrapper_type(add_same_module_namespaces=true) %}
  {{param_maybe_mojom_type}} local_maybe_{{name}}{ {{- unnullable_kind|default_constructor_args -}} };
{%-         endif %}
{%-       endfor %}

{%-       for param in method.parameters -%}
{%-         set name = param.name|camel_to_under %}
{%-         set kind = param.kind %}
{%-         if not kind|is_nullable_kind %}
  mojolpm_result &= FromProto(input.m_{{name}}(), local_{{name}});
{%-         else %}
  if (FromProto(input.m_{{name}}(), local_maybe_{{name}})) {
    local_{{name}} = std::move(local_maybe_{{name}});
  }
{%-         endif %}
{%-       endfor %}
  if (mojolpm_result) {
    (*instance)->{{method.name}}(
{%-       for param in method.parameters -%}
{%-         set name = param.name|camel_to_under %}
{%-         set kind = param.kind %}
{%- if kind|is_non_const_ref_kind %}
      local_{{name}}{{ ',' if not loop.last }}
{%- else %}
      std::move(local_{{name}}){{ ',' if not loop.last }}
{% endif %}
{%-       endfor -%}
{%-       if method.response_parameters != None -%}
{{ ',' if method.parameters }}
      base::BindOnce(&{{interface.name}}_{{method.name}}Callback));
{%-       else -%}
);
{%-       endif -%}
    mojolpm::GetContext()->EndDeserialization(Context::Rollback::kNoRollback);
  } else {
    mojolpm::GetContext()->EndDeserialization(Context::Rollback::kRollback);
    mojolpmdbg("call failed\n");
  }

  return mojolpm_result;
}

bool HandleRemoteCall(
    uint32_t instance_id, const {{proto_type}}::{{interface.name}}_{{method.name}}& input) {
  mojolpmdbg("HandleRemoteCall({{interface.name}}::{{method.name}})\n");
  return HandleCall<::mojo::Remote<{{mojom_type}}>>(instance_id, input);
}

bool HandleAssociatedRemoteCall(
    uint32_t instance_id, const {{proto_type}}::{{interface.name}}_{{method.name}}& input) {
  mojolpmdbg("HandleAssociatedRemoteCall({{interface.name}}::{{method.name}})\n");
  return HandleCall<::mojo::AssociatedRemote<{{mojom_type}}>>(instance_id, input);
}

bool HandleResponse(
    uint32_t callback_id, const {{proto_type}}::{{interface.name}}_{{method.name}}Response& input) {
  mojolpmdbg("HandleResponse({{interface.name}}::{{method.name}})\n");
{%-       if method.response_parameters == None %}
  return true;
{%-     else %}
  auto mojolpm_callback = mojolpm::GetContext()->GetAndRemoveInstance<
    {{mojom_type}}::{{method.name}}Callback>(callback_id);

  if (!mojolpm_callback) {
    return true;
  }

  mojolpm::GetContext()->StartDeserialization();

  bool mojolpm_result = true;
{%-         for param in method.response_parameters %}
{%-           set name = param.name|camel_to_under %}
{%-           set kind = param.kind %}
{%-           set param_mojom_type = kind|cpp_wrapper_type(add_same_module_namespaces=true) %}
  {{param_mojom_type}} local_{{name}}{ {{- kind|default_constructor_args -}} };
{%-           if kind|is_nullable_kind %}
{%-           set unnullable_kind = kind|to_unnullable_kind %}
{%-           set param_maybe_mojom_type = unnullable_kind|cpp_wrapper_type(add_same_module_namespaces=true) %}
  {{param_maybe_mojom_type}} local_maybe_{{name}}{ {{- unnullable_kind|default_constructor_args -}} };
{%-           endif %}
{%-         endfor %}

{%-         for param in method.response_parameters -%}
{%-           set name = param.name|camel_to_under %}
{%-           set kind = param.kind %}
{%-           if not kind|is_nullable_kind %}
  mojolpm_result &= FromProto(input.m_{{name}}(), local_{{name}});
  mojolpmdbg("{{name}} %i\n", mojolpm_result);
{%-           else %}
  if (FromProto(input.m_{{name}}(), local_maybe_{{name}})) {
    local_{{name}} = std::move(local_maybe_{{name}});
  }
{%-           endif %}
{%-         endfor %}
  if (mojolpm_result) {
    std::move(*mojolpm_callback).Run(
{%-         for param in method.response_parameters -%}
{%-           set name = param.name|camel_to_under %}
{%-           set kind = param.kind %}
{%-           if kind|is_interface_kind or kind|is_associated_kind %}
      {{kind|cpp_wrapper_param_type(add_same_module_namespaces=true)}}(std::move(local_{{name}})){{ ',' if not loop.last }}
{%-           else %}
      std::move(local_{{name}}){{ ',' if not loop.last }}
{%-           endif %}
{%-         endfor -%}
);
    mojolpm::GetContext()->EndDeserialization(Context::Rollback::kNoRollback);
  } else {
    mojolpm::GetContext()->EndDeserialization(Context::Rollback::kRollback);
    mojolpm::GetContext()->AddInstance<
      {{mojom_type}}::{{method.name}}Callback>(callback_id, std::move(*mojolpm_callback));
  }

  return mojolpm_result;
{%-       endif %}
}{{"\n"-}}
{%-     endfor %}
{%-   endif %}
{%- endfor -%}
}  // namespace mojolpm