1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
|
# -*- coding: utf-8 -*-
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Objects for describing template code to be generated from structured.xml."""
import templates_validator as validator_tmpl
from codegen_util import FileInfo, Util
from code_generator import (EventTemplateBase, ProjectInfoBase,
EventInfoBase, MetricInfoBase)
CHROMIUM_TARGET="chromium"
class ProjectInfoCpp(ProjectInfoBase):
"""Codegen-related info about a project in C++."""
def __init__(self, project):
super().__init__(project, CHROMIUM_TARGET)
self.validator = '{}ProjectValidator'.format(self.name)
self.validator_snake_name = Util.camel_to_snake(self.validator)
def build_validator_code(self) -> str:
event_infos = list(EventInfoCpp(event, self) for event in self.events)
# Generate map entries.
validator_map_str = ';\n '.join(
'event_validators_.emplace("{}", std::make_unique<{}>())'.format(
event_info.name, event_info.validator_name)
for event_info in event_infos)
event_name_map_str = ';\n '.join(
'event_name_map_.emplace(UINT64_C({}), "{}")'.format(
event_info.name_hash, event_info.name)
for event_info in event_infos)
return validator_tmpl.IMPL_PROJECT_VALIDATOR_TEMPLATE.format(
project=self,
event_validator_map=validator_map_str,
event_name_map=event_name_map_str)
class EventInfoCpp(EventInfoBase):
"""Codegen-related info about an event in C++."""
def __init__(self, event, project_info):
super().__init__(event, project_info)
self.validator_name = '{}_{}EventValidator'.format(self.project_name,
self.name)
self.validator_snake_name = Util.camel_to_snake(self.validator_name)
def build_metric_hash_map(self) -> str:
if self.platform == 'cros':
return ''
metric_infos = (MetricInfoCpp(metric) for metric in self.metrics)
return ',\n '.join(
'{{\"{}\", {{ Event::MetricType::{}, UINT64_C({})}}}}'.format(
metric_info.name, metric_info.type_enum, metric_info.hash)
for metric_info in metric_infos)
def build_metric_name_map(self) -> str:
metric_infos = (MetricInfoCpp(metric) for metric in self.metrics)
return ',\n '.join(
'{{ UINT64_C({}), "{}" }}'.format(metric_info.hash, metric_info.name)
for metric_info in metric_infos)
def build_validator_code(self) -> str:
return validator_tmpl.IMPL_EVENT_VALIDATOR_TEMPLATE.format(
event=self,
metric_hash_map=self.build_metric_hash_map(),
metrics_name_map=self.build_metric_name_map())
class MetricInfoCpp(MetricInfoBase):
"""Codegen-related info about a metric in C++."""
def __init__(self, metric):
super().__init__(metric)
if metric.type == 'hmac-string':
self.type = 'std::string&'
self.setter = 'AddHmacMetric'
self.type_enum = 'kHmac'
self.base_value = 'base::Value(value)'
elif metric.type == 'int':
self.type = 'int64_t'
self.setter = 'AddIntMetric'
self.type_enum = 'kLong'
self.base_value = 'base::Value(base::NumberToString(value))'
elif metric.type == 'raw-string':
self.type = 'std::string&'
self.setter = 'AddRawStringMetric'
self.type_enum = 'kRawString'
self.base_value = 'base::Value(value)'
elif metric.type == 'double':
self.type = 'double'
self.setter = 'AddDoubleMetric'
self.type_enum = 'kDouble'
self.base_value = 'base::Value(value)'
elif metric.type == 'int-array':
# todo(b/341807121): support int array in chromium.
self.type_enum = ''
else:
if self.is_enum:
self.type = metric.type
self.setter = f'AddEnumMetric<{self.type}>'
self.type_enum = 'kInt'
self.base_value = 'base::Value((int) value)'
else:
raise ValueError('Invalid metric type.')
class TemplateCpp(EventTemplateBase):
"""Template for producing C++ code from structured.xml."""
def __init__(self, model, dirname, basename, file_template, project_template,
enum_template, event_template, metric_template, header):
super().__init__(model, dirname, basename, file_template, project_template,
enum_template, event_template, metric_template)
self.header = header
def write_file(self):
file_info = FileInfo(self.dirname, self.basename)
with open(file_info.filepath, 'w') as f:
f.write(self._stamp_file(file_info))
def _stamp_file(self, file_info):
project_code = ''.join(
self._stamp_project(file_info, p) for p in self.model.projects)
return self.file_template.format(file=file_info, project_code=project_code)
def _stamp_project(self, file_info, project):
project_info = ProjectInfoCpp(project)
event_code = ''.join(
self._stamp_event(file_info, project_info, event)
for event in project.events)
if self.header:
enum_code = '\n\n'.join(
[self._stamp_enum(enum) for enum in project.enums])
return self.project_template.format(file=file_info,
project=project_info,
enum_code=enum_code,
event_code=event_code)
return self.project_template.format(file=file_info,
project=project_info,
event_code=event_code)
def _stamp_event(self, file_info, project_info, event):
event_info = EventInfoCpp(event, project_info)
metric_code = ''
if project_info.platform != 'cros':
metric_code = ''.join(
self._stamp_metric(file_info, event_info, metric)
for metric in event.metrics)
return self.event_template.format(file=file_info,
project=project_info,
event=event_info,
metric_code=metric_code)
def _stamp_enum(self, enum):
variants = ',\n'.join(
['{v.name} = {v.value}'.format(v=v) for v in enum.variants])
return self.enum_template.format(enum=enum, variants=variants)
def _stamp_metric(self, file_info, event_info, metric):
return self.metric_template.format(file=file_info,
event=event_info,
metric=MetricInfoCpp(metric))
class ValidatorHeaderTemplate:
"""Template for generating header validator code from structured.xml."""
def __init__(self, dirname, basename):
self.dirname = dirname
self.basename = basename
def write_file(self) -> None:
file_info = FileInfo(self.dirname, self.basename)
with open(file_info.filepath, 'w') as f:
f.write(self._stamp_file(file_info))
def _stamp_file(self, file_info) -> str:
return validator_tmpl.HEADER_FILE_TEMPLATE.format(file=file_info)
class ValidatorImplTemplate:
"""Template for generating implementation validator code from structured.xml.
The generated file will store a static map containing all the validators
mapped by event name. All validators are initialized statically.
Almost everything is generated in an anonymous namespace as the generated map
should not be exposed. The generated code will be in the following order:
1) EventValidator class implementation.
2) Project map initialization mapping event name to corresponding
EventValidator.
3) Project class implementation.
4) Map initialization mapping project name to ProjectValidator.
"""
def __init__(self, structured_model, dirname, basename):
self.structured_model = structured_model
self.dirname = dirname
self.basename = basename
self.projects = self.structured_model.projects
def write_file(self) -> None:
file_info = FileInfo(self.dirname, self.basename)
with open(file_info.filepath, 'w') as f:
f.write(self._stamp_file(file_info))
def _stamp_file(self, file_info) -> str:
event_code = []
project_event_maps = []
project_code = []
for project in self.projects:
project_info = ProjectInfoCpp(project)
event_infos = (EventInfoCpp(event, project_info)
for event in project.events)
project_event_code = '\n'.join(event_info.build_validator_code()
for event_info in event_infos)
event_code.append(project_event_code)
project_code.append(project_info.build_validator_code())
# Turn all lists into strings.
events_code_str = ''.join(event_code)
project_event_maps_str = '\n'.join(project_event_maps)
project_code_str = ''.join(project_code)
return validator_tmpl.IMPL_FILE_TEMPLATE.format(
file=file_info,
projects_code=project_code_str,
event_code=events_code_str,
project_event_maps=project_event_maps_str,
project_map=self._build_project_map(),
name_map=self._build_name_map())
def _build_project_map(self) -> str:
project_infos = (ProjectInfoCpp(project) for project in self.projects)
return ';\n '.join(
'validators_.emplace("{}", std::make_unique<{}>())'.format(
project.name, project.validator) for project in project_infos)
def _build_name_map(self):
project_infos = (ProjectInfoCpp(project) for project in self.projects)
return ';\n '.join('project_name_map_.emplace(UINT64_C({}), "{}")'.format(
project.name_hash, project.name) for project in project_infos)
|