forked from jorgecasar/flow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathruleset_union.ml
151 lines (135 loc) · 5.2 KB
/
ruleset_union.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
(**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
module S = Flow_ast.Statement;;
module E = Flow_ast.Expression;;
module T = Flow_ast.Type;;
module P = Flow_ast.Pattern;;
module Utils = Flowtestgen_utils;;
(* ESSENTIAL: Syntax type and related functions *)
module Syntax = Syntax_base;;
open Ruleset_base;;
class ruleset_union = object(self)
inherit Ruleset_base.ruleset_base
method! get_name () : string = "union"
method! weak_assert b = self#backtrack_on_false b
(* check t1 <: t2 *)
method! is_subtype (t1 : (Loc.t, Loc.t) T.t') (t2 : (Loc.t, Loc.t) T.t') : bool =
match t1, t2 with
| (t, T.Union ((_, tu1), (_, tu2), tlist)) -> (* t should be one of the branches of Union *)
List.mem t (tu1 :: tu2 :: (List.map snd tlist))
| T.Object o1, T.Object o2 -> self#is_subtype_obj o1 o2
| T.Function f1, T.Function f2 -> self#is_subtype_func f1 f2
| _ when t1 = t2 -> true
| _ -> false
(* Using a loose form of subtyping from ruleset_depth, so we can allow
passing { p : 3 } to a function that expects { p : number | string }.
In general this is unsound, and Flow allows this only in certain
situations.
*)
method! is_subtype_obj (o1 : (Loc.t, Loc.t) T.Object.t) (o2 : (Loc.t, Loc.t) T.Object.t) =
let get_prop_set (o : (Loc.t, Loc.t) T.Object.t) =
let tbl = Hashtbl.create 1000 in
let open T.Object.Property in
List.iter (fun p -> match p with
| T.Object.Property (_, {key = E.Object.Property.Identifier (_, name);
value = Init (_, t);
optional = _;
static = _;
proto = _;
_method = _;
variance = _;}) -> Hashtbl.add tbl name t
| _ -> ()) T.Object.(o.properties);
tbl in
let s1 = get_prop_set o1 in
let s2 = get_prop_set o2 in
let subtype = ref true in
(* check non optional properties *)
Hashtbl.iter (fun n t ->
(* Shouldn't use call is_subtyping recursivingly. We should
use equality to limit depth subtyping *)
if (not (Hashtbl.mem s1 n)) || (not (self#is_subtype (Hashtbl.find s1 n) t)) then
subtype := false) s2;
!subtype
method! rule_func_call (env : env_t) : (Syntax.t * env_t) =
(* require a function from the environment.*)
let func = self#choose 0 (fun () -> self#require_expr env) in
let func_expr, func_type = match func with
| Expr (e, t) -> e, t
| _ -> failwith "This has to be an expression" in
self#backtrack_on_false (match func_type with
| T.Function _ -> true
| _ -> false);
(* get the type of the parameter assuming we only have one param *)
let f_ptype =
match func_type with
| T.Function ft ->
let ft_param = T.Function.(ft.params) |> snd in
let params = T.Function.Params.(ft_param.params) |> List.hd |> snd in
T.Function.Param.(params.annot)
| _ -> failwith "This has to a function type" in
(* parameter *)
let param = self#choose 1 (fun () -> self#require_expr env) in
let param_expr, param_type = match param with
| Expr (e, t) -> e, t
| _ -> failwith "This has to be an expression" in
self#backtrack_on_false (match param_expr with
| E.Identifier _ -> true
| _ -> false);
self#weak_assert (self#is_subtype param_type (snd f_ptype));
let func_call = Syntax.mk_func_call func_expr param_expr in
let ret_type = T.Function.(match func_type with
| T.Function {params = _;
return = (_, rt);
tparams =_} -> rt
| _ -> failwith "This has to be a function type") in
let new_env =
self#add_binding
env
(Expr ((match func_call with
| Syntax.Expr e -> e
| _ -> failwith "This has to be an expression"),
ret_type)) in
let new_env = self#add_binding new_env (Type ret_type) in
func_call, new_env
method! rule_obj_type (prop_num : int) (opt_num : int) (env : env_t) : (Syntax.t * env_t) =
let ret_type = self#gen_obj_type
prop_num
opt_num
env
~cons:(fun elt ->
(match elt with
| Type (T.Union _) -> true
| _ -> false)) in
let new_env =
self#add_binding env (Type ret_type) in
Syntax.Empty, new_env
method! get_all_rules () =
[|self#rule_num_lit;
self#rule_str_lit;
self#rule_obj_lit 1 0;
self#rule_vardecl;
self#rule_union_type 2;
self#rule_obj_type 1 0;
self#rule_func_mutate;
self#rule_func_call;
self#rule_runtime_check;
(*
self#rule_vardecl_with_type; (*make it challenging*)
self#rule_prop_update;
self#rule_vardecl_with_type;
self#rule_prop_update;
self#rule_func_mutate;
self#rule_func_call;
self#rule_prop_read;
*)
|]
end
class ruleset_random_union = object
inherit ruleset_union
method! weak_assert b =
if (not b) && ((Random.int 5) > 0) then raise Engine.Backtrack
end