/
main.ml
4601 lines (4265 loc) · 140 KB
/
main.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
(*
* Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
* Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller,
* Jesper Andersen
* This file is part of Coccinelle.
*
* Coccinelle 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, according to version 2 of the License.
*
* Coccinelle 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 Coccinelle. If not, see <http://www.gnu.org/licenses/>.
*
* The authors reserve the right to distribute this or future versions of
* Coccinelle under other licenses.
*)
open Gtree
open Common
let do_dmine = ref true
let malign = ref true
let abs = ref false
let spec = ref false
let mfile = ref "specfile"
let max_level = ref 0
let depth = ref 0
let strict = ref false
let mvars = ref false
let fixed = ref false
let exceptions = ref 0
let threshold = ref 0
let print_raw = ref false
let print_uniq = ref false
let print_adding = ref false
let noncompact = ref false
let prune = ref false
let strip_eq = ref false
let patterns = ref false
let spatch = ref false
let fun_common = ref false
let default_abstractness = ref 2.0
let only_changes = ref false
let nesting_depth = ref 1
let filter_patterns = ref false
let filter_spatches = ref false
let print_support = ref false
let cache = ref false
let max_embedded_depth = ref 2 (* this allows us to find similarities in fun signatures; higher values is very expensive *)
let tmp_flag = ref false
let show_support = ref false
let flow_support = ref 0
let print_chunks = ref false
(* the 'focus_function' allows us to specify a fun name which any
found pattern should match *)
let focus_function = ref ""
let speclist =
Arg.align
[
"-uniq_local" ,Arg.Clear Jconfig.uniq_local, " make names of local variables unique (defaults to true)";
"-focus_function",Arg.Set_string focus_function, "[STRING] name of fun to focus on";
"-specfile", Arg.Set_string mfile, "[filename] name of specification file (defaults to \"specfile\") ";
"-threshold", Arg.Set_int threshold, "[num] the minimum number of occurrences required";
"-noif0_passing", Arg.Clear Flag_parsing_c.if0_passing,
" also parse if0 blocks";
"-print_abs", Arg.Set Jconfig.print_abs, " print abstract updates for each term pair";
"-relax_safe", Arg.Set Diff.relax, " consider non-application safe [experimental]";
"-print_inline", Arg.Set Diff.inline_type_print, " print types of identifiers inline";
"-print_raw", Arg.Set print_raw, " print the raw list of generated simple updates";
"-print_uniq", Arg.Set print_uniq, " print the unique solutions before removing smaller ones";
"-print_add", Arg.Set print_adding, " print statement when adding a new solution";
"-prune", Arg.Set prune, " try to prune search space by various means";
"-strip_eq", Arg.Set strip_eq, " use eq_classes for initial atomic patches";
"-patterns", Arg.Set patterns, " look for common patterns in LHS files";
"-spatch", Arg.Set spatch, " find semantic patches (not generic)";
"-only_changes", Arg.Set only_changes, " only look for patterns in changed functions";
"-verbose", Arg.Set Jconfig.verbose, " print more intermediate results";
"-filter_patterns", Arg.Set filter_patterns, " only produce largest patterns";
"-no_malign", Arg.Clear malign, " *DON'T* use the edit-dist subpatch relation definition";
"-filter_spatches", Arg.Set filter_spatches, " filter non-largest spatches";
"-macro_file", Arg.Set_string Config.std_h, "[filename] default macros";
"-fun_common", Arg.Set fun_common, " infer one abstraction of all functions given";
"-print_support", Arg.Set print_support, " whether to also print the concrete matched functions for fun_common";
"-cache", Arg.Set cache, " only print the term pairs to stdout";
"-read_generic", Arg.Set Jconfig.read_generic, " input is given in gtree-format";
"-max_embedding", Arg.Set_int max_embedded_depth, " how deep to look inside terms when discovering nested patterns";
"-tmp", Arg.Set tmp_flag, " find embedded PATCHES also";
"-show_support", Arg.Set show_support, " show the support of each sem. patch inferred";
"-flow_support", Arg.Set_int flow_support," threshold required of flow-patterns";
"-useless_abs", Arg.Set Jconfig.useless_abs," also include very abstract term-patterns";
"-print_chunks", Arg.Set print_chunks, " print the chunks used to grow semantic patches";
"-to_print", Arg.Set Jconfig.to_print," set internal printing flag";
"-include_larger",Arg.Set Jconfig.include_larger," include more node patterns initially"
]
exception Impossible of int
let v_print s = if !Jconfig.verbose then (print_string s; flush stdout)
let v_print_string s = v_print s
let v_print_endline s = if !Jconfig.verbose then print_endline s
let v_print_newline () = v_print_endline ""
let ddd = Diff.ddd
let cmddd = mkC("CM", [Diff.ddd])
let (+>) o f = f o
(* tail recursive version of flatten; does *not* preserve order of elements in
* the lists
*)
let tail_flatten lss =
lss +> List.fold_left List.rev_append []
let (+++) x xs = if List.mem x xs then xs else x :: xs
let for_some n f ls =
let rec loop n ls =
(* n = 0 || *)
match ls with
| x :: xs when f x -> loop (n - 1) xs
| _ :: xs -> loop n xs
| [] -> n <= 0 in
loop n ls
let file_pairs = ref []
let changeset_from_pair fixf gt1 gt2 =
Diff.unabstracted_sol gt1 gt2
(*if !abs *)
(*then Diff.make_sol fixf gt1 gt2*)
(*else Diff.unabstracted_sol gt1 gt2*)
(*let changesets = ref []*)
(* this filter takes a list of patches and cleans out the updates in each
* patch that is unsafe/unsound/incorrect with respect to every file
*)
let soundness_filter dpairs patches =
let safe_for_all_up up =
if List.for_all (fun (gt1, gt2) -> (
(*print_endline "considering:";*)
(*print_endline (Diff.string_of_gtree' gt1);*)
(*print_endline (Diff.string_of_gtree' gt2);*)
Diff.safe_update gt1 gt2 up)) dpairs
then (
(*print_endline ("OK:" ^ Diff.string_of_diff up); *)
true)
else (
(*print_endline ("NO:" ^ Diff.string_of_diff up); *)
false)
in
let filter_patch patch =
List.filter safe_for_all_up patch
in
List.map filter_patch patches
let print_patch_lists pl =
List.iter
(fun ups ->
print_endline "patches ****";
List.iter
(fun up ->
print_endline (Diff.string_of_diff up);
print_endline "||"
)
ups
)
pl
let list_at_least n f ls =
(List.fold_left (fun acc_n e ->
if f e
then acc_n + 1
else acc_n
) 0 ls) >= n
let do_datamining abs_patches =
(*let threshold = List.length abs_patches in*)
print_endline ("[Main] Finding unit patches with minimum support at least: " ^
(*string_of_int (!threshold - !exceptions));*)
string_of_int !threshold);
Diff.filter_some abs_patches
let bp_of_list ls =
let rec loop ls = match ls with
| [] -> None
| l :: ls -> match loop ls with
| None -> Some l
| Some bp -> Some (Difftype.SEQ (l, bp))
in
match loop ls with
| None -> raise (Diff.Fail "None?")
| Some bp -> bp
let list_of_bp bp =
let rec loop bp = match bp with
| Difftype.SEQ (Difftype.UP (a, b), bps) -> (Difftype.UP (a,b)) :: loop bps
| Difftype.UP _ -> [bp]
| _ -> raise (Diff.Fail "list_of_bp format")
in
loop bp
let generate_sols_old chgset_orig simple_patches =
let lcnt = ref 1 in
print_string "[Main] generating solutions for ";
print_string (string_of_int (List.length simple_patches));
print_endline " simple patches";
let (^^) a b = List.map (fun bs -> a :: bs) b in
let make_one a = [[a]] in
let rec loop chgset bp =
match Diff.get_applicable chgset bp simple_patches with
| (_, []) -> make_one bp
| (chgset', bps) ->
bp ^^
List.flatten (List.map (fun bp' -> loop chgset' bp') bps)
in
List.flatten (List.map (fun bp ->
print_string "[Main] generating for ";
print_string (string_of_int !lcnt);
print_endline " simple patch";
lcnt := !lcnt + 1;
loop chgset_orig bp) simple_patches)
(* fun to detect whether two solutions (a solution is a list of
bp's) are really equal, but with different orderings of the bp's
*)
let redundant_sol sol1 sol2 =
List.for_all (fun bp1 -> List.mem bp1 sol2) sol1 &&
List.for_all (fun bp2 -> List.mem bp2 sol1) sol2
let rec filter_redundant solutions =
match solutions with
| [] -> []
| s :: sols -> s :: List.filter (fun s' ->
not(redundant_sol (list_of_bp s) (list_of_bp s')))
(filter_redundant sols)
let implies b1 b2 = not(b1) || b2
let filter_more_abstract abs_terms =
let keep_sol p =
List.for_all (fun p' ->
implies (Diff.can_match p p' || Diff.can_match p' p) (gsize p <= gsize p')
) abs_terms in
List.filter keep_sol abs_terms
let filter_smaller chgset solutions =
let keep_sol bp =
List.for_all
(fun bp' -> bp = bp' ||
if Diff.subpatch_changeset chgset bp bp'
&& Diff.subpatch_changeset chgset bp' bp
then
!noncompact || Difftype.csize bp' <= Difftype.csize bp
else true
)
solutions
in
(*print_string "[Main] filter_small considering ";*)
(*print_string (string_of_int (List.length solutions));*)
(*print_endline " solutions";*)
(*List.map list_of_bp *)
(List.filter keep_sol solutions)
let generate_sols chgset_orig simple_patches =
(*Diff.no_occurs := List.length chgset_orig - !exceptions;*)
print_endline ("[Main] min sup = " ^ string_of_int !Diff.no_occurs);
let unwrap bp = match bp with
| None -> raise (Diff.Fail "unable to unwrap")
| Some bp -> bp in
let extend_bp bp_old bp_new =
let rec loop bp_old bp_new =
match bp_old with
| Difftype.UP _ -> Difftype.SEQ(bp_old,bp_new)
| Difftype.SEQ (a, b) -> Difftype.SEQ (a, loop b bp_new)
| _ -> raise (Diff.Fail "extend_bp format")
in
match bp_old with
| None -> Some bp_new
| Some bp_old -> Some (loop bp_old bp_new) in
let app_pred cur_bp bp =
(
let nbp = unwrap (extend_bp (Some cur_bp) bp) in
if !Diff.relax
then (
let chgset' = Diff.apply_changeset nbp chgset_orig in
let chgset''= Diff.apply_changeset cur_bp chgset_orig in
not(chgset' = chgset'')
) && Diff.safe_part_changeset nbp chgset_orig
else
Diff.safe_part_changeset nbp chgset_orig
)
in
(* let restrict_bps cur_bp bps =
List.filter (fun bp ->
not(Diff.subpatch_changeset chgset_orig bp cur_bp)
) bps in
*)
let next_bps bps cur_bp = match cur_bp with
| None -> List.filter (fun bp ->
Diff.safe_part_changeset bp chgset_orig
) bps (*simple_patches*)
| Some cur_bp -> (
(*print_string "[Main] considering next w.r.t.\n\t";*)
(*print_endline (Diff.string_of_diff cur_bp);*)
let res = List.filter (fun bp ->
try app_pred cur_bp bp with Diff.Nomatch -> false) bps
in
res
)
in
let add_sol cur_bp sol =
match cur_bp with
| None -> print_endline "[Main] no solutions?"; []
| Some cur_bp -> (
if !print_adding
then (
print_endline ("[Main] trying solution... (" ^
string_of_int (List.length sol) ^")");
print_endline (Diff.string_of_diff cur_bp)
);
filter_smaller chgset_orig (cur_bp :: sol)
) in
(* let isComplete bp = Diff.complete_changeset
chgset_orig (list_of_bp bp) in
*)
let rec gen sol bps_pool cur_bp =
(*if try isComplete (unwrap cur_bp) with _ -> false*)
(*then add_sol cur_bp sol*)
(*else*)
(*let bps' = filter_smaller chgset_orig (next_bps bps cur_bp) in*)
let bps' = next_bps bps_pool cur_bp in
if bps' = []
then add_sol cur_bp sol
else (
(*print_endline ("[Main] bps'.length " ^*)
(*string_of_int (List.length bps'));*)
List.fold_left (fun sol bp ->
let nbp = extend_bp cur_bp bp in
(* let nbps = restrict_bps (unwrap nbp) bps_pool in *)
(* gen sol nbps nbp *)
(* remove bp from the list of next bps to select since we know that
* the same bp can not be applied twice
*)
if !prune
then
if List.exists (fun bp' ->
Diff.subpatch_changeset chgset_orig (unwrap nbp) bp'
) sol
then sol
else
let bps_new_pool = List.filter (fun bp' -> not(bp = bp')) bps_pool in
gen sol bps_new_pool nbp
else
let bps_new_pool = List.filter (fun bp' -> not(bp = bp')) bps_pool in
gen sol bps_new_pool nbp
) sol bps'
)
in
if simple_patches = []
then (
print_endline "[Main] no input to work with";
[])
else
let res = gen [] simple_patches None in
let res_final =
if !prune
then
res +> List.filter
(fun bp -> res +> List.for_all
(fun bp' ->
(* bp' <= bp || not(bp <= bp') *)
(* Diff.subpatch_changeset chgset_orig bp' bp
|| *)
if bp = bp' || not(Diff.subpatch_changeset chgset_orig bp bp')
then true
else begin
print_endline "[Main] final pruning : ";
print_endline (Diff.string_of_diff bp);
print_endline "[Main] because we found a larger patch:";
print_endline (Diff.string_of_diff bp');
false
end
)
)
else res
in
print_endline ("[Main] found " ^ string_of_int (List.length res_final) ^ " solutions");
List.map list_of_bp res_final
(* a solution is a list of TU patches, not a SEQ value *)
let print_sol sol =
print_endline "{{{";
List.iter (fun bp ->
print_endline (Diff.string_of_diff bp);
) sol;
print_endline "}}}"
let print_sols sols =
let cnt = ref 1 in
List.iter (fun sol ->
print_string "[Main] solution #";
print_endline (string_of_int !cnt);
print_sol sol;
cnt := !cnt + 1
) sols
(* if we are relaxed get all the simple patches that are relaxed_safe for
* the changeset and otherwise we can simply get all the ones that occur
* everywhere
*)
let get_all_safe changeset abs_patches =
print_endline "[Main] filter_all";
if not(!Diff.relax)
then Diff.filter_all abs_patches
else
List.fold_left
(fun acc_bps bp_list ->
Diff.non_dub_app
(List.filter
(fun bp -> Diff.safe_part_changeset bp changeset)
bp_list)
acc_bps
)
[]
abs_patches
(* we are given a big list of TUs and now we wish to produce SEQ-patches
* (basically lists of TUs) so that we have one which is largest. We note that
* each TU is derived such that it should actually be applied in parallel with
* all others. Thus, there could be cases where two patches overlap without
* being in a subpatch relationship. The question is now: is it always the case
* that one could be applied before the other?
*)
(* this fun takes a list a atomic patches and partitions them into those
* that are equivalent and those for which that information is unknown; finally
* it returns the "unknown" and one from each of the equivalence classes
*)
let strip term_pairs abs_patches =
let in_eq atomp eq_class =
match eq_class with
| [] -> raise (Diff.Fail "in_eq empty")
| atomp' :: _ ->
Diff.subpatch_changeset term_pairs atomp atomp' &&
Diff.subpatch_changeset term_pairs atomp' atomp
in
let rec add_atom part atomp =
match part with
| [] -> [[atomp]]
| eq_class :: part ->
if in_eq atomp eq_class
then
if !noncompact
then (atomp :: eq_class) :: part
else filter_smaller term_pairs (atomp :: eq_class) :: part
else eq_class :: add_atom part atomp
in
let pot_res = List.fold_left (fun part atomp ->
add_atom part atomp
) [] abs_patches in
(*List.map (fun eq_class -> List.hd (filter_smaller term_pairs eq_class )) pot_res*)
List.rev_map (fun eq_class -> List.hd eq_class) pot_res
let spec_main () =
Diff.abs_depth := !depth;
Diff.abs_subterms := !max_level;
file_pairs := Reader.read_spec !mfile; (* gets names to process in file_pairs *)
(* now make diff-pairs is a list of abs-term pairs *)
let term_pairs =
List.fold_left (fun acc_pairs (lfile, rfile) ->
try
Reader.read_filepair_defs lfile rfile @ acc_pairs
with (Failure _) -> acc_pairs)
[] !file_pairs
+> List.filter
(fun (l,r) -> not(!only_changes) || not(l = r))
in
(* assume that a threshold of 0 means the user did not set it
* thus, set it to max instead
*)
if !threshold = 0
then threshold := List.length term_pairs;
Diff.no_occurs := !threshold;
print_endline ("[Main] Constructing all safe parts for " ^
string_of_int (List.length term_pairs) ^ " term pairs");
let filtered_patches =
(* if !do_dmine *)
(* then do_datamining abs_patches *)
(* else get_all_safe term_pairs abs_patches *)
Diff.find_simple_updates_merge_changeset term_pairs
+> (fun x ->
begin
print_string "[Main] find_simple_updates_merge return this many updates: ";
x +> List.length +> string_of_int +> print_endline;
if !Jconfig.print_abs
then begin
x +> List.iter (fun tu ->
tu
+> Diff.string_of_diff
+> print_endline);
end;
x
end
)
+> (fun x ->
print_endline "[Main] filtering all safe patches.";
x)
+> List.filter (fun tu ->
term_pairs +>
for_some !threshold
(Diff.safe_part tu)
)
+> (fun tus -> begin
print_string "[Main] after filtering we have this many updates: ";
tus +> List.length +> string_of_int +> print_endline;
tus
end
)
+> List.rev_map Diff.renumber_metas_up
+> Diff.rm_dub
in
let stripped_patches =
if !strip_eq
then strip term_pairs filtered_patches
else filtered_patches
in
if !print_raw
then (
print_endline "Raw list of simple updates";
print_endline "{{{";
List.iter (fun d ->
print_endline (Diff.string_of_diff d);
print_endline " ++ ";
) stripped_patches;
print_endline "}}}"
);
print_endline ("[Main] generating solutions from "
^ string_of_int (List.length stripped_patches)
^ " inputs");
let solutions = generate_sols term_pairs stripped_patches in
print_sols solutions
let spec_main_term_pairs term_pairs =
Diff.abs_depth := !depth;
Diff.abs_subterms := !max_level;
(* assume that a threshold of 0 means the user did not set it
* thus, set it to max instead
*)
if !threshold = 0
then threshold := List.length term_pairs;
Diff.no_occurs := !threshold;
print_endline ("[Main] Constructing all safe parts for " ^
string_of_int (List.length term_pairs) ^ " term pairs");
let filtered_patches =
(* if !do_dmine *)
(* then do_datamining abs_patches *)
(* else get_all_safe term_pairs abs_patches *)
Diff.find_simple_updates_merge_changeset term_pairs
+> (fun x ->
begin
print_string "[Main] find_simple_updates_merge return this many updates: ";
x +> List.length +> string_of_int +> print_endline;
if !Jconfig.print_abs
then begin
x +> List.iter (fun tu ->
tu
+> Diff.string_of_diff
+> print_endline);
end;
x
end
)
+> (fun x ->
print_endline "[Main] filtering all safe patches.";
x)
+> List.filter (fun tu ->
term_pairs +>
for_some !threshold
(Diff.safe_part tu)
)
+> (fun tus -> begin
print_string "[Main] after filtering we have this many updates: ";
tus +> List.length +> string_of_int +> print_endline;
tus
end
)
+> List.rev_map Diff.renumber_metas_up
in
if !print_raw
then (
print_endline "Raw list of simple updates";
print_endline "{{{";
List.iter (fun d ->
print_endline (Diff.string_of_diff d);
print_endline " ++ ";
) filtered_patches;
print_endline "}}}"
);
print_endline "[Main] generating solutions...";
let stripped_patches =
if !strip_eq
then strip term_pairs filtered_patches
else filtered_patches
in
let solutions = generate_sols term_pairs stripped_patches in
print_sols solutions
(* ---------------------------------------------------------- *
* imported from graph.ml *
* *
* In the end, this could do well with being put in its own *
* module. *
* ---------------------------------------------------------- *)
let meta_counter = ref 0
let reset_meta () = meta_counter := 0
let inc_meta () = meta_counter := !meta_counter + 1
let new_meta_id () =
let v = !meta_counter in (
inc_meta();
let mid = "X" ^ string_of_int v in
mkA("meta", mid), mid
)
let new_meta () =
let v = !meta_counter in (
inc_meta();
mkA("meta", "X" ^ string_of_int v)
)
let nodes_of_graph g =
g#nodes#tolist +> List.rev_map fst
let nodes_of_graph2 g =
g#nodes#tolist
+> List.filter (fun (i,gt) -> Diff.non_phony gt && not(Diff.is_control_node gt))
+> List.rev_map fst
let renumber_metas t metas =
match view t with
| A("meta", mvar) -> (try
let v = List.assoc mvar metas in
mkA ("meta", v), metas
with _ ->
let nm, mid = new_meta_id () in
nm, (mvar, mid) :: metas)
| _ -> t, metas
(* generic folding on gterms; produces new term and some accumulated result
* which is useful when one wants to modify a gterm and return some env
* computed as part of the transformation
*)
let fold_botup term upfun initial_result =
let rec loop t acc_result =
match view t with
| A _ -> upfun t acc_result
| C (ct, ts) ->
let new_terms, new_acc_result = List.fold_left
(fun (ts, acc_res) t ->
let new_t, new_acc = loop t acc_res in
new_t :: ts, new_acc
) ([], acc_result) ts
in
upfun (mkC(ct, List.rev new_terms)) new_acc_result
in
loop term initial_result
let string_of_pattern p =
let loc p = match view p with
| C("CM", [t]) ->
if !Jconfig.verbose
then Diff.verbose_string_of_gtree t
else Diff.string_of_gtree' t
| skip when skip == view ddd -> "..."
| gt -> raise (Match_failure (Diff.string_of_gtree' p, 604,0)) in
(*
let meta_strings = List.rev_map Diff.collect_metas p +> tail_flatten in
let head = "@@\n" ^
String.concat "\n" meta_strings ^
"\n@@\n"
in
*)
(* head ^ *)
String.concat "\n" (List.map loc p)
let renumber_metas_pattern pat =
let old_meta = !meta_counter in
reset_meta ();
let loop (acc_pat, env) p =
match view p with
| C("CM", [p]) ->
let p', env' = fold_botup p renumber_metas env in
(mkC("CM", [p'])) :: acc_pat, env'
| skip when skip == view ddd -> ddd :: acc_pat, env
| _ -> raise (Match_failure (string_of_pattern [p], 613,0))
in
let (rev_pat, env) = List.fold_left loop ([], []) pat in
(meta_counter := old_meta; List.rev rev_pat)
let renumber_fresh_metas_pattern patterns =
let rec rename_pat p =
match view p with
| A("meta",_) -> new_meta ()
| C(ty,ts) -> mkC(ty, List.rev_map rename_pat ts +> List.rev)
| skip when skip == view ddd -> ddd
| _ -> p in
List.rev_map rename_pat patterns
let renumber_metas_gtree gt =
let old_meta = !meta_counter in
reset_meta ();
let gt', env = fold_botup gt renumber_metas [] in
meta_counter := old_meta; gt'
let renumber_metas_gtree_env env gt =
let old_meta = !meta_counter in
reset_meta ();
let gt', env = fold_botup gt renumber_metas env in
meta_counter := old_meta;
List.iter (fun (m1,m2) -> print_string (" ;" ^ m1 ^ "->" ^ m2)) env;
print_newline ();
gt
let rev_assoc e a_list =
mkA("meta", fst (List.find (fun (k,v) -> v == e) a_list))
(* given element "a" and lists, prefix all lists with "a" *)
let prefix a lists =
List.rev_map (fun l -> a :: l) lists +> List.rev
(* tail recursive append that preserves ordering *)
let (@@@) l1 l2 = List.rev l1 +> List.fold_left
(fun acc_l e1 -> e1 :: acc_l) l2
(* given list "l" and lists "ls" prefix each element of list "l" to each list in
* "ls";
*)
let prefix_all l ls =
l +> List.fold_left (fun acc_prefix e ->
prefix e ls @@@ acc_prefix
) []
(* given a list of lists, produce all permutations where one
* takes one element from each list in the order given
*)
let rec gen_perms lists =
match lists with
| [] -> [[]]
| l :: ls -> prefix_all l (gen_perms ls)
let is_meta m = match view m with
| A("meta", _) -> true
| _ -> false
let is_match m = match view m with
| C("CM", [p]) -> true
| _ -> false
let get_metas_single p =
let rec loop acc_metas p =
if is_meta p then p +++ acc_metas
else match view p with
| A _ -> acc_metas
| C (_,ts) -> List.fold_left (fun acc_metas p -> loop acc_metas p) acc_metas ts
in
loop [] p
let get_metas_pattern ps =
List.fold_left (fun acc_metas p ->
List.fold_left (fun acc_metas m -> m +++ acc_metas) acc_metas (get_metas_single p)
) [] ps
let intersect_lists l1 l2 =
List.filter (fun e1 -> List.mem e1 l2) l1
let rec is_subset_list eq_f l1 l2 =
l1 = [] ||
match l1, l2 with
| x :: xs, y :: ys when eq_f x y -> is_subset_list eq_f xs ys
| _, _ -> false
let num_metas p =
List.length (get_metas_single p)
let rec num_subterms p =
match view p with
| A _ -> 1
| C (_, ts) -> List.fold_left (fun acc_sum p -> num_subterms p + acc_sum) 1 ts
(* a higher value allows a term means the term is more abstract and a
lower means it is less abstract; "number of meta per subterm"
f(X) = 1/3
f(sizeof(X)) = 1/5
thus, the latter is less abstract than the former
*)
let abstractness p =
let mv = num_metas p in
let st = num_subterms p in
float mv /. float st
let infeasible p = Diff.infeasible p
let (=>) = Diff.(=>)
let cont_match = Diff.cont_match_new
(* let cont_match = Diff.cont_match *)
(* we have a focus_fun iff !focus_function != "" *)
let haveFocusFun () = String.compare !focus_function "" != 0
let get_fun_name_gflow f =
let head_node =
f#nodes#tolist
+> List.find (fun (i,n) -> match view n with
| C("head:def", [{Hashcons.node=C("def",_)}]) -> true
| _ -> false) in
match view (snd head_node) with
| C("head:def",[{Hashcons.node=C("def",name::_)}]) ->
(match view name with
| A("fname",name_str) -> name_str
| _ -> raise (Diff.Fail "impossible match get_fun_name_gflow")
)
| _ -> raise (Diff.Fail "get_fun_name?")
let sameName other_fun =
if String.compare !focus_function other_fun = 0
then begin
v_print_endline ("ff: " ^ !focus_function ^ " == " ^ other_fun);
true
end
else begin
v_print_endline ("ff: " ^ !focus_function ^ " != " ^ other_fun);
false
end
let get_focus_function gss =
List.hd (
List.find (function
| [g] -> get_fun_name_gflow g = !focus_function
| _ -> raise (Impossible 120)
) gss
)
let get_focus_function' gss =
List.find (fun g -> get_fun_name_gflow g = !focus_function) gss
let exists_cont_match g p = nodes_of_graph g +> List.exists (cont_match g p)
let (|-) g p = exists_cont_match g p
let exists_cont_match_graphs gss p =
if haveFocusFun ()
then
let focus_flow = get_focus_function' gss in
if focus_flow |- p
then for_some !threshold (fun g -> exists_cont_match g p) gss
else false
else for_some !threshold (fun g -> exists_cont_match g p) gss
(*
let g_glob = ref (None : (Diff.gflow * 'a) option )
*)
let get_indices t g =
g#nodes#tolist +> List.fold_left (fun acc_i (i,t') -> if t' == t then i :: acc_i else acc_i) []
let abs_ht = Diff.TT.create 591
let misses = ref 0
let common_num = ref 0
let common_calls = ref 0
let rec abstract_term_hashed depth t =
try
Diff.TT.find abs_ht t
with Not_found -> (
let res = abstract_term depth [] t in
misses := !misses + 1;
Diff.TT.replace abs_ht t res;
res
)
and abstract_term depth env t =
let rec rm_dups xs = List.fold_left (fun acc_xs x -> x +++ acc_xs) [] xs in
(*
let follow_abs t_sub =
match !g_glob with
| None -> false
| Some (g, pa) ->
let idxs = pa +> List.fold_left (fun acc_i t' ->
if Diff.can_match t t'
then get_indices t' g +>
List.fold_left (fun acc_i i -> i +++ acc_i) acc_i
else acc_i) [] in
(* among the nodes, where the top-level term t occurs is there some
* location where in all the following paths eventually the subterm
* occurs?
*)
List.exists (fun i -> Diff.find_embedded_succ g i t_sub) idxs
(* if List.exists (fun i -> Diff.find_embedded_succ g i t_sub) idxs
then (print_endline ("[Main] follow_abs: " ^ (Diff.string_of_gtree' t_sub)); true) else false *)
in
*)
let rec loop depth env t =
try [rev_assoc t env], env
with Not_found ->
let meta, id = new_meta_id ()
in
if depth = 0 (* || follow_abs t *)
then [meta], (id, t) => env
else
if is_meta t
then [t],env
else
(match view t with
| A _ -> (* always abstract atoms *)
[meta], (id, t) => env
| C("storage", _) -> [t], env
| C("call", f :: ts) ->
(* generate abstract versions for each t ∈ ts *)
let meta_lists, env_acc =
List.fold_left (fun (acc_lists, env') t ->
let meta', env'' = loop (depth - 1) env' t
in (meta' :: acc_lists), env''
) ([], env) ts in
(* generate all permutations of the list of lists *)
let meta_perm = gen_perms (List.rev meta_lists) in
(* construct new term from lists *)
t :: List.rev (List.fold_left (fun acc_meta meta_list ->
mkC("call", f :: meta_list) :: acc_meta
) [] meta_perm), env_acc
| C(ty, ts) ->
(* generate abstract versions for each t ∈ ts *)
let meta_lists, env_acc =
List.fold_left (fun (acc_lists, env') t ->
let meta', env'' = loop (depth - 1) env' t
in (meta' :: acc_lists), env''
) ([], env) ts in
(* generate all permutations of the list of lists *)
let meta_perm = gen_perms (List.rev meta_lists) in
(* construct new term from lists *)
t :: List.rev (List.fold_left (fun acc_meta meta_list ->
mkC(ty, meta_list) :: acc_meta
) [] meta_perm), env_acc
(*
| _ -> [meta;t], (id, t) => env
*)
)
in
let metas, env = loop depth env t in
List.filter (fun p -> not(infeasible p)) (rm_dups metas), env
let print_patterns gss ps =
List.iter (fun p ->
print_endline "[[[";
print_endline (string_of_pattern p);
print_endline "]]]";
(* show supporting functions *)
List.iter (
function
| [f] -> if exists_cont_match f p
then print_string (" " ^ Diff.get_fun_name_gflow f)
| _ -> ();
) gss;
print_newline ();
) ps
(* let non_phony p = match view p with *)
(* | A("phony", _) *)
(* | C("phony", _) *)
(* | A(_, "N/H") *)