Skip to content

Commit 475ce8b

Browse files
authored
Merge pull request #1211 from aarongreig/aaron/allowHandlesInStructs
Update scripts and loader template to allow object handles in structs.
2 parents 057b52a + eb039c2 commit 475ce8b

File tree

4 files changed

+278
-12
lines changed

4 files changed

+278
-12
lines changed

scripts/core/CONTRIB.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,29 @@ arguments:
225225
* Pointer to handle arguments, such as out parameters, are prefixed with
226226
``ph`` i.e. ``phQueue``.
227227

228+
Limitations
229+
-----------
230+
231+
There are some limitations on the patterns our spec generator can handle. These
232+
limitations are due to convenience of implementation rather than design: if
233+
they are preventing you from implementing a feature please open an issue and we
234+
will be happy to try and accommodate your use case. Otherwise beware of the
235+
following:
236+
237+
* A function parameter or struct member which is a struct type that has any of
238+
the following members in its type definition must not have the ``[range]``
239+
tag:
240+
241+
* An object handle with the ``[range]`` tag
242+
243+
* A struct type with the ``[range]`` tag that has an object handle member
244+
245+
* A struct member which is a pointer to a struct type must not have the
246+
``[optional]`` tag if that struct (or any of its members, recursively) has
247+
an object handle member in its definition.
248+
249+
* A struct member which is an object handle must not have the ``[out]`` tag.
250+
228251
Forks and Pull Requests
229252
=======================
230253

scripts/parse_specs.py

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def _get_etor_value(value, prev):
8383
"""
8484
validate documents meet some basic (easily detectable) requirements of code generation
8585
"""
86-
def _validate_doc(f, d, tags, line_num):
86+
def _validate_doc(f, d, tags, line_num, meta):
8787
is_iso = lambda x : re.match(r"[_a-zA-Z][_a-zA-Z0-9]{0,30}", x)
8888

8989
def __validate_ordinal(d):
@@ -265,7 +265,35 @@ def __validate_base(d):
265265
elif type_traits.is_properties(d['name']) and not d.get('base', "").endswith("base_properties_t"):
266266
raise Exception("'base' must be '%s_base_properties_t': %s"%(namespace, d['name']))
267267

268-
def __validate_members(d, tags):
268+
def __validate_struct_range_members(name, members, meta):
269+
def has_handle(members, meta):
270+
for m in members:
271+
if type_traits.is_handle(m):
272+
return True
273+
if type_traits.is_struct(m, meta):
274+
return has_handle(
275+
type_traits.get_struct_members(m['type']), meta)
276+
return False
277+
278+
for m in members:
279+
if param_traits.is_range(m) and type_traits.is_handle(m['type']):
280+
raise Exception(
281+
f"struct range {name} must not contain range of object handles {m['name']}"
282+
)
283+
if type_traits.is_struct(m['type'], meta):
284+
member_members = type_traits.get_struct_members(
285+
m['type'], meta)
286+
# We can't handle a range of structs with handles within a range of structs
287+
if param_traits.is_range(m) and has_handle(
288+
member_members, meta):
289+
raise Exception(
290+
f"struct range {m['name']} is already within struct range {name}, and must not contain an object handle"
291+
)
292+
# We keep passing the original name so we can report it in
293+
# exception messages.
294+
__validate_struct_range_members(name, member_members, meta)
295+
296+
def __validate_members(d, tags, meta):
269297
if 'members' not in d:
270298
raise Exception("'%s' requires the following sequence of mappings: {`members`}"%d['type'])
271299

@@ -286,21 +314,28 @@ def __validate_members(d, tags):
286314
if not annotation:
287315
raise Exception(prefix+"'desc' must start with {'[in]', '[out]', '[in,out]'}")
288316

289-
if type_traits.is_handle(item['type']):
290-
raise Exception(prefix+"'type' must not be '*_handle_t': %s"%item['type'])
291-
292317
if item['type'].endswith("flag_t"):
293318
raise Exception(prefix+"'type' must not be '*_flag_t': %s"%item['type'])
294319

295320
if d['type'] == 'union'and item.get('tag') is None:
296321
raise Exception(prefix + f"union member {item['name']} must include a 'tag' annotation")
297322

