diff options
Diffstat (limited to 'mojo/public/tools/bindings/generators/cpp_templates')
36 files changed, 2894 insertions, 0 deletions
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl new file mode 100644 index 0000000000..f0d503e5e0 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl @@ -0,0 +1,131 @@ +{#--- + Macro for enum definition, and the declaration of associated functions. +---#} + +{%- macro enum_decl(enum) %} +{%- set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %} +enum class {{enum_name}} : int32_t { +{%- for field in enum.fields %} +{%- if field.value %} + {{field.name}} = {{field.value|expression_to_text}}, +{%- else %} + {{field.name}}, +{%- endif %} +{%- endfor %} +}; + +inline std::ostream& operator<<(std::ostream& os, {{enum_name}} value) { +{%- if enum.fields %} + switch(value) { +{%- for _, values in enum.fields|groupby('numeric_value') %} + case {{enum_name}}::{{values[0].name}}: + return os << "{{enum_name}}:: +{%- if values|length > 1 -%} + {{'{'}} +{%- endif -%} + {{values|map(attribute='name')|join(', ')}} +{%- if values|length > 1 -%} + {{'}'}} +{%- endif -%} + "; +{%- endfor %} + default: + return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value); + } +{%- else %} + return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value); +{%- endif %} +} + +{#- Returns true if the given enum value exists in this version of enum. #} +inline bool IsKnownEnumValue({{enum_name}} value) { + return {{enum|get_name_for_kind(internal=True, + flatten_nested_kind=True)}}::IsKnownValue( + static_cast<int32_t>(value)); +} +{%- endmacro %} + +{%- macro enum_data_decl(enum) %} +{%- set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %} +struct {{enum_name}}_Data { + public: + static bool constexpr kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %}; + + static bool IsKnownValue(int32_t value) { +{%- if enum.fields %} + switch (value) { +{%- for enum_field in enum.fields|groupby('numeric_value') %} + case {{enum_field[0]}}: +{%- endfor %} + return true; + } +{%- endif %} + return false; + } + + static bool Validate(int32_t value, + mojo::internal::ValidationContext* validation_context) { + if (kIsExtensible || IsKnownValue(value)) + return true; + + ReportValidationError(validation_context, + mojo::internal::VALIDATION_ERROR_UNKNOWN_ENUM_VALUE); + return false; + } +}; +{%- endmacro %} + +{%- macro enum_hash(enum) %} +{%- set enum_name = enum|get_qualified_name_for_kind( + flatten_nested_kind=True) %} +template <> +struct hash<{{enum_name}}> + : public mojo::internal::EnumHashImpl<{{enum_name}}> {}; +{%- endmacro %} + +{%- macro enum_hash_blink(enum) %} +{%- set enum_name = enum|get_qualified_name_for_kind( + flatten_nested_kind=True, include_variant=False) %} +{%- set hash_fn_name = enum|wtf_hash_fn_name_for_enum %} +{# We need two unused enum values: #} +{%- set empty_value = -1000000 %} +{%- set deleted_value = -1000001 %} +{%- set empty_value_unused = "false" if empty_value in enum|all_enum_values else "true" %} +{%- set deleted_value_unused = "false" if empty_value in enum|all_enum_values else "true" %} +namespace WTF { +struct {{hash_fn_name}} { + static unsigned hash(const {{enum_name}}& value) { + typedef base::underlying_type<{{enum_name}}>::type utype; + return DefaultHash<utype>::Hash().hash(static_cast<utype>(value)); + } + static bool equal(const {{enum_name}}& left, const {{enum_name}}& right) { + return left == right; + } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +template <> +struct DefaultHash<{{enum_name}}> { + using Hash = {{hash_fn_name}}; +}; + +template <> +struct HashTraits<{{enum_name}}> + : public GenericHashTraits<{{enum_name}}> { + static_assert({{empty_value_unused}}, + "{{empty_value}} is a reserved enum value"); + static_assert({{deleted_value_unused}}, + "{{deleted_value}} is a reserved enum value"); + static const bool hasIsEmptyValueFunction = true; + static bool isEmptyValue(const {{enum_name}}& value) { + return value == static_cast<{{enum_name}}>({{empty_value}}); + } + static void constructDeletedValue({{enum_name}}& slot, bool) { + slot = static_cast<{{enum_name}}>({{deleted_value}}); + } + static bool isDeletedValue(const {{enum_name}}& value) { + return value == static_cast<{{enum_name}}>({{deleted_value}}); + } +}; +} // namespace WTF +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl new file mode 100644 index 0000000000..d7d0e5d873 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl @@ -0,0 +1,29 @@ +{%- set mojom_type = enum|get_qualified_name_for_kind( + flatten_nested_kind=True) %} + +template <> +struct EnumTraits<{{mojom_type}}, {{mojom_type}}> { + static {{mojom_type}} ToMojom({{mojom_type}} input) { return input; } + static bool FromMojom({{mojom_type}} input, {{mojom_type}}* output) { + *output = input; + return true; + } +}; + +namespace internal { + +template <typename MaybeConstUserType> +struct Serializer<{{mojom_type}}, MaybeConstUserType> { + using UserType = typename std::remove_const<MaybeConstUserType>::type; + using Traits = EnumTraits<{{mojom_type}}, UserType>; + + static void Serialize(UserType input, int32_t* output) { + *output = static_cast<int32_t>(Traits::ToMojom(input)); + } + + static bool Deserialize(int32_t input, UserType* output) { + return Traits::FromMojom(static_cast<{{mojom_type}}>(input), output); + } +}; + +} // namespace internal diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl new file mode 100644 index 0000000000..7f6497475a --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl @@ -0,0 +1,65 @@ +{%- import "interface_macros.tmpl" as interface_macros %} +class {{interface.name}}Proxy; + +template <typename ImplRefTraits> +class {{interface.name}}Stub; + +class {{interface.name}}RequestValidator; +{%- if interface|has_callbacks %} +class {{interface.name}}ResponseValidator; +{%- endif %} + +class {{export_attribute}} {{interface.name}} + : public {{interface.name}}InterfaceBase { + public: + static const char Name_[]; + static constexpr uint32_t Version_ = {{interface.version}}; + static constexpr bool PassesAssociatedKinds_ = {% if interface|passes_associated_kinds %}true{% else %}false{% endif %}; + static constexpr bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %}; + + using Proxy_ = {{interface.name}}Proxy; + + template <typename ImplRefTraits> + using Stub_ = {{interface.name}}Stub<ImplRefTraits>; + + using RequestValidator_ = {{interface.name}}RequestValidator; +{%- if interface|has_callbacks %} + using ResponseValidator_ = {{interface.name}}ResponseValidator; +{%- else %} + using ResponseValidator_ = mojo::PassThroughFilter; +{%- endif %} + +{#--- Metadata #} + enum MethodMinVersions : uint32_t { +{%- for method in interface.methods %} + k{{method.name}}MinVersion = {{method.min_version|default(0, true)}}, +{%- endfor %} + }; + +{#--- Enums #} +{%- for enum in interface.enums %} + using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}}; +{%- endfor %} + +{#--- Constants #} +{%- for constant in interface.constants %} + static {{constant|format_constant_declaration(nested=True)}}; +{%- endfor %} + +{#--- Methods #} + virtual ~{{interface.name}}() {} + +{%- for method in interface.methods %} +{% if method.response_parameters != None %} +{%- if method.sync %} + // Sync method. This signature is used by the client side; the service side + // should implement the signature with callback below. + virtual bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}}); +{%- endif %} + + using {{method.name}}Callback = {{interface_macros.declare_callback(method, + for_blink, use_once_callback)}}; +{%- endif %} + virtual void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) = 0; +{%- endfor %} +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl new file mode 100644 index 0000000000..aba18380af --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -0,0 +1,448 @@ +{%- import "interface_macros.tmpl" as interface_macros %} +{%- import "struct_macros.tmpl" as struct_macros %} + +{%- set class_name = interface.name %} +{%- set proxy_name = interface.name ~ "Proxy" %} +{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %} + +{%- macro alloc_params(struct, params, message, description) %} + mojo::internal::SerializationContext serialization_context; + serialization_context.handles.Swap(({{message}})->mutable_handles()); + serialization_context.associated_endpoint_handles.swap( + *({{message}})->mutable_associated_endpoint_handles()); + bool success = true; +{%- for param in struct.packed.packed_fields_in_ordinal_order %} + {{param.field.kind|cpp_wrapper_type}} p_{{param.field.name}}{}; +{%- endfor %} + {{struct.name}}DataView input_data_view({{params}}, &serialization_context); + {{struct_macros.deserialize(struct, "input_data_view", "p_%s", "success")}} + if (!success) { + ReportValidationErrorForMessage( + {{message}}, + mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED, + "{{description}} deserializer"); + return false; + } +{%- endmacro %} + +{%- macro pass_params(parameters) %} +{%- for param in parameters %} +std::move(p_{{param.name}}) +{%- if not loop.last %}, {% endif %} +{%- endfor %} +{%- endmacro %} + +{%- macro build_message(struct, input_pattern, struct_display_name, + serialization_context) -%} + {{struct_macros.serialize(struct, struct_display_name, input_pattern, + "params", "builder.buffer()", + serialization_context)}} + ({{serialization_context}})->handles.Swap( + builder.message()->mutable_handles()); + ({{serialization_context}})->associated_endpoint_handles.swap( + *builder.message()->mutable_associated_endpoint_handles()); +{%- endmacro %} + +{#--- Begin #} +const char {{class_name}}::Name_[] = "{{namespace_as_string}}::{{class_name}}"; + +{#--- Constants #} +{%- for constant in interface.constants %} +{%- if constant.kind|is_string_kind %} +const char {{interface.name}}::{{constant.name}}[] = {{constant|constant_value}}; +{%- endif %} +{%- endfor %} + + +{%- for method in interface.methods %} +{%- if method.sync %} +bool {{class_name}}::{{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) { + NOTREACHED(); + return false; +} +{%- endif %} +{%- endfor %} + +{#--- ForwardToCallback definition #} +{%- for method in interface.methods -%} +{%- if method.response_parameters != None %} +{%- if method.sync %} +class {{class_name}}_{{method.name}}_HandleSyncResponse + : public mojo::MessageReceiver { + public: + {{class_name}}_{{method.name}}_HandleSyncResponse( + bool* result +{%- for param in method.response_parameters -%} + , {{param.kind|cpp_wrapper_type}}* out_{{param.name}} +{%- endfor %}) + : result_(result) +{%- for param in method.response_parameters -%} + , out_{{param.name}}_(out_{{param.name}}) +{%- endfor %} { + DCHECK(!*result_); + } + bool Accept(mojo::Message* message) override; + private: + bool* result_; +{%- for param in method.response_parameters %} + {{param.kind|cpp_wrapper_type}}* out_{{param.name}}_; +{%- endfor -%} + DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_HandleSyncResponse); +}; +bool {{class_name}}_{{method.name}}_HandleSyncResponse::Accept( + mojo::Message* message) { + internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params = + reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>( + message->mutable_payload()); + +{%- set desc = class_name~"::"~method.name~" response" %} + {{alloc_params(method.response_param_struct, "params", "message", desc)}} + +{%- for param in method.response_parameters %} + *out_{{param.name}}_ = std::move(p_{{param.name}}); +{%- endfor %} + mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage( + message); + *result_ = true; + return true; +} +{%- endif %} + +class {{class_name}}_{{method.name}}_ForwardToCallback + : public mojo::MessageReceiver { + public: + {{class_name}}_{{method.name}}_ForwardToCallback( +{%- if use_once_callback %} + {{class_name}}::{{method.name}}Callback callback +{%- else %} + const {{class_name}}::{{method.name}}Callback& callback +{%- endif %} + ) : callback_(std::move(callback)) { + } + bool Accept(mojo::Message* message) override; + private: + {{class_name}}::{{method.name}}Callback callback_; + DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback); +}; +bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( + mojo::Message* message) { + internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params = + reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>( + message->mutable_payload()); + +{%- set desc = class_name~"::"~method.name~" response" %} + {{alloc_params(method.response_param_struct, "params", "message", desc)}} + if (!callback_.is_null()) { + mojo::internal::MessageDispatchContext context(message); + std::move(callback_).Run({{pass_params(method.response_parameters)}}); + } + return true; +} +{%- endif %} +{%- endfor %} + +{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver) + : receiver_(receiver) { +} + +{#--- Proxy definitions #} + +{%- for method in interface.methods %} +{%- set message_name = + "internal::k%s_%s_Name"|format(interface.name, method.name) %} +{%- set params_struct = method.param_struct %} +{%- set params_description = + "%s.%s request"|format(interface.name, method.name) %} +{%- if method.sync %} +bool {{proxy_name}}::{{method.name}}( + {{interface_macros.declare_sync_method_params("param_", method)}}) { + mojo::internal::SerializationContext serialization_context; + {{struct_macros.get_serialized_size(params_struct, "param_%s", + "&serialization_context")}} + + mojo::internal::MessageBuilder builder( + {{message_name}}, + mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse, + size, serialization_context.associated_endpoint_count); + + {{build_message(params_struct, "param_%s", params_description, + "&serialization_context")}} + + bool result = false; + std::unique_ptr<mojo::MessageReceiver> responder( + new {{class_name}}_{{method.name}}_HandleSyncResponse( + &result +{%- for param in method.response_parameters -%} + , param_{{param.name}} +{%- endfor %})); + ignore_result(receiver_->AcceptWithResponder(builder.message(), + std::move(responder))); + return result; +} +{%- endif %} + +void {{proxy_name}}::{{method.name}}( + {{interface_macros.declare_request_params("in_", method, use_once_callback)}}) { + mojo::internal::SerializationContext serialization_context; + {{struct_macros.get_serialized_size(params_struct, "in_%s", + "&serialization_context")}} + +{%- if method.response_parameters != None %} + constexpr uint32_t kFlags = mojo::Message::kFlagExpectsResponse; +{%- else %} + constexpr uint32_t kFlags = 0; +{%- endif %} + mojo::internal::MessageBuilder builder( + {{message_name}}, kFlags, size, + serialization_context.associated_endpoint_count); + + {{build_message(params_struct, "in_%s", params_description, + "&serialization_context")}} + +{%- if method.response_parameters != None %} + std::unique_ptr<mojo::MessageReceiver> responder( + new {{class_name}}_{{method.name}}_ForwardToCallback( + std::move(callback))); + ignore_result(receiver_->AcceptWithResponder(builder.message(), + std::move(responder))); +{%- else %} + // This return value may be ignored as false implies the Connector has + // encountered an error, which will be visible through other means. + ignore_result(receiver_->Accept(builder.message())); +{%- endif %} +} +{%- endfor %} + +{#--- ProxyToResponder definition #} +{%- for method in interface.methods -%} +{%- if method.response_parameters != None %} +{%- set message_name = + "internal::k%s_%s_Name"|format(interface.name, method.name) %} +{%- set response_params_struct = method.response_param_struct %} +{%- set params_description = + "%s.%s response"|format(interface.name, method.name) %} +class {{class_name}}_{{method.name}}_ProxyToResponder { + public: + static {{class_name}}::{{method.name}}Callback CreateCallback( + uint64_t request_id, + bool is_sync, + std::unique_ptr<mojo::MessageReceiverWithStatus> responder) { + std::unique_ptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy( + new {{class_name}}_{{method.name}}_ProxyToResponder( + request_id, is_sync, std::move(responder))); + return base::Bind(&{{class_name}}_{{method.name}}_ProxyToResponder::Run, + base::Passed(&proxy)); + } + + ~{{class_name}}_{{method.name}}_ProxyToResponder() { +#if DCHECK_IS_ON() + if (responder_) { + // Is the Service destroying the callback without running it + // and without first closing the pipe? + responder_->DCheckInvalid("The callback passed to " + "{{class_name}}::{{method.name}}() was never run."); + } +#endif + // If the Callback was dropped then deleting the responder will close + // the pipe so the calling application knows to stop waiting for a reply. + responder_ = nullptr; + } + + private: + {{class_name}}_{{method.name}}_ProxyToResponder( + uint64_t request_id, + bool is_sync, + std::unique_ptr<mojo::MessageReceiverWithStatus> responder) + : request_id_(request_id), + is_sync_(is_sync), + responder_(std::move(responder)) { + } + + void Run( + {{interface_macros.declare_responder_params( + "in_", method.response_parameters, for_blink)}}); + + uint64_t request_id_; + bool is_sync_; + std::unique_ptr<mojo::MessageReceiverWithStatus> responder_; + + DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder); +}; + +void {{class_name}}_{{method.name}}_ProxyToResponder::Run( + {{interface_macros.declare_responder_params( + "in_", method.response_parameters, for_blink)}}) { + mojo::internal::SerializationContext serialization_context; + {{struct_macros.get_serialized_size(response_params_struct, "in_%s", + "&serialization_context")}} + + uint32_t flags = (is_sync_ ? mojo::Message::kFlagIsSync : 0) | + mojo::Message::kFlagIsResponse; + mojo::internal::MessageBuilder builder( + {{message_name}}, flags, size, + serialization_context.associated_endpoint_count); + builder.message()->set_request_id(request_id_); + + {{build_message(response_params_struct, "in_%s", params_description, + "&serialization_context")}} + ignore_result(responder_->Accept(builder.message())); + // TODO(darin): Accept() returning false indicates a malformed message, and + // that may be good reason to close the connection. However, we don't have a + // way to do that from here. We should add a way. + responder_ = nullptr; +} +{%- endif -%} +{%- endfor %} + +{#--- StubDispatch definition #} + +// static +bool {{class_name}}StubDispatch::Accept( + {{interface.name}}* impl, + mojo::Message* message) { +{%- if interface.methods %} + switch (message->header()->name) { +{%- for method in interface.methods %} + case internal::k{{class_name}}_{{method.name}}_Name: { +{%- if method.response_parameters == None %} + internal::{{class_name}}_{{method.name}}_Params_Data* params = + reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>( + message->mutable_payload()); + +{%- set desc = class_name~"::"~method.name %} + {{alloc_params(method.param_struct, "params", "message", desc)| + indent(4)}} + // A null |impl| means no implementation was bound. + assert(impl); + TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); + mojo::internal::MessageDispatchContext context(message); + impl->{{method.name}}({{pass_params(method.parameters)}}); + return true; +{%- else %} + break; +{%- endif %} + } +{%- endfor %} + } +{%- endif %} + return false; +} + +// static +bool {{class_name}}StubDispatch::AcceptWithResponder( + {{interface.name}}* impl, + mojo::Message* message, + std::unique_ptr<mojo::MessageReceiverWithStatus> responder) { +{%- if interface.methods %} + switch (message->header()->name) { +{%- for method in interface.methods %} + case internal::k{{class_name}}_{{method.name}}_Name: { +{%- if method.response_parameters != None %} + internal::{{class_name}}_{{method.name}}_Params_Data* params = + reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>( + message->mutable_payload()); + +{%- set desc = class_name~"::"~method.name %} + {{alloc_params(method.param_struct, "params", "message", desc)| + indent(4)}} + {{class_name}}::{{method.name}}Callback callback = + {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback( + message->request_id(), + message->has_flag(mojo::Message::kFlagIsSync), + std::move(responder)); + // A null |impl| means no implementation was bound. + assert(impl); + TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); + mojo::internal::MessageDispatchContext context(message); + impl->{{method.name}}( +{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}std::move(callback)); + return true; +{%- else %} + break; +{%- endif %} + } +{%- endfor %} + } +{%- endif %} + return false; +} + +{#--- Request validator definitions #} + +bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) + return true; + + mojo::internal::ValidationContext validation_context( + message->payload(), message->payload_num_bytes(), + message->handles()->size(), message->payload_num_interface_ids(), message, + "{{class_name}} RequestValidator"); + + switch (message->header()->name) { +{%- for method in interface.methods %} + case internal::k{{class_name}}_{{method.name}}_Name: { +{%- if method.response_parameters != None %} + if (!mojo::internal::ValidateMessageIsRequestExpectingResponse( + message, &validation_context)) { + return false; + } +{%- else %} + if (!mojo::internal::ValidateMessageIsRequestWithoutResponse( + message, &validation_context)) { + return false; + } +{%- endif %} + if (!mojo::internal::ValidateMessagePayload< + internal::{{class_name}}_{{method.name}}_Params_Data>( + message, &validation_context)) { + return false; + } + return true; + } +{%- endfor %} + default: + break; + } + + // Unrecognized message. + ReportValidationError( + &validation_context, + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD); + return false; +} + +{#--- Response validator definitions #} +{% if interface|has_callbacks %} +bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) + return true; + + mojo::internal::ValidationContext validation_context( + message->payload(), message->payload_num_bytes(), + message->handles()->size(), message->payload_num_interface_ids(), message, + "{{class_name}} ResponseValidator"); + + if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context)) + return false; + switch (message->header()->name) { +{%- for method in interface.methods if method.response_parameters != None %} + case internal::k{{class_name}}_{{method.name}}_Name: { + if (!mojo::internal::ValidateMessagePayload< + internal::{{class_name}}_{{method.name}}_ResponseParams_Data>( + message, &validation_context)) { + return false; + } + return true; + } +{%- endfor %} + default: + break; + } + + // Unrecognized message. + ReportValidationError( + &validation_context, + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD); + return false; +} +{%- endif -%} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl new file mode 100644 index 0000000000..8649273633 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl @@ -0,0 +1,49 @@ +{%- macro declare_params(prefix, parameters) %} +{%- for param in parameters -%} +{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}} +{%- if not loop.last %}, {% endif %} +{%- endfor %} +{%- endmacro %} + +{%- macro declare_responder_params(prefix, parameters, for_blink) %} +{%- for param in parameters -%} +{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}} +{%- if not loop.last %}, {% endif %} +{%- endfor %} +{%- endmacro %} + +{%- macro declare_callback(method, for_blink, use_once_callback) -%} +{%- if use_once_callback -%} +base::OnceCallback<void( +{%- else -%} +base::Callback<void( +{%- endif -%} +{%- for param in method.response_parameters -%} +{{param.kind|cpp_wrapper_param_type}} +{%- if not loop.last %}, {% endif %} +{%- endfor -%} +)> +{%- endmacro -%} + +{%- macro declare_request_params(prefix, method, use_once_callback) -%} +{{declare_params(prefix, method.parameters)}} +{%- if method.response_parameters != None -%} +{%- if method.parameters %}, {% endif -%} +{%- if use_once_callback -%} +{{method.name}}Callback callback +{%- else -%} +const {{method.name}}Callback& callback +{%- endif -%} +{%- endif -%} +{%- endmacro -%} + +{%- macro declare_sync_method_params(prefix, method) -%} +{{declare_params(prefix, method.parameters)}} +{%- if method.response_parameters %} +{%- if method.parameters %}, {% endif %} +{%- for param in method.response_parameters -%} +{{param.kind|cpp_wrapper_type}}* {{prefix}}{{param.name}} +{%- if not loop.last %}, {% endif %} +{%- endfor %} +{%- endif -%} +{%- endmacro -%} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl new file mode 100644 index 0000000000..0a158ec3e8 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl @@ -0,0 +1,16 @@ +{%- import "interface_macros.tmpl" as interface_macros %} +class {{export_attribute}} {{interface.name}}Proxy + : public {{interface.name}} { + public: + explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver); + +{%- for method in interface.methods %} +{%- if method.sync %} + bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) override; +{%- endif %} + void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) override; +{%- endfor %} + + private: + mojo::MessageReceiverWithResponder* receiver_; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl new file mode 100644 index 0000000000..a00d14886d --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl @@ -0,0 +1,4 @@ +class {{export_attribute}} {{interface.name}}RequestValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) { + public: + bool Accept(mojo::Message* message) override; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl new file mode 100644 index 0000000000..e2caa02c79 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl @@ -0,0 +1,4 @@ +class {{export_attribute}} {{interface.name}}ResponseValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) { + public: + bool Accept(mojo::Message* message) override; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl new file mode 100644 index 0000000000..79ab46f337 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl @@ -0,0 +1,41 @@ +class {{export_attribute}} {{interface.name}}StubDispatch { + public: + static bool Accept({{interface.name}}* impl, mojo::Message* message); + static bool AcceptWithResponder( + {{interface.name}}* impl, + mojo::Message* message, + std::unique_ptr<mojo::MessageReceiverWithStatus> responder); +}; + +template <typename ImplRefTraits = + mojo::RawPtrImplRefTraits<{{interface.name}}>> +class {{interface.name}}Stub + : public NON_EXPORTED_BASE(mojo::MessageReceiverWithResponderStatus) { + public: + using ImplPointerType = typename ImplRefTraits::PointerType; + + {{interface.name}}Stub() {} + ~{{interface.name}}Stub() override {} + + void set_sink(ImplPointerType sink) { sink_ = std::move(sink); } + ImplPointerType& sink() { return sink_; } + + bool Accept(mojo::Message* message) override { + if (ImplRefTraits::IsNull(sink_)) + return false; + return {{interface.name}}StubDispatch::Accept( + ImplRefTraits::GetRawPointer(&sink_), message); + } + + bool AcceptWithResponder( + mojo::Message* message, + std::unique_ptr<mojo::MessageReceiverWithStatus> responder) override { + if (ImplRefTraits::IsNull(sink_)) + return false; + return {{interface.name}}StubDispatch::AcceptWithResponder( + ImplRefTraits::GetRawPointer(&sink_), message, std::move(responder)); + } + + private: + ImplPointerType sink_; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl new file mode 100644 index 0000000000..964b25438e --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl @@ -0,0 +1,96 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +{%- set header_guard = "%s_SHARED_INTERNAL_H_"|format( + module.path|upper|replace("/","_")|replace(".","_")| + replace("-", "_")) %} + +#ifndef {{header_guard}} +#define {{header_guard}} + +#include <stdint.h> + +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/map_data_internal.h" +#include "mojo/public/cpp/bindings/lib/native_enum_data.h" +#include "mojo/public/cpp/bindings/lib/native_struct_data.h" +#include "mojo/public/cpp/bindings/lib/buffer.h" + +{%- for import in imports %} +#include "{{import.module.path}}-shared-internal.h" +{%- endfor %} + +namespace mojo { +namespace internal { +class ValidationContext; +} +} + +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} +namespace internal { + +{#--- Internal forward declarations #} +{%- for struct in structs %} +{%- if struct|is_native_only_kind %} +using {{struct.name}}_Data = mojo::internal::NativeStruct_Data; +{%- else %} +class {{struct.name}}_Data; +{%- endif %} +{%- endfor %} + +{%- for union in unions %} +class {{union.name}}_Data; +{%- endfor %} + +{#--- Enums #} +{%- from "enum_macros.tmpl" import enum_data_decl -%} +{%- for enum in all_enums %} +{%- if enum|is_native_only_kind %} +using {{enum|get_name_for_kind(flatten_nested_kind=True)}}_Data = + mojo::internal::NativeEnum_Data; +{%- else %} +{{enum_data_decl(enum)}} +{%- endif %} +{%- endfor %} + +#pragma pack(push, 1) + +{#--- Unions must be declared first because they can be members of structs #} +{#--- Union class declarations #} +{%- for union in unions %} +{% include "union_declaration.tmpl" %} +{%- endfor %} + +{#--- Struct class declarations #} +{%- for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "struct_declaration.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Interface parameter definitions #} +{%- for interface in interfaces %} +{%- for method in interface.methods %} +{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %} +constexpr uint32_t {{method_name}} = {{method.ordinal}}; +{%- set struct = method.param_struct %} +{% include "struct_declaration.tmpl" %} +{%- if method.response_parameters != None %} +{%- set struct = method.response_param_struct %} +{% include "struct_declaration.tmpl" %} +{%- endif %} +{%- endfor %} +{%- endfor %} + +#pragma pack(pop) + +} // namespace internal +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} + +#endif // {{header_guard}} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl new file mode 100644 index 0000000000..645bb692b0 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl @@ -0,0 +1,64 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4065) +#endif + +#include "{{module.path}}-shared.h" + +#include <utility> + +#include "base/logging.h" +#include "mojo/public/cpp/bindings/lib/validate_params.h" +#include "mojo/public/cpp/bindings/lib/validation_context.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/bindings/lib/validation_util.h" + +{%- for header in extra_traits_headers %} +#include "{{header}}" +{%- endfor %} + +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} + +namespace internal { + +{#--- Union definitions #} +{%- for union in unions %} +{% include "union_definition.tmpl" %} +{%- endfor %} + +{#--- Struct definitions #} +{%- for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "struct_definition.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Interface parameter definitions #} +{%- for interface in interfaces %} +{%- for method in interface.methods %} +{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %} +{%- set struct = method.param_struct %} +{% include "struct_definition.tmpl" %} +{%- if method.response_parameters != None %} +{%- set struct = method.response_param_struct %} +{% include "struct_definition.tmpl" %} +{%- endif %} +{%- endfor %} +{%- endfor %} + +} // namespace internal + +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl new file mode 100644 index 0000000000..dd13466de1 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl @@ -0,0 +1,212 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +{%- set header_guard = "%s_SHARED_H_"|format( + module.path|upper|replace("/","_")|replace(".","_")| + replace("-", "_")) %} + +{%- macro mojom_type_traits(kind) %} +template <> +struct MojomTypeTraits<{{kind|get_qualified_name_for_kind}}DataView> { + using Data = {{kind|get_qualified_name_for_kind(internal=True)}}; +{%- if kind|is_union_kind %} + using DataAsArrayElement = Data; + static constexpr MojomTypeCategory category = MojomTypeCategory::UNION; +{%- else %} + using DataAsArrayElement = Pointer<Data>; + static constexpr MojomTypeCategory category = MojomTypeCategory::STRUCT; +{%- endif %} +}; +{%- endmacro %} + +{%- macro namespace_begin() %} +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} +{%- endmacro %} + +{%- macro namespace_end() %} +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} +{%- endmacro %} + +#ifndef {{header_guard}} +#define {{header_guard}} + +#include <stdint.h> + +#include <functional> +#include <ostream> +#include <type_traits> +#include <utility> + +#include "base/compiler_specific.h" +#include "mojo/public/cpp/bindings/array_data_view.h" +#include "mojo/public/cpp/bindings/enum_traits.h" +#include "mojo/public/cpp/bindings/interface_data_view.h" +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" +#include "mojo/public/cpp/bindings/lib/serialization.h" +#include "mojo/public/cpp/bindings/map_data_view.h" +#include "mojo/public/cpp/bindings/native_enum.h" +#include "mojo/public/cpp/bindings/native_struct_data_view.h" +#include "mojo/public/cpp/bindings/string_data_view.h" +#include "{{module.path}}-shared-internal.h" +{%- for import in imports %} +#include "{{import.module.path}}-shared.h" +{%- endfor %} + +{{namespace_begin()}} + +{#--- Struct Forward Declarations -#} +{%- for struct in structs %} +{%- if struct|is_native_only_kind %} +using {{struct.name}}DataView = mojo::NativeStructDataView; +{%- else %} +class {{struct.name}}DataView; +{%- endif %} +{% endfor %} + +{#--- Union Forward Declarations -#} +{%- for union in unions %} +class {{union.name}}DataView; +{%- endfor %} + +{{namespace_end()}} + +namespace mojo { +namespace internal { + +{%- for struct in structs %} +{%- if not struct|is_native_only_kind %} +{{mojom_type_traits(struct)}} +{%- endif %} +{%- endfor %} + +{%- for union in unions %} +{{mojom_type_traits(union)}} +{%- endfor %} + +} // namespace internal +} // namespace mojo + +{{namespace_begin()}} + +{#--- Enums #} +{%- from "enum_macros.tmpl" import enum_decl%} +{%- for enum in all_enums %} +{%- if enum|is_native_only_kind %} +using {{enum|get_name_for_kind(flatten_nested_kind=True)}} = mojo::NativeEnum; +{%- else %} +{{enum_decl(enum)}} +{%- endif %} +{%- endfor %} + +{#--- Interfaces #} +{%- if interfaces %} +// Interface base classes. They are used for type safety check. +{%- endif %} +{%- for interface in interfaces %} +class {{interface.name}}InterfaceBase {}; + +using {{interface.name}}PtrDataView = + mojo::InterfacePtrDataView<{{interface.name}}InterfaceBase>; +using {{interface.name}}RequestDataView = + mojo::InterfaceRequestDataView<{{interface.name}}InterfaceBase>; +using {{interface.name}}AssociatedPtrInfoDataView = + mojo::AssociatedInterfacePtrInfoDataView<{{interface.name}}InterfaceBase>; +using {{interface.name}}AssociatedRequestDataView = + mojo::AssociatedInterfaceRequestDataView<{{interface.name}}InterfaceBase>; + +{%- endfor %} + +{#--- Structs #} +{%- for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "struct_data_view_declaration.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Interface parameter definitions #} +{%- for interface in interfaces %} +{%- for method in interface.methods %} +{%- set struct = method.param_struct %} +{% include "struct_data_view_declaration.tmpl" %} +{%- if method.response_parameters != None %} +{%- set struct = method.response_param_struct %} +{% include "struct_data_view_declaration.tmpl" %} +{%- endif %} +{%- endfor %} +{%- endfor %} + +{#--- Unions #} +{%- for union in unions %} +{% include "union_data_view_declaration.tmpl" %} +{%- endfor %} + +{{namespace_end()}} + +namespace std { + +{%- from "enum_macros.tmpl" import enum_hash %} +{%- for enum in all_enums %} +{%- if not enum|is_native_only_kind %} +{{enum_hash(enum)}} +{%- endif %} +{%- endfor %} + +} // namespace std + +namespace mojo { + +{#--- Enum Serialization Helpers -#} +{%- for enum in all_enums %} +{%- if not enum|is_native_only_kind %} +{% include "enum_serialization_declaration.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Struct Serialization Helpers -#} +{% for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "struct_serialization_declaration.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Union Serialization Helpers -#} +{% if unions %} +{%- for union in unions %} +{% include "union_serialization_declaration.tmpl" %} +{%- endfor %} +{%- endif %} + +} // namespace mojo + +{{namespace_begin()}} + +{%- for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "struct_data_view_definition.tmpl" %} +{%- endif %} +{%- endfor %} + +{%- for interface in interfaces %} +{%- for method in interface.methods %} +{%- set struct = method.param_struct %} +{% include "struct_data_view_definition.tmpl" %} +{%- if method.response_parameters != None %} +{%- set struct = method.response_param_struct %} +{% include "struct_data_view_definition.tmpl" %} +{%- endif %} +{%- endfor %} +{%- endfor %} + +{%- for union in unions %} +{% include "union_data_view_definition.tmpl" %} +{%- endfor %} + +{{namespace_end()}} + +#endif // {{header_guard}} + diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl new file mode 100644 index 0000000000..2c66a85f87 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl @@ -0,0 +1,111 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +{%- if variant -%} +{%- set variant_path = "%s-%s"|format(module.path, variant) -%} +{%- else -%} +{%- set variant_path = module.path -%} +{%- endif %} + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4056) +#pragma warning(disable:4065) +#pragma warning(disable:4756) +#endif + +#include "{{variant_path}}.h" + +#include <math.h> +#include <stdint.h> +#include <utility> + +#include "base/logging.h" +#include "base/trace_event/trace_event.h" +#include "mojo/public/cpp/bindings/lib/message_builder.h" +#include "mojo/public/cpp/bindings/lib/serialization_util.h" +#include "mojo/public/cpp/bindings/lib/validate_params.h" +#include "mojo/public/cpp/bindings/lib/validation_context.h" +#include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h" + +{%- if for_blink %} +#include "mojo/public/cpp/bindings/lib/wtf_serialization.h" +{%- endif %} + +{%- for header in extra_traits_headers %} +#include "{{header}}" +{%- endfor %} + +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} +{%- if variant %} +namespace {{variant}} { +{%- endif %} + +{#--- Constants #} +{%- for constant in module.constants %} +{%- if constant.kind|is_string_kind %} +const char {{constant.name}}[] = {{constant|constant_value}}; +{%- endif %} +{%- endfor %} + +{#--- Struct Constants #} +{%- for struct in structs %} +{%- for constant in struct.constants %} +{%- if constant.kind|is_string_kind %} +const char {{struct.name}}::{{constant.name}}[] = {{constant|constant_value}}; +{%- endif %} +{%- endfor %} +{%- endfor %} + +{#--- Struct builder definitions #} +{%- for struct in structs %} +{%- if not struct|is_native_only_kind %} +{%- include "wrapper_class_definition.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Union builder definitions #} +{%- for union in unions %} +{%- include "wrapper_union_class_definition.tmpl" %} +{%- endfor %} + +{#--- Interface definitions #} +{%- for interface in interfaces %} +{%- include "interface_definition.tmpl" %} +{%- endfor %} + +{%- if variant %} +} // namespace {{variant}} +{%- endif %} +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} + +namespace mojo { + +{#--- Struct Serialization Helpers -#} +{% for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "struct_traits_definition.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Union Serialization Helpers #} +{%- for union in unions %} +{%- include "union_traits_definition.tmpl" %} +{%- endfor %} + +} // namespace mojo + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl new file mode 100644 index 0000000000..804a46b6f8 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl @@ -0,0 +1,236 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +{%- if variant -%} +{%- set variant_path = "%s-%s"|format(module.path, variant) -%} +{%- else -%} +{%- set variant_path = module.path -%} +{%- endif -%} + +{%- set header_guard = "%s_H_"|format( + variant_path|upper|replace("/","_")|replace(".","_")| + replace("-", "_")) %} + +{%- macro namespace_begin() %} +{%- for namespace in namespaces_as_array %} +namespace {{namespace}} { +{%- endfor %} +{%- if variant %} +namespace {{variant}} { +{%- endif %} +{%- endmacro %} + +{%- macro namespace_end() %} +{%- if variant %} +} // namespace {{variant}} +{%- endif %} +{%- for namespace in namespaces_as_array|reverse %} +} // namespace {{namespace}} +{%- endfor %} +{%- endmacro %} + +#ifndef {{header_guard}} +#define {{header_guard}} + +#include <stdint.h> + +#include <limits> +#include <type_traits> +#include <utility> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/optional.h" +#include "mojo/public/cpp/bindings/associated_interface_ptr.h" +#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" +#include "mojo/public/cpp/bindings/associated_interface_request.h" +#include "mojo/public/cpp/bindings/clone_traits.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/lib/equals_traits.h" +#include "mojo/public/cpp/bindings/lib/control_message_handler.h" +#include "mojo/public/cpp/bindings/lib/control_message_proxy.h" +#include "mojo/public/cpp/bindings/lib/serialization.h" +#include "mojo/public/cpp/bindings/lib/union_accessor.h" +#include "mojo/public/cpp/bindings/native_struct.h" +#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" +#include "mojo/public/cpp/bindings/struct_ptr.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h" +#include "mojo/public/cpp/bindings/union_traits.h" +#include "{{module.path}}-shared.h" +{%- for import in imports %} +{%- if variant %} +#include "{{"%s-%s.h"|format(import.module.path, variant)}}" +{%- else %} +#include "{{import.module.path}}.h" +{%- endif %} +{%- endfor %} +{%- if not for_blink %} +#include <string> +#include <vector> +{%- else %} +{# hash_util.h includes template specializations that should be present for + every use of {Inlined}StructPtr. #} +#include "mojo/public/cpp/bindings/lib/wtf_hash_util.h" +#include "third_party/WebKit/Source/wtf/HashFunctions.h" +#include "third_party/WebKit/Source/wtf/Optional.h" +#include "third_party/WebKit/Source/wtf/text/WTFString.h" +{%- endif %} + +{%- for header in extra_public_headers %} +#include "{{header}}" +{%- endfor %} + +{%- if export_header %} +#include "{{export_header}}" +{%- endif %} + +{#--- WTF enum hashing #} +{%- from "enum_macros.tmpl" import enum_hash_blink%} +{%- if for_blink %} +{%- for enum in all_enums %} +{%- if not enum|is_native_only_kind %} +{{enum_hash_blink(enum)}} +{%- endif %} +{%- endfor %} +{%- endif %} + +{{namespace_begin()}} + +{#--- Enums #} +{%- if variant %} +{%- for enum in enums %} +using {{enum.name}} = {{enum.name}}; // Alias for definition in the parent namespace. +{%- endfor %} +{%- endif %} + +{#--- Constants #} +{%- for constant in module.constants %} +{{constant|format_constant_declaration}}; +{%- endfor %} + +{#--- Interface Forward Declarations -#} +{% for interface in interfaces %} +class {{interface.name}}; +using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>; +using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>; +using ThreadSafe{{interface.name}}Ptr = + mojo::ThreadSafeInterfacePtr<{{interface.name}}>; +using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>; +using {{interface.name}}AssociatedPtr = + mojo::AssociatedInterfacePtr<{{interface.name}}>; +using ThreadSafe{{interface.name}}AssociatedPtr = + mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>; +using {{interface.name}}AssociatedPtrInfo = + mojo::AssociatedInterfacePtrInfo<{{interface.name}}>; +using {{interface.name}}AssociatedRequest = + mojo::AssociatedInterfaceRequest<{{interface.name}}>; +{% endfor %} + +{#--- Struct Forward Declarations -#} +{% for struct in structs %} +{%- if struct|is_native_only_kind %} +using {{struct.name}} = mojo::NativeStruct; +using {{struct.name}}Ptr = mojo::NativeStructPtr; +{%- else %} +class {{struct.name}}; +{%- if struct|should_inline %} +using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>; +{%- else %} +using {{struct.name}}Ptr = mojo::StructPtr<{{struct.name}}>; +{%- endif %} +{%- endif %} +{% endfor %} + +{#--- Union Forward Declarations -#} +{% for union in unions %} +class {{union.name}}; +{% if union|should_inline_union %} +typedef mojo::InlinedStructPtr<{{union.name}}> {{union.name}}Ptr; +{% else %} +typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr; +{% endif %} +{%- endfor %} + +{#--- Interfaces -#} +{% for interface in interfaces %} +{% include "interface_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Proxies -#} +{% for interface in interfaces %} +{% include "interface_proxy_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Stubs -#} +{% for interface in interfaces %} +{% include "interface_stub_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Request Validators -#} +{% for interface in interfaces %} +{% include "interface_request_validator_declaration.tmpl" %} +{%- endfor %} + +{#--- Interface Response Validators -#} +{% for interface in interfaces if interface|has_callbacks %} +{% include "interface_response_validator_declaration.tmpl" %} +{%- endfor %} + +{#--- NOTE: Unions and non-inlined structs may have pointers to inlined structs, + so we need to fully define inlined structs ahead of the others. #} + +{#--- Inlined structs #} +{% for struct in structs %} +{% if struct|should_inline and not struct|is_native_only_kind %} +{% include "wrapper_class_declaration.tmpl" %} +{% endif %} +{%- endfor %} + +{#--- Unions must be declared before non-inlined structs because they can be + members of structs. #} +{#--- Unions #} +{% for union in unions %} +{% include "wrapper_union_class_declaration.tmpl" %} +{%- endfor %} + +{#--- Non-inlined structs #} +{% for struct in structs %} +{% if not struct|should_inline and not struct|is_native_only_kind %} +{% include "wrapper_class_declaration.tmpl" %} +{% endif %} +{%- endfor %} + +{%- for union in unions %} +{% include "wrapper_union_class_template_definition.tmpl" %} +{%- endfor %} + +{%- for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "wrapper_class_template_definition.tmpl" %} +{%- endif %} +{%- endfor %} + +{{namespace_end()}} + +namespace mojo { + +{#--- Struct Serialization Helpers -#} +{% for struct in structs %} +{%- if not struct|is_native_only_kind %} +{% include "struct_traits_declaration.tmpl" %} +{%- endif %} +{%- endfor %} + +{#--- Union Serialization Helpers -#} +{% if unions %} +{%- for union in unions %} +{% include "union_traits_declaration.tmpl" %} +{%- endfor %} +{%- endif %} + +} // namespace mojo + +#endif // {{header_guard}} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl new file mode 100644 index 0000000000..96e0d614d8 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl @@ -0,0 +1,118 @@ +class {{struct.name}}DataView { + public: + {{struct.name}}DataView() {} + + {{struct.name}}DataView( + internal::{{struct.name}}_Data* data, + mojo::internal::SerializationContext* context) +{%- if struct|requires_context_for_data_view %} + : data_(data), context_(context) {} +{%- else %} + : data_(data) {} +{%- endif %} + + bool is_null() const { return !data_; } + +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set kind = pf.field.kind %} +{%- set name = pf.field.name %} +{%- if kind|is_union_kind %} + inline void Get{{name|under_to_camel}}DataView( + {{kind|cpp_data_view_type}}* output); + + template <typename UserType> + WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) { +{%- if pf.min_version != 0 %} + auto* pointer = data_->header_.version >= {{pf.min_version}} + ? &data_->{{name}} : nullptr; +{%- else %} + auto* pointer = &data_->{{name}}; +{%- endif %} + return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + pointer, output, context_); + } + +{%- elif kind|is_object_kind %} + inline void Get{{name|under_to_camel}}DataView( + {{kind|cpp_data_view_type}}* output); + + template <typename UserType> + WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) { +{%- if pf.min_version != 0 %} + auto* pointer = data_->header_.version >= {{pf.min_version}} + ? data_->{{name}}.Get() : nullptr; +{%- else %} + auto* pointer = data_->{{name}}.Get(); +{%- endif %} + return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + pointer, output, context_); + } + +{%- elif kind|is_enum_kind %} + template <typename UserType> + WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const { +{%- if pf.min_version != 0 %} + auto data_value = data_->header_.version >= {{pf.min_version}} + ? data_->{{name}} : 0; +{%- else %} + auto data_value = data_->{{name}}; +{%- endif %} + return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + data_value, output); + } + + {{kind|cpp_data_view_type}} {{name}}() const { +{%- if pf.min_version != 0 %} + if (data_->header_.version < {{pf.min_version}}) + return {{kind|get_qualified_name_for_kind}}{}; +{%- endif %} + return static_cast<{{kind|cpp_data_view_type}}>(data_->{{name}}); + } + +{%- elif kind|is_any_handle_kind %} + {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() { + {{kind|cpp_data_view_type}} result; +{%- if pf.min_version != 0 %} + if (data_->header_.version < {{pf.min_version}}) + return result; +{%- endif %} + bool ret = + mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + &data_->{{name}}, &result, context_); + DCHECK(ret); + return result; + } + +{%- elif kind|is_any_interface_kind %} + template <typename UserType> + UserType Take{{name|under_to_camel}}() { + UserType result; +{%- if pf.min_version != 0 %} + if (data_->header_.version < {{pf.min_version}}) + return result; +{%- endif %} + bool ret = + mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + &data_->{{name}}, &result, context_); + DCHECK(ret); + return result; + } + +{%- else %} + {{kind|cpp_data_view_type}} {{name}}() const { +{%- if pf.min_version != 0 %} + if (data_->header_.version < {{pf.min_version}}) + return {{kind|cpp_data_view_type}}{}; +{%- endif %} + return data_->{{name}}; + } + +{%- endif %} +{%- endfor %} + private: + internal::{{struct.name}}_Data* data_ = nullptr; +{%- if struct|requires_context_for_data_view %} + mojo::internal::SerializationContext* context_ = nullptr; +{%- endif %} +}; + diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl new file mode 100644 index 0000000000..95311dc124 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl @@ -0,0 +1,30 @@ +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set kind = pf.field.kind %} +{%- set name = pf.field.name %} + +{%- if kind|is_union_kind %} +inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView( + {{kind|cpp_data_view_type}}* output) { +{%- if pf.min_version != 0 %} + auto pointer = data_->header_.version >= {{pf.min_version}} + ? &data_->{{name}} : nullptr; +{%- else %} + auto pointer = &data_->{{name}}; +{%- endif %} + *output = {{kind|cpp_data_view_type}}(pointer, context_); +} + +{%- elif kind|is_object_kind %} +inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView( + {{kind|cpp_data_view_type}}* output) { +{%- if pf.min_version != 0 %} + auto pointer = data_->header_.version >= {{pf.min_version}} + ? data_->{{name}}.Get() : nullptr; +{%- else %} + auto pointer = data_->{{name}}.Get(); +{%- endif %} + *output = {{kind|cpp_data_view_type}}(pointer, context_); +} +{%- endif %} +{%- endfor %} + diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl new file mode 100644 index 0000000000..156f7742c4 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl @@ -0,0 +1,46 @@ +{%- set class_name = struct.name ~ "_Data" -%} + +class {{class_name}} { + public: + static {{class_name}}* New(mojo::internal::Buffer* buf) { + return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}(); + } + + static bool Validate(const void* data, + mojo::internal::ValidationContext* validation_context); + + mojo::internal::StructHeader header_; +{%- for packed_field in struct.packed.packed_fields %} +{%- set name = packed_field.field.name %} +{%- set kind = packed_field.field.kind %} +{%- if kind.spec == 'b' %} + uint8_t {{name}} : 1; +{%- else %} + {{kind|cpp_field_type}} {{name}}; +{%- endif %} +{%- if not loop.last %} +{%- set next_pf = struct.packed.packed_fields[loop.index0 + 1] %} +{%- set pad = next_pf.offset - (packed_field.offset + packed_field.size) %} +{%- if pad > 0 %} + uint8_t pad{{loop.index0}}_[{{pad}}]; +{%- endif %} +{%- endif %} +{%- endfor %} + +{%- set num_fields = struct.versions[-1].num_fields %} +{%- if num_fields > 0 %} +{%- set last_field = struct.packed.packed_fields[num_fields - 1] %} +{%- set offset = last_field.offset + last_field.size %} +{%- set pad = offset|get_pad(8) %} +{%- if pad > 0 %} + uint8_t padfinal_[{{pad}}]; +{%- endif %} +{%- endif %} + + private: + {{class_name}}() : header_({sizeof(*this), {{struct.versions[-1].version}}}) { + } + ~{{class_name}}() = delete; +}; +static_assert(sizeof({{class_name}}) == {{struct.versions[-1].num_bytes}}, + "Bad sizeof({{class_name}})"); diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl new file mode 100644 index 0000000000..60dca4010e --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl @@ -0,0 +1,70 @@ +{%- import "validation_macros.tmpl" as validation_macros %} +{%- set class_name = struct.name ~ "_Data" %} + +// static +bool {{class_name}}::Validate( + const void* data, + mojo::internal::ValidationContext* validation_context) { + if (!data) + return true; + + if (!ValidateStructHeaderAndClaimMemory(data, validation_context)) + return false; + + // NOTE: The memory backing |object| may be smaller than |sizeof(*object)| if + // the message comes from an older version. + const {{class_name}}* object = static_cast<const {{class_name}}*>(data); + + static constexpr struct { + uint32_t version; + uint32_t num_bytes; + } kVersionSizes[] = { +{%- for version in struct.versions -%} + { {{version.version}}, {{version.num_bytes}} }{% if not loop.last %}, {% endif -%} +{%- endfor -%} + }; + + if (object->header_.version <= + kVersionSizes[arraysize(kVersionSizes) - 1].version) { + // Scan in reverse order to optimize for more recent versions. + for (int i = arraysize(kVersionSizes) - 1; i >= 0; --i) { + if (object->header_.version >= kVersionSizes[i].version) { + if (object->header_.num_bytes == kVersionSizes[i].num_bytes) + break; + + ReportValidationError( + validation_context, + mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } + } else if (object->header_.num_bytes < + kVersionSizes[arraysize(kVersionSizes) - 1].num_bytes) { + ReportValidationError( + validation_context, + mojo::internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + +{#- Before validating fields introduced at a certain version, we need to add + a version check, which makes sure we skip further validation if |object| + is from an earlier version. |last_checked_version| records the last + version that we have added such version check. #} +{%- set last_checked_version = 0 %} +{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %} +{%- set kind = packed_field.field.kind %} +{%- if kind|is_object_kind or kind|is_any_handle_or_interface_kind or + kind|is_enum_kind %} +{%- if packed_field.min_version > last_checked_version %} +{%- set last_checked_version = packed_field.min_version %} + if (object->header_.version < {{packed_field.min_version}}) + return true; +{%- endif %} +{%- set field_expr = "object->" ~ packed_field.field.name %} +{{validation_macros.validate_field(packed_field.field, field_expr, struct.name, true)}} +{%- endif %} +{%- endfor %} + + return true; +} + diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl new file mode 100644 index 0000000000..bb5fb9c496 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl @@ -0,0 +1,161 @@ +{# TODO(yzshen): Make these templates more readable. #} + +{# Computes the serialized size for the specified struct. + |struct| is the struct definition. + |input_field_pattern| should be a pattern that contains one string + placeholder, for example, "input->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the input fields. + |context| is the name of the serialization context. + |input_may_be_temp| indicates whether any input may be temporary obejcts. + We need to assign temporary objects to local variables before passing it to + Serializer, because it is illegal to pass temporary objects as non-const + references. + This macro is expanded to compute seriailized size for both: + - user-defined structs: the input is an instance of the corresponding struct + wrapper class. + - method parameters/response parameters: the input is a list of + arguments. + It declares |size| of type size_t to store the resulting size. #} +{%- macro get_serialized_size(struct, input_field_pattern, context, + input_may_be_temp=False) -%} + size_t size = sizeof({{struct|get_qualified_name_for_kind(internal=True)}}); +{%- for pf in struct.packed.packed_fields_in_ordinal_order + if pf.field.kind|is_object_kind or pf.field.kind|is_associated_kind %} +{%- set name = pf.field.name -%} +{%- set kind = pf.field.kind -%} +{%- set original_input_field = input_field_pattern|format(name) %} +{%- set input_field = "in_%s"|format(name) if input_may_be_temp + else original_input_field %} +{%- if input_may_be_temp %} + decltype({{original_input_field}}) in_{{name}} = {{original_input_field}}; +{%- endif %} + +{%- set serializer_type = kind|unmapped_type_for_serializer %} +{%- if kind|is_union_kind %} + size += mojo::internal::PrepareToSerialize<{{serializer_type}}>( + {{input_field}}, true, {{context}}); +{%- else %} + size += mojo::internal::PrepareToSerialize<{{serializer_type}}>( + {{input_field}}, {{context}}); +{%- endif %} +{%- endfor %} +{%- endmacro -%} + +{# Serializes the specified struct. + |struct| is the struct definition. + |struct_display_name| is the display name for the struct that can be showed + in error/log messages, for example, "FooStruct", "FooMethod request". + |input_field_pattern| should be a pattern that contains one string + placeholder, for example, "input->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the input fields. + |output| is the name of the output struct instance. + |buffer| is the name of the Buffer instance used. + |context| is the name of the serialization context. + |input_may_be_temp|: please see the comments of get_serialized_size. + This macro is expanded to do serialization for both: + - user-defined structs: the input is an instance of the corresponding struct + wrapper class. + - method parameters/response parameters: the input is a list of + arguments. #} +{%- macro serialize(struct, struct_display_name, input_field_pattern, output, + buffer, context, input_may_be_temp=False) -%} + auto {{output}} = + {{struct|get_qualified_name_for_kind(internal=True)}}::New({{buffer}}); + ALLOW_UNUSED_LOCAL({{output}}); +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set input_field = input_field_pattern|format(pf.field.name) %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- set serializer_type = kind|unmapped_type_for_serializer %} + +{%- if kind|is_object_kind or kind|is_any_handle_or_interface_kind %} +{%- set original_input_field = input_field_pattern|format(name) %} +{%- set input_field = "in_%s"|format(name) if input_may_be_temp + else original_input_field %} +{%- if input_may_be_temp %} + decltype({{original_input_field}}) in_{{name}} = {{original_input_field}}; +{%- endif %} +{%- endif %} + +{%- if kind|is_object_kind %} +{%- if kind|is_array_kind or kind|is_map_kind %} + typename decltype({{output}}->{{name}})::BaseType* {{name}}_ptr; + const mojo::internal::ContainerValidateParams {{name}}_validate_params( + {{kind|get_container_validate_params_ctor_args|indent(10)}}); + mojo::internal::Serialize<{{serializer_type}}>( + {{input_field}}, {{buffer}}, &{{name}}_ptr, &{{name}}_validate_params, + {{context}}); + {{output}}->{{name}}.Set({{name}}_ptr); +{%- elif kind|is_union_kind %} + auto {{name}}_ptr = &{{output}}->{{name}}; + mojo::internal::Serialize<{{serializer_type}}>( + {{input_field}}, {{buffer}}, &{{name}}_ptr, true, {{context}}); +{%- else %} + typename decltype({{output}}->{{name}})::BaseType* {{name}}_ptr; + mojo::internal::Serialize<{{serializer_type}}>( + {{input_field}}, {{buffer}}, &{{name}}_ptr, {{context}}); + {{output}}->{{name}}.Set({{name}}_ptr); +{%- endif %} +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + {{output}}->{{name}}.is_null(), + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null {{name}} in {{struct_display_name}}"); +{%- endif %} + +{%- elif kind|is_any_handle_or_interface_kind %} + mojo::internal::Serialize<{{serializer_type}}>( + {{input_field}}, &{{output}}->{{name}}, {{context}}); +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !mojo::internal::IsHandleOrInterfaceValid({{output}}->{{name}}), +{%- if kind|is_associated_kind %} + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID, +{%- else %} + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, +{%- endif %} + "invalid {{name}} in {{struct_display_name}}"); +{%- endif %} + +{%- elif kind|is_enum_kind %} + mojo::internal::Serialize<{{serializer_type}}>( + {{input_field}}, &{{output}}->{{name}}); + +{%- else %} + {{output}}->{{name}} = {{input_field}}; +{%- endif %} +{%- endfor %} +{%- endmacro -%} + +{# Deserializes the specified struct. + |struct| is the struct definition. + |input| is the name of the input struct data view. It is expected to be + non-null. + |output_field_pattern| should be a pattern that contains one string + placeholder, for example, "result->%s", "p_%s". The placeholder will be + substituted with struct field names to refer to the output fields. + |context| is the name of the serialization context. + |success| is the name of a bool variable to track success of the operation. + This macro is expanded to do deserialization for both: + - user-defined structs: the output is an instance of the corresponding + struct wrapper class. + - method parameters/response parameters: the output is a list of + arguments. #} +{%- macro deserialize(struct, input, output_field_pattern, success) -%} +{%- for pf in struct.packed.packed_fields_in_ordinal_order %} +{%- set output_field = output_field_pattern|format(pf.field.name) %} +{%- set name = pf.field.name %} +{%- set kind = pf.field.kind %} +{%- if kind|is_object_kind or kind|is_enum_kind %} + if (!{{input}}.Read{{name|under_to_camel}}(&{{output_field}})) + {{success}} = false; +{%- elif kind|is_any_handle_kind %} + {{output_field}} = {{input}}.Take{{name|under_to_camel}}(); +{%- elif kind|is_any_interface_kind %} + {{output_field}} = + {{input}}.Take{{name|under_to_camel}}<decltype({{output_field}})>(); +{%- else %} + {{output_field}} = {{input}}.{{name}}(); +{%- endif %} +{%- endfor %} +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl new file mode 100644 index 0000000000..835178beda --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl @@ -0,0 +1,57 @@ +{%- import "struct_macros.tmpl" as struct_macros %} +{%- set data_view = struct|get_qualified_name_for_kind ~ "DataView" %} +{%- set data_type = struct|get_qualified_name_for_kind(internal=True) %} + +namespace internal { + +template <typename MaybeConstUserType> +struct Serializer<{{data_view}}, MaybeConstUserType> { + using UserType = typename std::remove_const<MaybeConstUserType>::type; + using Traits = StructTraits<{{data_view}}, UserType>; + + static size_t PrepareToSerialize(MaybeConstUserType& input, + SerializationContext* context) { + if (CallIsNullIfExists<Traits>(input)) + return 0; + + void* custom_context = CustomContextHelper<Traits>::SetUp(input, context); + ALLOW_UNUSED_LOCAL(custom_context); + + {{struct_macros.get_serialized_size( + struct, "CallWithContext(Traits::%s, input, custom_context)", + "context", True)|indent(2)}} + return size; + } + + static void Serialize(MaybeConstUserType& input, + Buffer* buffer, + {{data_type}}** output, + SerializationContext* context) { + if (CallIsNullIfExists<Traits>(input)) { + *output = nullptr; + return; + } + + void* custom_context = CustomContextHelper<Traits>::GetNext(context); + + {{struct_macros.serialize( + struct, struct.name ~ " struct", + "CallWithContext(Traits::%s, input, custom_context)", "result", + "buffer", "context", True)|indent(2)}} + *output = result; + + CustomContextHelper<Traits>::TearDown(input, custom_context); + } + + static bool Deserialize({{data_type}}* input, + UserType* output, + SerializationContext* context) { + if (!input) + return CallSetToNullIfExists<Traits>(output); + + {{data_view}} data_view(input, context); + return Traits::Read(data_view, output); + } +}; + +} // namespace internal diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl new file mode 100644 index 0000000000..1b7cf8954b --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl @@ -0,0 +1,32 @@ +{%- set mojom_type = struct|get_qualified_name_for_kind %} + +template <> +struct {{export_attribute}} StructTraits<{{mojom_type}}::DataView, + {{mojom_type}}Ptr> { + static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; } + static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); } + +{%- for field in struct.fields %} +{%- set return_ref = field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} +{# We want the field accessor to be const whenever possible to allow + structs to be used as map keys. + TODO(tibell): Make this check more precise to deal with e.g. + custom types which don't contain handles but require non-const + reference for serialization. #} +{%- set maybe_const = "" if field.kind|contains_handles_or_interfaces else "const" %} +{%- if return_ref %} + static {{maybe_const}} decltype({{mojom_type}}::{{field.name}})& {{field.name}}( + {{maybe_const}} {{mojom_type}}Ptr& input) { + return input->{{field.name}}; + } +{%- else %} + static decltype({{mojom_type}}::{{field.name}}) {{field.name}}( + const {{mojom_type}}Ptr& input) { + return input->{{field.name}}; + } +{%- endif %} +{%- endfor %} + + static bool Read({{mojom_type}}::DataView input, {{mojom_type}}Ptr* output); +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl new file mode 100644 index 0000000000..f84337f5bf --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl @@ -0,0 +1,14 @@ +{%- import "struct_macros.tmpl" as struct_macros %} +{%- set mojom_type = struct|get_qualified_name_for_kind %} + +// static +bool StructTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read( + {{mojom_type}}::DataView input, + {{mojom_type}}Ptr* output) { + bool success = true; + {{mojom_type}}Ptr result({{mojom_type}}::New()); + {{struct_macros.deserialize(struct, "input", "result->%s", + "success")|indent(4)}} + *output = std::move(result); + return success; +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl new file mode 100644 index 0000000000..5973ba294b --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl @@ -0,0 +1,92 @@ +class {{union.name}}DataView { + public: + using Tag = internal::{{union.name}}_Data::{{union.name}}_Tag; + + {{union.name}}DataView() {} + + {{union.name}}DataView( + internal::{{union.name}}_Data* data, + mojo::internal::SerializationContext* context) +{%- if union|requires_context_for_data_view %} + : data_(data), context_(context) {} +{%- else %} + : data_(data) {} +{%- endif %} + + bool is_null() const { + // For inlined unions, |data_| is always non-null. In that case we need to + // check |data_->is_null()|. + return !data_ || data_->is_null(); + } + + Tag tag() const { return data_->tag; } + +{%- for field in union.fields %} +{%- set kind = field.kind %} +{%- set name = field.name %} + bool is_{{name}}() const { return data_->tag == Tag::{{name|upper}}; } + +{%- if kind|is_object_kind %} + inline void Get{{name|under_to_camel}}DataView( + {{kind|cpp_data_view_type}}* output); + + template <typename UserType> + WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) { + DCHECK(is_{{name}}()); + return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + data_->data.f_{{name}}.Get(), output, context_); + } + +{%- elif kind|is_enum_kind %} + template <typename UserType> + WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const { + DCHECK(is_{{name}}()); + return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + data_->data.f_{{name}}, output); + } + + {{kind|cpp_data_view_type}} {{name}}() const { + DCHECK(is_{{name}}()); + return static_cast<{{kind|cpp_data_view_type}}>( + data_->data.f_{{name}}); + } + +{%- elif kind|is_any_handle_kind %} + {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() { + DCHECK(is_{{name}}()); + {{kind|cpp_data_view_type}} result; + bool ret = + mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + &data_->data.f_{{name}}, &result, context_); + DCHECK(ret); + return result; + } + +{%- elif kind|is_any_interface_kind %} + template <typename UserType> + UserType Take{{name|under_to_camel}}() { + DCHECK(is_{{name}}()); + UserType result; + bool ret = + mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( + &data_->data.f_{{name}}, &result, context_); + DCHECK(ret); + return result; + } + +{%- else %} + {{kind|cpp_data_view_type}} {{name}}() const { + DCHECK(is_{{name}}()); + return data_->data.f_{{name}}; + } + +{%- endif %} +{%- endfor %} + + private: + internal::{{union.name}}_Data* data_ = nullptr; +{%- if union|requires_context_for_data_view %} + mojo::internal::SerializationContext* context_ = nullptr; +{%- endif %} +}; + diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl new file mode 100644 index 0000000000..6da9280a73 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl @@ -0,0 +1,12 @@ +{%- for field in union.fields %} +{%- set kind = field.kind %} +{%- set name = field.name %} + +{%- if kind|is_object_kind %} +inline void {{union.name}}DataView::Get{{name|under_to_camel}}DataView( + {{kind|cpp_data_view_type}}* output) { + DCHECK(is_{{name}}()); + *output = {{kind|cpp_data_view_type}}(data_->data.f_{{name}}.Get(), context_); +} +{%- endif %} +{%- endfor %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl new file mode 100644 index 0000000000..005ba76b61 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl @@ -0,0 +1,56 @@ +{%- set class_name = union.name ~ "_Data" -%} +{%- set enum_name = union.name ~ "_Tag" -%} +{%- import "struct_macros.tmpl" as struct_macros %} + +class {{class_name}} { + public: + // Used to identify Mojom Union Data Classes. + typedef void MojomUnionDataType; + + {{class_name}}() {} + // Do nothing in the destructor since it won't be called when it is a + // non-inlined union. + ~{{class_name}}() {} + + static {{class_name}}* New(mojo::internal::Buffer* buf) { + return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}(); + } + + static bool Validate(const void* data, + mojo::internal::ValidationContext* validation_context, + bool inlined); + + bool is_null() const { return size == 0; } + + void set_null() { + size = 0U; + tag = static_cast<{{enum_name}}>(0); + data.unknown = 0U; + } + + enum class {{enum_name}} : uint32_t { +{% for field in union.fields %} + {{field.name|upper}}, +{%- endfor %} + }; + + // A note on layout: + // "Each non-static data member is allocated as if it were the sole member of + // a struct." - Section 9.5.2 ISO/IEC 14882:2011 (The C++ Spec) + union MOJO_ALIGNAS(8) Union_ { +{%- for field in union.fields %} +{%- if field.kind.spec == 'b' %} + uint8_t f_{{field.name}} : 1; +{%- else %} + {{field.kind|cpp_union_field_type}} f_{{field.name}}; +{%- endif %} +{%- endfor %} + uint64_t unknown; + }; + + uint32_t size; + {{enum_name}} tag; + Union_ data; +}; +static_assert(sizeof({{class_name}}) == mojo::internal::kUnionDataSize, + "Bad sizeof({{class_name}})"); diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl new file mode 100644 index 0000000000..af5ea9f8a8 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl @@ -0,0 +1,47 @@ +{%- import "validation_macros.tmpl" as validation_macros %} +{%- set class_name = union.name ~ "_Data" %} +{%- set enum_name = union.name ~ "_Tag" -%} + +// static +bool {{class_name}}::Validate( + const void* data, + mojo::internal::ValidationContext* validation_context, + bool inlined) { + if (!data) { + DCHECK(!inlined); + return true; + } + + // If it is inlined, the alignment is already enforced by its enclosing + // object. We don't have to validate that. + DCHECK(!inlined || mojo::internal::IsAligned(data)); + + if (!inlined && + !mojo::internal::ValidateNonInlinedUnionHeaderAndClaimMemory( + data, validation_context)) { + return false; + } + + const {{class_name}}* object = static_cast<const {{class_name}}*>(data); + ALLOW_UNUSED_LOCAL(object); + + if (inlined && object->is_null()) + return true; + + switch (object->tag) { +{% for field in union.fields %} + case {{enum_name}}::{{field.name|upper}}: { +{%- set field_expr = "object->data.f_" ~ field.name %} +{{validation_macros.validate_field(field, field_expr, union.name, false)|indent(4)}} + return true; + } +{%- endfor %} + default: { + ReportValidationError( + validation_context, + mojo::internal::VALIDATION_ERROR_UNKNOWN_UNION_TAG, + "unknown tag in {{union.name}}"); + return false; + } + } +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl new file mode 100644 index 0000000000..b589ae9147 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl @@ -0,0 +1,141 @@ +{%- set data_view = union|get_qualified_name_for_kind ~ "DataView" %} +{%- set data_type = union|get_qualified_name_for_kind(internal=True) %} + +namespace internal { + +template <typename MaybeConstUserType> +struct Serializer<{{data_view}}, MaybeConstUserType> { + using UserType = typename std::remove_const<MaybeConstUserType>::type; + using Traits = UnionTraits<{{data_view}}, UserType>; + + static size_t PrepareToSerialize(MaybeConstUserType& input, + bool inlined, + SerializationContext* context) { + size_t size = inlined ? 0 : sizeof({{data_type}}); + + if (CallIsNullIfExists<Traits>(input)) + return size; + + void* custom_context = CustomContextHelper<Traits>::SetUp(input, context); + ALLOW_UNUSED_LOCAL(custom_context); + + switch (CallWithContext(Traits::GetTag, input, custom_context)) { +{%- for field in union.fields %} +{%- set name = field.name %} + case {{data_view}}::Tag::{{name|upper}}: { +{%- if field.kind|is_object_kind or field.kind|is_associated_kind %} +{%- set kind = field.kind %} +{%- set serializer_type = kind|unmapped_type_for_serializer %} + decltype(CallWithContext(Traits::{{name}}, input, custom_context)) + in_{{name}} = CallWithContext(Traits::{{name}}, input, + custom_context); +{%- if kind|is_union_kind %} + size += mojo::internal::PrepareToSerialize<{{serializer_type}}>( + in_{{name}}, false, context); +{%- else %} + size += mojo::internal::PrepareToSerialize<{{serializer_type}}>( + in_{{name}}, context); +{%- endif %} +{%- endif %} + break; + } +{%- endfor %} + } + return size; + } + + static void Serialize(MaybeConstUserType& input, + Buffer* buffer, + {{data_type}}** output, + bool inlined, + SerializationContext* context) { + if (CallIsNullIfExists<Traits>(input)) { + if (inlined) + (*output)->set_null(); + else + *output = nullptr; + return; + } + + void* custom_context = CustomContextHelper<Traits>::GetNext(context); + + if (!inlined) + *output = {{data_type}}::New(buffer); + + {{data_type}}* result = *output; + ALLOW_UNUSED_LOCAL(result); + // TODO(azani): Handle unknown and objects. + // Set the not-null flag. + result->size = kUnionDataSize; + result->tag = CallWithContext(Traits::GetTag, input, custom_context); + switch (result->tag) { +{%- for field in union.fields %} +{%- set name = field.name %} +{%- set kind = field.kind %} +{%- set serializer_type = kind|unmapped_type_for_serializer %} + case {{data_view}}::Tag::{{field.name|upper}}: { + decltype(CallWithContext(Traits::{{name}}, input, custom_context)) + in_{{name}} = CallWithContext(Traits::{{name}}, input, + custom_context); +{%- if kind|is_object_kind %} + typename decltype(result->data.f_{{name}})::BaseType* ptr; +{%- if kind|is_union_kind %} + mojo::internal::Serialize<{{serializer_type}}>( + in_{{name}}, buffer, &ptr, false, context); +{%- elif kind|is_array_kind or kind|is_map_kind %} + const ContainerValidateParams {{name}}_validate_params( + {{kind|get_container_validate_params_ctor_args|indent(16)}}); + mojo::internal::Serialize<{{serializer_type}}>( + in_{{name}}, buffer, &ptr, &{{name}}_validate_params, context); +{%- else %} + mojo::internal::Serialize<{{serializer_type}}>( + in_{{name}}, buffer, &ptr, context); +{%- endif %} + result->data.f_{{name}}.Set(ptr); +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !ptr, mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + "null {{name}} in {{union.name}} union"); +{%- endif %} + +{%- elif kind|is_any_handle_or_interface_kind %} + mojo::internal::Serialize<{{serializer_type}}>( + in_{{name}}, &result->data.f_{{name}}, context); +{%- if not kind|is_nullable_kind %} + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !mojo::internal::IsHandleOrInterfaceValid(result->data.f_{{name}}), +{%- if kind|is_associated_kind %} + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID, +{%- else %} + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, +{%- endif %} + "invalid {{name}} in {{union.name}} union"); +{%- endif %} + +{%- elif kind|is_enum_kind %} + mojo::internal::Serialize<{{serializer_type}}>( + in_{{name}}, &result->data.f_{{name}}); + +{%- else %} + result->data.f_{{name}} = in_{{name}}; +{%- endif %} + break; + } +{%- endfor %} + } + + CustomContextHelper<Traits>::TearDown(input, custom_context); + } + + static bool Deserialize({{data_type}}* input, + UserType* output, + SerializationContext* context) { + if (!input || input->is_null()) + return CallSetToNullIfExists<Traits>(output); + + {{data_view}} data_view(input, context); + return Traits::Read(data_view, output); + } +}; + +} // namespace internal diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl new file mode 100644 index 0000000000..4933e57871 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl @@ -0,0 +1,24 @@ +{%- set mojom_type = union|get_qualified_name_for_kind %} + +template <> +struct {{export_attribute}} UnionTraits<{{mojom_type}}::DataView, + {{mojom_type}}Ptr> { + static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; } + static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); } + + static {{mojom_type}}::Tag GetTag(const {{mojom_type}}Ptr& input) { + return input->which(); + } + +{%- for field in union.fields %} +{%- set maybe_const_in = "" if field.kind|contains_handles_or_interfaces else "const" %} +{%- set maybe_const_out = "" if field.kind|contains_handles_or_interfaces or not field.kind|is_reference_kind else "const" %} +{# We want the field accessor to be const whenever possible to allow + structs to be used as map keys. #} + static {{maybe_const_out}} {{field.kind|cpp_union_trait_getter_return_type}} {{field.name}}({{maybe_const_in}} {{mojom_type}}Ptr& input) { + return input->get_{{field.name}}(); + } +{%- endfor %} + + static bool Read({{mojom_type}}::DataView input, {{mojom_type}}Ptr* output); +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl new file mode 100644 index 0000000000..cde3f95669 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl @@ -0,0 +1,47 @@ +{%- set mojom_type = union|get_qualified_name_for_kind %} + +// static +bool UnionTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read( + {{mojom_type}}::DataView input, + {{mojom_type}}Ptr* output) { + *output = {{mojom_type}}::New(); + {{mojom_type}}Ptr& result = *output; + + internal::UnionAccessor<{{mojom_type}}> result_acc(result.get()); + switch (input.tag()) { +{%- for field in union.fields %} + case {{mojom_type}}::Tag::{{field.name|upper}}: { +{%- set name = field.name %} +{%- set kind = field.kind %} +{%- set serializer_type = kind|unmapped_type_for_serializer %} +{%- if kind|is_object_kind %} + result_acc.SwitchActive({{mojom_type}}::Tag::{{name|upper}}); + if (!input.Read{{name|under_to_camel}}(result_acc.data()->{{name}})) + return false; + +{%- elif kind|is_any_handle_kind %} + auto result_{{name}} = input.Take{{name|under_to_camel}}(); + result->set_{{name}}(std::move(result_{{name}})); + +{%- elif kind|is_any_interface_kind %} + auto result_{{name}} = + input.Take{{name|under_to_camel}}<typename std::remove_reference<decltype(result->get_{{name}}())>::type>(); + result->set_{{name}}(std::move(result_{{name}})); + +{%- elif kind|is_enum_kind %} + decltype(result->get_{{name}}()) result_{{name}}; + if (!input.Read{{name|under_to_camel}}(&result_{{name}})) + return false; + result->set_{{name}}(result_{{name}}); + +{%- else %} + result->set_{{name}}(input.{{name}}()); +{%- endif %} + break; + } +{%- endfor %} + default: + return false; + } + return true; +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl new file mode 100644 index 0000000000..a50a585c09 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl @@ -0,0 +1,82 @@ +{#- Validates the specified field, which is supposed to be an object + (struct/array/string/map/union). If it is a union, |union_is_inlined| + indicates whether the union is inlined. (Nested unions are not inlined.) + This macro is expanded by the Validate() method. #} +{%- macro validate_object(field, field_expr, object_name, union_is_inlined) %} +{%- set name = field.name %} +{%- set kind = field.kind %} +{%- if not kind|is_nullable_kind %} +{%- if kind|is_union_kind and union_is_inlined %} + if (!mojo::internal::ValidateInlinedUnionNonNullable( + {{field_expr}}, "null {{name}} field in {{object_name}}", + validation_context)) { + return false; + } +{%- else %} + if (!mojo::internal::ValidatePointerNonNullable( + {{field_expr}}, "null {{name}} field in {{object_name}}", + validation_context)) { + return false; + } +{%- endif %} +{%- endif %} +{%- if kind|is_array_kind or kind|is_string_kind or kind|is_map_kind %} + const mojo::internal::ContainerValidateParams {{name}}_validate_params( + {{kind|get_container_validate_params_ctor_args|indent(6)}}); + if (!mojo::internal::ValidateContainer({{field_expr}}, validation_context, + &{{name}}_validate_params)) { + return false; + } +{%- elif kind|is_struct_kind %} + if (!mojo::internal::ValidateStruct({{field_expr}}, validation_context)) + return false; +{%- elif kind|is_union_kind %} +{%- if union_is_inlined %} + if (!mojo::internal::ValidateInlinedUnion({{field_expr}}, validation_context)) + return false; +{%- else %} + if (!mojo::internal::ValidateNonInlinedUnion({{field_expr}}, + validation_context)) + return false; +{%- endif %} +{%- else %} +#error Not reached! +{%- endif %} +{%- endmacro %} + +{#- Validates the specified field, which is supposed to be a handle, + an interface, an associated interface or an associated interface request. + This macro is expanded by the Validate() method. #} +{%- macro validate_handle_or_interface(field, field_expr, object_name) %} +{%- set name = field.name %} +{%- set kind = field.kind %} +{%- if not kind|is_nullable_kind %} + if (!mojo::internal::ValidateHandleOrInterfaceNonNullable( + {{field_expr}}, + "invalid {{name}} field in {{object_name}}", validation_context)) { + return false; + } +{%- endif %} + if (!mojo::internal::ValidateHandleOrInterface({{field_expr}}, + validation_context)) { + return false; + } +{%- endmacro %} + +{#- Validates the specified field, which is supposed to be an enum. + This macro is expanded by the Validate() method. #} +{%- macro validate_enum(field, field_expr) %} + if (!{{field.kind|get_qualified_name_for_kind(internal=True,flatten_nested_kind=True)}} + ::Validate({{field_expr}}, validation_context)) + return false; +{%- endmacro %} + +{%- macro validate_field(field, field_expr, object_name, union_is_inlined) %} +{%- if field.kind|is_object_kind -%} +{{validate_object(field, field_expr, object_name, union_is_inlined)}} +{%- elif field.kind|is_any_handle_or_interface_kind -%} +{{validate_handle_or_interface(field, field_expr, object_name)}} +{%- elif field.kind|is_enum_kind %} +{{validate_enum(field, field_expr)}} +{%- endif %} +{%- endmacro %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl new file mode 100644 index 0000000000..7ad9b4e1bc --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl @@ -0,0 +1,94 @@ +class {{export_attribute}} {{struct.name}} { + public: + using DataView = {{struct.name}}DataView; + using Data_ = internal::{{struct.name}}_Data; + +{#--- Enums #} +{%- for enum in struct.enums -%} + using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}}; +{%- endfor %} + +{#--- Constants #} +{%- for constant in struct.constants %} + static {{constant|format_constant_declaration(nested=True)}}; +{%- endfor %} + + template <typename... Args> + static {{struct.name}}Ptr New(Args&&... args) { + return {{struct.name}}Ptr( + base::in_place, + std::forward<Args>(args)...); + } + + template <typename U> + static {{struct.name}}Ptr From(const U& u) { + return mojo::TypeConverter<{{struct.name}}Ptr, U>::Convert(u); + } + + template <typename U> + U To() const { + return mojo::TypeConverter<U, {{struct.name}}>::Convert(*this); + } + +{% for constructor in struct|struct_constructors %} + {% if constructor.params|length == 1 %}explicit {% endif %}{{struct.name}}( +{%- for field in constructor.params %} +{%- set type = field.kind|cpp_wrapper_param_type %} +{%- set name = field.name %} + {{type}} {{name}} +{%- if not loop.last -%},{%- endif %} +{%- endfor %}); +{% endfor %} + ~{{struct.name}}(); + + // Clone() is a template so it is only instantiated if it is used. Thus, the + // bindings generator does not need to know whether Clone() or copy + // constructor/assignment are available for members. + template <typename StructPtrType = {{struct.name}}Ptr> + {{struct.name}}Ptr Clone() const; + + // Equals() is a template so it is only instantiated if it is used. Thus, the + // bindings generator does not need to know whether Equals() or == operator + // are available for members. + template <typename T, + typename std::enable_if<std::is_same< + T, {{struct.name}}>::value>::type* = nullptr> + bool Equals(const T& other) const; + +{%- if struct|is_hashable %} + size_t Hash(size_t seed) const; +{%- endif %} + +{%- set serialization_result_type = "WTF::Vector<uint8_t>" + if for_blink else "std::vector<uint8_t>" %} + + template <typename UserType> + static {{serialization_result_type}} Serialize(UserType* input) { + return mojo::internal::StructSerializeImpl< + {{struct.name}}::DataView, {{serialization_result_type}}>(input); + } + + template <typename UserType> + static bool Deserialize(const {{serialization_result_type}}& input, + UserType* output) { + return mojo::internal::StructDeserializeImpl< + {{struct.name}}::DataView, {{serialization_result_type}}>( + input, output, Validate); + } + +{#--- Struct members #} +{% for field in struct.fields %} +{%- set type = field.kind|cpp_wrapper_type %} +{%- set name = field.name %} + {{type}} {{name}}; +{%- endfor %} + + private: + static bool Validate(const void* data, + mojo::internal::ValidationContext* validation_context); + +{%- if struct|contains_move_only_members %} + DISALLOW_COPY_AND_ASSIGN({{struct.name}}); +{%- endif %} +}; + diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl new file mode 100644 index 0000000000..ab8c22d49c --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl @@ -0,0 +1,39 @@ +{% for constructor in struct|struct_constructors %} +{{struct.name}}::{{struct.name}}( +{%- for field in constructor.params %} +{%- set type = field.kind|cpp_wrapper_param_type %} +{%- set name = field.name %} + {{type}} {{name}}_in +{%- if not loop.last -%},{%- endif %} +{%- endfor %}) +{%- for field, is_parameter in constructor.fields %} +{%- set name = field.name %} + {% if loop.first %}:{% else %} {% endif %} {{name}}( +{%- if is_parameter -%} +std::move({{name}}_in) +{%- else -%} +{{ field|default_value }} +{%- endif -%} +){% if not loop.last %},{% endif %} +{%- endfor %} {} +{% endfor %} +{{struct.name}}::~{{struct.name}}() = default; + +{%- if struct|is_hashable %} +size_t {{struct.name}}::Hash(size_t seed) const { +{%- for field in struct.fields %} +{%- if for_blink %} + seed = mojo::internal::WTFHash(seed, this->{{field.name}}); +{%- else %} + seed = mojo::internal::Hash(seed, this->{{field.name}}); +{%- endif %} +{%- endfor %} + return seed; +} +{%- endif %} + +bool {{struct.name}}::Validate( + const void* data, + mojo::internal::ValidationContext* validation_context) { + return Data_::Validate(data, validation_context); +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl new file mode 100644 index 0000000000..feb861569f --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl @@ -0,0 +1,20 @@ +template <typename StructPtrType> +{{struct.name}}Ptr {{struct.name}}::Clone() const { + return New( +{%- for field in struct.fields %} + mojo::Clone({{field.name}}) +{%- if not loop.last -%},{%- endif %} +{%- endfor %} + ); +} + +template <typename T, + typename std::enable_if<std::is_same< + T, {{struct.name}}>::value>::type*> +bool {{struct.name}}::Equals(const T& other) const { +{%- for field in struct.fields %} + if (!mojo::internal::Equals(this->{{field.name}}, other.{{field.name}})) + return false; +{%- endfor %} + return true; +} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl new file mode 100644 index 0000000000..8b7cf9e6b1 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl @@ -0,0 +1,80 @@ +class {{export_attribute}} {{union.name}} { + public: + using DataView = {{union.name}}DataView; + using Data_ = internal::{{union.name}}_Data; + using Tag = Data_::{{union.name}}_Tag; + + static {{union.name}}Ptr New(); + + template <typename U> + static {{union.name}}Ptr From(const U& u) { + return mojo::TypeConverter<{{union.name}}Ptr, U>::Convert(u); + } + + template <typename U> + U To() const { + return mojo::TypeConverter<U, {{union.name}}>::Convert(*this); + } + + {{union.name}}(); + ~{{union.name}}(); + + // Clone() is a template so it is only instantiated if it is used. Thus, the + // bindings generator does not need to know whether Clone() or copy + // constructor/assignment are available for members. + template <typename UnionPtrType = {{union.name}}Ptr> + {{union.name}}Ptr Clone() const; + + // Equals() is a template so it is only instantiated if it is used. Thus, the + // bindings generator does not need to know whether Equals() or == operator + // are available for members. + template <typename T, + typename std::enable_if<std::is_same< + T, {{union.name}}>::value>::type* = nullptr> + bool Equals(const T& other) const; + +{%- if union|is_hashable %} + size_t Hash(size_t seed) const; +{%- endif %} + + Tag which() const { + return tag_; + } + +{% for field in union.fields %} + bool is_{{field.name}}() const { return tag_ == Tag::{{field.name|upper}}; } + + {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const { + DCHECK(tag_ == Tag::{{field.name|upper}}); +{%- if field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} + return *(data_.{{field.name}}); +{%- else %} + return data_.{{field.name}}; +{%- endif %} + } + + void set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}}); +{%- endfor %} + + private: + friend class mojo::internal::UnionAccessor<{{union.name}}>; + union Union_ { + Union_() {} + ~Union_() {} + +{%- for field in union.fields %} +{%- if field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} + {{field.kind|cpp_wrapper_type}}* {{field.name}}; +{%- else %} + {{field.kind|cpp_wrapper_type}} {{field.name}}; +{%- endif %} +{%- endfor %} + }; + void SwitchActive(Tag new_active); + void SetActive(Tag new_active); + void DestroyActive(); + Tag tag_; + Union_ data_; +}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl new file mode 100644 index 0000000000..b9e416a9f4 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl @@ -0,0 +1,85 @@ +// static +{{union.name}}Ptr {{union.name}}::New() { + return {{union.name}}Ptr(base::in_place); +} + +{{union.name}}::{{union.name}}() { + // TODO(azani): Implement default values here when/if we support them. + // TODO(azani): Set to UNKNOWN when unknown is implemented. + SetActive(static_cast<Tag>(0)); +} + +{{union.name}}::~{{union.name}}() { + DestroyActive(); +} + +{% for field in union.fields %} +void {{union.name}}::set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}}) { + SwitchActive(Tag::{{field.name|upper}}); +{% if field.kind|is_string_kind %} + *(data_.{{field.name}}) = {{field.name}}; +{% elif field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} + *(data_.{{field.name}}) = std::move({{field.name}}); +{%- else %} + data_.{{field.name}} = {{field.name}}; +{%- endif %} +} +{%- endfor %} + +void {{union.name}}::SwitchActive(Tag new_active) { + if (new_active == tag_) { + return; + } + + DestroyActive(); + SetActive(new_active); +} + +void {{union.name}}::SetActive(Tag new_active) { + switch (new_active) { +{% for field in union.fields %} + case Tag::{{field.name|upper}}: +{% if field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} + data_.{{field.name}} = new {{field.kind|cpp_wrapper_type}}(); +{%- endif %} + break; +{%- endfor %} + } + + tag_ = new_active; +} + +void {{union.name}}::DestroyActive() { + switch (tag_) { +{% for field in union.fields %} + case Tag::{{field.name|upper}}: +{% if field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} + delete data_.{{field.name}}; +{%- endif %} + break; +{%- endfor %} + } +} + +{%- if union|is_hashable %} +size_t {{union.name}}::Hash(size_t seed) const { + seed = mojo::internal::HashCombine(seed, static_cast<uint32_t>(tag_)); + switch (tag_) { +{% for field in union.fields %} + case Tag::{{field.name|upper}}: +{%- if for_blink %} + return mojo::internal::WTFHash(seed, data_.{{field.name}}); +{%- else %} + return mojo::internal::Hash(seed, data_.{{field.name}}); +{%- endif %} +{%- endfor %} + default: + NOTREACHED(); + return seed; + } +} + +{%- endif %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl new file mode 100644 index 0000000000..4c4851fa83 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl @@ -0,0 +1,41 @@ +template <typename UnionPtrType> +{{union.name}}Ptr {{union.name}}::Clone() const { + // Use UnionPtrType to prevent the compiler from trying to compile this + // without being asked. + UnionPtrType rv(New()); + switch (tag_) { +{%- for field in union.fields %} + case Tag::{{field.name|upper}}: +{%- if field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} + rv->set_{{field.name}}(mojo::Clone(*data_.{{field.name}})); +{%- else %} + rv->set_{{field.name}}(mojo::Clone(data_.{{field.name}})); +{%- endif %} + break; +{%- endfor %} + }; + return rv; +} + +template <typename T, + typename std::enable_if<std::is_same< + T, {{union.name}}>::value>::type*> +bool {{union.name}}::Equals(const T& other) const { + if (tag_ != other.which()) + return false; + + switch (tag_) { +{%- for field in union.fields %} + case Tag::{{field.name|upper}}: +{%- if field.kind|is_object_kind or + field.kind|is_any_handle_or_interface_kind %} + return mojo::internal::Equals(*(data_.{{field.name}}), *(other.data_.{{field.name}})); +{%- else %} + return mojo::internal::Equals(data_.{{field.name}}, other.data_.{{field.name}}); +{%- endif %} +{%- endfor %} + }; + + return false; +} |