risingwave_jni_core/
macros.rs

1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Utils
16/// A macro that splits the input by comma and calls the callback `$macro` with the split result.
17/// The each part of the split result is within a bracket, and the callback `$macro` can have its
18/// first parameter as `{$({$($args:tt)+})*}` to match the split result.
19///
20/// ```
21/// macro_rules! call_split_by_comma {
22///     ({$({$($args:tt)+})*}) => {
23///         [$(
24///            stringify! {$($args)+}
25///         ),*]
26///     };
27///     ($($input:tt)*) => {{
28///         risingwave_jni_core::split_by_comma! {
29///             {$($input)*},
30///             call_split_by_comma
31///         }
32///     }};
33/// }
34///
35/// let expected_result = [
36///     "hello",
37///     "my friend",
38/// ];
39///
40/// assert_eq!(expected_result, {call_split_by_comma!(hello, my friend)});
41/// ```
42#[macro_export]
43macro_rules! split_by_comma {
44    // Entry of macro. Put input as the first parameter and initialize empty
45    // second and third parameter.
46    (
47        {$($input:tt)*},
48        $macro:path $(,$args:tt)*
49    ) => {
50        $crate::split_by_comma! {
51            {$($input)*},
52            {}, // previous splits
53            {}, // current split
54            $macro $(,$args)*
55        }
56    };
57    // When the first token is comma, move the tokens of current split to the
58    // list of previous splits with a bracket wrapped, and then clear the current
59    // split.
60    (
61        {
62            ,
63            $($rest:tt)*
64        },
65        {
66            $(
67                {$($prev_split:tt)*}
68            )*
69        },
70        {
71            $($current_split:tt)*
72        },
73        $macro:path $(,$args:tt)*
74    ) => {
75        $crate::split_by_comma! {
76            {
77                $($rest)*
78            },
79            {
80                $(
81                    {$($prev_split)*}
82                )*
83                {
84                    $($current_split)*
85                }
86            },
87            {},
88            $macro $(,$args)*
89        }
90    };
91    // Do the same as the previous match when we just reach the end of input,
92    // with the content of last split not added to the list of previous split yet.
93    (
94        {},
95        {
96            $(
97                {$($prev_split:tt)*}
98            )*
99        },
100        {
101            $($current_split:tt)+
102        },
103        $macro:path $(,$args:tt)*
104    ) => {
105        $crate::split_by_comma! {
106            {},
107            {
108                $(
109                    {$($prev_split)*}
110                )*
111                {
112                    $($current_split)+
113                }
114            },
115            {},
116            $macro $(,$args)*
117        }
118    };
119    // For a token that is not comma, add to the list of current staging split.
120    (
121        {
122            $first:tt
123            $($rest:tt)*
124        },
125        {
126            $(
127                {$($prev_split:tt)*}
128            )*
129        },
130        {
131            $($current_split:tt)*
132        },
133        $macro:path $(,$args:tt)*
134    ) => {
135        $crate::split_by_comma! {
136            {
137                $($rest)*
138            },
139            {
140                $(
141                    {$($prev_split)*}
142                )*
143            },
144            {
145                $($current_split)* $first
146            },
147            $macro $(,$args)*
148        }
149    };
150    // When all split result are added to the list, call the callback `$macro` with the result.
151    (
152        {},
153        {
154            $(
155                {$($prev_split:tt)*}
156            )*
157        },
158        {},
159        $macro:path $(,$args:tt)*
160    ) => {
161        $macro! {
162            {
163                $(
164                    {$($prev_split)*}
165                )*
166            }
167            $(,$args)*
168        }
169    };
170}
171// End of utils part
172
173/// Generate the dot separated java class name to the slash separated name.
174///
175/// ```
176/// assert_eq!(
177///     "java/lang/String",
178///     risingwave_jni_core::gen_class_name!(java.lang.String)
179/// );
180/// assert_eq!(
181///     "java/lang/String",
182///     risingwave_jni_core::gen_class_name!(String)
183/// );
184/// ```
185#[macro_export]
186macro_rules! gen_class_name {
187    // A single part class name will be prefixed with `java.lang.`
188    ($single_part_class:ident $($param_name:ident)?) => {
189        $crate::gen_class_name! { @inner java.lang.$single_part_class }
190    };
191    ($($class:ident).+ $($param_name:ident)?) => {
192        $crate::gen_class_name! { @inner $($class).+ }
193    };
194    (@inner $last:ident) => {
195        stringify! {$last}
196    };
197    (@inner $first:ident . $($rest:ident).+) => {
198        concat! {stringify! {$first}, "/", $crate::gen_class_name! {@inner $($rest).+} }
199    }
200}
201
202/// Generate the type signature of a single type
203/// ```
204/// use risingwave_jni_core::gen_jni_type_sig;
205/// assert_eq!("Z", gen_jni_type_sig!(boolean));
206/// assert_eq!("B", gen_jni_type_sig!(byte));
207/// assert_eq!("C", gen_jni_type_sig!(char));
208/// assert_eq!("S", gen_jni_type_sig!(short));
209/// assert_eq!("I", gen_jni_type_sig!(int));
210/// assert_eq!("J", gen_jni_type_sig!(long));
211/// assert_eq!("F", gen_jni_type_sig!(float));
212/// assert_eq!("D", gen_jni_type_sig!(double));
213/// assert_eq!("V", gen_jni_type_sig!(void));
214/// assert_eq!("Ljava/lang/Class;", gen_jni_type_sig!(Class<?>));
215/// assert_eq!("Ljava/lang/String;", gen_jni_type_sig!(String));
216/// assert_eq!("[B", gen_jni_type_sig!(byte[]));
217/// ```
218#[macro_export]
219macro_rules! gen_jni_type_sig {
220    (boolean $($param_name:ident)?) => {
221        "Z"
222    };
223    (byte $($param_name:ident)?) => {
224        "B"
225    };
226    (char $($param_name:ident)?) => {
227        "C"
228    };
229    (short $($param_name:ident)?) => {
230        "S"
231    };
232    (int $($param_name:ident)?) => {
233        "I"
234    };
235    (long $($param_name:ident)?) => {
236        "J"
237    };
238    (float $($param_name:ident)?) => {
239        "F"
240    };
241    (double $($param_name:ident)?) => {
242        "D"
243    };
244    (void) => {
245        "V"
246    };
247    (Class $(< ? >)? $($param_name:ident)?) => {
248        $crate::gen_jni_type_sig! { java.lang.Class }
249    };
250    ($($class_part:ident).+ $($param_name:ident)?) => {
251        concat! {"L", $crate::gen_class_name! {$($class_part).+}, ";"}
252    };
253    ($($class_part:ident).+ [] $($param_name:ident)?) => {
254        concat! { "[", $crate::gen_jni_type_sig! {$($class_part).+}}
255    };
256    ($($invalid:tt)*) => {
257        compile_error!(concat!("unsupported type `", stringify!($($invalid)*), "`"))
258    };
259}
260
261/// Cast a `JValueGen` to a concrete type by the given type
262///
263/// ```
264/// use jni::objects::JValue;
265/// use jni::sys::JNI_TRUE;
266/// use risingwave_jni_core::cast_jvalue;
267/// assert_eq!(cast_jvalue!({boolean}, JValue::Bool(JNI_TRUE)), true);
268/// assert_eq!(cast_jvalue!({byte}, JValue::Byte(10 as i8)), 10);
269/// assert_eq!(cast_jvalue!({char}, JValue::Char('c' as u16)), 'c' as u16);
270/// assert_eq!(cast_jvalue!({double}, JValue::Double(3.14)), 3.14);
271/// assert_eq!(cast_jvalue!({float}, JValue::Float(3.14)), 3.14);
272/// assert_eq!(cast_jvalue!({int}, JValue::Int(10)), 10);
273/// assert_eq!(cast_jvalue!({long}, JValue::Long(10)), 10);
274/// assert_eq!(cast_jvalue!({short}, JValue::Short(10)), 10);
275/// cast_jvalue!({void}, JValue::Void);
276/// let null = jni::objects::JObject::null();
277/// let _: &jni::objects::JObject<'_> = cast_jvalue!({String}, JValue::Object(&null));
278/// let _: jni::objects::JByteArray<'_> = cast_jvalue!({byte[]}, jni::objects::JValueOwned::Object(null));
279/// ```
280#[macro_export]
281macro_rules! cast_jvalue {
282    ({ boolean }, $value:expr) => {{ $value.z().expect("should be bool") }};
283    ({ byte }, $value:expr) => {{ $value.b().expect("should be byte") }};
284    ({ char }, $value:expr) => {{ $value.c().expect("should be char") }};
285    ({ double }, $value:expr) => {{ $value.d().expect("should be double") }};
286    ({ float }, $value:expr) => {{ $value.f().expect("should be float") }};
287    ({ int }, $value:expr) => {{ $value.i().expect("should be int") }};
288    ({ long }, $value:expr) => {{ $value.j().expect("should be long") }};
289    ({ short }, $value:expr) => {{ $value.s().expect("should be short") }};
290    ({ void }, $value:expr) => {{ $value.v().expect("should be void") }};
291    ({ byte[] }, $value:expr) => {{
292        let obj = $value.l().expect("should be object");
293        unsafe { jni::objects::JByteArray::from_raw(obj.into_raw()) }
294    }};
295    ({ $($class:tt)+ }, $value:expr) => {{ $value.l().expect("should be object") }};
296}
297
298/// Cast a `JValueGen` to a concrete type by the given type
299///
300/// ```
301/// use jni::sys::JNI_TRUE;
302/// use risingwave_jni_core::{cast_jvalue, to_jvalue};
303/// assert_eq!(
304///     cast_jvalue!({ boolean }, to_jvalue!({ boolean }, JNI_TRUE)),
305///     true
306/// );
307/// assert_eq!(cast_jvalue!({ byte }, to_jvalue!({ byte }, 10)), 10);
308/// assert_eq!(
309///     cast_jvalue!({ char }, to_jvalue!({ char }, 'c')),
310///     'c' as u16
311/// );
312/// assert_eq!(cast_jvalue!({ double }, to_jvalue!({ double }, 3.14)), 3.14);
313/// assert_eq!(cast_jvalue!({ float }, to_jvalue!({ float }, 3.14)), 3.14);
314/// assert_eq!(cast_jvalue!({ int }, to_jvalue!({ int }, 10)), 10);
315/// assert_eq!(cast_jvalue!({ long }, to_jvalue!({ long }, 10)), 10);
316/// assert_eq!(cast_jvalue!({ short }, to_jvalue!({ short }, 10)), 10);
317/// let obj = jni::objects::JObject::null();
318/// cast_jvalue!({ String }, to_jvalue!({ String }, &obj));
319/// ```
320#[macro_export]
321macro_rules! to_jvalue {
322    ({ boolean $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Bool($value as _) }};
323    ({ byte $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Byte($value as _) }};
324    ({ char $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Char($value as _) }};
325    ({ double $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Double($value as _) }};
326    ({ float $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Float($value as _) }};
327    ({ int $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Int($value as _) }};
328    ({ long $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Long($value as _) }};
329    ({ short $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Short($value as _) }};
330    ({ void }, $value:expr) => {{
331        compile_error! {concat! {"unlike to pass void value: ", stringify! {$value} }}
332    }};
333    ({ $($class:ident)+ $([])? $($param_name:ident)? }, $value:expr) => {{ jni::objects::JValue::Object($value as _) }};
334}
335
336/// Generate the jni signature of a given function
337/// ```
338/// use risingwave_jni_core::gen_jni_sig;
339/// assert_eq!(gen_jni_sig!(boolean f(int, short, byte[])), "(IS[B)Z");
340/// assert_eq!(
341///     gen_jni_sig!(boolean f(int, short, byte[], java.lang.String)),
342///     "(IS[BLjava/lang/String;)Z"
343/// );
344/// assert_eq!(
345///     gen_jni_sig!(boolean f(int, java.lang.String)),
346///     "(ILjava/lang/String;)Z"
347/// );
348/// assert_eq!(gen_jni_sig!(public static native int defaultVnodeCount()), "()I");
349/// assert_eq!(
350///     gen_jni_sig!(long hummockIteratorNew(byte[] readPlan)),
351///     "([B)J"
352/// );
353/// assert_eq!(gen_jni_sig!(long hummockIteratorNext(long pointer)), "(J)J");
354/// assert_eq!(
355///     gen_jni_sig!(void hummockIteratorClose(long pointer)),
356///     "(J)V"
357/// );
358/// assert_eq!(gen_jni_sig!(byte[] rowGetKey(long pointer)), "(J)[B");
359/// assert_eq!(
360///     gen_jni_sig!(java.sql.Timestamp rowGetTimestampValue(long pointer, int index)),
361///     "(JI)Ljava/sql/Timestamp;"
362/// );
363/// assert_eq!(
364///     gen_jni_sig!(String rowGetStringValue(long pointer, int index)),
365///     "(JI)Ljava/lang/String;"
366/// );
367/// assert_eq!(
368///     gen_jni_sig!(static native Object rowGetArrayValue(long pointer, int index, Class clazz)),
369///     "(JILjava/lang/Class;)Ljava/lang/Object;"
370/// );
371/// ```
372#[macro_export]
373macro_rules! gen_jni_sig {
374    // handle the result of `split_by_comma`
375    ({$({$($args:tt)+})*}, {return {$($ret:tt)*}}) => {{
376        concat! {
377            "(", $($crate::gen_jni_type_sig!{ $($args)+ },)* ")",
378            $crate::gen_jni_type_sig! {$($ret)+}
379        }
380    }};
381    ({$($ret:tt)*}, {$($args:tt)*}) => {{
382        $crate::split_by_comma! {
383            {$($args)*},
384            $crate::gen_jni_sig,
385            {return {$($ret)*}}
386        }
387    }};
388    // handle the result of `split_extract_plain_native_methods`
389    ({{$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}}}) => {{
390        $crate::gen_jni_sig! {
391            {$($ret)*}, {$($args)*}
392        }
393    }};
394    ($($input:tt)*) => {{
395        $crate::split_extract_plain_native_methods! {{$($input)*;}, $crate::gen_jni_sig}
396    }}
397}
398
399#[macro_export]
400macro_rules! for_all_plain_native_methods {
401    ($macro:path $(,$args:tt)*) => {
402        $macro! {
403            {
404                public static native void tracingSlf4jEvent(String threadName, String name, int level, String message, String stackTrace);
405
406                public static native boolean tracingSlf4jEventEnabled(int level);
407
408                public static native int defaultVnodeCount();
409
410                static native long iteratorNewStreamChunk(long pointer);
411
412                static native boolean iteratorNext(long pointer);
413
414                static native void iteratorClose(long pointer);
415
416                static native long newStreamChunkFromPayload(byte[] streamChunkPayload);
417
418                static native long newStreamChunkFromPretty(String str);
419
420                static native void streamChunkClose(long pointer);
421
422                static native byte[] iteratorGetKey(long pointer);
423
424                static native int iteratorGetOp(long pointer);
425
426                static native boolean iteratorIsNull(long pointer, int index);
427
428                static native short iteratorGetInt16Value(long pointer, int index);
429
430                static native int iteratorGetInt32Value(long pointer, int index);
431
432                static native long iteratorGetInt64Value(long pointer, int index);
433
434                static native float iteratorGetFloatValue(long pointer, int index);
435
436                static native double iteratorGetDoubleValue(long pointer, int index);
437
438                static native boolean iteratorGetBooleanValue(long pointer, int index);
439
440                static native String iteratorGetStringValue(long pointer, int index);
441
442                static native java.time.LocalDateTime iteratorGetTimestampValue(long pointer, int index);
443
444                static native java.time.OffsetDateTime iteratorGetTimestamptzValue(long pointer, int index);
445
446                static native java.math.BigDecimal iteratorGetDecimalValue(long pointer, int index);
447
448                static native java.time.LocalTime iteratorGetTimeValue(long pointer, int index);
449
450                static native java.time.LocalDate iteratorGetDateValue(long pointer, int index);
451
452                static native String iteratorGetIntervalValue(long pointer, int index);
453
454                static native String iteratorGetJsonbValue(long pointer, int index);
455
456                static native byte[] iteratorGetByteaValue(long pointer, int index);
457
458                // TODO: object or object array?
459                static native Object iteratorGetArrayValue(long pointer, int index, Class<?> clazz);
460
461                public static native boolean sendCdcSourceMsgToChannel(long channelPtr, byte[] msg);
462
463                public static native boolean sendCdcSourceErrorToChannel(long channelPtr, String errorMsg);
464
465                public static native void cdcSourceSenderClose(long channelPtr);
466
467                public static native com.risingwave.java.binding.JniSinkWriterStreamRequest
468                    recvSinkWriterRequestFromChannel(long channelPtr);
469
470                public static native boolean sendSinkWriterResponseToChannel(long channelPtr, byte[] msg);
471
472                public static native boolean sendSinkWriterErrorToChannel(long channelPtr, String msg);
473
474                public static native byte[] recvSinkCoordinatorRequestFromChannel(long channelPtr);
475
476                public static native boolean sendSinkCoordinatorResponseToChannel(long channelPtr, byte[] msg);
477            }
478            $(,$args)*
479        }
480    };
481}
482
483/// Given the plain text of a list native methods, split the methods by semicolon (;), extract
484/// the return type, argument list and name of the methods and pass the result to the callback
485/// `$macro` with the extracted result as the first parameter. The result can be matched with
486/// pattern `{$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}`
487///
488/// ```
489/// macro_rules! call_split_extract_plain_native_methods {
490///     ({$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}) => {
491///         [$(
492///             (stringify! {$func_name}, stringify!{$($ret)*}, stringify!{($($args)*)})
493///         ),*]
494///     };
495///     ($($input:tt)*) => {{
496///         risingwave_jni_core::split_extract_plain_native_methods! {
497///             {$($input)*},
498///             call_split_extract_plain_native_methods
499///         }
500///     }}
501/// }
502/// assert_eq!([
503///     ("f", "int", "(int param1, boolean param2)"),
504///     ("f2", "boolean[]", "()"),
505///     ("f3", "java.lang.String", "(byte[] param)")
506/// ], call_split_extract_plain_native_methods!(
507///     int f(int param1, boolean param2);
508///     boolean[] f2();
509///     java.lang.String f3(byte[] param);
510/// ))
511/// ```
512#[macro_export]
513macro_rules! split_extract_plain_native_methods {
514    (
515        {$($input:tt)*},
516        $macro:path
517        $(,$extra_args:tt)*
518    ) => {{
519        $crate::split_extract_plain_native_methods! {
520            {$($input)*},
521            {},
522            $macro
523            $(,$extra_args)*
524        }
525    }};
526    (
527        {
528            $(public)? static native $($first:tt)*
529        },
530        {
531            $($second:tt)*
532        },
533        $macro:path
534        $(,$extra_args:tt)*
535    ) => {
536        $crate::split_extract_plain_native_methods! {
537            {
538                $($first)*
539            },
540            {
541                $($second)*
542            },
543            $macro
544            $(,$extra_args)*
545        }
546    };
547    (
548        {
549            $($ret:tt).+ $func_name:ident($($args:tt)*); $($rest:tt)*
550        },
551        {
552            $({$prev_func_name:ident, {$($prev_ret:tt)*}, {$($prev_args:tt)*}})*
553        },
554        $macro:path
555        $(,$extra_args:tt)*
556    ) => {
557        $crate::split_extract_plain_native_methods! {
558            {$($rest)*},
559            {
560                $({$prev_func_name, {$($prev_ret)*}, {$($prev_args)*}})*
561                {$func_name, {$($ret).+}, {$($args)*}}
562            },
563            $macro
564            $(,$extra_args)*
565        }
566    };
567    (
568        {
569            $($ret:tt).+ [] $func_name:ident($($args:tt)*); $($rest:tt)*
570        },
571        {
572            $({$prev_func_name:ident, {$($prev_ret:tt)*}, {$($prev_args:tt)*}})*
573        },
574        $macro:path
575        $(,$extra_args:tt)*
576    ) => {
577        $crate::split_extract_plain_native_methods! {
578            {$($rest)*},
579            {
580                $({$prev_func_name, {$($prev_ret)*}, {$($prev_args)*}})*
581                {$func_name, {$($ret).+ []}, {$($args)*}}
582            },
583            $macro
584            $(,$extra_args)*
585        }
586    };
587    (
588        {},
589        {
590            $({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*
591        },
592        $macro:path
593        $(,$extra_args:tt)*
594    ) => {
595        $macro! {
596            {
597                $({$func_name, {$($ret)*}, {$($args)*}})*
598            }
599            $(,$extra_args)*
600        }
601    };
602    ($($invalid:tt)*) => {
603        compile_error!(concat!("unable to split extract `", stringify!($($invalid)*), "`"))
604    };
605}
606
607/// Pass the information of all native methods to the callback `$macro`. The input can be matched
608/// with pattern `{$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}`
609#[macro_export]
610macro_rules! for_all_native_methods {
611    ($macro:path $(,$args:tt)*) => {{
612        $crate::for_all_plain_native_methods! {
613            $crate::for_all_native_methods,
614            $macro
615            $(,$args)*
616        }
617    }};
618    (
619        {
620            $({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*
621        },
622        $macro:path
623        $(,$extra_args:tt)*
624    ) => {{
625        $macro! {
626            {$({$func_name, {$($ret)*}, {$($args)*}})*}
627            $(,$extra_args)*
628        }
629    }};
630    (
631        {$($input:tt)*},
632        $macro:path
633        $(,$extra_args:tt)*
634    ) => {{
635        $crate::split_extract_plain_native_methods! {
636            {$($input)*},
637            $crate::for_all_native_methods,
638            $macro
639            $(,$extra_args)*
640        }
641    }};
642}
643
644/// Convert the argument value list when invoking a method to a list of `JValue` by the argument type list in the signature
645/// ```
646/// use risingwave_jni_core::convert_args_list;
647/// use jni::objects::{JObject, JValue};
648/// use jni::sys::JNI_TRUE;
649/// let list: [JValue<'static, 'static>; 3] = convert_args_list!(
650///     {boolean first, int second, byte third},
651///     {true, 10, 20}
652/// );
653/// match &list[0] {
654///     JValue::Bool(b) => assert_eq!(*b, JNI_TRUE),
655///     value => unreachable!("wrong value: {:?}", value),
656/// }
657/// match &list[1] {
658///     JValue::Int(v) => assert_eq!(*v, 10),
659///     value => unreachable!("wrong value: {:?}", value),
660/// }
661/// match &list[2] {
662///     JValue::Byte(v) => assert_eq!(*v, 20),
663///     value => unreachable!("wrong value: {:?}", value),
664/// }
665/// ```
666#[macro_export]
667macro_rules! convert_args_list {
668    (
669        {
670            {$($first_args:tt)+}
671            $({$($args:tt)+})*
672        },
673        {
674            {$first_value:expr}
675            $({$value:expr})*
676        },
677        {
678            $({$converted:expr})*
679        }) => {
680        $crate::convert_args_list! {
681            {$({$($args)+})*},
682            {$({$value})*},
683            {
684                $({$converted})*
685                {
686                    $crate::to_jvalue! {
687                        {$($first_args)+},
688                        {$first_value}
689                    }
690                }
691            }
692        }
693    };
694    ({$($args:tt)+}, {}, {$({$converted:expr})*}) => {
695        compile_error! {concat!{"trailing argument not passed: ", stringify!{$($args)+}}}
696    };
697    ({}, {$($value:tt)+}, {$({$converted:expr})*}) => {
698        compile_error! {concat!{"trailing extra value passed: ", stringify!{$($value)+}}}
699    };
700    ({}, {}, {$({$converted:expr})*}) => {
701        [$(
702            $converted
703        ),*]
704    };
705    ({$($args:tt)*}, {$($value:expr),*}) => {{
706        $crate::split_by_comma! {
707            {$($args)*},
708            $crate::convert_args_list,
709            {$({$value})*},
710            {}
711        }
712    }};
713    ($($invalid:tt)*) => {
714        compile_error!(concat!("failed to convert `", stringify!($($invalid)*), "`"))
715    };
716}
717
718#[macro_export]
719macro_rules! call_static_method {
720    (
721        {{$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}}},
722        {$($class:ident).+},
723        {$env:expr} $(, $method_args:expr)*
724    ) => {{
725        $crate::call_static_method! {
726            $env,
727            $crate::gen_class_name!($($class).+),
728            stringify! {$func_name},
729            {{$($ret)*}, {$($args)*}}
730            $(, $method_args)*
731        }
732    }};
733    ($env:expr, {$($class:ident).+}, {$($method:tt)*} $(, $args:expr)*) => {{
734        $crate::split_extract_plain_native_methods! {
735            {$($method)*;},
736            $crate::call_static_method,
737            {$($class).+},
738            {$env} $(, $args)*
739        }
740    }};
741    (
742        $env:expr,
743        $class_name:expr,
744        $func_name:expr,
745        {{$($ret:tt)*}, {$($args:tt)*}}
746        $(, $method_args:expr)*
747    ) => {{
748        $env.call_static_method(
749            $class_name,
750            $func_name,
751            $crate::gen_jni_sig! { {$($ret)+}, {$($args)*}},
752            &{
753                $crate::convert_args_list! {
754                    {$($args)*},
755                    {$($method_args),*}
756                }
757            },
758        ).map(|jvalue| {
759            $crate::cast_jvalue! {
760                {$($ret)*},
761                jvalue
762            }
763        })
764    }};
765}
766
767#[macro_export]
768macro_rules! call_method {
769    (
770        {{$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}}},
771        $env:expr, $obj:expr $(, $method_args:expr)*
772    ) => {{
773        $env.call_method(
774            $obj,
775            stringify!{$func_name},
776            $crate::gen_jni_sig! { {$($ret)+}, {$($args)*}},
777            &{
778                $crate::convert_args_list! {
779                    {$($args)*},
780                    {$($method_args),*}
781                }
782            },
783        ).map(|jvalue| {
784            $crate::cast_jvalue! {
785                {$($ret)*},
786                jvalue
787            }
788        })
789    }};
790    ($env:expr, $obj:expr, {$($method:tt)*} $(, $args:expr)*) => {{
791        $crate::split_extract_plain_native_methods! {
792            {$($method)*;},
793            $crate::call_method,
794            $env, $obj $(, $args)*
795        }
796    }};
797}
798
799#[macro_export]
800macro_rules! gen_native_method_entry {
801    (
802        $class_prefix:ident, $func_name:ident, {$($ret:tt)+}, {$($args:tt)*}
803    ) => {{
804        {
805            let fn_ptr = $crate::paste! {[<$class_prefix $func_name> ]} as *mut c_void;
806            let sig = $crate::gen_jni_sig! { {$($ret)+}, {$($args)*}};
807            jni::NativeMethod {
808                name: jni::strings::JNIString::from(stringify! {$func_name}),
809                sig: jni::strings::JNIString::from(sig),
810                fn_ptr,
811            }
812        }
813    }};
814}
815
816#[cfg(test)]
817mod tests {
818    use std::fmt::Formatter;
819
820    #[test]
821    fn test_for_all_gen() {
822        macro_rules! gen_array {
823            (test) => {{
824                for_all_native_methods! {
825                    {
826                        public static native int defaultVnodeCount();
827                        static native long hummockIteratorNew(byte[] readPlan);
828                        public static native byte[] rowGetKey(long pointer);
829                    },
830                    gen_array
831                }
832            }};
833            (all) => {{
834                for_all_native_methods! {
835                    gen_array
836                }
837            }};
838            ({$({ $func_name:ident, {$($ret:tt)+}, {$($args:tt)*} })*}) => {{
839                [
840                    $(
841                        (stringify! {$func_name}, gen_jni_sig! { {$($ret)+}, {$($args)*}}),
842                    )*
843                ]
844            }};
845        }
846        let sig: [(_, _); 3] = gen_array!(test);
847        assert_eq!(
848            sig,
849            [
850                ("defaultVnodeCount", "()I"),
851                ("hummockIteratorNew", "([B)J"),
852                ("rowGetKey", "(J)[B")
853            ]
854        );
855
856        let sig = gen_array!(all);
857        assert!(!sig.is_empty());
858    }
859
860    #[test]
861    fn test_all_native_methods() {
862        // This test shows the signature of all native methods
863        let expected = expect_test::expect![[r#"
864            [
865                tracingSlf4jEvent                        (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V,
866                tracingSlf4jEventEnabled                 (I)Z,
867                defaultVnodeCount                        ()I,
868                iteratorNewStreamChunk                   (J)J,
869                iteratorNext                             (J)Z,
870                iteratorClose                            (J)V,
871                newStreamChunkFromPayload                ([B)J,
872                newStreamChunkFromPretty                 (Ljava/lang/String;)J,
873                streamChunkClose                         (J)V,
874                iteratorGetKey                           (J)[B,
875                iteratorGetOp                            (J)I,
876                iteratorIsNull                           (JI)Z,
877                iteratorGetInt16Value                    (JI)S,
878                iteratorGetInt32Value                    (JI)I,
879                iteratorGetInt64Value                    (JI)J,
880                iteratorGetFloatValue                    (JI)F,
881                iteratorGetDoubleValue                   (JI)D,
882                iteratorGetBooleanValue                  (JI)Z,
883                iteratorGetStringValue                   (JI)Ljava/lang/String;,
884                iteratorGetTimestampValue                (JI)Ljava/time/LocalDateTime;,
885                iteratorGetTimestamptzValue              (JI)Ljava/time/OffsetDateTime;,
886                iteratorGetDecimalValue                  (JI)Ljava/math/BigDecimal;,
887                iteratorGetTimeValue                     (JI)Ljava/time/LocalTime;,
888                iteratorGetDateValue                     (JI)Ljava/time/LocalDate;,
889                iteratorGetIntervalValue                 (JI)Ljava/lang/String;,
890                iteratorGetJsonbValue                    (JI)Ljava/lang/String;,
891                iteratorGetByteaValue                    (JI)[B,
892                iteratorGetArrayValue                    (JILjava/lang/Class;)Ljava/lang/Object;,
893                sendCdcSourceMsgToChannel                (J[B)Z,
894                sendCdcSourceErrorToChannel              (JLjava/lang/String;)Z,
895                cdcSourceSenderClose                     (J)V,
896                recvSinkWriterRequestFromChannel         (J)Lcom/risingwave/java/binding/JniSinkWriterStreamRequest;,
897                sendSinkWriterResponseToChannel          (J[B)Z,
898                sendSinkWriterErrorToChannel             (JLjava/lang/String;)Z,
899                recvSinkCoordinatorRequestFromChannel    (J)[B,
900                sendSinkCoordinatorResponseToChannel     (J[B)Z,
901            ]
902        "#]];
903
904        struct MethodInfo {
905            name: &'static str,
906            sig: &'static str,
907        }
908
909        impl std::fmt::Debug for MethodInfo {
910            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
911                write!(f, "{:40} {}", self.name, self.sig)
912            }
913        }
914
915        macro_rules! gen_all_native_method_info {
916            () => {{
917                $crate::for_all_native_methods! {
918                    gen_all_native_method_info
919                }
920            }};
921            ({$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}) => {
922                [$(
923                    (
924                        MethodInfo {
925                            name: stringify! {$func_name},
926                            sig: $crate::gen_jni_sig! {
927                                {$($ret)*}, {$($args)*}
928                            },
929                        }
930                    )
931                ),*]
932            }
933        }
934        let info = gen_all_native_method_info!();
935        expected.assert_debug_eq(&info);
936    }
937}