Skip to content

Commit

Permalink
Compile-time generation for CHIPClusters-JNI.cpp (project-chip#15171)
Browse files Browse the repository at this point in the history
* Compile-time generation for CHIPClusters-JNI.cpp

* Split Android-generated CHIPClusters.cpp into per-cluster files

* Restyled by whitespace

* Restyled by clang-format

* Update tests

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
2 people authored and fuxingguo16 committed Apr 26, 2022
1 parent 9e9970f commit 797bede
Show file tree
Hide file tree
Showing 19 changed files with 975 additions and 40,112 deletions.
7 changes: 7 additions & 0 deletions scripts/idl/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pw_python_package("idl") {
"matter_grammar.lark",

# Templates used for generation
"generators/java/ChipClustersCpp.jinja",
"generators/java/ChipClustersRead.jinja",

# Unit test data
Expand All @@ -34,11 +35,17 @@ pw_python_package("idl") {
"tests/inputs/several_clusters.matter",
"tests/inputs/simple_attribute.matter",
"tests/outputs/cluster_struct_attribute/jni/DemoClusterClient-ReadImpl.cpp",
"tests/outputs/cluster_struct_attribute/jni/DemoClusterClient-InvokeSubscribeImpl.cpp",
"tests/outputs/global_struct_attribute/jni/DemoClusterClient-ReadImpl.cpp",
"tests/outputs/global_struct_attribute/jni/DemoClusterClient-InvokeSubscribeImpl.cpp",
"tests/outputs/several_clusters/jni/FirstClient-ReadImpl.cpp",
"tests/outputs/several_clusters/jni/SecondClient-ReadImpl.cpp",
"tests/outputs/several_clusters/jni/ThirdClient-ReadImpl.cpp",
"tests/outputs/several_clusters/jni/FirstClient-InvokeSubscribeImpl.cpp",
"tests/outputs/several_clusters/jni/SecondClient-InvokeSubscribeImpl.cpp",
"tests/outputs/several_clusters/jni/ThirdClient-InvokeSubscribeImpl.cpp",
"tests/outputs/simple_attribute/jni/MyClusterClient-ReadImpl.cpp",
"tests/outputs/simple_attribute/jni/MyClusterClient-InvokeSubscribeImpl.cpp",
]

sources = [
Expand Down
26 changes: 0 additions & 26 deletions scripts/idl/generators/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,6 @@

import logging
import stringcase
import enum

from .types import ParseDataType, BasicInteger, BasicString, FundamentalType, IdlType


def filter_IsBasicInteger(type) -> bool:
return type(type) == BasicInteger


def filter_IsBasicString(type) -> bool:
return type(type) == BasicString


def filter_IsFundamentalType(type) -> bool:
return type(type) == FundamentalType


def filter_IsIdlType(type) -> bool:
return type(type) == IdlType


def RegisterCommonFilters(filtermap):
Expand All @@ -47,10 +28,3 @@ def RegisterCommonFilters(filtermap):
filtermap['pascalcase'] = stringcase.pascalcase
filtermap['snakecase'] = stringcase.snakecase
filtermap['spinalcase'] = stringcase.spinalcase

# type conversion is generally very used
filtermap['parseDataType'] = ParseDataType
filtermap['isBasicInteger'] = filter_IsBasicInteger
filtermap['isBasicString'] = filter_IsBasicString
filtermap['isFundamentalType'] = filter_IsFundamentalType
filtermap['isIdlType'] = filter_IsIdlType
211 changes: 211 additions & 0 deletions scripts/idl/generators/java/ChipClustersCpp.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
{%- macro encode_optional(target, source, depth, encodable) -%}
if ({{source}} != nullptr) {
jobject optionalValue_{{depth}};
chip::JniReferences::GetInstance().GetOptionalValue({{source}}, optionalValue_{{depth}});
auto & definedValue_{{depth}} = {{target}}.Emplace();
{{ encode_value(
"definedValue_{}".format(depth),
"optionalValue_{}".format(depth),
depth+1,
encodable.without_optional()
)}}
}
{%- endmacro %}

{%- macro encode_nullable(target, source, depth, encodable) -%}
if ({{source}} == nullptr) {
{{target}}.SetNull();
} else {
auto & nonNullValue_{{depth}} = {{target}}.SetNonNull();
{{encode_value("nonNullValue_{}".format(depth), source, depth+1, encodable.without_nullable())}}
}
{%- endmacro %}

{%- macro encode_list(target, source, depth, encodable) -%}
{
using ListType_{{depth}} = std::remove_reference_t<decltype({{target}})>;
using ListMemberType_{{depth}} = ListMemberTypeGetter<ListType_{{depth}}>::Type;
jint {{source}}Size;
chip::JniReferences::GetInstance().GetArrayListSize({{source}}, {{source}}Size);
if ({{source}}Size != 0) {
auto * listHolder_{{depth}} = new ListHolder<ListMemberType_{{depth}}>({{source}}Size);
listFreer.add(listHolder_{{depth}});

for (size_t i_{{depth}} = 0; i_{{depth}} < static_cast<size_t>({{source}}Size); ++i_{{depth}}) {
jobject element_{{depth}};
chip::JniReferences::GetInstance().GetArrayListItem({{source}}, i_{{depth}}, element_{{depth}});
{{encode_value(
"listHolder_{}->mList[i_{}]".format(depth, depth),
"element_{}".format(depth),
depth+1, encodable.without_list()
)}}
}
{{target}} = ListType_{{depth}}(listHolder_{{depth}}->mList, {{source}}Size);
} else {
{{target}} = ListType_{{depth}}();
}
}
{%- endmacro %}

{%- macro encode_value(target, source, depth, encodable) -%}
{%- if encodable.is_optional -%}
{{encode_optional(target, source, depth, encodable)}}
{%- elif encodable.is_nullable -%}
{{encode_nullable(target, source, depth, encodable)}}
{%- elif encodable.is_list -%}
{{encode_list(target, source, depth, encodable)}}
{%- elif encodable.is_octet_string -%}
cleanupByteArrays.push_back(chip::Platform::MakeUnique<chip::JniByteArray>(env, static_cast<jbyteArray>({{source}})));
{{target}} = cleanupByteArrays.back()->byteSpan();
{%- elif encodable.is_char_string -%}
cleanupStrings.push_back(chip::Platform::MakeUnique<chip::JniUtfString>(env, static_cast<jstring>({{source}})));
{{target}} = cleanupStrings.back()->charSpan();
{%- elif encodable.is_struct -%}
{% set struct = encodable.get_underlying_struct() -%}
{% for field in struct.fields %}
{% set fieldEncodable = field | asEncodable(encodable.context) -%}
jobject {{source}}_{{field.name}}Item_{{depth}};
chip::JniReferences::GetInstance().GetObjectField({{source}}, "{{field.name}}", "{{fieldEncodable.boxed_java_signature}}", {{source}}_{{field.name}}Item_{{depth}});
{{ encode_value(
"{}.{}".format(target, field.name | lowercaseFirst),
"{}_{}Item_{}".format(source, field.name, depth),
depth + 1,
fieldEncodable
)}}
{%- endfor -%}
{%- elif encodable.is_enum -%}
{{target}} = static_cast<std::remove_reference_t<decltype({{target}})>>(chip::JniReferences::GetInstance().IntegerToPrimitive({{source}}));
{%- elif encodable.is_bitmap -%}
{{target}} = static_cast<std::remove_reference_t<decltype({{target}})>>(chip::JniReferences::GetInstance().{{encodable.boxed_java_type}}ToPrimitive({{source}}));
{% else -%}
{{target}} = static_cast<std::remove_reference_t<decltype({{target}})>>(chip::JniReferences::GetInstance().{{encodable.boxed_java_type}}ToPrimitive({{source}}));
{% endif -%}
{% endmacro -%}

#include <controller/java/zap-generated/CHIPCallbackTypes.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>
#include <zap-generated/CHIPClusters.h>

#include <controller/java/AndroidCallbacks.h>
#include <controller/java/AndroidClusterExceptions.h>
#include <controller/java/CHIPDefaultCallbacks.h>
#include <jni.h>
#include <lib/support/CHIPListUtils.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <lib/support/Span.h>
#include <platform/PlatformManager.h>
#include <vector>

#define JNI_METHOD(RETURN, CLASS_NAME, METHOD_NAME) \
extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_ChipClusters_00024##CLASS_NAME##_##METHOD_NAME

using namespace chip;
using namespace chip::Controller;

JNI_METHOD(jlong, {{cluster.name | capitalcase}}Cluster, initWithDevice)(JNIEnv * env, jobject self, jlong devicePtr, jint endpointId)
{
chip::DeviceLayer::StackLock lock;
{{cluster.name | capitalcase}}Cluster * cppCluster = new {{cluster.name | capitalcase}}Cluster();

cppCluster->Associate(reinterpret_cast<DeviceProxy *>(devicePtr), endpointId);
return reinterpret_cast<jlong>(cppCluster);
}

{% for command in cluster.commands -%}

JNI_METHOD(void, {{cluster.name | capitalcase}}Cluster,
{{command.name | lowercaseFirst}})(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback,
{%- if command.input_param -%}
{%- for field in (cluster.structs | named(command.input_param)).fields -%}
{{ field | toBoxedJavaType }} {{field.name}},
{%- endfor -%}
{%- endif -%}
jobject timedInvokeTimeoutMs)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
{{cluster.name | capitalcase}}Cluster * cppCluster;

ListFreer listFreer;
chip::app::Clusters::{{cluster.name | capitalcase}}::Commands::{{command.name | capitalcase}}::Type request;

std::vector<Platform::UniquePtr<JniByteArray>> cleanupByteArrays;
std::vector<Platform::UniquePtr<JniUtfString>> cleanupStrings;
{%- if command.input_param -%}
{%- for field in (cluster.structs | named(command.input_param)).fields -%}
{{ encode_value(
"request." + (field.name | lowercaseFirst),
(field.name | lowercaseFirst),
0,
field | asEncodable(typeLookup)
)}}
{%- endfor -%}
{% endif %}

{% set callbackName = command | commandCallbackName(cluster) %}
std::unique_ptr<CHIP{{callbackName}}Callback, void (*)(CHIP{{callbackName}}Callback *)> onSuccess(
Platform::New<CHIP{{callbackName}}Callback>(callback), Platform::Delete<CHIP{{callbackName}}Callback>);
std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), Platform::Delete<CHIPDefaultFailureCallback>);
VerifyOrReturn(onSuccess.get() != nullptr, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native callback", CHIP_ERROR_NO_MEMORY));
VerifyOrReturn(onFailure.get() != nullptr, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native callback", CHIP_ERROR_NO_MEMORY));

cppCluster = reinterpret_cast<{{cluster.name | capitalcase}}Cluster *>(clusterPtr);
VerifyOrReturn(cppCluster != nullptr, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error getting native cluster", CHIP_ERROR_INCORRECT_STATE));

auto successFn = chip::Callback::Callback<CHIP{{callbackName}}CallbackType>::FromCancelable(onSuccess->Cancel());
auto failureFn = chip::Callback::Callback<CHIPDefaultFailureCallbackType>::FromCancelable(onFailure->Cancel());

{% if command.is_timed_invoke -%}
err = cppCluster->InvokeCommand(request, onSuccess->mContext, successFn->mCall, failureFn->mCall, chip::JniReferences::GetInstance().IntegerToPrimitive(timedInvokeTimeoutMs));
{%- else -%}
if (timedInvokeTimeoutMs == nullptr) {
err = cppCluster->InvokeCommand(request, onSuccess->mContext, successFn->mCall, failureFn->mCall);
} else {
err = cppCluster->InvokeCommand(request, onSuccess->mContext, successFn->mCall, failureFn->mCall, chip::JniReferences::GetInstance().IntegerToPrimitive(timedInvokeTimeoutMs));
}
{%- endif %}
VerifyOrReturn(err == CHIP_NO_ERROR, AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error invoking command", CHIP_ERROR_INCORRECT_STATE));

onSuccess.release();
onFailure.release();
}
{% endfor %}

{%- for attr in cluster.attributes if attr.is_subscribable -%}
{%- if attr | canGenerateSubscribe(typeLookup) -%}

JNI_METHOD(void, {{cluster.name}}Cluster, subscribe{{attr.definition.name | capitalcase}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback, jint minInterval, jint maxInterval)
{
chip::DeviceLayer::StackLock lock;

{%- set callbackName = attr | callbackName(cluster, typeLookup) -%}

std::unique_ptr<{{callbackName}}, void (*)({{callbackName}} *)> onSuccess(Platform::New<{{callbackName}}>(callback, true), chip::Platform::Delete<{{callbackName}}>);
VerifyOrReturn(onSuccess.get() != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native success callback", CHIP_ERROR_NO_MEMORY));

std::unique_ptr<CHIPDefaultFailureCallback, void (*)(CHIPDefaultFailureCallback *)> onFailure(Platform::New<CHIPDefaultFailureCallback>(callback), chip::Platform::Delete<CHIPDefaultFailureCallback>);
VerifyOrReturn(onFailure.get() != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native failure callback", CHIP_ERROR_NO_MEMORY));

CHIP_ERROR err = CHIP_NO_ERROR;
{{cluster.name}}Cluster * cppCluster = reinterpret_cast<{{cluster.name}}Cluster *>(clusterPtr);
VerifyOrReturn(cppCluster != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Could not get native cluster", CHIP_ERROR_INCORRECT_STATE));

using TypeInfo = chip::app::Clusters::{{cluster.name}}::Attributes::{{attr.definition.name | capitalcase}}::TypeInfo;
auto successFn = chip::Callback::Callback<CHIP{{cluster.name}}Cluster{{attr.definition.name | capitalcase}}AttributeCallbackType>::FromCancelable(onSuccess->Cancel());
auto failureFn = chip::Callback::Callback<CHIPDefaultFailureCallbackType>::FromCancelable(onFailure->Cancel());

err = cppCluster->SubscribeAttribute<TypeInfo>(onSuccess->mContext, successFn->mCall, failureFn->mCall, static_cast<uint16_t>(minInterval), static_cast<uint16_t>(maxInterval), {{callbackName}}::OnSubscriptionEstablished);
VerifyOrReturn(err == CHIP_NO_ERROR, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error subscribing to attribute", err));

onSuccess.release();
onFailure.release();
}

{%- endif -%}
{% endfor %}
4 changes: 2 additions & 2 deletions scripts/idl/generators/java/ChipClustersRead.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
#define JNI_METHOD(RETURN, CLASS_NAME, METHOD_NAME) \
extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_ChipClusters_00024##CLASS_NAME##_##METHOD_NAME

{%- for attr in cluster.attributes | attributesWithCallback(known_enums) %}
{%- for attr in cluster.attributes | attributesWithCallback(typeLookup) %}
JNI_METHOD(void, {{cluster.name | capitalcase}}Cluster, read{{attr.definition.name | capitalcase}}Attribute)(JNIEnv * env, jobject self, jlong clusterPtr, jobject callback)
{
chip::DeviceLayer::StackLock lock;
using TypeInfo = chip::app::Clusters::{{cluster.name | capitalcase}}::Attributes::{{attr.definition.name | capitalcase}}::TypeInfo;
{%- set callbackName = attr | callbackName(cluster, known_enums) %}
{%- set callbackName = attr | callbackName(cluster, typeLookup) %}
std::unique_ptr<{{callbackName}}, void (*)({{callbackName}} *)> onSuccess(chip::Platform::New<{{callbackName}}>(callback, false), chip::Platform::Delete<{{callbackName}}>);
VerifyOrReturn(onSuccess.get() != nullptr, chip::AndroidClusterExceptions::GetInstance().ReturnIllegalStateException(env, callback, "Error creating native success callback", CHIP_ERROR_NO_MEMORY));

Expand Down
Loading

0 comments on commit 797bede

Please sign in to comment.