Skip to content

Commit 1099538

Browse files
andy31415andreilitvin
authored andcommitted
Move CHIPReadCallbacks.h in java codegen from zap to jinja (#25865)
* Start a first codegen for the read callbacks header * Start fixing includes * Fix unit tests and builds * Fix file naming * Fix an include path to point to compile time codegen * Fix another include path * Remove unused file --------- Co-authored-by: Andrei Litvin <andreilitvin@google.com>
1 parent ac0e2d0 commit 1099538

33 files changed

+2177
-15103
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include <jni/CHIPCallbackTypes.h>
18+
19+
#include <controller/java/AndroidCallbacks.h>
20+
#include <jni.h>
21+
#include <lib/support/ErrorStr.h>
22+
#include <lib/support/JniReferences.h>
23+
#include <zap-generated/CHIPClientCallbacks.h>
24+
25+
{% for type in globalTypes -%}
26+
class CHIP{{type.name}}AttributeCallback : public chip::Callback::Callback<{{type.name}}AttributeCallback>
27+
{
28+
public:
29+
CHIP{{type.name}}AttributeCallback(jobject javaCallback, bool keepAlive = false);
30+
31+
~CHIP{{type.name}}AttributeCallback();
32+
33+
static void maybeDestroy(CHIP{{type.name}}AttributeCallback * callback) {
34+
if (!callback->keepAlive) {
35+
callback->Cancel();
36+
chip::Platform::Delete<CHIP{{type.name}}AttributeCallback>(callback);
37+
}
38+
}
39+
40+
static void CallbackFn(void * context, {{type.cpp_type}} value);
41+
static void OnSubscriptionEstablished(void * context, chip::SubscriptionId subscriptionId) {
42+
CHIP_ERROR err = chip::JniReferences::GetInstance().CallSubscriptionEstablished(reinterpret_cast<CHIP{{type.name}}AttributeCallback *>(context)->javaCallbackRef, subscriptionId);
43+
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error calling onSubscriptionEstablished: %s", ErrorStr(err)));
44+
};
45+
46+
private:
47+
jobject javaCallbackRef;
48+
bool keepAlive;
49+
};
50+
{% endfor %}
51+
52+
{% for cluster in clientClusters | sort(attribute='code') %}
53+
{% set typeLookup = idl | createLookupContext(cluster) %}
54+
{%- for attr in cluster.attributes | rejectattr('definition', 'is_using_global_callback', typeLookup) %}
55+
class CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback : public chip::Callback::Callback<CHIP{{cluster.name}}Cluster{{attr.definition.name | capitalcase}}AttributeCallbackType>
56+
{
57+
public:
58+
CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback(jobject javaCallback, bool keepAlive = false);
59+
60+
~CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback();
61+
62+
static void maybeDestroy(CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback * callback) {
63+
if (!callback->keepAlive) {
64+
callback->Cancel();
65+
chip::Platform::Delete<CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback>(callback);
66+
}
67+
}
68+
69+
static void CallbackFn(void * context, {{attr.definition | decodableJniType(typeLookup)}} {%if attr.definition.is_list%}list{%else%}value{%endif%});
70+
static void OnSubscriptionEstablished(void * context, chip::SubscriptionId subscriptionId) {
71+
CHIP_ERROR err = chip::JniReferences::GetInstance().CallSubscriptionEstablished(reinterpret_cast<CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback *>(context)->javaCallbackRef, subscriptionId);
72+
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error calling onSubscriptionEstablished: %s", ErrorStr(err)));
73+
};
74+
75+
private:
76+
jobject javaCallbackRef;
77+
bool keepAlive;
78+
};
79+
80+
{% endfor %}
81+
{% endfor %}

scripts/py_matter_idl/matter_idl/generators/java/ChipClustersCpp.jinja

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@
8585
{% endmacro -%}
8686

8787
#include <jni/CHIPCallbackTypes.h>
88+
#include <jni/CHIPReadCallbacks.h>
8889
#include <controller/java/zap-generated/CHIPInvokeCallbacks.h>
89-
#include <controller/java/zap-generated/CHIPReadCallbacks.h>
9090

9191
#include <app-common/zap-generated/cluster-objects.h>
9292
#include <zap-generated/CHIPClientCallbacks.h>

scripts/py_matter_idl/matter_idl/generators/java/ChipClustersRead.jinja

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
#}
15-
#include <controller/java/zap-generated/CHIPReadCallbacks.h>
15+
#include <jni/CHIPReadCallbacks.h>
1616

1717
#include <app-common/zap-generated/cluster-objects.h>
1818
#include <zap-generated/CHIPClusters.h>

scripts/py_matter_idl/matter_idl/generators/java/__init__.py

+205-27
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
import dataclasses
1617
import enum
1718
import logging
1819
import os
@@ -26,19 +27,37 @@
2627
from stringcase import capitalcase
2728

2829

29-
def FieldToGlobalName(field: Field, context: TypeLookupContext) -> Union[str, None]:
30-
"""Global names are used for generic callbacks shared across
31-
all clusters (e.g. for bool/float/uint32 and similar)
32-
"""
33-
if field.is_list:
34-
return None # lists are always specific per cluster
30+
@dataclasses.dataclass
31+
class GenerateTarget:
32+
template: str
33+
output_name: str
3534

36-
if FieldQuality.NULLABLE & field.qualities:
37-
return None
3835

39-
if FieldQuality.OPTIONAL & field.qualities:
40-
return None
36+
@dataclasses.dataclass
37+
class GlobalType:
38+
name: str # java name
39+
cpp_type: str # underlying type
40+
41+
42+
# types that java should see globally
43+
_GLOBAL_TYPES = [
44+
GlobalType("Boolean", "bool"),
45+
GlobalType("CharString", "const chip::CharSpan"),
46+
GlobalType("Double", "double"),
47+
GlobalType("Float", "float"),
48+
GlobalType("Int8s", "int8_t"),
49+
GlobalType("Int8u", "uint8_t"),
50+
GlobalType("Int16s", "int16_t"),
51+
GlobalType("Int16u", "uint16_t"),
52+
GlobalType("Int32s", "int32_t"),
53+
GlobalType("Int32u", "uint32_t"),
54+
GlobalType("Int64s", "int64_t"),
55+
GlobalType("Int64u", "uint64_t"),
56+
GlobalType("OctetString", "const chip::ByteSpan"),
57+
]
4158

59+
60+
def _UnderlyingType(field: Field, context: TypeLookupContext) -> Union[str, None]:
4261
actual = ParseDataType(field.data_type, context)
4362
if type(actual) == IdlEnumType:
4463
actual = actual.base_type
@@ -68,6 +87,99 @@ def FieldToGlobalName(field: Field, context: TypeLookupContext) -> Union[str, No
6887
return None
6988

7089

90+
def FieldToGlobalName(field: Field, context: TypeLookupContext) -> Union[str, None]:
91+
"""Global names are used for generic callbacks shared across
92+
all clusters (e.g. for bool/float/uint32 and similar)
93+
"""
94+
if field.is_list:
95+
return None # lists are always specific per cluster
96+
97+
if FieldQuality.NULLABLE & field.qualities:
98+
return None
99+
100+
if FieldQuality.OPTIONAL & field.qualities:
101+
return None
102+
103+
return _UnderlyingType(field, context)
104+
105+
106+
# Based on atomicType in ZAP:
107+
# src-electron/generator/matter/app/zap-templates/common/override.js
108+
_KNOWN_DECODABLE_TYPES = {
109+
'action_id': 'chip::ActionId',
110+
'attrib_id': 'chip::AttributeId',
111+
'cluster_id': 'chip::ClusterId',
112+
'command_id': 'chip::CommandId',
113+
'data_ver': 'chip::DataVersion',
114+
'devtype_id': 'chip::DeviceTypeId',
115+
'endpoint_no': 'chip::EndpointId',
116+
'eui64': 'chip::NodeId',
117+
'event_id': 'chip::EventId',
118+
'event_no': 'chip::EventNumber',
119+
'fabric_id': 'chip::FabricId',
120+
'fabric_idx': 'chip::FabricIndex',
121+
'fabric_idx': 'chip::FabricIndex',
122+
'field_id': 'chip::FieldId',
123+
'group_id': 'chip::GroupId',
124+
'node_id': 'chip::NodeId',
125+
'percent': 'chip::Percent',
126+
'percent100ths': 'chip::Percent100ths',
127+
'transaction_id': 'chip::TransactionId',
128+
'vendor_id': 'chip::VendorId',
129+
130+
# non-named enums
131+
'enum8': 'uint8_t',
132+
'enum16': 'uint16_t',
133+
'enum32': 'uint32_t',
134+
'enum64': 'uint64_t',
135+
}
136+
137+
138+
def _CppType(field: Field, context: TypeLookupContext) -> Union[str, None]:
139+
if field.data_type.name.lower() in _KNOWN_DECODABLE_TYPES:
140+
return _KNOWN_DECODABLE_TYPES[field.data_type.name.lower()]
141+
142+
actual = ParseDataType(field.data_type, context)
143+
if isinstance(actual, BasicString):
144+
if actual.is_binary:
145+
return 'chip::ByteSpan'
146+
else:
147+
return 'chip::CharSpan'
148+
elif isinstance(actual, BasicInteger):
149+
if actual.is_signed:
150+
return "int{}_t".format(actual.power_of_two_bits)
151+
else:
152+
return "uint{}_t".format(actual.power_of_two_bits)
153+
elif isinstance(actual, FundamentalType):
154+
if actual == FundamentalType.BOOL:
155+
return 'bool'
156+
elif actual == FundamentalType.FLOAT:
157+
return 'float'
158+
elif actual == FundamentalType.DOUBLE:
159+
return 'double'
160+
else:
161+
logging.warn('Unknown fundamental type: %r' % actual)
162+
elif isinstance(actual, IdlType):
163+
return f"chip::app::Clusters::{context.cluster.name}::Structs::{field.data_type.name}::DecodableType"
164+
elif isinstance(actual, IdlBitmapType):
165+
return f"chip::BitMask<chip::app::Clusters::{context.cluster.name}::{field.data_type.name}>"
166+
167+
# Handles IdlEnumType
168+
return f"chip::app::Clusters::{context.cluster.name}::{field.data_type.name}"
169+
170+
171+
def DecodableJniType(field: Field, context: TypeLookupContext) -> str:
172+
actual = _CppType(field, context)
173+
174+
if field.is_list:
175+
return f"const chip::app::DataModel::DecodableList<{actual}> &"
176+
177+
if field.is_nullable:
178+
return f"const chip::app::DataModel::Nullable<{actual}> &"
179+
180+
return actual
181+
182+
71183
def GlobalNameToJavaName(name: str) -> str:
72184
if name in {'Int8u', 'Int8s', 'Int16u', 'Int16s'}:
73185
return 'Integer'
@@ -143,6 +255,51 @@ def attributesWithSupportedCallback(attrs, context: TypeLookupContext):
143255
yield attr
144256

145257

258+
def _IsUsingGlobalCallback(field: Field, context: TypeLookupContext):
259+
"""Test to determine if the data type of a field can use one of
260+
the global callbacks (i.e. it is a basic double/integer/bool etc.)
261+
"""
262+
if field.is_list:
263+
return False
264+
265+
if field.is_nullable:
266+
return False
267+
268+
return field.data_type.name in {
269+
"boolean",
270+
"single",
271+
"double",
272+
"int8s",
273+
"int8u",
274+
"int16s",
275+
"int16u",
276+
"int24s",
277+
"int24u",
278+
"int32s",
279+
"int32u",
280+
"int40s",
281+
"int40u",
282+
"int48s",
283+
"int48u",
284+
"int56s",
285+
"int56u",
286+
"int64s",
287+
"int64u",
288+
"enum8",
289+
"enum16",
290+
"enum32",
291+
"enum64",
292+
"bitmap8",
293+
"bitmap16",
294+
"bitmap32",
295+
"bitmap64",
296+
"char_string",
297+
"long_char_string",
298+
"octet_string",
299+
"long_octet_string",
300+
}
301+
302+
146303
def NamedFilter(choices: List, name: str):
147304
for choice in choices:
148305
if choice.name == name:
@@ -419,8 +576,10 @@ def __init__(self, storage: GeneratorStorage, idl: Idl, **kargs):
419576
self.jinja_env.filters['asEncodable'] = EncodableValueFrom
420577
self.jinja_env.filters['createLookupContext'] = CreateLookupContext
421578
self.jinja_env.filters['canGenerateSubscribe'] = CanGenerateSubscribe
579+
self.jinja_env.filters['decodableJniType'] = DecodableJniType
422580

423581
self.jinja_env.tests['is_response_struct'] = IsResponseStruct
582+
self.jinja_env.tests['is_using_global_callback'] = _IsUsingGlobalCallback
424583

425584

426585
class JavaJNIGenerator(__JavaCodeGenerator):
@@ -434,6 +593,31 @@ def internal_render_all(self):
434593
Renders .CPP files required for JNI support.
435594
"""
436595

596+
large_targets = [
597+
GenerateTarget(template="CHIPCallbackTypes.jinja",
598+
output_name="jni/CHIPCallbackTypes.h"),
599+
GenerateTarget(template="CHIPReadCallbacks_h.jinja",
600+
output_name="jni/CHIPReadCallbacks.h")
601+
]
602+
603+
for target in large_targets:
604+
self.internal_render_one_output(
605+
template_path=target.template,
606+
output_file_name=target.output_name,
607+
vars={
608+
'idl': self.idl,
609+
'clientClusters': [c for c in self.idl.clusters if c.side == ClusterSide.CLIENT],
610+
'globalTypes': _GLOBAL_TYPES,
611+
}
612+
)
613+
614+
cluster_targets = [
615+
GenerateTarget(template="ChipClustersRead.jinja",
616+
output_name="jni/{cluster_name}Client-ReadImpl.cpp"),
617+
GenerateTarget(template="ChipClustersCpp.jinja",
618+
output_name="jni/{cluster_name}Client-InvokeSubscribeImpl.cpp"),
619+
]
620+
437621
self.internal_render_one_output(
438622
template_path="CHIPCallbackTypes.jinja",
439623
output_file_name="jni/CHIPCallbackTypes.h",
@@ -449,23 +633,17 @@ def internal_render_all(self):
449633
if cluster.side != ClusterSide.CLIENT:
450634
continue
451635

452-
self.internal_render_one_output(
453-
template_path="ChipClustersRead.jinja",
454-
output_file_name="jni/%sClient-ReadImpl.cpp" % cluster.name,
455-
vars={
456-
'cluster': cluster,
457-
'typeLookup': TypeLookupContext(self.idl, cluster),
458-
}
459-
)
460-
461-
self.internal_render_one_output(
462-
template_path="ChipClustersCpp.jinja",
463-
output_file_name="jni/%sClient-InvokeSubscribeImpl.cpp" % cluster.name,
464-
vars={
465-
'cluster': cluster,
466-
'typeLookup': TypeLookupContext(self.idl, cluster),
467-
}
468-
)
636+
for target in cluster_targets:
637+
self.internal_render_one_output(
638+
template_path=target.template,
639+
output_file_name=target.output_name.format(
640+
cluster_name=cluster.name),
641+
vars={
642+
'cluster': cluster,
643+
'typeLookup': TypeLookupContext(self.idl, cluster),
644+
'globalTypes': _GLOBAL_TYPES,
645+
}
646+
)
469647

470648

471649
class JavaClassGenerator(__JavaCodeGenerator):

0 commit comments

Comments
 (0)