323+
if type_traits.is_struct(item['type'],
324+
meta) and param_traits.is_range(item):
325+
member_members = type_traits.get_struct_members(
326+
item['type'], meta)
327+
__validate_struct_range_members(item['name'], member_members,
328+
meta)
329+
330+
if type_traits.is_handle(item['type']) and param_traits.is_output(item):
331+
raise Exception(prefix + f"struct member {item['name']} is an object handle, so it must not be have the [out] tag")
332+
298333
ver = __validate_version(item, prefix=prefix, base_version=d_ver)
299334
if ver < max_ver:
300335
raise Exception(prefix+"'version' must be increasing: %s"%item['version'])
301336
max_ver = ver
302337

303-
def __validate_params(d, tags):
338+
def __validate_params(d, tags, meta):
304339
if 'params' not in d:
305340
raise Exception("'function' requires the following sequence of mappings: {`params`}")
306341

@@ -347,6 +382,11 @@ def __validate_params(d, tags):
347382
if not has_queue:
348383
raise Exception(prefix+"bounds must only be used on entry points which take a `hQueue` parameter")
349384

385+
if type_traits.is_struct(item['type'],
386+
meta) and param_traits.is_range(item):
387+
members = type_traits.get_struct_members(item['type'], meta)
388+
__validate_struct_range_members(item['name'], members, meta)
389+
350390
ver = __validate_version(item, prefix=prefix, base_version=d_ver)
351391
if ver < max_ver:
352392
raise Exception(prefix+"'version' must be increasing: %s"%item['version'])
@@ -421,7 +461,7 @@ def __validate_union_tag(d):
421461
__validate_union_tag(d)
422462
__validate_type(d, 'name', tags)
423463
__validate_base(d)
424-
__validate_members(d, tags)
464+
__validate_members(d, tags, meta)
425465
__validate_details(d)
426466
__validate_ordinal(d)
427467
__validate_version(d)
@@ -435,7 +475,7 @@ def __validate_union_tag(d):
435475
else:
436476
__validate_name(d, 'name', tags, case='camel')
437477

438-
__validate_params(d, tags)
478+
__validate_params(d, tags, meta)
439479
__validate_details(d)
440480
__validate_ordinal(d)
441481
__validate_version(d)
@@ -893,7 +933,7 @@ def parse(section, version, tags, meta, ref):
893933

894934
for i, d in enumerate(docs):
895935
d = _preprocess(d)
896-
if not _validate_doc(f, d, tags, line_nums[i]):
936+
if not _validate_doc(f, d, tags, line_nums[i], meta):
897937
continue
898938

899939
d = _filter_version(d, float(version))

scripts/templates/helper.py

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,15 +231,15 @@ def is_array(cls, name):
231231
return True if re.match(cls.RE_ARRAY, name) else False
232232
except:
233233
return False
234-
234+
235235
@classmethod
236236
def get_array_length(cls, name):
237237
if not cls.is_array(name):
238238
raise Exception("Cannot find array length of non-array type.")
239239

240240
match = re.match(cls.RE_ARRAY, name)
241241
return match.groups()[1]
242-
242+
243243
@classmethod
244244
def get_array_element_type(cls, name):
245245
if not cls.is_array(name):
@@ -248,6 +248,14 @@ def get_array_element_type(cls, name):
248248
match = re.match(cls.RE_ARRAY, name)
249249
return match.groups()[0]
250250

251+
@staticmethod
252+
def get_struct_members(type_name, meta):
253+
struct_type = _remove_const_ptr(type_name)
254+
if not struct_type in meta['struct']:
255+
raise Exception(
256+
f"Cannot return members of non-struct type {struct_type}")
257+
return meta['struct'][struct_type]['members']
258+
251259
"""
252260
Extracts traits from a value name
253261
"""
@@ -427,7 +435,7 @@ def is_bounds(cls, item):
427435
return True if re.match(cls.RE_BOUNDS, item['desc']) else False
428436
except:
429437
return False
430-
438+
431439
@classmethod
432440
def tagged_member(cls, item):
433441
try:
@@ -1266,6 +1274,120 @@ def get_loader_prologue(namespace, tags, obj, meta):
12661274

12671275
return prologue
12681276

