forked from TheBlueMatt/ldk-garbagecollected
-
Notifications
You must be signed in to change notification settings - Fork 0
/
java_strings.py
1678 lines (1540 loc) · 83.6 KB
/
java_strings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from bindingstypes import *
from enum import Enum
import sys
class Target(Enum):
JAVA = 1,
ANDROID = 2
MACOS = 3
class Consts:
def __init__(self, DEBUG: bool, target: Target, **kwargs):
self.target = target
self.c_array_class_caches = set()
self.c_type_map = dict(
bool = ['boolean'],
uint8_t = ['byte'],
uint16_t = ['short'],
uint32_t = ['int'],
uint64_t = ['long'],
int64_t = ['long'],
double = ['double'],
)
self.java_type_map = dict(
String = "String"
)
self.java_hu_type_map = dict(
String = "String"
)
self.to_hu_conv_templates = dict(
ptr = '{human_type} {var_name}_hu_conv = null; if ({var_name} < 0 || {var_name} > 4096) { {var_name}_hu_conv = new {human_type}(null, {var_name}); }',
default = '{human_type} {var_name}_hu_conv = null; if ({var_name} < 0 || {var_name} > 4096) { {var_name}_hu_conv = new {human_type}(null, {var_name}); }'
)
self.bindings_header = """package org.ldk.impl;
import org.ldk.enums.*;
import org.ldk.impl.version;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class bindings {
static {
try {
// Try to load natively first, this works on Android and in testing.
System.loadLibrary(\"lightningjni\");
} catch (UnsatisfiedLinkError system_load_err) {
// Otherwise try to load from the library jar.
File tmpdir = new File(System.getProperty("java.io.tmpdir"), "ldk-java-nativelib");
tmpdir.mkdir(); // If it fails to create, assume it was there already
tmpdir.deleteOnExit();
String libname = "liblightningjni_" + System.getProperty("os.name").replaceAll(" ", "") +
"-" + System.getProperty("os.arch").replaceAll(" ", "") + ".nativelib";
try (InputStream is = bindings.class.getResourceAsStream("/" + libname)) {
Path libpath = new File(tmpdir.toPath().toString(), "liblightningjni.so").toPath();
Files.copy(is, libpath, StandardCopyOption.REPLACE_EXISTING);
Runtime.getRuntime().load(libpath.toString());
} catch (Exception e) {
System.err.println("Failed to load LDK native library.");
System.err.println("System LDK native library load failed with: " + system_load_err);
System.err.println("Resource-based LDK native library load failed with: " + e);
throw new IllegalArgumentException(e);
}
}
init(java.lang.Enum.class);
init_class_cache();
if (!get_lib_version_string().equals(version.get_ldk_java_bindings_version()))
throw new IllegalArgumentException("Compiled LDK library and LDK class failes do not match");
// Fetching the LDK versions from C also checks that the header and binaries match
System.err.println("Loaded LDK-Java Bindings " + version.get_ldk_java_bindings_version() + " with LDK " + get_ldk_version() + " and LDK-C-Bindings " + get_ldk_c_bindings_version());
}
static native void init(java.lang.Class c);
static native void init_class_cache();
static native String get_lib_version_string();
public static native String get_ldk_c_bindings_version();
public static native String get_ldk_version();
"""
self.bindings_version_file = """package org.ldk.impl;
public class version {
public static String get_ldk_java_bindings_version() {
return "<git_version_ldk_garbagecollected>";
}
}"""
self.util_fn_pfx = """package org.ldk.structs;
import org.ldk.impl.bindings;
import org.ldk.enums.*;
import org.ldk.util.*;
import java.util.Arrays;
import java.lang.ref.Reference;
import javax.annotation.Nullable;
public class UtilMethods {
"""
self.util_fn_sfx = "}"
self.common_base = """package org.ldk.structs;
import java.util.LinkedList;
class CommonBase {
long ptr;
LinkedList<Object> ptrs_to = new LinkedList();
protected CommonBase(long ptr) { assert ptr < 0 || ptr > 4096; this.ptr = ptr; }
}
"""
self.txout_defn = """public class TxOut extends CommonBase {
/** The script_pubkey in this output */
public final byte[] script_pubkey;
/** The value, in satoshis, of this output */
public final long value;
TxOut(java.lang.Object _dummy, long ptr) {
super(ptr);
this.script_pubkey = bindings.TxOut_get_script_pubkey(ptr);
this.value = bindings.TxOut_get_value(ptr);
}
public TxOut(long value, byte[] script_pubkey) {
super(bindings.TxOut_new(script_pubkey, value));
this.script_pubkey = bindings.TxOut_get_script_pubkey(ptr);
this.value = bindings.TxOut_get_value(ptr);
}
@Override @SuppressWarnings(\"deprecation\")
protected void finalize() throws Throwable {
super.finalize();
if (ptr != 0) { bindings.TxOut_free(ptr); }
}
}"""
self.txin_defn = """public class TxIn extends CommonBase {
/** The witness in this input, in serialized form */
public final byte[] witness;
/** The script_sig in this input */
public final byte[] script_sig;
/** The transaction output's sequence number */
public final int sequence;
/** The txid this input is spending */
public final byte[] previous_txid;
/** The output index within the spent transaction of the output this input is spending */
public final int previous_vout;
TxIn(java.lang.Object _dummy, long ptr) {
super(ptr);
this.witness = bindings.TxIn_get_witness(ptr);
this.script_sig = bindings.TxIn_get_script_sig(ptr);
this.sequence = bindings.TxIn_get_sequence(ptr);
this.previous_txid = bindings.TxIn_get_previous_txid(ptr);
this.previous_vout = bindings.TxIn_get_previous_vout(ptr);
}
/** Constructs a new TxIn, note that previous_txid must be exactly 32 bytes */
public TxIn(byte[] witness, byte[] script_sig, int sequence, byte[] previous_txid, int previous_vout) {
this(null, bindings.TxIn_new(witness, script_sig, sequence, previous_txid, previous_vout));
}
@Override @SuppressWarnings(\"deprecation\")
protected void finalize() throws Throwable {
super.finalize();
if (ptr != 0) { bindings.TxIn_free(ptr); }
}
}"""
self.scalar_defn = """public class BigEndianScalar extends CommonBase {
/** The bytes of the scalar value, in big endian */
public final byte[] scalar_bytes;
BigEndianScalar(java.lang.Object _dummy, long ptr) {
super(ptr);
this.scalar_bytes = bindings.BigEndianScalar_get_bytes(ptr);
}
public BigEndianScalar(byte[] scalar_bytes) {
super(bindings.BigEndianScalar_new(scalar_bytes));
this.scalar_bytes = bindings.BigEndianScalar_get_bytes(ptr);
}
@Override @SuppressWarnings(\"deprecation\")
protected void finalize() throws Throwable {
super.finalize();
if (ptr != 0) { bindings.BigEndianScalar_free(ptr); }
}
}"""
self.witness_program_defn = """public class WitnessProgram extends CommonBase {
/** The witness program bytes themselves */
public final byte[] program;
/** The witness version */
public final WitnessVersion version;
WitnessProgram(java.lang.Object _dummy, long ptr) {
super(ptr);
this.program = bindings.WitnessProgram_get_program(ptr);
this.version = new WitnessVersion(bindings.WitnessProgram_get_version(ptr));
}
static byte check_args(byte[] program, WitnessVersion version) {
if (program.length < 2 || program.length > 40) throw new IllegalArgumentException();
if (version.getVal() == 0 && program.length != 20 && program.length != 32) throw new IllegalArgumentException();
return version.getVal();
}
public WitnessProgram(byte[] program, WitnessVersion version) {
super(bindings.WitnessProgram_new(check_args(program, version), program));
this.program = bindings.WitnessProgram_get_program(ptr);
this.version = new WitnessVersion(bindings.WitnessProgram_get_version(ptr));
}
@Override @SuppressWarnings(\"deprecation\")
protected void finalize() throws Throwable {
super.finalize();
if (ptr != 0) { bindings.WitnessProgram_free(ptr); }
}
}"""
self.c_file_pfx = """#include <jni.h>
// On OSX jlong (ie long long) is not equivalent to int64_t, so we override here
#define int64_t jlong
#include \"org_ldk_impl_bindings.h\"
#include <lightning.h>
#include <string.h>
#include <stdatomic.h>
#include <stdlib.h>
#define LIKELY(v) __builtin_expect(!!(v), 1)
#define UNLIKELY(v) __builtin_expect(!!(v), 0)
"""
if self.target == Target.ANDROID:
self.c_file_pfx = self.c_file_pfx + """
#include <android/log.h>
#define DEBUG_PRINT(...) __android_log_print(ANDROID_LOG_ERROR, "LDK", __VA_ARGS__)
#include <unistd.h>
#include <pthread.h>
static void* redirect_stderr(void* ignored) {
int pipes[2];
pipe(pipes);
dup2(pipes[1], STDERR_FILENO);
char buf[8192];
memset(buf, 0, 8192);
ssize_t len;
while ((len = read(pipes[0], buf, 8191)) > 0) {
DEBUG_PRINT("%s\\n", buf);
memset(buf, 0, 8192);
}
DEBUG_PRINT("End of stderr???\\n");
return 0;
}
void __attribute__((constructor)) spawn_stderr_redirection() {
pthread_t thread;
pthread_create(&thread, NULL, &redirect_stderr, NULL);
}
"""
else:
self.c_file_pfx = self.c_file_pfx + "#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)\n"
if not DEBUG or self.target == Target.MACOS:
self.c_file_pfx = self.c_file_pfx + """#define MALLOC(a, _) malloc(a)
#define FREE(p) if ((uint64_t)(p) > 4096) { free(p); }
#define CHECK_ACCESS(p)
#define CHECK_INNER_FIELD_ACCESS_OR_NULL(v)
"""
if not DEBUG:
self.c_file_pfx += """#define DO_ASSERT(a) (void)(a)
#define CHECK(a)
"""
if DEBUG:
self.c_file_pfx = self.c_file_pfx + """#include <assert.h>
// Always run a, then assert it is true:
#define DO_ASSERT(a) do { bool _assert_val = (a); assert(_assert_val); } while(0)
// Assert a is true or do nothing
#define CHECK(a) DO_ASSERT(a)
void __attribute__((constructor)) debug_log_version() {
if (check_get_ldk_version() == NULL)
DEBUG_PRINT("LDK version did not match the header we built against\\n");
if (check_get_ldk_bindings_version() == NULL)
DEBUG_PRINT("LDK C Bindings version did not match the header we built against\\n");
}
"""
if self.target != Target.MACOS:
self.c_file_pfx += """
// Running a leak check across all the allocations and frees of the JDK is a mess,
// so instead we implement our own naive leak checker here, relying on the -wrap
// linker option to wrap malloc/calloc/realloc/free, tracking everyhing allocated
// and free'd in Rust or C across the generated bindings shared library.
#include <threads.h>
"""
if self.target == Target.ANDROID:
self.c_file_pfx = self.c_file_pfx + """
#include <unwind.h>
#include <dlfcn.h>
// Implement the execinfo functions using _Unwind_backtrace. This seems to miss many Rust
// symbols, so we only use it on Android, where execinfo.h is unavailable.
struct BacktraceState {
void** current;
void** end;
};
static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context* context, void* arg) {
struct BacktraceState* state = (struct BacktraceState*)arg;
uintptr_t pc = _Unwind_GetIP(context);
if (pc) {
if (state->current == state->end) {
return _URC_END_OF_STACK;
} else {
*state->current++ = (void*)(pc);
}
}
return _URC_NO_REASON;
}
int backtrace(void** buffer, int max) {
struct BacktraceState state = { buffer, buffer + max };
_Unwind_Backtrace(unwind_callback, &state);
return state.current - buffer;
}
void backtrace_symbols_fd(void ** buffer, int count, int _fd) {
for (int idx = 0; idx < count; ++idx) {
Dl_info info;
if (dladdr(buffer[idx], &info) && info.dli_sname) {
DEBUG_PRINT("%p: %s", buffer[idx], info.dli_sname);
} else {
DEBUG_PRINT("%p: ???", buffer[idx]);
}
}
}
"""
else:
self.c_file_pfx = self.c_file_pfx + "#include <execinfo.h>\n"
self.c_file_pfx = self.c_file_pfx + """
#include <unistd.h>
#include <pthread.h>
static pthread_mutex_t allocation_mtx;
void __attribute__((constructor)) init_mtx() {
DO_ASSERT(!pthread_mutex_init(&allocation_mtx, NULL));
}
#define BT_MAX 128
typedef struct allocation {
struct allocation* next;
void* ptr;
const char* struct_name;
void* bt[BT_MAX];
int bt_len;
unsigned long alloc_len;
} allocation;
static allocation* allocation_ll = NULL;
void* __real_malloc(size_t len);
void* __real_calloc(size_t nmemb, size_t len);
static void new_allocation(void* res, const char* struct_name, size_t len) {
allocation* new_alloc = __real_malloc(sizeof(allocation));
new_alloc->ptr = res;
new_alloc->struct_name = struct_name;
new_alloc->bt_len = backtrace(new_alloc->bt, BT_MAX);
new_alloc->alloc_len = len;
DO_ASSERT(!pthread_mutex_lock(&allocation_mtx));
new_alloc->next = allocation_ll;
allocation_ll = new_alloc;
DO_ASSERT(!pthread_mutex_unlock(&allocation_mtx));
}
static void* MALLOC(size_t len, const char* struct_name) {
void* res = __real_malloc(len);
new_allocation(res, struct_name, len);
return res;
}
void __real_free(void* ptr);
static void alloc_freed(void* ptr) {
allocation* p = NULL;
DO_ASSERT(!pthread_mutex_lock(&allocation_mtx));
allocation* it = allocation_ll;
while (it->ptr != ptr) {
p = it; it = it->next;
if (it == NULL) {
DEBUG_PRINT("ERROR: Tried to free unknown pointer %p at:\\n", ptr);
void* bt[BT_MAX];
int bt_len = backtrace(bt, BT_MAX);
backtrace_symbols_fd(bt, bt_len, STDERR_FILENO);
DEBUG_PRINT("\\n\\n");
DO_ASSERT(!pthread_mutex_unlock(&allocation_mtx));
return; // addrsan should catch malloc-unknown and print more info than we have
}
}
if (p) { p->next = it->next; } else { allocation_ll = it->next; }
DO_ASSERT(!pthread_mutex_unlock(&allocation_mtx));
DO_ASSERT(it->ptr == ptr);
__real_free(it);
}
static void FREE(void* ptr) {
if ((uint64_t)ptr <= 4096) return; // Rust loves to create pointers to the NULL page for dummys
alloc_freed(ptr);
__real_free(ptr);
}
void* __wrap_malloc(size_t len) {
void* res = __real_malloc(len);
new_allocation(res, "malloc call", len);
return res;
}
void* __wrap_calloc(size_t nmemb, size_t len) {
void* res = __real_calloc(nmemb, len);
new_allocation(res, "calloc call", len);
return res;
}
void __wrap_free(void* ptr) {
if (ptr == NULL) return;
alloc_freed(ptr);
__real_free(ptr);
}
static void CHECK_ACCESS(const void* ptr) {
DO_ASSERT(!pthread_mutex_lock(&allocation_mtx));
allocation* it = allocation_ll;
while (it->ptr != ptr) {
it = it->next;
if (it == NULL) {
DEBUG_PRINT("ERROR: Tried to access unknown pointer %p at:\\n", ptr);
void* bt[BT_MAX];
int bt_len = backtrace(bt, BT_MAX);
backtrace_symbols_fd(bt, bt_len, STDERR_FILENO);
DEBUG_PRINT("\\n\\n");
DO_ASSERT(!pthread_mutex_unlock(&allocation_mtx));
return; // addrsan should catch and print more info than we have
}
}
DO_ASSERT(!pthread_mutex_unlock(&allocation_mtx));
}
#define CHECK_INNER_FIELD_ACCESS_OR_NULL(v) \\
if (v.is_owned && v.inner != NULL) { \\
const void *p = __unmangle_inner_ptr(v.inner); \\
if (p != NULL) { \\
CHECK_ACCESS(p); \\
} \\
}
void* __real_realloc(void* ptr, size_t newlen);
void* __wrap_realloc(void* ptr, size_t len) {
if (ptr != NULL) alloc_freed(ptr);
void* res = __real_realloc(ptr, len);
new_allocation(res, "realloc call", len);
return res;
}
void __wrap_reallocarray(void* ptr, size_t new_sz) {
// Rust doesn't seem to use reallocarray currently
DO_ASSERT(false);
}
void __attribute__((destructor)) check_leaks() {
unsigned long alloc_count = 0;
unsigned long alloc_size = 0;
DEBUG_PRINT("The following LDK-allocated blocks still remain.\\n");
DEBUG_PRINT("Note that this is only accurate if System.gc(); System.runFinalization()\\n");
DEBUG_PRINT("was called prior to exit after all LDK objects were out of scope.\\n");
for (allocation* a = allocation_ll; a != NULL; a = a->next) {
DEBUG_PRINT("%s %p (%lu bytes) remains:\\n", a->struct_name, a->ptr, a->alloc_len);
backtrace_symbols_fd(a->bt, a->bt_len, STDERR_FILENO);
DEBUG_PRINT("\\n\\n");
alloc_count++;
alloc_size += a->alloc_len;
}
DEBUG_PRINT("%lu allocations remained for %lu bytes.\\n", alloc_count, alloc_size);
DEBUG_PRINT("Note that this is only accurate if System.gc(); System.runFinalization()\\n");
DEBUG_PRINT("was called prior to exit after all LDK objects were out of scope.\\n");
}
"""
self.c_file_pfx = self.c_file_pfx + """
static jmethodID ordinal_meth = NULL;
JNIEXPORT void Java_org_ldk_impl_bindings_init(JNIEnv * env, jclass _b, jclass enum_class) {
ordinal_meth = (*env)->GetMethodID(env, enum_class, "ordinal", "()I");
CHECK(ordinal_meth != NULL);
}
// We assume that CVec_u8Z and u8slice are the same size and layout (and thus pointers to the two can be mixed)
_Static_assert(sizeof(LDKCVec_u8Z) == sizeof(LDKu8slice), "Vec<u8> and [u8] need to have been mapped identically");
_Static_assert(offsetof(LDKCVec_u8Z, data) == offsetof(LDKu8slice, data), "Vec<u8> and [u8] need to have been mapped identically");
_Static_assert(offsetof(LDKCVec_u8Z, datalen) == offsetof(LDKu8slice, datalen), "Vec<u8> and [u8] need to have been mapped identically");
_Static_assert(sizeof(jlong) == sizeof(int64_t), "We assume that j-types are the same as C types");
_Static_assert(sizeof(jbyte) == sizeof(char), "We assume that j-types are the same as C types");
_Static_assert(sizeof(void*) <= 8, "Pointers must fit into 64 bits");
typedef jlongArray int64_tArray;
typedef jbyteArray int8_tArray;
typedef jshortArray int16_tArray;
static inline jstring str_ref_to_java(JNIEnv *env, const unsigned char* chars, size_t len) {
// Java uses "Modified UTF-8" rather than UTF-8. This requires special
// handling for codepoints above 0xFFFF, which get converted from four
// bytes to six. We don't know upfront how many codepoints in the string
// are above 0xFFFF, so we just allocate an extra 33% up front and waste a
// bit of space.
unsigned char* java_chars = MALLOC(len * 3 / 2 + 1, "str conv buf");
unsigned char* next_java_char = java_chars;
const unsigned char* next_in_char = chars;
const unsigned char* end = chars + len;
#define COPY_CHAR_TO_JAVA do { *next_java_char = *next_in_char; next_java_char++; next_in_char++; } while (0)
while (next_in_char < end) {
if (!*next_in_char) break;
if (!(*next_in_char & 0b10000000)) {
COPY_CHAR_TO_JAVA;
} else if ((*next_in_char & 0b11100000) == 0b11000000) {
if (next_in_char + 2 > end) { CHECK(false); break; } // bad string
COPY_CHAR_TO_JAVA;
COPY_CHAR_TO_JAVA;
} else if ((*next_in_char & 0b11110000) == 0b11100000) {
if (next_in_char + 3 > end) { CHECK(false); break; } // bad string
COPY_CHAR_TO_JAVA;
COPY_CHAR_TO_JAVA;
COPY_CHAR_TO_JAVA;
} else if ((*next_in_char & 0b11111000) == 0b11110000) {
if (next_in_char + 4 > end) { CHECK(false); break; } // bad string
uint32_t codepoint = 0;
codepoint |= (((uint32_t)*(next_in_char )) & 0b00000111) << 18;
codepoint |= (((uint32_t)*(next_in_char + 1)) & 0b00111111) << 12;
codepoint |= (((uint32_t)*(next_in_char + 2)) & 0b00111111) << 6;
codepoint |= (((uint32_t)*(next_in_char + 3)) & 0b00111111) << 0;
codepoint -= 0x10000;
*next_java_char = 0b11101101;
next_java_char++;
*next_java_char = 0b10100000 | ((codepoint >> 16) & 0b00001111);
next_java_char++;
*next_java_char = 0b10000000 | ((codepoint >> 10) & 0b00111111);
next_java_char++;
*next_java_char = 0b11101101;
next_java_char++;
*next_java_char = 0b10110000 | ((codepoint >> 6) & 0b00001111);
next_java_char++;
*next_java_char = 0b10000000 | ((codepoint >> 0) & 0b00111111);
next_java_char++;
next_in_char += 4;
} else {
// Bad string
CHECK(false);
break;
}
}
*next_java_char = 0;
jstring ret = (*env)->NewStringUTF(env, java_chars);
FREE(java_chars);
return ret;
}
static inline LDKStr java_to_owned_str(JNIEnv *env, jstring str) {
uint64_t str_len = (*env)->GetStringUTFLength(env, str);
// Java uses "Modified UTF-8" rather than UTF-8. This requires special
// handling for codepoints above 0xFFFF, which we implement below.
unsigned char* newchars = MALLOC(str_len, "String chars");
unsigned char* next_newchar = newchars;
uint64_t utf8_len = 0;
const unsigned char* jchars = (*env)->GetStringUTFChars(env, str, NULL);
const unsigned char* next_char = jchars;
const unsigned char* end = jchars + str_len;
#define COPY_CHAR_FROM_JAVA do { *next_newchar = *next_char; next_newchar++; next_char++; utf8_len++; } while (0)
while (next_char < end) {
if (!(*next_char & 0b10000000)) {
CHECK(*next_char != 0); // Bad Modified UTF-8 string, but we'll just cut here
COPY_CHAR_FROM_JAVA;
} else if ((*next_char & 0b11100000) == 0b11000000) {
if (next_char + 2 > end) { CHECK(false); break; } // bad string
uint16_t codepoint = 0;
codepoint |= (((uint16_t)(*next_char & 0x1f)) << 6);
codepoint |= *(next_char + 1) & 0x3f;
if (codepoint == 0) {
// We should really never get null codepoints, but java allows them.
// Just skip it.
next_char += 2;
} else {
COPY_CHAR_FROM_JAVA;
COPY_CHAR_FROM_JAVA;
}
} else if ((*next_char & 0b11110000) == 0b11100000) {
if (next_char + 3 > end) { CHECK(false); break; } // bad string
if (*next_char == 0b11101101 && (*(next_char + 1) & 0b11110000) == 0b10100000) {
// Surrogate code unit shoul indicate we have a codepoint above
// 0xFFFF, which is where Modified UTF-8 and UTF-8 diverge.
if (next_char + 6 > end) { CHECK(false); break; } // bad string
CHECK(*(next_char + 3) == 0b11101101);
CHECK((*(next_char + 4) & 0b11110000) == 0b10110000);
// Calculate the codepoint per https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16542
uint32_t codepoint = 0x10000;
codepoint += ((((uint32_t)*(next_char + 1)) & 0x0f) << 16);
codepoint += ((((uint32_t)*(next_char + 2)) & 0x3f) << 10);
codepoint += ((((uint32_t)*(next_char + 4)) & 0x0f) << 6);
codepoint += (((uint32_t)*(next_char + 5)) & 0x3f);
*next_newchar = 0b11110000 | ((codepoint >> 18) & 0b111);
next_newchar++;
*next_newchar = 0b10000000 | ((codepoint >> 12) & 0b111111);
next_newchar++;
*next_newchar = 0b10000000 | ((codepoint >> 6) & 0b111111);
next_newchar++;
*next_newchar = 0b10000000 | ( codepoint & 0b111111);
next_newchar++;
next_char += 6;
utf8_len += 4;
} else {
COPY_CHAR_FROM_JAVA;
COPY_CHAR_FROM_JAVA;
COPY_CHAR_FROM_JAVA;
}
} else {
// Bad string
CHECK(false);
break;
}
}
(*env)->ReleaseStringUTFChars(env, str, jchars);
LDKStr res = {
.chars = newchars,
.len = utf8_len,
.chars_is_owned = true
};
return res;
}
JNIEXPORT jstring JNICALL Java_org_ldk_impl_bindings_get_1ldk_1c_1bindings_1version(JNIEnv *env, jclass _c) {
return str_ref_to_java(env, check_get_ldk_bindings_version(), strlen(check_get_ldk_bindings_version()));
}
JNIEXPORT jstring JNICALL Java_org_ldk_impl_bindings_get_1ldk_1version(JNIEnv *env, jclass _c) {
return str_ref_to_java(env, check_get_ldk_version(), strlen(check_get_ldk_version()));
}
#include "version.c"
"""
self.c_version_file = """JNIEXPORT jstring JNICALL Java_org_ldk_impl_bindings_get_1lib_1version_1string(JNIEnv *env, jclass _c) {
return str_ref_to_java(env, "<git_version_ldk_garbagecollected>", strlen("<git_version_ldk_garbagecollected>"));
}"""
self.hu_struct_file_prefix = """package org.ldk.structs;
import org.ldk.impl.bindings;
import org.ldk.enums.*;
import org.ldk.util.*;
import java.util.Arrays;
import java.lang.ref.Reference;
import javax.annotation.Nullable;
"""
self.hu_struct_file_suffix = ""
self.c_fn_ty_pfx = "JNIEXPORT "
self.c_fn_args_pfx = "JNIEnv *env, jclass clz"
self.file_ext = ".java"
self.ptr_c_ty = "int64_t"
self.ptr_native_ty = "long"
self.u128_native_ty = "UInt128"
self.usize_c_ty = "int64_t"
self.usize_native_ty = "long"
self.native_zero_ptr = "0"
self.unitary_enum_c_ty = "jclass"
self.ptr_arr = "jobjectArray"
self.is_arr_some_check = ("", " != NULL")
self.get_native_arr_len_call = ("(*env)->GetArrayLength(env, ", ")")
def bindings_footer(self):
return "}\n"
def construct_jenv(self):
res = "JNIEnv *env;\n"
res += "jint get_jenv_res = (*j_calls->vm)->GetEnv(j_calls->vm, (void**)&env, JNI_VERSION_1_6);\n"
res += "if (get_jenv_res == JNI_EDETACHED) {\n"
if self.target == Target.ANDROID:
res += "\tDO_ASSERT((*j_calls->vm)->AttachCurrentThread(j_calls->vm, &env, NULL) == JNI_OK);\n"
else:
res += "\tDO_ASSERT((*j_calls->vm)->AttachCurrentThread(j_calls->vm, (void**)&env, NULL) == JNI_OK);\n"
res += "} else {\n"
res += "\tDO_ASSERT(get_jenv_res == JNI_OK);\n"
res += "}\n"
return res
def deconstruct_jenv(self):
res = "if (get_jenv_res == JNI_EDETACHED) {\n"
res += "\tDO_ASSERT((*j_calls->vm)->DetachCurrentThread(j_calls->vm) == JNI_OK);\n"
res += "}\n"
return res
def release_native_arr_ptr_call(self, ty_info, arr_var, arr_ptr_var):
if ty_info.subty is None or not ty_info.subty.c_ty.endswith("Array"):
return "(*env)->ReleasePrimitiveArrayCritical(env, " + arr_var + ", " + arr_ptr_var + ", 0)"
return None
def create_native_arr_call(self, arr_len, ty_info):
if ty_info.c_ty == "int8_tArray":
return "(*env)->NewByteArray(env, " + arr_len + ")"
elif ty_info.subty.c_ty.endswith("Array"):
clz_var = ty_info.java_fn_ty_arg[1:].replace("[", "arr_of_")
self.c_array_class_caches.add(clz_var)
return "(*env)->NewObjectArray(env, " + arr_len + ", " + clz_var + "_clz, NULL);\n"
elif ty_info.subty.c_ty == "jstring":
return "(*env)->NewObjectArray(env, " + arr_len + ", String_clz, NULL);\n"
else:
return "(*env)->New" + ty_info.java_ty.strip("[]").title() + "Array(env, " + arr_len + ")"
def set_native_arr_contents(self, arr_name, arr_len, ty_info):
if ty_info.c_ty == "int8_tArray":
return ("(*env)->SetByteArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", ", ")")
elif ty_info.c_ty == "int16_tArray":
return ("(*env)->SetShortArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", ", ")")
else:
assert False
def get_native_arr_contents(self, arr_name, dest_name, arr_len, ty_info, copy):
if "String" in ty_info.java_ty:
return None
if ty_info.c_ty == "int8_tArray" or ty_info.c_ty == "int16_tArray":
fn_ty = "Byte" if ty_info.c_ty == "int8_tArray" else "Short"
if copy:
return "(*env)->Get" + fn_ty + "ArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", " + dest_name + ")"
else:
return "(*env)->Get" + fn_ty + "ArrayElements (env, " + arr_name + ", NULL)"
elif not ty_info.java_ty[:len(ty_info.java_ty) - 2].endswith("[]"):
return "(*env)->Get" + ty_info.subty.java_ty.title() + "ArrayElements (env, " + arr_name + ", NULL)"
else:
return None
def get_native_arr_elem(self, arr_name, idxc, ty_info):
if self.get_native_arr_contents(arr_name, "", "", ty_info, False) is None:
return "(*env)->GetObjectArrayElement(env, " + arr_name + ", " + idxc + ")"
else:
assert False # Only called if above is None
def get_native_arr_ptr_call(self, ty_info):
if ty_info.subty is not None and ty_info.subty.c_ty.endswith("Array"):
return None
return ("(*env)->GetPrimitiveArrayCritical(env, ", ", NULL)")
def get_native_arr_entry_call(self, ty_info, arr_name, idxc, entry_access):
if ty_info.subty is None or not ty_info.subty.c_ty.endswith("Array"):
return None
return "(*env)->SetObjectArrayElement(env, " + arr_name + ", " + idxc + ", " + entry_access + ")"
def cleanup_native_arr_ref_contents(self, arr_name, dest_name, arr_len, ty_info):
if ty_info.c_ty == "int8_tArray":
return "(*env)->ReleaseByteArrayElements(env, " + arr_name + ", (int8_t*)" + dest_name + ", 0);"
else:
return "(*env)->Release" + ty_info.java_ty.strip("[]").title() + "ArrayElements(env, " + arr_name + ", " + dest_name + ", 0)"
def map_hu_array_elems(self, arr_name, conv_name, arr_ty, elem_ty, is_nullable):
if elem_ty.java_ty == "long" and elem_ty.java_hu_ty != "long":
return arr_name + " != null ? Arrays.stream(" + arr_name + ").mapToLong(" + conv_name + " -> " + elem_ty.from_hu_conv[0] + ").toArray() : null"
elif elem_ty.java_ty == "long":
return arr_name + " != null ? Arrays.stream(" + arr_name + ").map(" + conv_name + " -> " + elem_ty.from_hu_conv[0] + ").toArray() : null"
elif elem_ty.java_hu_ty == "UInt5":
return arr_name + " != null ? InternalUtils.convUInt5Array(" + arr_name + ") : null"
elif elem_ty.java_hu_ty == "WitnessVersion":
return arr_name + " != null ? InternalUtils.convWitnessVersionArray(" + arr_name + ") : null"
else:
return arr_name + " != null ? Arrays.stream(" + arr_name + ").map(" + conv_name + " -> " + elem_ty.from_hu_conv[0] + ").toArray(" + arr_ty.java_ty + "::new) : null"
def str_ref_to_native_call(self, var_name, str_len):
return "str_ref_to_java(env, " + var_name + ", " + str_len + ")"
def str_ref_to_c_call(self, var_name):
return "java_to_owned_str(env, " + var_name + ")"
def str_to_hu_conv(self, var_name):
return None
def str_from_hu_conv(self, var_name):
return None
def c_fn_name_define_pfx(self, fn_name, has_args):
if has_args:
return "JNICALL Java_org_ldk_impl_bindings_" + fn_name.replace("_", "_1") + "(JNIEnv *env, jclass clz, "
return "JNICALL Java_org_ldk_impl_bindings_" + fn_name.replace("_", "_1") + "(JNIEnv *env, jclass clz"
def init_str(self):
res = ""
for ty in sorted(self.c_array_class_caches):
res = res + "static jclass " + ty + "_clz = NULL;\n"
res = res + "static jclass String_clz = NULL;\n"
res = res + "JNIEXPORT void Java_org_ldk_impl_bindings_init_1class_1cache(JNIEnv * env, jclass clz) {\n"
for ty in sorted(self.c_array_class_caches):
res = res + "\t" + ty + "_clz = (*env)->FindClass(env, \"" + ty.replace("arr_of_", "[") + "\");\n"
res = res + "\tCHECK(" + ty + "_clz != NULL);\n"
res = res + "\t" + ty + "_clz = (*env)->NewGlobalRef(env, " + ty + "_clz);\n"
res = res + "\tString_clz = (*env)->FindClass(env, \"java/lang/String\");\n"
res = res + "\tCHECK(String_clz != NULL);\n"
res = res + "\tString_clz = (*env)->NewGlobalRef(env, String_clz);\n"
res = res + "}\n"
return res
def var_decl_statement(self, ty_string, var_name, statement):
return ty_string + " " + var_name + " = " + statement
def get_java_arr_len(self, arr_name):
return arr_name + ".length"
def get_java_arr_elem(self, elem_ty, arr_name, idx):
return arr_name + "[" + idx + "]"
def constr_hu_array(self, ty_info, arr_len):
base_ty = ty_info.subty.java_hu_ty.split("[")[0].split("<")[0]
conv = "new " + base_ty + "[" + arr_len + "]"
if "[" in ty_info.subty.java_hu_ty.split("<")[0]:
# Do a bit of a dance to move any excess [] to the end
conv += "[" + ty_info.subty.java_hu_ty.split("<")[0].split("[")[1]
return conv
def cleanup_converted_native_array(self, ty_info, arr_name):
return None
def primitive_arr_from_hu(self, arr_ty, fixed_len, arr_name):
mapped_ty = arr_ty.subty
if arr_ty.rust_obj == "LDKU128":
return ("" + arr_name + ".getLEBytes()", "")
if fixed_len is not None:
if mapped_ty.c_ty == "int8_t" or mapped_ty.c_ty == "uint8_t":
return ("InternalUtils.check_arr_len(" + arr_name + ", " + fixed_len + ")", "")
elif mapped_ty.c_ty == "int16_t" or mapped_ty.c_ty == "uint16_t":
return ("InternalUtils.check_arr_16_len(" + arr_name + ", " + fixed_len + ")", "")
else:
print(arr_ty.c_ty)
assert False
return None
def primitive_arr_to_hu(self, arr_ty, fixed_len, arr_name, conv_name):
if arr_ty.rust_obj == "LDKU128":
return "org.ldk.util.UInt128 " + conv_name + " = new org.ldk.util.UInt128(" + arr_name + ");"
return None
def java_arr_ty_str(self, elem_ty_str):
return elem_ty_str + "[]"
def for_n_in_range(self, n, minimum, maximum):
return "for (int " + n + " = " + minimum + "; " + n + " < " + maximum + "; " + n + "++) {"
def for_n_in_arr(self, n, arr_name, arr_elem_ty):
return ("for (" + arr_elem_ty.java_hu_ty + " " + n + ": " + arr_name + ") { ", " }")
def get_ptr(self, var):
return var + ".ptr"
def set_null_skip_free(self, var):
return var + ".ptr" + " = 0;"
def add_ref(self, holder, referent):
return "if (" + holder + " != null) { " + holder + ".ptrs_to.add(" + referent + "); }"
def fully_qualified_hu_ty_path(self, ty):
if ty.java_fn_ty_arg.startswith("L") and ty.java_fn_ty_arg.endswith(";"):
return ty.java_fn_ty_arg.strip("L;").replace("/", ".")
if ty.java_hu_ty == "UnqualifiedError" or ty.java_hu_ty == "UInt128" or ty.java_hu_ty == "UInt5" or ty.java_hu_ty == "WitnessVersion":
return "org.ldk.util." + ty.java_hu_ty
if not ty.is_native_primitive and ty.rust_obj is not None and not "[]" in ty.java_hu_ty:
return "org.ldk.structs." + ty.java_hu_ty
return ty.java_hu_ty
def native_c_unitary_enum_map(self, struct_name, variants, enum_doc_comment):
out_java_enum = "package org.ldk.enums;\n\n"
out_java = ""
out_c = ""
out_c += "static inline LDK" + struct_name + " LDK" + struct_name + "_from_java(" + self.c_fn_args_pfx + ") {\n"
out_c += "\tjint ord = (*env)->CallIntMethod(env, clz, ordinal_meth);\n"
out_c += "\tif (UNLIKELY((*env)->ExceptionCheck(env))) {\n"
out_c += "\t\t(*env)->ExceptionDescribe(env);\n"
out_c += "\t\t(*env)->FatalError(env, \"A call to " + struct_name + ".ordinal() from rust threw an exception.\");\n"
out_c += "\t}\n"
out_c += "\tswitch (ord) {\n"
if enum_doc_comment is not None:
out_java_enum += "/**\n * " + enum_doc_comment.replace("\n", "\n * ") + "\n */\n"
out_java_enum += "public enum " + struct_name + " {\n"
ord_v = 0
for var, var_docs in variants:
if var_docs is not None:
out_java_enum += "\t/**\n\t * " + var_docs.replace("\n", "\n\t * ") + "\n\t */\n"
out_java_enum += "\t" + var + ",\n"
out_c = out_c + "\t\tcase %d: return %s;\n" % (ord_v, var)
ord_v = ord_v + 1
out_java_enum = out_java_enum + "\t; static native void init();\n"
out_java_enum = out_java_enum + "\tstatic { init(); }\n"
out_java_enum = out_java_enum + "}"
out_java = out_java + "\tstatic { " + struct_name + ".values(); /* Force enum statics to run */ }\n"
out_c += "\t}\n"
out_c += "\t(*env)->FatalError(env, \"A call to " + struct_name + ".ordinal() from rust returned an invalid value.\");\n"
out_c += "\tabort(); // Unreachable, but will let the compiler know we don't return here\n"
out_c += "}\n"
out_c = out_c + "static jclass " + struct_name + "_class = NULL;\n"
for var, _ in variants:
out_c = out_c + "static jfieldID " + struct_name + "_" + var + " = NULL;\n"
out_c = out_c + self.c_fn_ty_pfx + "void JNICALL Java_org_ldk_enums_" + struct_name.replace("_", "_1") + "_init (" + self.c_fn_args_pfx + ") {\n"
out_c = out_c + "\t" + struct_name + "_class = (*env)->NewGlobalRef(env, clz);\n"
out_c = out_c + "\tCHECK(" + struct_name + "_class != NULL);\n"
for var, _ in variants:
out_c = out_c + "\t" + struct_name + "_" + var + " = (*env)->GetStaticFieldID(env, " + struct_name + "_class, \"" + var + "\", \"Lorg/ldk/enums/" + struct_name + ";\");\n"
out_c = out_c + "\tCHECK(" + struct_name + "_" + var + " != NULL);\n"
out_c = out_c + "}\n"
out_c = out_c + "static inline jclass LDK" + struct_name + "_to_java(JNIEnv *env, LDK" + struct_name + " val) {\n"
out_c = out_c + "\tswitch (val) {\n"
ord_v = 0
for var, _ in variants:
out_c = out_c + "\t\tcase " + var + ":\n"
out_c = out_c + "\t\t\treturn (*env)->GetStaticObjectField(env, " + struct_name + "_class, " + struct_name + "_" + var + ");\n"
ord_v = ord_v + 1
out_c = out_c + "\t\tdefault: abort();\n"
out_c = out_c + "\t}\n"
out_c = out_c + "}\n\n"
return (out_c, out_java_enum, out_java)
def c_unitary_enum_to_native_call(self, ty_info):
return (ty_info.rust_obj + "_to_java(env, ", ")")
def native_unitary_enum_to_c_call(self, ty_info):
return (ty_info.rust_obj + "_from_java(env, ", ")")
def c_complex_enum_pfx(self, struct_name, variants, init_meth_jty_strs):
out_c = ""
for var in variants:
out_c = out_c + "static jclass " + struct_name + "_" + var + "_class = NULL;\n"
out_c = out_c + "static jmethodID " + struct_name + "_" + var + "_meth = NULL;\n"
out_c = out_c + self.c_fn_ty_pfx + "void JNICALL Java_org_ldk_impl_bindings_00024" + struct_name.replace("_", "_1") + "_init (" + self.c_fn_args_pfx + ") {\n"
for var_name in variants:
out_c += "\t" + struct_name + "_" + var_name + "_class =\n"
out_c += "\t\t(*env)->NewGlobalRef(env, (*env)->FindClass(env, \"org/ldk/impl/bindings$" + struct_name + "$" + var_name + "\"));\n"
out_c += "\tCHECK(" + struct_name + "_" + var_name + "_class != NULL);\n"
out_c += "\t" + struct_name + "_" + var_name + "_meth = (*env)->GetMethodID(env, " + struct_name + "_" + var_name + "_class, \"<init>\", \"(" + init_meth_jty_strs[var_name] + ")V\");\n"
out_c += "\tCHECK(" + struct_name + "_" + var_name + "_meth != NULL);\n"
out_c = out_c + "}\n"
return out_c
def c_complex_enum_pass_ty(self, struct_name):
return "jobject"
def c_constr_native_complex_enum(self, struct_name, variant, c_params):
ret = "(*env)->NewObject(env, " + struct_name + "_" + variant + "_class, " + struct_name + "_" + variant + "_meth"
for param in c_params:
ret = ret + ", " + param
return ret + ")"
def native_c_map_trait(self, struct_name, field_vars, flattened_field_vars, field_fns, trait_doc_comment):
out_java_trait = ""
out_java = ""
# First generate most of the Java code, note that we need information about java method argument strings for C
out_java_trait = out_java_trait + self.hu_struct_file_prefix
if trait_doc_comment is not None:
out_java_trait += "/**\n * " + trait_doc_comment.replace("\n", "\n * ") + "\n */\n"
out_java_trait += "@SuppressWarnings(\"unchecked\") // We correctly assign various generic arrays\n"
out_java_trait = out_java_trait + "public class " + struct_name.replace("LDK","") + " extends CommonBase {\n"
out_java_trait = out_java_trait + "\tfinal bindings." + struct_name + " bindings_instance;\n"
out_java_trait = out_java_trait + "\t" + struct_name.replace("LDK", "") + "(Object _dummy, long ptr) { super(ptr); bindings_instance = null; }\n"
out_java_trait = out_java_trait + "\tprivate " + struct_name.replace("LDK", "") + "(bindings." + struct_name + " arg"
for var in flattened_field_vars:
if isinstance(var, ConvInfo):
out_java_trait = out_java_trait + ", " + var.java_hu_ty + " " + var.arg_name
else:
out_java_trait = out_java_trait + ", bindings." + var[0] + " " + var[1]
out_java_trait = out_java_trait + ") {\n"
out_java_trait = out_java_trait + "\t\tsuper(bindings." + struct_name + "_new(arg"
for var in flattened_field_vars:
if isinstance(var, ConvInfo):
if var.from_hu_conv is not None:
out_java_trait = out_java_trait + ", " + var.from_hu_conv[0]
else:
out_java_trait = out_java_trait + ", " + var.arg_name
else:
out_java_trait = out_java_trait + ", " + var[1]
out_java_trait = out_java_trait + "));\n"
out_java_trait = out_java_trait + "\t\tthis.ptrs_to.add(arg);\n"
for var in flattened_field_vars:
if isinstance(var, ConvInfo):
if var.from_hu_conv is not None and var.from_hu_conv[1] != "":
out_java_trait = out_java_trait + "\t\t" + var.from_hu_conv[1].replace("\n", "\n\t\t") + ";\n"
else:
out_java_trait = out_java_trait + "\t\tthis.ptrs_to.add(" + var[1] + ");\n"
out_java_trait += f""" this.bindings_instance = arg;
}}
@Override @SuppressWarnings("deprecation")
protected void finalize() throws Throwable {{
if (ptr != 0) {{ bindings.{struct_name.replace("LDK","")}_free(ptr); }} super.finalize();
}}
/**
* Destroys the object, freeing associated resources. After this call, any access
* to this object may result in a SEGFAULT or worse.
*
* You should generally NEVER call this method. You should let the garbage collector
* do this for you when it finalizes objects. However, it may be useful for types
* which represent locks and should be closed immediately to avoid holding locks
* until the GC runs.
*/
public void destroy() {{
if (ptr != 0) {{ bindings.{struct_name.replace("LDK","")}_free(ptr); }}
ptr = 0;
}}
"""
java_trait_constr = "\tprivate static class " + struct_name + "Holder { " + struct_name.replace("LDK", "") + " held; }\n"
java_trait_constr = java_trait_constr + "\tpublic static " + struct_name.replace("LDK", "") + " new_impl(" + struct_name.replace("LDK", "") + "Interface arg"
for var in flattened_field_vars:
if isinstance(var, ConvInfo):
java_trait_constr = java_trait_constr + ", " + var.java_hu_ty + " " + var.arg_name
else:
# Ideally we'd be able to take any instance of the interface, but our C code can only represent
# Java-implemented version, so we require users pass a Java implementation here :/
java_trait_constr = java_trait_constr + ", " + var[0].replace("LDK", "") + "." + var[0].replace("LDK", "") + "Interface " + var[1] + "_impl"
java_trait_constr = java_trait_constr + ") {\n\t\tfinal " + struct_name + "Holder impl_holder = new " + struct_name + "Holder();\n"
java_trait_constr = java_trait_constr + "\t\timpl_holder.held = new " + struct_name.replace("LDK", "") + "(new bindings." + struct_name + "() {\n"
out_java_trait = out_java_trait + "\tpublic static interface " + struct_name.replace("LDK", "") + "Interface {\n"
out_java = out_java + "\tpublic interface " + struct_name + " {\n"
java_meths = []
for fn_line in field_fns:
java_meth_descr = "("
if fn_line.fn_name != "free" and fn_line.fn_name != "cloned":
out_java = out_java + "\t\t " + fn_line.ret_ty_info.java_ty + " " + fn_line.fn_name + "("
java_trait_constr = java_trait_constr + "\t\t\t@Override public " + fn_line.ret_ty_info.java_ty + " " + fn_line.fn_name + "("
out_java_trait += "\t\t/**\n\t\t * " + fn_line.docs.replace("\n", "\n\t\t * ") + "\n\t\t */\n"