Skip to main content

risingwave_jni_core/
macros.rs

1// Copyright 2023 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                public static native boolean validateCdcSourceColumnType(
411                    int cdcTableType,
412                    String upstreamTypeName,
413                    int rwTypeName,
414                    long charMaxLength,
415                    boolean isUnsigned,
416                    String postgresUdtName);
417
418                static native long iteratorNewStreamChunk(long pointer);
419
420                static native boolean iteratorNext(long pointer);
421
422                public static native void initObjectStoreForTest(String stateStoreUrl, String dataDirectory);
423
424                public static native void putObject(String objectName, byte[] data);
425
426                public static native String getObjectStoreType();
427
428                public static native void deleteObjects(String dir);
429
430                public static native byte[] getObject(String objectName);
431
432                public static native String[] listObject(String dir);
433
434                static native void iteratorClose(long pointer);
435
436                static native long newStreamChunkFromPayload(byte[] streamChunkPayload);
437
438                static native long newStreamChunkFromPretty(String str);
439
440                static native void streamChunkClose(long pointer);
441
442                static native byte[] iteratorGetKey(long pointer);
443
444                static native int iteratorGetOp(long pointer);
445
446                static native boolean iteratorIsNull(long pointer, int index);
447
448                static native short iteratorGetInt16Value(long pointer, int index);
449
450                static native int iteratorGetInt32Value(long pointer, int index);
451
452                static native long iteratorGetInt64Value(long pointer, int index);
453
454                static native float iteratorGetFloatValue(long pointer, int index);
455
456                static native double iteratorGetDoubleValue(long pointer, int index);
457
458                static native boolean iteratorGetBooleanValue(long pointer, int index);
459
460                static native String iteratorGetStringValue(long pointer, int index);
461
462                static native java.time.LocalDateTime iteratorGetTimestampValue(long pointer, int index);
463
464                static native java.time.OffsetDateTime iteratorGetTimestamptzValue(long pointer, int index);
465
466                static native java.math.BigDecimal iteratorGetDecimalValue(long pointer, int index);
467
468                static native java.time.LocalTime iteratorGetTimeValue(long pointer, int index);
469
470                static native java.time.LocalDate iteratorGetDateValue(long pointer, int index);
471
472                static native String iteratorGetIntervalValue(long pointer, int index);
473
474                static native String iteratorGetJsonbValue(long pointer, int index);
475
476                static native byte[] iteratorGetByteaValue(long pointer, int index);
477
478                // TODO: object or object array?
479                static native Object iteratorGetArrayValue(long pointer, int index, Class<?> clazz);
480
481                public static native boolean sendCdcSourceMsgToChannel(long channelPtr, byte[] msg);
482
483                public static native boolean sendCdcSourceErrorToChannel(long channelPtr, String errorMsg);
484
485                public static native void cdcSourceSenderClose(long channelPtr);
486
487                public static native com.risingwave.java.binding.JniSinkWriterStreamRequest
488                    recvSinkWriterRequestFromChannel(long channelPtr);
489
490                public static native boolean sendSinkWriterResponseToChannel(long channelPtr, byte[] msg);
491
492                public static native boolean sendSinkWriterErrorToChannel(long channelPtr, String msg);
493
494                public static native byte[] recvSinkCoordinatorRequestFromChannel(long channelPtr);
495
496                public static native boolean sendSinkCoordinatorResponseToChannel(long channelPtr, byte[] msg);
497            }
498            $(,$args)*
499        }
500    };
501}
502
503/// Given the plain text of a list native methods, split the methods by semicolon (;), extract
504/// the return type, argument list and name of the methods and pass the result to the callback
505/// `$macro` with the extracted result as the first parameter. The result can be matched with
506/// pattern `{$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}`
507///
508/// ```
509/// macro_rules! call_split_extract_plain_native_methods {
510///     ({$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}) => {
511///         [$(
512///             (stringify! {$func_name}, stringify!{$($ret)*}, stringify!{($($args)*)})
513///         ),*]
514///     };
515///     ($($input:tt)*) => {{
516///         risingwave_jni_core::split_extract_plain_native_methods! {
517///             {$($input)*},
518///             call_split_extract_plain_native_methods
519///         }
520///     }}
521/// }
522/// assert_eq!([
523///     ("f", "int", "(int param1, boolean param2)"),
524///     ("f2", "boolean[]", "()"),
525///     ("f3", "java.lang.String", "(byte[] param)")
526/// ], call_split_extract_plain_native_methods!(
527///     int f(int param1, boolean param2);
528///     boolean[] f2();
529///     java.lang.String f3(byte[] param);
530/// ))
531/// ```
532#[macro_export]
533macro_rules! split_extract_plain_native_methods {
534    (
535        {$($input:tt)*},
536        $macro:path
537        $(,$extra_args:tt)*
538    ) => {{
539        $crate::split_extract_plain_native_methods! {
540            {$($input)*},
541            {},
542            $macro
543            $(,$extra_args)*
544        }
545    }};
546    (
547        {
548            $(public)? static native $($first:tt)*
549        },
550        {
551            $($second:tt)*
552        },
553        $macro:path
554        $(,$extra_args:tt)*
555    ) => {
556        $crate::split_extract_plain_native_methods! {
557            {
558                $($first)*
559            },
560            {
561                $($second)*
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            $($ret:tt).+ [] $func_name:ident($($args:tt)*); $($rest:tt)*
590        },
591        {
592            $({$prev_func_name:ident, {$($prev_ret:tt)*}, {$($prev_args:tt)*}})*
593        },
594        $macro:path
595        $(,$extra_args:tt)*
596    ) => {
597        $crate::split_extract_plain_native_methods! {
598            {$($rest)*},
599            {
600                $({$prev_func_name, {$($prev_ret)*}, {$($prev_args)*}})*
601                {$func_name, {$($ret).+ []}, {$($args)*}}
602            },
603            $macro
604            $(,$extra_args)*
605        }
606    };
607    (
608        {},
609        {
610            $({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*
611        },
612        $macro:path
613        $(,$extra_args:tt)*
614    ) => {
615        $macro! {
616            {
617                $({$func_name, {$($ret)*}, {$($args)*}})*
618            }
619            $(,$extra_args)*
620        }
621    };
622    ($($invalid:tt)*) => {
623        compile_error!(concat!("unable to split extract `", stringify!($($invalid)*), "`"))
624    };
625}
626
627/// Pass the information of all native methods to the callback `$macro`. The input can be matched
628/// with pattern `{$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}`
629#[macro_export]
630macro_rules! for_all_native_methods {
631    ($macro:path $(,$args:tt)*) => {{
632        $crate::for_all_plain_native_methods! {
633            $crate::for_all_native_methods,
634            $macro
635            $(,$args)*
636        }
637    }};
638    (
639        {
640            $({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*
641        },
642        $macro:path
643        $(,$extra_args:tt)*
644    ) => {{
645        $macro! {
646            {$({$func_name, {$($ret)*}, {$($args)*}})*}
647            $(,$extra_args)*
648        }
649    }};
650    (
651        {$($input:tt)*},
652        $macro:path
653        $(,$extra_args:tt)*
654    ) => {{
655        $crate::split_extract_plain_native_methods! {
656            {$($input)*},
657            $crate::for_all_native_methods,
658            $macro
659            $(,$extra_args)*
660        }
661    }};
662}
663
664/// Convert the argument value list when invoking a method to a list of `JValue` by the argument type list in the signature
665/// ```
666/// use risingwave_jni_core::convert_args_list;
667/// use jni::objects::{JObject, JValue};
668/// use jni::sys::JNI_TRUE;
669/// let list: [JValue<'static, 'static>; 3] = convert_args_list!(
670///     {boolean first, int second, byte third},
671///     {true, 10, 20}
672/// );
673/// match &list[0] {
674///     JValue::Bool(b) => assert_eq!(*b, JNI_TRUE),
675///     value => unreachable!("wrong value: {:?}", value),
676/// }
677/// match &list[1] {
678///     JValue::Int(v) => assert_eq!(*v, 10),
679///     value => unreachable!("wrong value: {:?}", value),
680/// }
681/// match &list[2] {
682///     JValue::Byte(v) => assert_eq!(*v, 20),
683///     value => unreachable!("wrong value: {:?}", value),
684/// }
685/// ```
686#[macro_export]
687macro_rules! convert_args_list {
688    (
689        {
690            {$($first_args:tt)+}
691            $({$($args:tt)+})*
692        },
693        {
694            {$first_value:expr}
695            $({$value:expr})*
696        },
697        {
698            $({$converted:expr})*
699        }) => {
700        $crate::convert_args_list! {
701            {$({$($args)+})*},
702            {$({$value})*},
703            {
704                $({$converted})*
705                {
706                    $crate::to_jvalue! {
707                        {$($first_args)+},
708                        {$first_value}
709                    }
710                }
711            }
712        }
713    };
714    ({$($args:tt)+}, {}, {$({$converted:expr})*}) => {
715        compile_error! {concat!{"trailing argument not passed: ", stringify!{$($args)+}}}
716    };
717    ({}, {$($value:tt)+}, {$({$converted:expr})*}) => {
718        compile_error! {concat!{"trailing extra value passed: ", stringify!{$($value)+}}}
719    };
720    ({}, {}, {$({$converted:expr})*}) => {
721        [$(
722            $converted
723        ),*]
724    };
725    ({$($args:tt)*}, {$($value:expr),*}) => {{
726        $crate::split_by_comma! {
727            {$($args)*},
728            $crate::convert_args_list,
729            {$({$value})*},
730            {}
731        }
732    }};
733    ($($invalid:tt)*) => {
734        compile_error!(concat!("failed to convert `", stringify!($($invalid)*), "`"))
735    };
736}
737
738#[macro_export]
739macro_rules! call_static_method {
740    (
741        {{$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}}},
742        {$($class:ident).+},
743        {$env:expr} $(, $method_args:expr)*
744    ) => {{
745        $crate::call_static_method! {
746            $env,
747            $crate::gen_class_name!($($class).+),
748            stringify! {$func_name},
749            {{$($ret)*}, {$($args)*}}
750            $(, $method_args)*
751        }
752    }};
753    ($env:expr, {$($class:ident).+}, {$($method:tt)*} $(, $args:expr)*) => {{
754        $crate::split_extract_plain_native_methods! {
755            {$($method)*;},
756            $crate::call_static_method,
757            {$($class).+},
758            {$env} $(, $args)*
759        }
760    }};
761    (
762        $env:expr,
763        $class_name:expr,
764        $func_name:expr,
765        {{$($ret:tt)*}, {$($args:tt)*}}
766        $(, $method_args:expr)*
767    ) => {{
768        $env.call_static_method(
769            $class_name,
770            $func_name,
771            $crate::gen_jni_sig! { {$($ret)+}, {$($args)*}},
772            &{
773                $crate::convert_args_list! {
774                    {$($args)*},
775                    {$($method_args),*}
776                }
777            },
778        ).map(|jvalue| {
779            $crate::cast_jvalue! {
780                {$($ret)*},
781                jvalue
782            }
783        })
784    }};
785}
786
787#[macro_export]
788macro_rules! call_method {
789    (
790        {{$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}}},
791        $env:expr, $obj:expr $(, $method_args:expr)*
792    ) => {{
793        $env.call_method(
794            $obj,
795            stringify!{$func_name},
796            $crate::gen_jni_sig! { {$($ret)+}, {$($args)*}},
797            &{
798                $crate::convert_args_list! {
799                    {$($args)*},
800                    {$($method_args),*}
801                }
802            },
803        ).map(|jvalue| {
804            $crate::cast_jvalue! {
805                {$($ret)*},
806                jvalue
807            }
808        })
809    }};
810    ($env:expr, $obj:expr, {$($method:tt)*} $(, $args:expr)*) => {{
811        $crate::split_extract_plain_native_methods! {
812            {$($method)*;},
813            $crate::call_method,
814            $env, $obj $(, $args)*
815        }
816    }};
817}
818
819#[macro_export]
820macro_rules! gen_native_method_entry {
821    (
822        $class_prefix:ident, $func_name:ident, {$($ret:tt)+}, {$($args:tt)*}
823    ) => {{
824        {
825            let fn_ptr = $crate::paste! {[<$class_prefix $func_name> ]} as *mut c_void;
826            let sig = $crate::gen_jni_sig! { {$($ret)+}, {$($args)*}};
827            jni::NativeMethod {
828                name: jni::strings::JNIString::from(stringify! {$func_name}),
829                sig: jni::strings::JNIString::from(sig),
830                fn_ptr,
831            }
832        }
833    }};
834}
835
836#[cfg(test)]
837mod tests {
838    use std::fmt::Formatter;
839
840    #[test]
841    fn test_for_all_gen() {
842        macro_rules! gen_array {
843            (test) => {{
844                for_all_native_methods! {
845                    {
846                        public static native int defaultVnodeCount();
847                        static native long hummockIteratorNew(byte[] readPlan);
848                        public static native byte[] rowGetKey(long pointer);
849                    },
850                    gen_array
851                }
852            }};
853            (all) => {{
854                for_all_native_methods! {
855                    gen_array
856                }
857            }};
858            ({$({ $func_name:ident, {$($ret:tt)+}, {$($args:tt)*} })*}) => {{
859                [
860                    $(
861                        (stringify! {$func_name}, gen_jni_sig! { {$($ret)+}, {$($args)*}}),
862                    )*
863                ]
864            }};
865        }
866        let sig: [(_, _); 3] = gen_array!(test);
867        assert_eq!(
868            sig,
869            [
870                ("defaultVnodeCount", "()I"),
871                ("hummockIteratorNew", "([B)J"),
872                ("rowGetKey", "(J)[B")
873            ]
874        );
875
876        let sig = gen_array!(all);
877        assert!(!sig.is_empty());
878    }
879
880    #[test]
881    fn test_all_native_methods() {
882        // This test shows the signature of all native methods
883        let expected = expect_test::expect![[r#"
884            [
885                tracingSlf4jEvent                        (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V,
886                tracingSlf4jEventEnabled                 (I)Z,
887                defaultVnodeCount                        ()I,
888                validateCdcSourceColumnType              (ILjava/lang/String;IJZLjava/lang/String;)Z,
889                iteratorNewStreamChunk                   (J)J,
890                iteratorNext                             (J)Z,
891                initObjectStoreForTest                   (Ljava/lang/String;Ljava/lang/String;)V,
892                putObject                                (Ljava/lang/String;[B)V,
893                getObjectStoreType                       ()Ljava/lang/String;,
894                deleteObjects                            (Ljava/lang/String;)V,
895                getObject                                (Ljava/lang/String;)[B,
896                listObject                               (Ljava/lang/String;)[Ljava/lang/String;,
897                iteratorClose                            (J)V,
898                newStreamChunkFromPayload                ([B)J,
899                newStreamChunkFromPretty                 (Ljava/lang/String;)J,
900                streamChunkClose                         (J)V,
901                iteratorGetKey                           (J)[B,
902                iteratorGetOp                            (J)I,
903                iteratorIsNull                           (JI)Z,
904                iteratorGetInt16Value                    (JI)S,
905                iteratorGetInt32Value                    (JI)I,
906                iteratorGetInt64Value                    (JI)J,
907                iteratorGetFloatValue                    (JI)F,
908                iteratorGetDoubleValue                   (JI)D,
909                iteratorGetBooleanValue                  (JI)Z,
910                iteratorGetStringValue                   (JI)Ljava/lang/String;,
911                iteratorGetTimestampValue                (JI)Ljava/time/LocalDateTime;,
912                iteratorGetTimestamptzValue              (JI)Ljava/time/OffsetDateTime;,
913                iteratorGetDecimalValue                  (JI)Ljava/math/BigDecimal;,
914                iteratorGetTimeValue                     (JI)Ljava/time/LocalTime;,
915                iteratorGetDateValue                     (JI)Ljava/time/LocalDate;,
916                iteratorGetIntervalValue                 (JI)Ljava/lang/String;,
917                iteratorGetJsonbValue                    (JI)Ljava/lang/String;,
918                iteratorGetByteaValue                    (JI)[B,
919                iteratorGetArrayValue                    (JILjava/lang/Class;)Ljava/lang/Object;,
920                sendCdcSourceMsgToChannel                (J[B)Z,
921                sendCdcSourceErrorToChannel              (JLjava/lang/String;)Z,
922                cdcSourceSenderClose                     (J)V,
923                recvSinkWriterRequestFromChannel         (J)Lcom/risingwave/java/binding/JniSinkWriterStreamRequest;,
924                sendSinkWriterResponseToChannel          (J[B)Z,
925                sendSinkWriterErrorToChannel             (JLjava/lang/String;)Z,
926                recvSinkCoordinatorRequestFromChannel    (J)[B,
927                sendSinkCoordinatorResponseToChannel     (J[B)Z,
928            ]
929        "#]];
930
931        struct MethodInfo {
932            name: &'static str,
933            sig: &'static str,
934        }
935
936        impl std::fmt::Debug for MethodInfo {
937            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
938                write!(f, "{:40} {}", self.name, self.sig)
939            }
940        }
941
942        macro_rules! gen_all_native_method_info {
943            () => {{
944                $crate::for_all_native_methods! {
945                    gen_all_native_method_info
946                }
947            }};
948            ({$({$func_name:ident, {$($ret:tt)*}, {$($args:tt)*}})*}) => {
949                [$(
950                    (
951                        MethodInfo {
952                            name: stringify! {$func_name},
953                            sig: $crate::gen_jni_sig! {
954                                {$($ret)*}, {$($args)*}
955                            },
956                        }
957                    )
958                ),*]
959            }
960        }
961        let info = gen_all_native_method_info!();
962        expected.assert_debug_eq(&info);
963    }
964}