blob: 5f691bf1295faa279b69eae8c3c5bce9bbbb0dcb [file] [log] [blame]
{%- set class_name = struct.name ~ "_Data" %}
{#- TODO(yzshen): Consider eliminating _validate_object() and
_validate_handle(). #}
{#- Validates the specified struct field, which is supposed to be an object
(struct/array/string/map/union).
This macro is expanded by the Validate() method. #}
{%- macro _validate_object(struct, packed_field, err_string) %}
{%- set name = packed_field.field.name %}
{%- set kind = packed_field.field.kind %}
{%- set wrapper_type = kind|cpp_wrapper_type %}
{%- if not kind|is_nullable_kind %}
{%- if kind|is_union_kind %}
if (object->{{name}}.is_null()) {
{%- else %}
if (!object->{{name}}.offset) {
{%- endif %}
MOJO_INTERNAL_DEBUG_SET_ERROR_MSG({{err_string}}) <<
"null {{name}} field in {{struct.name}} struct";
return mojo::internal::ValidationError::UNEXPECTED_NULL_POINTER;
}
{%- endif %}
{%- if not kind|is_union_kind %}
if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) {
MOJO_INTERNAL_DEBUG_SET_ERROR_MSG({{err_string}}) << "";
return mojo::internal::ValidationError::ILLEGAL_POINTER;
}
{%- endif %}
{%- if kind|is_array_kind or kind|is_string_kind %}
const mojo::internal::ArrayValidateParams {{name}}_validate_params(
{{kind|get_array_validate_params_ctor_args|indent(6)}});
auto validate_retval =
{{wrapper_type}}::Data_::Validate(
mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
bounds_checker, &{{name}}_validate_params, {{err_string}});
if (validate_retval != mojo::internal::ValidationError::NONE) {
{%- elif kind|is_map_kind %}
const mojo::internal::ArrayValidateParams {{name}}_validate_params(
{{kind.value_kind|get_map_validate_params_ctor_args|indent(6)}});
auto validate_retval = {{wrapper_type}}::Data_::Validate(
mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
bounds_checker, &{{name}}_validate_params, {{err_string}});
if (validate_retval != mojo::internal::ValidationError::NONE) {
{%- elif kind|is_struct_kind %}
auto validate_retval = {{kind|get_name_for_kind}}::Data_::Validate(
mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
bounds_checker, {{err_string}});
if (validate_retval != mojo::internal::ValidationError::NONE) {
{%- elif kind|is_union_kind %}
auto validate_retval = {{kind|get_name_for_kind}}::Data_::Validate(
&object->{{name}}, bounds_checker, true, {{err_string}});
if (validate_retval != mojo::internal::ValidationError::NONE) {
{%- else %}
auto validate_retval = {{wrapper_type}}::Data_::Validate(
mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
bounds_checker, {{err_string}});
if (validate_retval != mojo::internal::ValidationError::NONE) {
{%- endif %}
return validate_retval;
}
{%- endmacro %}
{#- Validates the specified struct field, which is supposed to be a handle or
contain a handle (in the case of interfaces).
This macro is expanded by the Validate() method. #}
{%- macro _validate_handle(struct, packed_field, err_string) %}
{%- set name = packed_field.field.name %}
{%- set kind = packed_field.field.kind %}
{%- if kind|is_interface_kind %}
const mojo::Handle {{name}}_handle = object->{{name}}.handle;
{%- else %}
const mojo::Handle {{name}}_handle = object->{{name}};
{%- endif %}
{%- if not kind|is_nullable_kind %}
if ({{name}}_handle.value() == mojo::internal::kEncodedInvalidHandleValue) {
MOJO_INTERNAL_DEBUG_SET_ERROR_MSG({{err_string}})
<< "invalid {{name}} field in {{struct.name}} struct";
return mojo::internal::ValidationError::UNEXPECTED_INVALID_HANDLE;
}
{%- endif %}
if (!bounds_checker->ClaimHandle({{name}}_handle)) {
MOJO_INTERNAL_DEBUG_SET_ERROR_MSG({{err_string}}) << "";
return mojo::internal::ValidationError::ILLEGAL_HANDLE;
}
{%- endmacro %}
// static
{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) {
return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
}
// static
mojo::internal::ValidationError {{class_name}}::Validate(
const void* data,
mojo::internal::BoundsChecker* bounds_checker,
std::string* err) {
mojo::internal::ValidationError retval;
if (!data)
return mojo::internal::ValidationError::NONE;
retval = ValidateStructHeaderAndClaimMemory(data, bounds_checker, err);
if (retval != mojo::internal::ValidationError::NONE)
return retval;
// 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 const 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[MOJO_ARRAYSIZE(kVersionSizes) - 1].version) {
// Scan in reverse order to optimize for more recent versions.
for (int i = MOJO_ARRAYSIZE(kVersionSizes) - 1; i >= 0; --i) {
if (object->header_.version >= kVersionSizes[i].version) {
if (object->header_.num_bytes == kVersionSizes[i].num_bytes)
break;
MOJO_INTERNAL_DEBUG_SET_ERROR_MSG(err) << "";
return mojo::internal::ValidationError::UNEXPECTED_STRUCT_HEADER;
}
}
} else if (object->header_.num_bytes <
kVersionSizes[MOJO_ARRAYSIZE(kVersionSizes) - 1].num_bytes) {
MOJO_INTERNAL_DEBUG_SET_ERROR_MSG(err) << "";
return mojo::internal::ValidationError::UNEXPECTED_STRUCT_HEADER;
}
{#- 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_kind or kind|is_interface_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 mojo::internal::ValidationError::NONE;
{%- endif %}
{%- if kind|is_object_kind %}
{
{{_validate_object(struct, packed_field, "err")}}
}
{%- else %}
{
{{_validate_handle(struct, packed_field, "err")}}
}
{%- endif %}
{%- endif %}
{%- endfor %}
return mojo::internal::ValidationError::NONE;
}
void {{class_name}}::EncodePointersAndHandles(
std::vector<mojo::Handle>* handles) {
MOJO_CHECK(header_.version == {{struct.versions[-1].version}});
{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
{%- if pf.field.kind|is_union_kind %}
{{pf.field.name}}.EncodePointersAndHandles(handles);
{%- elif pf.field.kind|is_object_kind %}
mojo::internal::Encode(&this->{{pf.field.name}}, handles);
{%- elif pf.field.kind|is_any_handle_kind or pf.field.kind|is_interface_kind %}
mojo::internal::EncodeHandle(&this->{{pf.field.name}}, handles);
{%- endif %}
{%- endfor %}
}
void {{class_name}}::DecodePointersAndHandles(
std::vector<mojo::Handle>* handles) {
// NOTE: The memory backing |this| may has be smaller than |sizeof(*this)|, if
// the message comes from an older version.
{#- Before decoding fields introduced at a certain version, we need to add
a version check, which makes sure we skip further decoding if |this|
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 pf in struct.packed.packed_fields_in_ordinal_order %}
{%- set name = pf.field.name %}
{%- set kind = pf.field.kind %}
{%- if kind|is_object_kind or kind|is_any_handle_kind or kind|is_interface_kind %}
{%- if pf.min_version > last_checked_version %}
{%- set last_checked_version = pf.min_version %}
if (header_.version < {{pf.min_version}})
return;
{%- endif %}
{%- if kind|is_union_kind %}
{{name}}.DecodePointersAndHandles(handles);
{%- elif kind|is_object_kind %}
mojo::internal::Decode(&this->{{name}}, handles);
{%- else %}
mojo::internal::DecodeHandle(&this->{{name}}, handles);
{%- endif %}
{%- endif %}
{%- endfor %}
}
{{class_name}}::{{class_name}}() {
header_.num_bytes = sizeof(*this);
header_.version = {{struct.versions[-1].version}};
}