Skip to content

Commit 4aaa973

Browse files
Fix various helpers that were not handling name collisions between global and cluster-specific structs. (#1584)
Merging bypassing CI failures: matter failures are expected due to sorting order change. CI will turn back green once we use the new version of ZAP in matter.
1 parent d785a36 commit 4aaa973

File tree

5 files changed

+128
-32
lines changed

5 files changed

+128
-32
lines changed

docs/api.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19169,6 +19169,7 @@ This module provides the API to access various zcl utilities.
1916919169
* [~commandComparator(a, b)](#module_REST API_ various zcl utilities..commandComparator) ⇒
1917019170
* [~eventComparator(a, b)](#module_REST API_ various zcl utilities..eventComparator) ⇒
1917119171
* [~findStructByName(structs, name)](#module_REST API_ various zcl utilities..findStructByName) ⇒
19172+
* [~sortStructsByDependencyHelper()](#module_REST API_ various zcl utilities..sortStructsByDependencyHelper)
1917219173
* [~sortStructsByDependency(structs)](#module_REST API_ various zcl utilities..sortStructsByDependency) ⇒
1917319174
* [~calculateBytes(res, options, db, packageIds, isStructType)](#module_REST API_ various zcl utilities..calculateBytes)
1917419175
* [~optionsHashOrDefault(options, optionsKey, defaultValue)](#module_REST API_ various zcl utilities..optionsHashOrDefault)
@@ -19252,6 +19253,12 @@ Find struct by name from the given list of structs.
1925219253
| structs | <code>\*</code> |
1925319254
| name | <code>\*</code> |
1925419255

19256+
<a name="module_REST API_ various zcl utilities..sortStructsByDependencyHelper"></a>
19257+
19258+
### REST API: various zcl utilities~sortStructsByDependencyHelper()
19259+
Non-exported helper for sortStructsByDependency.
19260+
19261+
**Kind**: inner method of [<code>REST API: various zcl utilities</code>](#module_REST API_ various zcl utilities)
1925519262
<a name="module_REST API_ various zcl utilities..sortStructsByDependency"></a>
1925619263

1925719264
### REST API: various zcl utilities~sortStructsByDependency(structs) ⇒

src-electron/db/query-util.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ function sqlQueryForDataTypeByNameAndClusterName(
115115
${typeTableName}.${typeTableName}_ID,
116116
${structExtensionString}
117117
DATA_TYPE.NAME AS NAME,
118+
(SELECT COUNT(1) FROM DATA_TYPE_CLUSTER WHERE DATA_TYPE_CLUSTER.DATA_TYPE_REF = ${typeTableName}.${typeTableName}_ID) AS ${typeTableName}_CLUSTER_COUNT,
118119
${numberExtensionString}
119120
${typeTableName}.SIZE AS SIZE,
120121
CLUSTER.NAME AS CLUSTER_NAME

src-electron/db/query-zcl.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ async function selectStructsWithItemsImpl(db, packageIds, clusterId) {
356356
S.STRUCT_ID = SI.STRUCT_REF
357357
WHERE
358358
DT.PACKAGE_REF IN (${dbApi.toInClause(packageIds)})
359-
ORDER BY DT.NAME, SI.FIELD_IDENTIFIER`
359+
ORDER BY DT.NAME, DT.DATA_TYPE_ID, SI.FIELD_IDENTIFIER`
360360
} else {
361361
query = `
362362
SELECT
@@ -395,18 +395,19 @@ async function selectStructsWithItemsImpl(db, packageIds, clusterId) {
395395
DT.PACKAGE_REF IN (${dbApi.toInClause(packageIds)})
396396
AND
397397
DTC.CLUSTER_REF = ?
398-
ORDER BY DT.NAME, SI.FIELD_IDENTIFIER`
398+
ORDER BY DT.NAME, DT.DATA_TYPE_ID, SI.FIELD_IDENTIFIER`
399399
args = [clusterId]
400400
}
401401

402402
let rows = await dbApi.dbAll(db, query, args)
403403
return rows.reduce((acc, value) => {
404404
let objectToActOn
405-
if (acc.length == 0 || acc[acc.length - 1].name != value.STRUCT_NAME) {
405+
if (acc.length == 0 || acc[acc.length - 1].struct_id != value.STRUCT_ID) {
406406
// Create a new object
407407
objectToActOn = {
408408
id: value.STRUCT_ID,
409409
name: value.STRUCT_NAME,
410+
struct_id: value.STRUCT_ID,
410411
isFabricScoped: dbApi.fromDbBool(value.IS_FABRIC_SCOPED),
411412
apiMaturity: value.API_MATURITY,
412413
label: value.STRUCT_NAME,

src-electron/generator/matter/app/zap-templates/templates/app/helper.js

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -733,15 +733,42 @@ async function zapTypeToClusterObjectType(type, isDecodable, options) {
733733

734734
if (types.isStruct) {
735735
passByReference = true;
736-
const structObj = await zclQuery.selectStructByName(
737-
this.global.db,
738-
type,
739-
pkgId
740-
);
741-
const ns = nsValueToNamespace(
742-
options.hash.ns,
743-
structObj.structClusterCount
744-
);
736+
let clusterCount;
737+
if (options.hash.cluster === '') {
738+
// This is a non-global struct that is associated with multiple
739+
// clusters: in that case our caller can pass in cluster="", and
740+
// we know that clusterCount > 1, so just set it to 2.
741+
clusterCount = 2;
742+
} else {
743+
const cluster = options.hash.cluster || options.hash.ns;
744+
const structObj = await zclQuery.selectStructByNameAndClusterName(
745+
this.global.db,
746+
type,
747+
cluster,
748+
pkgId
749+
);
750+
if (structObj) {
751+
clusterCount = structObj.structClusterCount;
752+
} else if (options.hash.cluster === undefined) {
753+
// Backwards-compat case: we were called without ns or cluster at all
754+
// (not even cluster=""). Just get by name, since that's all we have to
755+
// work with. It won't work right when names are not unique, but that's
756+
// the best we can do.
757+
const backwardsCompatStructObj = await zclQuery.selectStructByName(
758+
this.global.db,
759+
type,
760+
pkgId
761+
);
762+
clusterCount = backwardsCompatStructObj.structClusterCount;
763+
} else {
764+
// Something is wrong here. Possibly the caller is passing in a munged
765+
// cluster name. Just fail out instead of silently returning bad data.
766+
throw new Error(
767+
`Unable to find struct ${type} in cluster ${options.hash.cluster}`
768+
);
769+
}
770+
}
771+
const ns = nsValueToNamespace(options.hash.ns, clusterCount);
745772
return (
746773
ns +
747774
'Structs::' +
@@ -847,11 +874,23 @@ async function _zapTypeToPythonClusterObjectType(type, options) {
847874
}
848875

849876
if (await typeChecker('isStruct')) {
850-
const structObj = await zclQuery.selectStructByName(
851-
this.global.db,
852-
type,
853-
pkgId
854-
);
877+
let structObj;
878+
if (options.hash.cluster) {
879+
structObj = await zclQuery.selectStructByNameAndClusterName(
880+
this.global.db,
881+
type,
882+
options.hash.cluster,
883+
pkgId
884+
);
885+
} else {
886+
// Backwards-compat case, which won't work when multiple structs have
887+
// the same name.
888+
structObj = await zclQuery.selectStructByName(
889+
this.global.db,
890+
type,
891+
pkgId
892+
);
893+
}
855894

856895
const ns = nsValueToPythonNamespace(
857896
options.hash.ns,
@@ -947,11 +986,23 @@ async function _getPythonFieldDefault(type, options) {
947986
}
948987

949988
if (await typeChecker('isStruct')) {
950-
const structObj = await zclQuery.selectStructByName(
951-
this.global.db,
952-
type,
953-
pkgId
954-
);
989+
let structObj;
990+
if (options.hash.cluster) {
991+
structObj = await zclQuery.selectStructByNameAndClusterName(
992+
this.global.db,
993+
type,
994+
options.hash.cluster,
995+
pkgId
996+
);
997+
} else {
998+
// Backwards-compat case, which won't work when multiple structs have
999+
// the same name.
1000+
structObj = await zclQuery.selectStructByName(
1001+
this.global.db,
1002+
type,
1003+
pkgId
1004+
);
1005+
}
9551006

9561007
const ns = nsValueToPythonNamespace(
9571008
options.hash.ns,
@@ -1133,7 +1184,19 @@ async function zcl_commands_that_need_timed_invoke(options) {
11331184
// struct.
11341185
async function if_is_fabric_scoped_struct(type, options) {
11351186
let packageIds = await templateUtil.ensureZclPackageIds(this);
1136-
let st = await zclQuery.selectStructByName(this.global.db, type, packageIds);
1187+
let st;
1188+
if (options.hash.cluster) {
1189+
st = await zclQuery.selectStructByNameAndClusterName(
1190+
this.global.db,
1191+
type,
1192+
options.hash.cluster,
1193+
packageIds
1194+
);
1195+
} else {
1196+
// Backwards compat case; will not work right when multiple structs share
1197+
// the same name.
1198+
st = await zclQuery.selectStructByName(this.global.db, type, packageIds);
1199+
}
11371200

11381201
if (st && st.isFabricScoped) {
11391202
return options.fn(this);

src-electron/util/zcl-util.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,9 @@ function findStructByName(structs, name) {
110110
}
111111

112112
/**
113-
* This method retrieves a bunch of structs sorted
114-
* alphabetically. It's expected to resort the structs into a list
115-
* where they are sorted in a way where dependency is observed.
116-
*
117-
* It uses the DFS-based topological sort algorithm.
118-
*
119-
* @param {*} structs
120-
* @returns sorted structs according to topological search.
113+
* Non-exported helper for sortStructsByDependency.
121114
*/
122-
async function sortStructsByDependency(structs) {
115+
function sortStructsByDependencyHelper(structs) {
123116
let allStructNames = structs.map((s) => s.name)
124117
let edges = []
125118

@@ -146,6 +139,37 @@ async function sortStructsByDependency(structs) {
146139
return finalSort
147140
}
148141

142+
/**
143+
* This method retrieves a bunch of structs sorted
144+
* alphabetically. It's expected to resort the structs into a list
145+
* where they are sorted in a way where dependency is observed.
146+
*
147+
* It uses the DFS-based topological sort algorithm.
148+
*
149+
* @param {*} structs
150+
* @returns sorted structs according to topological search.
151+
*/
152+
async function sortStructsByDependency(structs) {
153+
// Given global and non-global structs, the way dependencies work is this:
154+
//
155+
// 1) Global structs can only depend on other global structs.
156+
// 2) Non-global structs can depend on either non-global or global structs.
157+
//
158+
// So we can output all global structs first (sorted by dependency), and then
159+
// the non-global structs, again sorted by dependency.
160+
//
161+
// NOTE: the topological sort we use is not a stable sort, so adding some
162+
// structs to the list can entirely change the order of all the structs.
163+
let sortedGlobalStructs = sortStructsByDependencyHelper(
164+
structs.filter((s) => s.struct_cluster_count == 0)
165+
)
166+
let sortedNonGlobalStructs = sortStructsByDependencyHelper(
167+
structs.filter((s) => s.struct_cluster_count != 0)
168+
)
169+
170+
return sortedGlobalStructs.concat(sortedNonGlobalStructs)
171+
}
172+
149173
/**
150174
* This function calculates the number of bytes in the data type and based on
151175
* that returns the option specified in the template.

0 commit comments

Comments
 (0)