Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move CHIPReadCallbacks.h in java codegen from zap to jinja #25865

Merged
merged 11 commits into from
Apr 5, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni/CHIPCallbackTypes.h>

#include <controller/java/AndroidCallbacks.h>
#include <jni.h>
#include <lib/support/ErrorStr.h>
#include <lib/support/JniReferences.h>
#include <zap-generated/CHIPClientCallbacks.h>

{% for type in globalTypes -%}
class CHIP{{type.name}}AttributeCallback : public chip::Callback::Callback<{{type.name}}AttributeCallback>
{
public:
CHIP{{type.name}}AttributeCallback(jobject javaCallback, bool keepAlive = false);

~CHIP{{type.name}}AttributeCallback();

static void maybeDestroy(CHIP{{type.name}}AttributeCallback * callback) {
if (!callback->keepAlive) {
callback->Cancel();
chip::Platform::Delete<CHIP{{type.name}}AttributeCallback>(callback);
}
}

static void CallbackFn(void * context, {{type.cpp_type}} value);
static void OnSubscriptionEstablished(void * context, chip::SubscriptionId subscriptionId) {
CHIP_ERROR err = chip::JniReferences::GetInstance().CallSubscriptionEstablished(reinterpret_cast<CHIP{{type.name}}AttributeCallback *>(context)->javaCallbackRef, subscriptionId);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error calling onSubscriptionEstablished: %s", ErrorStr(err)));
};

private:
jobject javaCallbackRef;
bool keepAlive;
};
{% endfor %}

{% for cluster in clientClusters | sort(attribute='code') %}
{% set typeLookup = idl | createLookupContext(cluster) %}
{%- for attr in cluster.attributes | rejectattr('definition', 'is_using_global_callback', typeLookup) %}
class CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback : public chip::Callback::Callback<CHIP{{cluster.name}}Cluster{{attr.definition.name | capitalcase}}AttributeCallbackType>
{
public:
CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback(jobject javaCallback, bool keepAlive = false);

~CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback();

static void maybeDestroy(CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback * callback) {
if (!callback->keepAlive) {
callback->Cancel();
chip::Platform::Delete<CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback>(callback);
}
}

static void CallbackFn(void * context, {{attr.definition | decodableJniType(typeLookup)}} {%if attr.definition.is_list%}list{%else%}value{%endif%});
static void OnSubscriptionEstablished(void * context, chip::SubscriptionId subscriptionId) {
CHIP_ERROR err = chip::JniReferences::GetInstance().CallSubscriptionEstablished(reinterpret_cast<CHIP{{cluster.name}}{{attr.definition.name | capitalcase}}AttributeCallback *>(context)->javaCallbackRef, subscriptionId);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Zcl, "Error calling onSubscriptionEstablished: %s", ErrorStr(err)));
};

private:
jobject javaCallbackRef;
bool keepAlive;
};

{% endfor %}
{% endfor %}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
{% endmacro -%}

#include <jni/CHIPCallbackTypes.h>
#include <jni/CHIPReadCallbacks.h>
#include <controller/java/zap-generated/CHIPInvokeCallbacks.h>
#include <controller/java/zap-generated/CHIPReadCallbacks.h>

#include <app-common/zap-generated/cluster-objects.h>
#include <zap-generated/CHIPClientCallbacks.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#}
#include <controller/java/zap-generated/CHIPReadCallbacks.h>
#include <jni/CHIPReadCallbacks.h>

#include <app-common/zap-generated/cluster-objects.h>
#include <zap-generated/CHIPClusters.h>
Expand Down
232 changes: 205 additions & 27 deletions scripts/py_matter_idl/matter_idl/generators/java/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import dataclasses
import enum
import logging
import os
Expand All @@ -26,19 +27,37 @@
from stringcase import capitalcase


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

if FieldQuality.NULLABLE & field.qualities:
return None

if FieldQuality.OPTIONAL & field.qualities:
return None
@dataclasses.dataclass
class GlobalType:
name: str # java name
cpp_type: str # underlying type