1277+
1278+
"""
1279+
Private:
1280+
Takes a list of struct members and recursively searches for class handles.
1281+
Returns a list of class handles with access chains to reach them (e.g.
1282+
"struct_a->struct_b.handle"). Also handles ranges of class handles and
1283+
ranges of structs with class handle members, although the latter only works
1284+
to one level of recursion i.e. a range of structs with a range of structs
1285+
with a handle member will not work.
1286+
"""
1287+
def get_struct_handle_members(namespace,
1288+
tags,
1289+
meta,
1290+
members,
1291+
parent='',
1292+
is_struct_range=False):
1293+
handle_members = []
1294+
for m in members:
1295+
if type_traits.is_class_handle(m['type'], meta):
1296+
m_tname = _remove_const_ptr(subt(namespace, tags, m['type']))
1297+
m_objname = re.sub(r"(\w+)_handle_t", r"\1_object_t", m_tname)
1298+
# We can deal with a range of handles, but not if it's in a range of structs
1299+
if param_traits.is_range(m) and not is_struct_range:
1300+
handle_members.append({
1301+
'parent': parent,
1302+
'name': m['name'],
1303+
'obj_name': m_objname,
1304+
'type': m_tname,
1305+
'range_start': param_traits.range_start(m),
1306+
'range_end': param_traits.range_end(m)
1307+
})
1308+
else:
1309+
handle_members.append({
1310+
'parent': parent,
1311+
'name': m['name'],
1312+
'obj_name': m_objname,
1313+
'optional': param_traits.is_optional(m)
1314+
})
1315+
elif type_traits.is_struct(m['type'], meta):
1316+
member_struct_members = type_traits.get_struct_members(
1317+
m['type'], meta)
1318+
if param_traits.is_range(m):
1319+
# If we've hit a range of structs we need to start a new recursion looking
1320+
# for handle members. We do not support range within range, so skip that
1321+
if is_struct_range:
1322+
continue
1323+
range_handle_members = get_struct_handle_members(
1324+
namespace, tags, meta, member_struct_members, '', True)
1325+
if range_handle_members:
1326+
handle_members.append({
1327+
'parent': parent,
1328+
'name': m['name'],
1329+
'type': subt(namespace, tags, _remove_const_ptr(m['type'])),
1330+
'range_start': param_traits.range_start(m),
1331+
'range_end': param_traits.range_end(m),
1332+
'handle_members': range_handle_members
1333+
})
1334+
else:
1335+
# If it's just a struct we can keep recursing in search of handles
1336+
m_is_pointer = type_traits.is_pointer(m['type'])
1337+
new_parent_deref = '->' if m_is_pointer else '.'
1338+
new_parent = m['name'] + new_parent_deref
1339+
handle_members += get_struct_handle_members(
1340+
namespace, tags, meta, member_struct_members, new_parent,
1341+
is_struct_range)
1342+
1343+
return handle_members
1344+
1345+
1346+
"""
1347+
Public:
1348+
Strips a string of all dereferences.
1349+
1350+
This is useful in layer templates for creating unique variable names. For
1351+
instance if we need to copy pMyStruct->member.hObject out of a function
1352+
parameter into a local variable we use this to get the unique (or at least
1353+
distinct from any other parameter we might copy) variable name
1354+
pMyStructmemberhObject.
1355+
"""
1356+
def strip_deref(string_to_strip):
1357+
string_to_strip = string_to_strip.replace('.', '')
1358+
return string_to_strip.replace('->', '')
1359+
1360+
1361+
"""
1362+
Public:
1363+
Takes a function object and recurses through its struct parameters to return
1364+
a list of structs that have handle object members the loader will need to
1365+
convert.
1366+
"""
1367+
def get_object_handle_structs_to_convert(namespace, tags, obj, meta):
1368+
structs = []
1369+
params = _filter_param_list(obj['params'], ["[in]"])
1370+
1371+
for item in params:
1372+
if type_traits.is_struct(item['type'], meta):
1373+
members = type_traits.get_struct_members(item['type'], meta)
1374+
handle_members = get_struct_handle_members(namespace, tags, meta,
1375+
members)
1376+
if handle_members:
1377+
name = subt(namespace, tags, item['name'])
1378+
tname = _remove_const_ptr(subt(namespace, tags, item['type']))
1379+
struct = {
1380+
'name': name,
1381+
'type': tname,
1382+
'optional': param_traits.is_optional(item),
1383+
'members': handle_members
1384+
}
1385+
1386+
structs.append(struct)
1387+
1388+
return structs
1389+
1390+
12691391
"""
12701392
Public:
12711393
returns an enum object with the given name

scripts/templates/ldrddi.cpp.mako

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,87 @@ namespace ur_loader
177177
pPropSizeRet = &sizeret;
178178
%endif
179179
180+
## Here we deal with handles buried inside struct type parameters. First
181+
## we create a local copy of the struct, then we convert all the handles
182+
## in that local copy and set the parameter to point to it before forwarding
183+
## it to the final API call.
184+
<% handle_structs = th.get_object_handle_structs_to_convert(n, tags, obj, meta) %>
185+
%if handle_structs:
186+
// Deal with any struct parameters that have handle members we need to convert.
187+
%for struct in handle_structs:
188+
%if struct['optional']:
189+
${struct['type']} ${struct['name']}Local = {};
190+
if(${struct['name']})
191+
${struct['name']}Local = *${struct['name']};
192+
%else:
193+
auto ${struct['name']}Local = *${struct['name']};
194+
%endif
195+
%endfor
196+
197+
%for struct in handle_structs:
198+
%for member in struct['members']:
199+
## If this member has a handle_members field that means it's a range of
200+
## structs which each contain a handle to convert.
201+
%if 'handle_members' in member:
202+
## we use the parent info stripped of derefs for a unique variable name
203+
<%
204+
parent_no_deref = th.strip_deref(member['parent'])
205+
range_vector_name = struct['name'] + parent_no_deref + member['name']
206+
## we need to check if range bounds are literals or variables: variables
207+
## need the full reference chain prepended to them
208+
range_start = member['range_start']
209+
if not re.match(r"[0-9]+$", range_start):
210+
range_start = struct['name'] + "->" + member['parent'] + range_start
211+
range_end = member['range_end']
212+
if not re.match(r"[0-9]+$", range_end):
213+
range_end = struct['name'] + "->" + member['parent'] + range_end %>
214+
std::vector<${member['type']}> ${range_vector_name};
215+
for(uint32_t i = ${range_start}; i < ${range_end}; i++) {
216+
${member['type']} NewRangeStruct = ${struct['name']}Local.${member['parent']}${member['name']}[i];
217+
%for handle_member in member['handle_members']:
218+
%if handle_member['optional']:
219+
if(NewRangeStruct.${handle_member['parent']}${handle_member['name']})
220+
%endif
221+
NewRangeStruct.${handle_member['parent']}${handle_member['name']} =
222+
reinterpret_cast<${handle_member['obj_name']}*>(
223+
NewRangeStruct.${handle_member['parent']}${handle_member['name']})
224+
->handle;
225+
%endfor
226+
227+
${range_vector_name}.push_back(NewRangeStruct);
228+
}
229+
${struct['name']}Local.${member['parent']}${member['name']} = ${range_vector_name}.data();
230+
## If the member has range_start then its a range of handles
231+
%elif 'range_start' in member:
232+
## we use the parent info stripped of derefs for a unique variable name
233+
<%
234+
parent_no_deref = th.strip_deref(member['parent'])
235+
range_vector_name = struct['name'] + parent_no_deref + member['name'] %>
236+
std::vector<${member['type']}> ${range_vector_name};
237+
for(uint32_t i = 0;i < ${struct['name']}->${member['parent']}${member['range_end']};i++) {
238+
${range_vector_name}.push_back(reinterpret_cast<${member['obj_name']}*>(${struct['name']}->${member['parent']}${member['name']}[i])->handle);
239+
}
240+
${struct['name']}Local.${member['parent']}${member['name']} = ${range_vector_name}.data();
241+
%else:
242+
%if member['optional']:
243+
if(${struct['name']}Local.${member['parent']}${member['name']})
244+
%endif
245+
${struct['name']}Local.${member['parent']}${member['name']} =
246+
reinterpret_cast<${member['obj_name']}*>(
247+
${struct['name']}Local.${member['parent']}${member['name']})->handle;
248+
%endif
249+
%endfor
250+
%endfor
251+
252+
// Now that we've converted all the members update the param pointers
253+
%for struct in handle_structs:
254+
%if struct['optional']:
255+
if(${struct['name']})
256+
%endif
257+
${struct['name']} = &${struct['name']}Local;
258+
%endfor
259+
%endif
260+
180261
// forward to device-platform
181262
%if add_local:
182263
result = ${th.make_pfn_name(n, tags, obj)}( ${", ".join(th.make_param_lines(n, tags, obj, format=["name", "local"], replacements=param_replacements))} );

0 commit comments

Comments
 (0)