// 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