# types that java should see globally
_GLOBAL_TYPES = [
GlobalType("Boolean", "bool"),
GlobalType("CharString", "const chip::CharSpan"),
GlobalType("Double", "double"),
GlobalType("Float", "float"),
GlobalType("Int8s", "int8_t"),
GlobalType("Int8u", "uint8_t"),
GlobalType("Int16s", "int16_t"),
GlobalType("Int16u", "uint16_t"),
GlobalType("Int32s", "int32_t"),
GlobalType("Int32u", "uint32_t"),
GlobalType("Int64s", "int64_t"),
GlobalType("Int64u", "uint64_t"),
GlobalType("OctetString", "const chip::ByteSpan"),
]


def _UnderlyingType(field: Field, context: TypeLookupContext) -> Union[str, None]:
actual = ParseDataType(field.data_type, context)
if type(actual) == IdlEnumType:
actual = actual.base_type
Expand Down Expand Up @@ -68,6 +87,99 @@ def FieldToGlobalName(field: Field, context: TypeLookupContext) -> Union[str, No
return None


def FieldToGlobalName(field: Field, context: TypeLookupContext) -> Union[str, None]:
"""Global names are used for generic callbacks shared across
all clusters (e.g. for bool/float/uint32 and similar)
"""
if field.is_list:
return None # lists are always specific per cluster

if FieldQuality.NULLABLE & field.qualities:
return None

if FieldQuality.OPTIONAL & field.qualities:
return None

return _UnderlyingType(field, context)


# Based on atomicType in ZAP:
# src-electron/generator/matter/app/zap-templates/common/override.js
_KNOWN_DECODABLE_TYPES = {
'action_id': 'chip::ActionId',
'attrib_id': 'chip::AttributeId',
'cluster_id': 'chip::ClusterId',
'command_id': 'chip::CommandId',
'data_ver': 'chip::DataVersion',
'devtype_id': 'chip::DeviceTypeId',
'endpoint_no': 'chip::EndpointId',
'eui64': 'chip::NodeId',
'event_id': 'chip::EventId',
'event_no': 'chip::EventNumber',
'fabric_id': 'chip::FabricId',
'fabric_idx': 'chip::FabricIndex',
'fabric_idx': 'chip::FabricIndex',
'field_id': 'chip::FieldId',
'group_id': 'chip::GroupId',
'node_id': 'chip::NodeId',
'percent': 'chip::Percent',
'percent100ths': 'chip::Percent100ths',
'transaction_id': 'chip::TransactionId',
'vendor_id': 'chip::VendorId',

# non-named enums
'enum8': 'uint8_t',
'enum16': 'uint16_t',
'enum32': 'uint32_t',
'enum64': 'uint64_t',
}


def _CppType(field: Field, context: TypeLookupContext) -> Union[str, None]:
if field.data_type.name.lower() in _KNOWN_DECODABLE_TYPES:
return _KNOWN_DECODABLE_TYPES[field.data_type.name.lower()]

actual = ParseDataType(field.data_type, context)
if isinstance(actual, BasicString):
if actual.is_binary:
return 'chip::ByteSpan'
else:
return 'chip::CharSpan'
elif isinstance(actual, BasicInteger):
if actual.is_signed:
return "int{}_t".format(actual.power_of_two_bits)
else:
return "uint{}_t".format(actual.power_of_two_bits)
elif isinstance(actual, FundamentalType):
if actual == FundamentalType.BOOL:
return 'bool'
elif actual == FundamentalType.FLOAT:
return 'float'
elif actual == FundamentalType.DOUBLE:
return 'double'
else:
logging.warn('Unknown fundamental type: %r' % actual)
elif isinstance(actual, IdlType):
return f"chip::app::Clusters::{context.cluster.name}::Structs::{field.data_type.name}::DecodableType"
elif isinstance(actual, IdlBitmapType):
return f"chip::BitMask<chip::app::Clusters::{context.cluster.name}::{field.data_type.name}>"

# Handles IdlEnumType
return f"chip::app::Clusters::{context.cluster.name}::{field.data_type.name}"


def DecodableJniType(field: Field, context: TypeLookupContext) -> str:
actual = _CppType(field, context)

if field.is_list:
return f"const chip::app::DataModel::DecodableList<{actual}> &"

if field.is_nullable:
return f"const chip::app::DataModel::Nullable<{actual}> &"

return actual


def GlobalNameToJavaName(name: str) -> str:
if name in {'Int8u', 'Int8s', 'Int16u', 'Int16s'}:
return 'Integer'
Expand Down Expand Up @@ -143,6 +255,51 @@ def attributesWithSupportedCallback(attrs, context: TypeLookupContext):
yield attr


def _IsUsingGlobalCallback(field: Field, context: TypeLookupContext):
"""Test to determine if the data type of a field can use one of
the global callbacks (i.e. it is a basic double/integer/bool etc.)
"""
if field.is_list:
return False

if field.is_nullable:
return False

return field.data_type.name in {
"boolean",
"single",
"double",
"int8s",
"int8u",
"int16s",
"int16u",
"int24s",
"int24u",
"int32s",
"int32u",
"int40s",
"int40u",
"int48s",
"int48u",
"int56s",
"int56u",
"int64s",
"int64u",
"enum8",
"enum16",
"enum32",
"enum64",
"bitmap8",
"bitmap16",
"bitmap32",
"bitmap64",
"char_string",
"long_char_string",
"octet_string",
"long_octet_string",
}


def NamedFilter(choices: List, name: str):
for choice in choices:
if choice.name == name:
Expand Down Expand Up @@ -419,8 +576,10 @@ def __init__(self, storage: GeneratorStorage, idl: Idl, **kargs):
self.jinja_env.filters['asEncodable'] = EncodableValueFrom
self.jinja_env.filters['createLookupContext'] = CreateLookupContext
self.jinja_env.filters['canGenerateSubscribe'] = CanGenerateSubscribe
self.jinja_env.filters['decodableJniType'] = DecodableJniType

self.jinja_env.tests['is_response_struct'] = IsResponseStruct
self.jinja_env.tests['is_using_global_callback'] = _IsUsingGlobalCallback


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

large_targets = [
GenerateTarget(template="CHIPCallbackTypes.jinja",
output_name="jni/CHIPCallbackTypes.h"),
GenerateTarget(template="CHIPReadCallbacks_h.jinja",
output_name="jni/CHIPReadCallbacks.h")
]

for target in large_targets:
self.internal_render_one_output(
template_path=target.template,
output_file_name=target.output_name,
vars={
'idl': self.idl,
'clientClusters': [c for c in self.idl.clusters if c.side == ClusterSide.CLIENT],
'globalTypes': _GLOBAL_TYPES,
}
)

cluster_targets = [
GenerateTarget(template="ChipClustersRead.jinja",
output_name="jni/{cluster_name}Client-ReadImpl.cpp"),
GenerateTarget(template="ChipClustersCpp.jinja",
output_name="jni/{cluster_name}Client-InvokeSubscribeImpl.cpp"),
]

self.internal_render_one_output(
template_path="CHIPCallbackTypes.jinja",
output_file_name="jni/CHIPCallbackTypes.h",
Expand All @@ -449,23 +633,17 @@ def internal_render_all(self):
if cluster.side != ClusterSide.CLIENT:
continue

self.internal_render_one_output(
template_path="ChipClustersRead.jinja",
output_file_name="jni/%sClient-ReadImpl.cpp" % cluster.name,
vars={
'cluster': cluster,
'typeLookup': TypeLookupContext(self.idl, cluster),
}
)

self.internal_render_one_output(
template_path="ChipClustersCpp.jinja",
output_file_name="jni/%sClient-InvokeSubscribeImpl.cpp" % cluster.name,
vars={
'cluster': cluster,
'typeLookup': TypeLookupContext(self.idl, cluster),
}
)
for target in cluster_targets:
self.internal_render_one_output(
template_path=target.template,
output_file_name=target.output_name.format(
cluster_name=cluster.name),
vars={
'cluster': cluster,
'typeLookup': TypeLookupContext(self.idl, cluster),
'globalTypes': _GLOBAL_TYPES,
}
)


class JavaClassGenerator(__JavaCodeGenerator):
Expand Down
Loading