package jingoo

  1. Overview
  2. Docs

Source file jg_ast_optimize.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
open Jg_types
open Jg_ast_mapper

(** [dead_code_elimination ast] perform a dead code elimination on [ast].
    It is able to remove from ast:
    - macro and function definitions not being actually used in ast
*)
let dead_code_elimination stmts =
  let used = Hashtbl.create 512 in
  let local_variables : (string * string list) list ref = ref [("", [])] in
  let push_block name = local_variables := (name, []) :: !local_variables in
  let pop_block () = local_variables := List.tl !local_variables in
  let set_local x =
    let fst, snd = List.hd !local_variables in
    local_variables := (fst, x :: snd ) :: (List.tl !local_variables) in
  let is_local (x : string) = List.exists (fun (_, l) -> List.mem x l) !local_variables in
  let scope () =
    let rec loop = function
      | [] -> ""
      | ("", _) :: tl -> loop tl
      | (s, _) :: _ -> s in
    loop !local_variables in
  let rec maybe_set = function
    | SetExpr set -> List.iter maybe_set set
    | IdentExpr id -> set_local id
    | _ -> () in
  let statement self = function
    | SetStatement (id, _) as s ->
      maybe_set id ;
      default_mapper.statement self s
    | ForStatement (ids, _, _) as s ->
      push_block "" ;
      List.iter set_local ids ;
      let s = default_mapper.statement self s in
      pop_block () ;
      s
    | FunctionStatement (id, args, _)
    | MacroStatement (id, args, _) as s ->
      push_block id ;
      set_local id ;
      List.iter (fun (i, _) -> set_local i) args ;
      let s = default_mapper.statement self s in
      pop_block () ;
      s
    | CallStatement(id, args, _, _) as s ->
      Hashtbl.add used id (scope ()) ;
      push_block "" ;
      List.iter (fun (i, _) -> set_local i) args ;
      let s = default_mapper.statement self s in
      pop_block () ;
      s
    | TextStatement (_)
    | ExpandStatement (_)
    | IfStatement (_)
    | SwitchStatement (_, _)
    | IncludeStatement (_, _)
    | RawIncludeStatement _
    | ExtendsStatement _
    | ImportStatement (_, _)
    | FromImportStatement (_, _)
    | BlockStatement (_, _)
    | FilterStatement (_, _)
    | WithStatement (_, _)
    | AutoEscapeStatement (_, _)
    | NamespaceStatement (_, _)
    | Statements (_)
      as s -> default_mapper.statement self s
  in
  let expression self = function
    | IdentExpr name as x when not (is_local name) ->
      Hashtbl.add used name (scope ()) ; x
    | e -> default_mapper.expression self e in
  let mapper = { default_mapper with expression ; statement } in
  let _ = mapper.ast mapper stmts in
  let statement self = function
    | MacroStatement (id, _, _)
    | FunctionStatement (id, _, _) as s ->
      (* Find if name is present in instructions called from toplevel *)
      let rec loop n lists =
        if List.mem "" (List.hd lists) then default_mapper.statement self s
        else
          let list' =
            List.hd lists
            |> List.map (Hashtbl.find_all used)
            |> List.flatten
            |> List.sort_uniq compare in
          if List.mem list' lists then Statements []
          else loop (n+1) (list' :: lists)
      in
      loop 0 [ List.sort_uniq compare @@ Hashtbl.find_all used id ]
    | s -> default_mapper.statement self s in
  let mapper = { default_mapper with statement } in
  mapper.ast mapper stmts

(** [inline_include env ast]
    Inline the templates included in [ast] so it won't be necessary to
    open and parse theses parts when execution [ast].
*)
let inline_include env stmts =
  let open Jg_ast_mapper in
  let statement self = function
    | IncludeStatement (LiteralExpr (Tstr file), true) ->
      Statements (self.ast self @@ Jg_interp.ast_from_file ~env file)
    | RawIncludeStatement (LiteralExpr (Tstr file)) ->
      Statements (self.ast self @@ Jg_interp.ast_from_file ~env file)
    | e -> default_mapper.statement self e in
  let mapper = { default_mapper with statement } in
  mapper.ast mapper stmts
OCaml

Innovation. Community. Security.