forked from HaxeFoundation/haxe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
genjava.ml
3674 lines (3334 loc) · 136 KB
/
genjava.ml
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
(*
The Haxe Compiler
Copyright (C) 2005-2015 Haxe Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*)
open JData
open Unix
open Ast
open Common
open Type
open Gencommon
open Gencommon.SourceWriter
open Printf
open Option
open ExtString
module SS = Set.Make(String)
let is_boxed_type t = match follow t with
| TInst ({ cl_path = (["java";"lang"], "Boolean") }, [])
| TInst ({ cl_path = (["java";"lang"], "Double") }, [])
| TInst ({ cl_path = (["java";"lang"], "Integer") }, [])
| TInst ({ cl_path = (["java";"lang"], "Byte") }, [])
| TInst ({ cl_path = (["java";"lang"], "Short") }, [])
| TInst ({ cl_path = (["java";"lang"], "Character") }, [])
| TInst ({ cl_path = (["java";"lang"], "Float") }, []) -> true
| TAbstract ({ a_path = (["java";"lang"], "Boolean") }, [])
| TAbstract ({ a_path = (["java";"lang"], "Double") }, [])
| TAbstract ({ a_path = (["java";"lang"], "Integer") }, [])
| TAbstract ({ a_path = (["java";"lang"], "Byte") }, [])
| TAbstract ({ a_path = (["java";"lang"], "Short") }, [])
| TAbstract ({ a_path = (["java";"lang"], "Character") }, [])
| TAbstract ({ a_path = (["java";"lang"], "Float") }, []) -> true
| _ -> false
let unboxed_type gen t tbyte tshort tchar tfloat = match follow t with
| TInst ({ cl_path = (["java";"lang"], "Boolean") }, []) -> gen.gcon.basic.tbool
| TInst ({ cl_path = (["java";"lang"], "Double") }, []) -> gen.gcon.basic.tfloat
| TInst ({ cl_path = (["java";"lang"], "Integer") }, []) -> gen.gcon.basic.tint
| TInst ({ cl_path = (["java";"lang"], "Byte") }, []) -> tbyte
| TInst ({ cl_path = (["java";"lang"], "Short") }, []) -> tshort
| TInst ({ cl_path = (["java";"lang"], "Character") }, []) -> tchar
| TInst ({ cl_path = (["java";"lang"], "Float") }, []) -> tfloat
| TAbstract ({ a_path = (["java";"lang"], "Boolean") }, []) -> gen.gcon.basic.tbool
| TAbstract ({ a_path = (["java";"lang"], "Double") }, []) -> gen.gcon.basic.tfloat
| TAbstract ({ a_path = (["java";"lang"], "Integer") }, []) -> gen.gcon.basic.tint
| TAbstract ({ a_path = (["java";"lang"], "Byte") }, []) -> tbyte
| TAbstract ({ a_path = (["java";"lang"], "Short") }, []) -> tshort
| TAbstract ({ a_path = (["java";"lang"], "Character") }, []) -> tchar
| TAbstract ({ a_path = (["java";"lang"], "Float") }, []) -> tfloat
| _ -> assert false
let rec t_has_type_param t = match follow t with
| TInst({ cl_kind = KTypeParameter _ }, []) -> true
| TEnum(_, params)
| TAbstract(_, params)
| TInst(_, params) -> List.exists t_has_type_param params
| TFun(f,ret) -> t_has_type_param ret || List.exists (fun (_,_,t) -> t_has_type_param t) f
| _ -> false
let is_type_param t = match follow t with
| TInst({ cl_kind = KTypeParameter _ }, _) -> true
| _ -> false
let rec t_has_type_param_shallow last t = match follow t with
| TInst({ cl_kind = KTypeParameter _ }, []) -> true
| TEnum(_, params)
| TAbstract(_, params)
| TInst(_, params) when not last -> List.exists (t_has_type_param_shallow true) params
| TFun(f,ret) when not last -> t_has_type_param_shallow true ret || List.exists (fun (_,_,t) -> t_has_type_param_shallow true t) f
| _ -> false
let rec replace_type_param t = match follow t with
| TInst({ cl_kind = KTypeParameter _ }, []) -> t_dynamic
| TEnum(e, params) -> TEnum(e, List.map replace_type_param params)
| TAbstract(a, params) -> TAbstract(a, List.map replace_type_param params)
| TInst(cl, params) -> TInst(cl, List.map replace_type_param params)
| _ -> t
let is_java_basic_type t =
match follow t with
| TInst( { cl_path = (["haxe"], "Int32") }, [] )
| TInst( { cl_path = (["haxe"], "Int64") }, [] )
| TAbstract( { a_path = ([], "Single") }, [] )
| TAbstract( { a_path = (["java"], ("Int8" | "Int16" | "Char16" | "Int64")) }, [] )
| TAbstract( { a_path = ([], "Int") }, [] )
| TAbstract( { a_path = ([], "Float") }, [] )
| TAbstract( { a_path = ([], "Bool") }, [] ) ->
true
| _ -> false
let is_bool t =
match follow t with
| TAbstract ({ a_path = ([], "Bool") },[]) ->
true
| _ -> false
let like_bool t =
match follow t with
| TAbstract ({ a_path = ([], "Bool") },[])
| TAbstract ({ a_path = (["java";"lang"],"Boolean") },[])
| TInst ({ cl_path = (["java";"lang"],"Boolean") },[]) ->
true
| _ -> false
let is_int_float gen t =
match follow (gen.greal_type t) with
| TInst( { cl_path = (["haxe"], "Int32") }, [] )
| TAbstract( { a_path = ([], "Int") }, [] )
| TAbstract( { a_path = ([], "Float") }, [] ) ->
true
| (TAbstract _ as t) when like_float t && not (like_i64 t)-> true
| _ -> false
let parse_explicit_iface =
let regex = Str.regexp "\\." in
let parse_explicit_iface str =
let split = Str.split regex str in
let rec get_iface split pack =
match split with
| clname :: fn_name :: [] -> fn_name, (List.rev pack, clname)
| pack_piece :: tl -> get_iface tl (pack_piece :: pack)
| _ -> assert false
in
get_iface split []
in parse_explicit_iface
let is_string t =
match follow t with
| TInst( { cl_path = ([], "String") }, [] ) -> true
| _ -> false
let is_cl t = match follow t with
| TInst({ cl_path = ["java";"lang"],"Class" },_)
| TAbstract({ a_path = [], ("Class"|"Enum") },_) -> true
| TAnon(a) when is_some (anon_class t) -> true
| _ -> false
(* ******************************************* *)
(* JavaSpecificESynf *)
(* ******************************************* *)
(*
Some Java-specific syntax filters that must run before ExpressionUnwrap
dependencies:
It must run before ExprUnwrap, as it may not return valid Expr/Statement expressions
It must run before ClassInstance, as it will detect expressions that need unchanged TTypeExpr
It must run after CastDetect, as it changes casts
It must run after TryCatchWrapper, to change Std.is() calls inside there
*)
module JavaSpecificESynf =
struct
let name = "java_specific_e"
let priority = solve_deps name [ DBefore ExpressionUnwrap.priority; DBefore ClassInstance.priority; DAfter CastDetect.priority; DAfter TryCatchWrapper.priority ]
let get_cl_from_t t =
match follow t with
| TInst(cl,_) -> cl
| _ -> assert false
let traverse gen runtime_cl =
let basic = gen.gcon.basic in
let float_cl = get_cl ( get_type gen (["java";"lang"], "Double")) in
let i8_md = ( get_type gen (["java";"lang"], "Byte")) in
let i16_md = ( get_type gen (["java";"lang"], "Short")) in
let i64_md = ( get_type gen (["java";"lang"], "Long")) in
let c16_md = ( get_type gen (["java";"lang"], "Character")) in
let f_md = ( get_type gen (["java";"lang"], "Float")) in
let bool_md = get_type gen (["java";"lang"], "Boolean") in
let is_var = alloc_var "__is__" t_dynamic in
let rec run e =
match e.eexpr with
(* Math changes *)
| TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "NaN" }) ) ->
mk_static_field_access_infer float_cl "NaN" e.epos []
| TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "NEGATIVE_INFINITY" }) ) ->
mk_static_field_access_infer float_cl "NEGATIVE_INFINITY" e.epos []
| TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "POSITIVE_INFINITY" }) ) ->
mk_static_field_access_infer float_cl "POSITIVE_INFINITY" e.epos []
| TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "isNaN"}) ) ->
mk_static_field_access_infer float_cl "isNaN" e.epos []
| TCall( ({ eexpr = TField( (_ as ef), FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = ("ffloor" as f) }) ) } as fe), p)
| TCall( ({ eexpr = TField( (_ as ef), FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = ("fceil" as f) }) ) } as fe), p) ->
Type.map_expr run { e with eexpr = TCall({ fe with eexpr = TField(ef, FDynamic (String.sub f 1 (String.length f - 1))) }, p) }
| TCall( { eexpr = TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "floor" }) ) }, _)
| TCall( { eexpr = TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "round" }) ) }, _)
| TCall( { eexpr = TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "ceil" }) ) }, _) ->
mk_cast basic.tint (Type.map_expr run { e with etype = basic.tfloat })
| TCall( ( { eexpr = TField( _, FStatic({ cl_path = (["java";"lang"], "Math") }, { cf_name = "isFinite" }) ) } as efield ), [v]) ->
{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "isFinite" efield.epos [], [run v] ) }
(* end of math changes *)
(* Std.is() *)
| TCall(
{ eexpr = TField( _, FStatic({ cl_path = ([], "Std") }, { cf_name = "is" })) },
[ obj; { eexpr = TTypeExpr(md) } ]
) ->
let mk_is is_basic obj md =
let obj = if is_basic then mk_cast t_dynamic obj else obj in
{ e with eexpr = TCall( { eexpr = TLocal is_var; etype = t_dynamic; epos = e.epos }, [
run obj;
{ eexpr = TTypeExpr md; etype = t_dynamic (* this is after all a syntax filter *); epos = e.epos }
] ) }
in
(match follow_module follow md with
| TAbstractDecl({ a_path = ([], "Float") }) ->
{
eexpr = TCall(
mk_static_field_access_infer runtime_cl "isDouble" e.epos [],
[ run obj ]
);
etype = basic.tbool;
epos = e.epos
}
| TAbstractDecl{ a_path = ([], "Int") } ->
{
eexpr = TCall(
mk_static_field_access_infer runtime_cl "isInt" e.epos [],
[ run obj ]
);
etype = basic.tbool;
epos = e.epos
}
| TAbstractDecl{ a_path = ([], "Bool") } ->
mk_is true obj bool_md
| TAbstractDecl{ a_path = ([], "Single") } ->
mk_is true obj f_md
| TAbstractDecl{ a_path = (["java"], "Int8") } ->
mk_is true obj i8_md
| TAbstractDecl{ a_path = (["java"], "Int16") } ->
mk_is true obj i16_md
| TAbstractDecl{ a_path = (["java"], "Char16") } ->
mk_is true obj c16_md
| TAbstractDecl{ a_path = (["java"], "Int64") } ->
mk_is true obj i64_md
| TClassDecl{ cl_path = (["haxe"], "Int64") } ->
mk_is true obj i64_md
| TAbstractDecl{ a_path = ([], "Dynamic") }
| TClassDecl{ cl_path = ([], "Dynamic") } ->
(match obj.eexpr with
| TLocal _ | TConst _ -> { e with eexpr = TConst(TBool true) }
| _ -> { e with eexpr = TBlock([run obj; { e with eexpr = TConst(TBool true) }]) }
)
| _ ->
mk_is false obj md
)
(* end Std.is() *)
| _ -> Type.map_expr run e
in
run
let configure gen (mapping_func:texpr->texpr) =
let map e = Some(mapping_func e) in
gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
end;;
(* ******************************************* *)
(* JavaSpecificSynf *)
(* ******************************************* *)
(*
Some Java-specific syntax filters that can run after ExprUnwrap
dependencies:
Runs after ExprUnwarp
*)
module JavaSpecificSynf =
struct
let name = "java_specific"
let priority = solve_deps name [ DAfter ExpressionUnwrap.priority; DAfter ObjectDeclMap.priority; DAfter ArrayDeclSynf.priority; DBefore IntDivisionSynf.priority ]
let java_hash s =
let high_surrogate c = (c lsr 10) + 0xD7C0 in
let low_surrogate c = (c land 0x3FF) lor 0xDC00 in
let h = ref Int32.zero in
let thirtyone = Int32.of_int 31 in
(try
UTF8.validate s;
UTF8.iter (fun c ->
let c = (UChar.code c) in
if c > 0xFFFF then
(h := Int32.add (Int32.mul thirtyone !h)
(Int32.of_int (high_surrogate c));
h := Int32.add (Int32.mul thirtyone !h)
(Int32.of_int (low_surrogate c)))
else
h := Int32.add (Int32.mul thirtyone !h)
(Int32.of_int c)
) s
with UTF8.Malformed_code ->
String.iter (fun c ->
h := Int32.add (Int32.mul thirtyone !h)
(Int32.of_int (Char.code c))) s
);
!h
let rec is_final_return_expr is_switch e =
let is_final_return_expr = is_final_return_expr is_switch in
match e.eexpr with
| TReturn _
| TThrow _ -> true
(* this is hack to not use 'break' on switch cases *)
| TLocal { v_name = "__fallback__" } when is_switch -> true
| TCall( { eexpr = TLocal { v_name = "__goto__" } }, _ ) -> true
| TParenthesis p | TMeta (_,p) -> is_final_return_expr p
| TBlock bl -> is_final_return_block is_switch bl
| TSwitch (_, el_e_l, edef) ->
List.for_all (fun (_,e) -> is_final_return_expr e) el_e_l && Option.map_default is_final_return_expr false edef
(* | TMatch (_, _, il_vl_e_l, edef) ->
List.for_all (fun (_,_,e) -> is_final_return_expr e)il_vl_e_l && Option.map_default is_final_return_expr false edef *)
| TIf (_,eif, Some eelse) ->
is_final_return_expr eif && is_final_return_expr eelse
| TFor (_,_,e) ->
is_final_return_expr e
| TWhile (_,e,_) ->
is_final_return_expr e
| TFunction tf ->
is_final_return_expr tf.tf_expr
| TTry (e, ve_l) ->
is_final_return_expr e && List.for_all (fun (_,e) -> is_final_return_expr e) ve_l
| _ -> false
and is_final_return_block is_switch el =
match el with
| [] -> false
| final :: [] -> is_final_return_expr is_switch final
| hd :: tl -> is_final_return_block is_switch tl
let is_null e = match e.eexpr with | TConst(TNull) -> true | _ -> false
let rec is_equatable gen t =
match follow t with
| TInst(cl,_) ->
if cl.cl_path = (["haxe";"lang"], "IEquatable") then
true
else
List.exists (fun (cl,p) -> is_equatable gen (TInst(cl,p))) cl.cl_implements
|| (match cl.cl_super with | Some(cl,p) -> is_equatable gen (TInst(cl,p)) | None -> false)
| _ -> false
(*
Changing string switch
will take an expression like
switch(str)
{
case "a":
case "b":
}
and modify it to:
{
var execute_def = true;
switch(str.hashCode())
{
case (hashcode of a):
if (str == "a")
{
execute_def = false;
..code here
} //else if (str == otherVariableWithSameHashCode) {
...
}
...
}
if (execute_def)
{
..default code
}
}
this might actually be slower in some cases than a if/else approach, but it scales well and as a bonus,
hashCode in java are cached, so we only have the performance hit once to cache it.
*)
let change_string_switch gen eswitch e1 ecases edefault =
let basic = gen.gcon.basic in
let is_final_ret = is_final_return_expr false eswitch in
let has_default = is_some edefault in
let block = ref [] in
let local = match e1.eexpr with
| TLocal _ -> e1
| _ ->
let var = mk_temp gen "svar" e1.etype in
let added = { e1 with eexpr = TVar(var, Some(e1)); etype = basic.tvoid } in
let local = mk_local var e1.epos in
block := added :: !block;
local
in
let execute_def_var = mk_temp gen "executeDef" gen.gcon.basic.tbool in
let execute_def = mk_local execute_def_var e1.epos in
let execute_def_set = { eexpr = TBinop(Ast.OpAssign, execute_def, { eexpr = TConst(TBool false); etype = basic.tbool; epos = e1.epos }); etype = basic.tbool; epos = e1.epos } in
let hash_cache = ref None in
let local_hashcode = ref { local with
eexpr = TCall({ local with
eexpr = TField(local, FDynamic "hashCode");
etype = TFun([], basic.tint);
}, []);
etype = basic.tint
} in
let get_hash_cache () =
match !hash_cache with
| Some c -> c
| None ->
let var = mk_temp gen "hash" basic.tint in
let cond = !local_hashcode in
block := { eexpr = TVar(var, Some cond); etype = basic.tvoid; epos = local.epos } :: !block;
let local = mk_local var local.epos in
local_hashcode := local;
hash_cache := Some local;
local
in
let has_case = ref false in
(* first we need to reorder all cases so all collisions are close to each other *)
let get_str e = match e.eexpr with | TConst(TString s) -> s | _ -> assert false in
let has_conflict = ref false in
let rec reorder_cases unordered ordered =
match unordered with
| [] -> ordered
| (el, e) :: tl ->
let current = Hashtbl.create 1 in
List.iter (fun e ->
let str = get_str e in
let hash = java_hash str in
Hashtbl.add current hash true
) el;
let rec extract_fields cases found_cases ret_cases =
match cases with
| [] -> found_cases, ret_cases
| (el, e) :: tl ->
if List.exists (fun e -> Hashtbl.mem current (java_hash (get_str e)) ) el then begin
has_conflict := true;
List.iter (fun e -> Hashtbl.add current (java_hash (get_str e)) true) el;
extract_fields tl ( (el, e) :: found_cases ) ret_cases
end else
extract_fields tl found_cases ( (el, e) :: ret_cases )
in
let found, remaining = extract_fields tl [] [] in
let ret = if found <> [] then
let ret = List.sort (fun (e1,_) (e2,_) -> compare (List.length e2) (List.length e1) ) ( (el, e) :: found ) in
let rec loop ret acc =
match ret with
| (el, e) :: ( (_,_) :: _ as tl ) -> loop tl ( (true, el, e) :: acc )
| (el, e) :: [] -> ( (false, el, e) :: acc )
| _ -> assert false
in
List.rev (loop ret [])
else
(false, el, e) :: []
in
reorder_cases remaining (ordered @ ret)
in
let already_in_cases = Hashtbl.create 0 in
let change_case (has_fallback, el, e) =
let conds, el = List.fold_left (fun (conds,el) e ->
has_case := true;
match e.eexpr with
| TConst(TString s) ->
let hashed = java_hash s in
let equals_test = {
eexpr = TCall({ e with eexpr = TField(local, FDynamic "equals"); etype = TFun(["obj",false,t_dynamic],basic.tbool) }, [ e ]);
etype = basic.tbool;
epos = e.epos
} in
let hashed_expr = { eexpr = TConst(TInt hashed); etype = basic.tint; epos = e.epos } in
let hashed_exprs = if !has_conflict then begin
if Hashtbl.mem already_in_cases hashed then
el
else begin
Hashtbl.add already_in_cases hashed true;
hashed_expr :: el
end
end else hashed_expr :: el in
let conds = match conds with
| None -> equals_test
| Some c ->
(*
if there is more than one case, we should test first if hash equals to the one specified.
This way we can save a heavier string compare
*)
let equals_test = mk_paren {
eexpr = TBinop(Ast.OpBoolAnd, { eexpr = TBinop(Ast.OpEq, get_hash_cache(), hashed_expr); etype = basic.tbool; epos = e.epos }, equals_test);
etype = basic.tbool;
epos = e.epos;
} in
{ eexpr = TBinop(Ast.OpBoolOr, equals_test, c); etype = basic.tbool; epos = e1.epos }
in
Some conds, hashed_exprs
| _ -> assert false
) (None,[]) el in
let e = if has_default then Type.concat execute_def_set e else e in
let e = if !has_conflict then Type.concat e { e with eexpr = TBreak; etype = basic.tvoid } else e in
let e = {
eexpr = TIf(get conds, e, None);
etype = basic.tvoid;
epos = e.epos
} in
let e = if has_fallback then { e with eexpr = TBlock([ e; mk_local (alloc_var "__fallback__" t_dynamic) e.epos]) } else e in
(el, e)
in
let switch = { eswitch with
eexpr = TSwitch(!local_hashcode, List.map change_case (reorder_cases ecases []), None);
} in
(if !has_case then begin
(if has_default then block := { e1 with eexpr = TVar(execute_def_var, Some({ e1 with eexpr = TConst(TBool true); etype = basic.tbool })); etype = basic.tvoid } :: !block);
block := switch :: !block
end);
(match edefault with
| None -> ()
| Some edef when not !has_case ->
block := edef :: !block
| Some edef ->
let eelse = if is_final_ret then Some { eexpr = TThrow { eexpr = TConst(TNull); etype = t_dynamic; epos = edef.epos }; etype = basic.tvoid; epos = edef.epos } else None in
block := { edef with eexpr = TIf(execute_def, edef, eelse); etype = basic.tvoid } :: !block
);
{ eswitch with eexpr = TBlock(List.rev !block) }
let get_cl_from_t t =
match follow t with
| TInst(cl,_) -> cl
| _ -> assert false
let traverse gen runtime_cl =
let basic = gen.gcon.basic in
(* let tchar = mt_to_t_dyn ( get_type gen (["java"], "Char16") ) in *)
(* let tbyte = mt_to_t_dyn ( get_type gen (["java"], "Int8") ) in *)
(* let tshort = mt_to_t_dyn ( get_type gen (["java"], "Int16") ) in *)
(* let tsingle = mt_to_t_dyn ( get_type gen ([], "Single") ) in *)
let string_ext = get_cl ( get_type gen (["haxe";"lang"], "StringExt")) in
let is_string t = match follow t with | TInst({ cl_path = ([], "String") }, []) -> true | _ -> false in
let rec run e =
match e.eexpr with
(* for new NativeArray<T> issues *)
| TNew(({ cl_path = (["java"], "NativeArray") } as cl), [t], el) when is_type_param t ->
mk_cast (TInst(cl,[t])) (mk_cast t_dynamic ({ e with eexpr = TNew(cl, [t_empty], List.map run el) }))
(* Std.int() *)
| TCall(
{ eexpr = TField( _, FStatic({ cl_path = ([], "Std") }, { cf_name = "int" })) },
[obj]
) ->
run (mk_cast basic.tint obj)
(* end Std.int() *)
| TField( ef, FInstance({ cl_path = ([], "String") }, _, { cf_name = "length" }) ) ->
{ e with eexpr = TCall(Type.map_expr run e, []) }
| TField( ef, field ) when field_name field = "length" && is_string ef.etype ->
{ e with eexpr = TCall(Type.map_expr run e, []) }
| TCall( ( { eexpr = TField(ef, field) } as efield ), args ) when is_string ef.etype && String.get (field_name field) 0 = '_' ->
let field = field_name field in
{ e with eexpr = TCall({ efield with eexpr = TField(run ef, FDynamic (String.sub field 1 ( (String.length field) - 1)) )}, List.map run args) }
| TCall( ( { eexpr = TField(ef, FInstance({ cl_path = [], "String" }, _, field )) } as efield ), args ) ->
let field = field.cf_name in
(match field with
| "charAt" | "charCodeAt" | "split" | "indexOf"
| "lastIndexOf" | "substring" | "substr" ->
{ e with eexpr = TCall(mk_static_field_access_infer string_ext field e.epos [], [run ef] @ (List.map run args)) }
| _ ->
{ e with eexpr = TCall(run efield, List.map run args) }
)
(* | TCall( { eexpr = TField(ef, FInstance({ cl_path = [], "String" }, { cf_name = ("toString") })) }, [] ) ->
run ef *)
(* | TCast(expr, m) when is_boxed_type e.etype -> *)
(* (* let unboxed_type gen t tbyte tshort tchar tfloat = match follow t with *) *)
(* run { e with etype = unboxed_type gen e.etype tbyte tshort tchar tsingle } *)
| TCast(expr, _) when is_bool e.etype ->
{
eexpr = TCall(
mk_static_field_access_infer runtime_cl "toBool" expr.epos [],
[ run expr ]
);
etype = basic.tbool;
epos = e.epos
}
| TCast(expr, _) when is_int_float gen e.etype && not (is_int_float gen expr.etype) ->
let needs_cast = match gen.gfollow#run_f e.etype with
| TInst _ -> false
| _ -> true
in
let fun_name = if like_int e.etype then "toInt" else "toDouble" in
let ret = {
eexpr = TCall(
mk_static_field_access_infer runtime_cl fun_name expr.epos [],
[ run expr ]
);
etype = if fun_name = "toDouble" then basic.tfloat else basic.tint;
epos = expr.epos
} in
if needs_cast then mk_cast e.etype ret else ret
(*| TCast(expr, c) when is_int_float gen e.etype ->
(* cases when float x = (float) (java.lang.Double val); *)
(* FIXME: this fix is broken since it will fail on cases where float x = (float) (java.lang.Float val) or similar. FIX THIS *)
let need_second_cast = match gen.gfollow#run_f e.etype with
| TInst _ -> false
| _ -> true
in
if need_second_cast then { e with eexpr = TCast(mk_cast (follow e.etype) (run expr), c) } else Type.map_expr run e*)
| TBinop( (Ast.OpAssignOp OpAdd as op), e1, e2)
| TBinop( (Ast.OpAdd as op), e1, e2) when is_string e.etype || is_string e1.etype || is_string e2.etype ->
let is_assign = match op with Ast.OpAssignOp _ -> true | _ -> false in
let mk_to_string e = { e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" e.epos [], [run e] ); etype = gen.gcon.basic.tstring } in
let check_cast e = match gen.greal_type e.etype with
| TDynamic _
| TAbstract({ a_path = ([], "Float") }, [])
| TAbstract({ a_path = ([], "Single") }, []) ->
mk_to_string e
| _ -> run e
in
{ e with eexpr = TBinop(op, (if is_assign then run e1 else check_cast e1), check_cast e2) }
| TCast(expr, _) when is_string e.etype ->
{ e with eexpr = TCall( mk_static_field_access_infer runtime_cl "toString" expr.epos [], [run expr] ) }
| TSwitch(cond, ecases, edefault) when is_string cond.etype ->
(*let change_string_switch gen eswitch e1 ecases edefault =*)
change_string_switch gen e (run cond) (List.map (fun (el,e) -> (el, run e)) ecases) (Option.map run edefault)
| TBinop( (Ast.OpNotEq as op), e1, e2)
| TBinop( (Ast.OpEq as op), e1, e2) when not (is_null e2 || is_null e1) && (is_string e1.etype || is_string e2.etype || is_equatable gen e1.etype || is_equatable gen e2.etype) ->
let static = mk_static_field_access_infer (runtime_cl) "valEq" e1.epos [] in
let eret = { eexpr = TCall(static, [run e1; run e2]); etype = gen.gcon.basic.tbool; epos=e.epos } in
if op = Ast.OpNotEq then { eret with eexpr = TUnop(Ast.Not, Ast.Prefix, eret) } else eret
| TBinop( (Ast.OpNotEq | Ast.OpEq as op), e1, e2) when is_cl e1.etype && is_cl e2.etype ->
{ e with eexpr = TBinop(op, mk_cast t_empty (run e1), mk_cast t_empty (run e2)) }
| _ -> Type.map_expr run e
in
run
let configure gen (mapping_func:texpr->texpr) =
(if java_hash "Testing string hashCode implementation from haXe" <> (Int32.of_int 545883604) then assert false);
let map e = Some(mapping_func e) in
gen.gsyntax_filters#add ~name:name ~priority:(PCustom priority) map
end;;
(* ******************************************* *)
(* handle @:throws *)
(* ******************************************* *)
let rec is_checked_exc cl =
match cl.cl_path with
| ["java";"lang"],"RuntimeException" ->
false
| ["java";"lang"],"Throwable" ->
true
| _ -> match cl.cl_super with
| None -> false
| Some(c,_) -> is_checked_exc c
let rec cls_any_super cl supers =
PMap.mem cl.cl_path supers || match cl.cl_super with
| None -> false
| Some(c,_) -> cls_any_super c supers
let rec handle_throws gen cf =
List.iter (handle_throws gen) cf.cf_overloads;
match cf.cf_expr with
| Some ({ eexpr = TFunction(tf) } as e) ->
let rec collect_throws acc = function
| (Meta.Throws, [Ast.EConst (Ast.String path), _],_) :: meta -> (try
collect_throws (get_cl ( get_type gen (parse_path path)) :: acc) meta
with | Not_found | TypeNotFound _ ->
collect_throws acc meta)
| [] ->
acc
| _ :: meta ->
collect_throws acc meta
in
let cf_throws = collect_throws [] cf.cf_meta in
let throws = ref (List.fold_left (fun map cl ->
PMap.add cl.cl_path cl map
) PMap.empty cf_throws) in
let rec iter e = match e.eexpr with
| TTry(etry,ecatches) ->
let old = !throws in
let needs_check_block = ref true in
List.iter (fun (v,e) ->
Type.iter iter e;
match follow (run_follow gen v.v_type) with
| TInst({ cl_path = ["java";"lang"],"Throwable" },_)
| TDynamic _ ->
needs_check_block := false
| TInst(c,_) when is_checked_exc c ->
throws := PMap.add c.cl_path c !throws
| _ ->()
) ecatches;
if !needs_check_block then Type.iter iter etry;
throws := old
| TField(e, (FInstance(_,_,f) | FStatic(_,f) | FClosure(_,f))) ->
let tdefs = collect_throws [] f.cf_meta in
if tdefs <> [] && not (List.for_all (fun c -> cls_any_super c !throws) tdefs) then
raise Exit;
Type.iter iter e
| TThrow e -> (match follow (run_follow gen e.etype) with
| TInst(c,_) when is_checked_exc c && not (cls_any_super c !throws) ->
raise Exit
| _ -> iter e)
| _ -> Type.iter iter e
in
(try
Type.iter iter e
with | Exit -> (* needs typed exception to be caught *)
let throwable = get_cl (get_type gen (["java";"lang"],"Throwable")) in
let catch_var = alloc_var "typedException" (TInst(throwable,[])) in
let rethrow = mk_local catch_var e.epos in
let hx_exception = get_cl (get_type gen (["haxe";"lang"], "HaxeException")) in
let wrap_static = mk_static_field_access (hx_exception) "wrap" (TFun([("obj",false,t_dynamic)], t_dynamic)) rethrow.epos in
let wrapped = { rethrow with eexpr = TThrow { rethrow with eexpr = TCall(wrap_static, [rethrow]) }; } in
let map_throws cl =
let var = alloc_var "typedException" (TInst(cl,List.map (fun _ -> t_dynamic) cl.cl_params)) in
var, { tf.tf_expr with eexpr = TThrow (mk_local var e.epos) }
in
cf.cf_expr <- Some { e with
eexpr = TFunction({ tf with
tf_expr = mk_block { tf.tf_expr with eexpr = TTry(tf.tf_expr, List.map (map_throws) cf_throws @ [catch_var, wrapped]) }
})
})
| _ -> ()
let connecting_string = "?" (* ? see list here http://www.fileformat.info/info/unicode/category/index.htm and here for C# http://msdn.microsoft.com/en-us/library/aa664670.aspx *)
let default_package = "java"
let strict_mode = ref false (* strict mode is so we can check for unexpected information *)
(* reserved java words *)
let reserved = let res = Hashtbl.create 120 in
List.iter (fun lst -> Hashtbl.add res lst ("_" ^ lst)) ["abstract"; "assert"; "boolean"; "break"; "byte"; "case"; "catch"; "char"; "class";
"const"; "continue"; "default"; "do"; "double"; "else"; "enum"; "extends"; "final";
"false"; "finally"; "float"; "for"; "goto"; "if"; "implements"; "import"; "instanceof"; "int";
"interface"; "long"; "native"; "new"; "null"; "package"; "private"; "protected"; "public"; "return"; "short";
"static"; "strictfp"; "super"; "switch"; "synchronized"; "this"; "throw"; "throws"; "transient"; "true"; "try";
"void"; "volatile"; "while"; ];
res
let dynamic_anon = TAnon( { a_fields = PMap.empty; a_status = ref Closed } )
let rec get_class_modifiers meta cl_type cl_access cl_modifiers =
match meta with
| [] -> cl_type,cl_access,cl_modifiers
(*| (Meta.Struct,[],_) :: meta -> get_class_modifiers meta "struct" cl_access cl_modifiers*)
| (Meta.Protected,[],_) :: meta -> get_class_modifiers meta cl_type "protected" cl_modifiers
| (Meta.Internal,[],_) :: meta -> get_class_modifiers meta cl_type "" cl_modifiers
(* no abstract for now | (":abstract",[],_) :: meta -> get_class_modifiers meta cl_type cl_access ("abstract" :: cl_modifiers)
| (Meta.Static,[],_) :: meta -> get_class_modifiers meta cl_type cl_access ("static" :: cl_modifiers) TODO: support those types *)
| (Meta.Final,[],_) :: meta -> get_class_modifiers meta cl_type cl_access ("final" :: cl_modifiers)
| _ :: meta -> get_class_modifiers meta cl_type cl_access cl_modifiers
let rec get_fun_modifiers meta access modifiers =
match meta with
| [] -> access,modifiers
| (Meta.Protected,[],_) :: meta -> get_fun_modifiers meta "protected" modifiers
| (Meta.Internal,[],_) :: meta -> get_fun_modifiers meta "" modifiers
| (Meta.ReadOnly,[],_) :: meta -> get_fun_modifiers meta access ("final" :: modifiers)
(*| (Meta.Unsafe,[],_) :: meta -> get_fun_modifiers meta access ("unsafe" :: modifiers)*)
| (Meta.Volatile,[],_) :: meta -> get_fun_modifiers meta access ("volatile" :: modifiers)
| (Meta.Transient,[],_) :: meta -> get_fun_modifiers meta access ("transient" :: modifiers)
| (Meta.Native,[],_) :: meta -> get_fun_modifiers meta access ("native" :: modifiers)
| _ :: meta -> get_fun_modifiers meta access modifiers
(* this was the way I found to pass the generator context to be accessible across all functions here *)
(* so 'configure' is almost 'top-level' and will have all functions needed to make this work *)
let configure gen =
let native_arr_cl = get_cl ( get_type gen (["java"], "NativeArray") ) in
gen.gclasses.nativearray <- (fun t -> TInst(native_arr_cl,[t]));
gen.gclasses.nativearray_type <- (function TInst(_,[t]) -> t | _ -> assert false);
gen.gclasses.nativearray_len <- (fun e p -> mk_field_access gen e "length" p);
let basic = gen.gcon.basic in
let fn_cl = get_cl (get_type gen (["haxe";"lang"],"Function")) in
let runtime_cl = get_cl (get_type gen (["haxe";"lang"],"Runtime")) in
let nulltdef = get_tdef (get_type gen ([],"Null")) in
(*let string_ref = get_cl ( get_type gen (["haxe";"lang"], "StringRefl")) in*)
let ti64 = match ( get_type gen (["java"], "Int64") ) with | TAbstractDecl a -> TAbstract(a,[]) | _ -> assert false in
let has_tdynamic params =
List.exists (fun e -> match run_follow gen e with | TDynamic _ -> true | _ -> false) params
in
(*
The type parameters always need to be changed to their boxed counterparts
*)
let change_param_type md params =
match md with
| TClassDecl( { cl_path = (["java"], "NativeArray") } ) -> params
| TAbstractDecl { a_path=[],("Class" | "Enum") } | TClassDecl { cl_path = (["java";"lang"],("Class"|"Enum")) } ->
List.map (fun _ -> t_dynamic) params
| _ ->
match params with
| [] -> []
| _ ->
if has_tdynamic params then List.map (fun _ -> t_dynamic) params else
List.map (fun t ->
let f_t = gen.gfollow#run_f t in
match f_t with
| TAbstract ({ a_path = ([], "Bool") },[])
| TAbstract ({ a_path = ([],"Float") },[])
| TInst ({ cl_path = ["haxe"],"Int32" },[])
| TInst ({ cl_path = ["haxe"],"Int64" },[])
| TAbstract ({ a_path = ([],"Int") },[])
| TType ({ t_path = ["java"], "Int64" },[])
| TAbstract ({ a_path = ["java"], "Int64" },[])
| TType ({ t_path = ["java"],"Int8" },[])
| TAbstract ({ a_path = ["java"],"Int8" },[])
| TType ({ t_path = ["java"],"Int16" },[])
| TAbstract ({ a_path = ["java"],"Int16" },[])
| TType ({ t_path = ["java"],"Char16" },[])
| TAbstract ({ a_path = ["java"],"Char16" },[])
| TType ({ t_path = [],"Single" },[])
| TAbstract ({ a_path = [],"Single" },[]) ->
TType(nulltdef, [f_t])
(*| TType ({ t_path = [], "Null"*)
| TInst (cl, ((_ :: _) as p)) when cl.cl_path <> (["java"],"NativeArray") ->
(* TInst(cl, List.map (fun _ -> t_dynamic) p) *)
TInst(cl,p)
| TEnum (e, ((_ :: _) as p)) ->
TEnum(e, List.map (fun _ -> t_dynamic) p)
| _ -> t
) params
in
let change_clname name =
String.map (function | '$' -> '.' | c -> c) name
in
let change_id name = try Hashtbl.find reserved name with | Not_found -> name in
let rec change_ns ns = match ns with
| [] -> ["haxe"; "root"]
| _ -> List.map change_id ns
in
let change_field = change_id in
let write_id w name = write w (change_id name) in
let write_field w name = write w (change_field name) in
gen.gfollow#add ~name:"follow_basic" (fun t -> match t with
| TAbstract ({ a_path = ([], "Bool") },[])
| TAbstract ({ a_path = ([], "Void") },[])
| TAbstract ({ a_path = ([],"Float") },[])
| TAbstract ({ a_path = ([],"Int") },[])
| TInst( { cl_path = (["haxe"], "Int32") }, [] )
| TInst( { cl_path = (["haxe"], "Int64") }, [] )
| TType ({ t_path = ["java"], "Int64" },[])
| TAbstract ({ a_path = ["java"], "Int64" },[])
| TType ({ t_path = ["java"],"Int8" },[])
| TAbstract ({ a_path = ["java"],"Int8" },[])
| TType ({ t_path = ["java"],"Int16" },[])
| TAbstract ({ a_path = ["java"],"Int16" },[])
| TType ({ t_path = ["java"],"Char16" },[])
| TAbstract ({ a_path = ["java"],"Char16" },[])
| TType ({ t_path = [],"Single" },[])
| TAbstract ({ a_path = [],"Single" },[]) ->
Some t
| TType (({ t_path = [],"Null" } as tdef),[t2]) ->
Some (TType(tdef,[gen.gfollow#run_f t2]))
| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
Some (gen.gfollow#run_f ( Abstract.get_underlying_type a pl) )
| TAbstract( { a_path = ([], "EnumValue") }, _ )
| TInst( { cl_path = ([], "EnumValue") }, _ ) -> Some t_dynamic
| _ -> None);
let change_path path = (change_ns (fst path), change_clname (snd path)) in
let path_s path meta = try
match Meta.get Meta.JavaCanonical meta with
| (Meta.JavaCanonical, [EConst(String pack), _; EConst(String name), _], _) ->
if pack = "" then
name
else
pack ^ "." ^ name
| _ -> raise Not_found
with Not_found -> match path with
| (ns,clname) -> path_s (change_ns ns, change_clname clname)
in
let cl_cl = get_cl (get_type gen (["java";"lang"],"Class")) in
let rec real_type t =
let t = gen.gfollow#run_f t in
match t with
| TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) ->
real_type (Abstract.get_underlying_type a pl)
| TInst( { cl_path = (["haxe"], "Int32") }, [] ) -> gen.gcon.basic.tint
| TInst( { cl_path = (["haxe"], "Int64") }, [] ) -> ti64
| TAbstract( { a_path = ([], "Class") }, p )
| TAbstract( { a_path = ([], "Enum") }, p )
| TInst( { cl_path = ([], "Class") }, p )
| TInst( { cl_path = ([], "Enum") }, p ) -> TInst(cl_cl,[t_dynamic])
| TEnum(e,params) -> TEnum(e, List.map (fun _ -> t_dynamic) params)
| TInst(c,params) when Meta.has Meta.Enum c.cl_meta ->
TInst(c, List.map (fun _ -> t_dynamic) params)
| TInst({ cl_kind = KExpr _ }, _) -> t_dynamic
| TInst _ -> t
| TType({ t_path = ([], "Null") }, [t]) when is_java_basic_type (gen.gfollow#run_f t) -> t_dynamic
| TType({ t_path = ([], "Null") }, [t]) ->
(match follow t with
| TInst( { cl_kind = KTypeParameter _ }, []) ->
t_dynamic
(* real_type t *)
| _ -> real_type t
)
| TType _ | TAbstract _ -> t
| TAnon (anon) -> (match !(anon.a_status) with
| Statics _ | EnumStatics _ | AbstractStatics _ -> t
| _ -> t_dynamic)
| TFun _ -> TInst(fn_cl,[])
| _ -> t_dynamic
in
let scope = ref PMap.empty in
let imports = ref [] in
let clear_scope () =
scope := PMap.empty;
imports := [];
in
let add_scope name =
scope := PMap.add name () !scope
in
let add_import pos path meta =
let name = snd path in
let rec loop = function
| (pack, n) :: _ when name = n ->
if path <> (pack,n) then
gen.gcon.error ("This expression cannot be generated because " ^ path_s path meta ^ " is shadowed by the current scope and ") pos
| _ :: tl ->
loop tl
| [] ->
(* add import *)
imports := path :: !imports
in
loop !imports
in
let path_s_import pos path meta = match path with
| [], name when PMap.mem name !scope ->
gen.gcon.error ("This expression cannot be generated because " ^ name ^ " is shadowed by the current scope") pos;
name
| pack1 :: _, name when PMap.mem pack1 !scope -> (* exists in scope *)
add_import pos path meta;
(* check if name exists in scope *)
if PMap.mem name !scope then
gen.gcon.error ("This expression cannot be generated because " ^ pack1 ^ " and " ^ name ^ " are both shadowed by the current scope") pos;